Beispiel #1
0
 def test_undefined_state_is_invalid_if_no_initial_state(self):
     fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}})
     with self.assertRaises(InvalidStateError) as expected:
         fsm.change_to('FEN')
     self.assertEqual(
         "Cannot change state. 'FEN' is not a valid state.",
         str(expected.exception)
     )
Beispiel #2
0
 def test_transition_state_is_not_valid(self):
     with self.assertRaises(InconsistentModelError) as expected:
         self.fsm = SimpleFSM({
             'transitions': {
                 'defined': {'created'},
             }
         })
     self.assertEqual(
         "Transition definition references non-existent state: 'created'.",
         str(expected.exception)
     )
Beispiel #3
0
 def setUp(self):
     self.fsm = SimpleFSM({
         'initial': 'defined',
         'transitions': {
             'defined': {'created'},
             'created': {'configured'},
             'configured': {'active'},
             'active': {'destroyed', 'error'},
             'destroyed': {},
             'error': {'configured', 'active', 'destroyed'},
         }
     })
Beispiel #4
0
 def __init__(self, serial_instance):
     self._serial = serial_instance
     self._sm = SimpleFSM({
         'initial': 'idle',
         'transitions': {
             'idle': {'sending'},
             'sending': {'wait_response', 'success'},
             'wait_response': {'resending', 'check_response'},
             'check_response': {'resending', 'wait_response', 'success'},
             'resending': {'wait_response', 'failure'},
             'success': {'idle', 'sending'},
             'failure': {'idle', 'sending'},
         }
     })
     self.clear()
Beispiel #5
0
 def test_any_defined_state_is_valid_if_no_initial_state(self):
     fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}})
     fsm.change_to('YANG')
     self.assertEqual('YANG', fsm.current)
Beispiel #6
0
class TestSimpleFSM(unittest.TestCase):
    def setUp(self):
        self.fsm = SimpleFSM({
            'initial': 'defined',
            'transitions': {
                'defined': {'created'},
                'created': {'configured'},
                'configured': {'active'},
                'active': {'destroyed', 'error'},
                'destroyed': {},
                'error': {'configured', 'active', 'destroyed'},
            }
        })

    def test_empty_model_raises_exception(self):
        with self.assertRaises(ValueError) as expected:
            # pylint: disable=W0612
            SimpleFSM({})
        self.assertEqual(
            'The passed-in state model cannot by empty or None.',
            str(expected.exception)
        )

    def test_none_model_raises_exception(self):
        with self.assertRaises(ValueError) as expected:
            # pylint: disable=W0612
            SimpleFSM(None)
        self.assertEqual(
            'The passed-in state model cannot by empty or None.',
            str(expected.exception)
        )

    def test_fsm_with_no_initial_state(self):
        fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}})
        self.assertEqual(None, fsm.current)

    def test_any_defined_state_is_valid_if_no_initial_state(self):
        fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}})
        fsm.change_to('YANG')
        self.assertEqual('YANG', fsm.current)

    def test_undefined_state_is_invalid_if_no_initial_state(self):
        fsm = SimpleFSM({'transitions': {'YIN': {'YANG'}, 'YANG': {}}})
        with self.assertRaises(InvalidStateError) as expected:
            fsm.change_to('FEN')
        self.assertEqual(
            "Cannot change state. 'FEN' is not a valid state.",
            str(expected.exception)
        )

    def test_initial_state_defined(self):
        self.assertEqual('defined', self.fsm.current)

    def test_no_match_in_state_model_for_initial_state_raises_exception(self):
        with self.assertRaises(InconsistentModelError) as expected:
            # pylint: disable=W0612
            SimpleFSM({
                'initial': 'oranges',
                'transitions': {
                    'apples': {'pomegranates', 'ugli fruit'},
                    'pomegranates': {'apples'},
                }
            })
        self.assertEqual(
            "Initial state 'oranges' does not match any valid state.",
            str(expected.exception)
        )

    def test_check_for_a_valid_state_change(self):
        self.assertTrue(self.fsm.permitted('created'))
        self.assertFalse(self.fsm.restricted('created'))

    def test_check_for_an_invalid_state_change(self):
        self.fsm.change_to('created')
        self.assertTrue(self.fsm.restricted('active'))
        self.assertFalse(self.fsm.permitted('active'))

    def test_change_to_a_valid_state(self):
        self.fsm.change_to('created')
        self.assertEqual('created', self.fsm.current)

    def test_attempt_to_change_to_invalid_state_raises_exception(self):
        with self.assertRaises(InvalidStateError) as expected:
            self.fsm.change_to('configured')
        self.assertEqual(
            "Attempted change to state 'configured' is not permitted.",
            str(expected.exception)
        )

    def test_force_change_with_no_attempted_change_to_raises_exception(self):
        with self.assertRaises(MethodNotAllowedError) as expected:
            self.fsm.force_change_to('active')
        self.assertEqual(
            "'force_change_to' can only be called after a "
            "failed call to 'change_to' for the same state.",
            str(expected.exception)
        )

    def test_force_change_to_allowed_after_attempted_change_to(self):
        try:
            self.fsm.change_to('active')
        except InvalidStateError:
            self.fsm.force_change_to('active')
            self.assertEqual('active', self.fsm.current)

    def test_future_state_is_attainable(self):
        self.assertTrue(self.fsm.reachable('destroyed'))

    def test_future_state_is_not_attainable(self):
        self.fsm.change_to('created')
        self.assertFalse(self.fsm.reachable('defined'))

    def test_transition_state_is_not_valid(self):
        with self.assertRaises(InconsistentModelError) as expected:
            self.fsm = SimpleFSM({
                'transitions': {
                    'defined': {'created'},
                }
            })
        self.assertEqual(
            "Transition definition references non-existent state: 'created'.",
            str(expected.exception)
        )

    def test_multiple_transition_states_not_valid(self):
        with self.assertRaises(InconsistentModelError) as expected:
            self.fsm = SimpleFSM({
                'transitions': {
                    'defined': {'created'},
                    'active': {'deleted'},
                }
            })
        self.assertEqual(
            "Transition definition references non-existent "
            "states: 'created', 'deleted'.",
            str(expected.exception)
        )
Beispiel #7
0
class Tx():

    def __init__(self, serial_instance):
        self._serial = serial_instance
        self._sm = SimpleFSM({
            'initial': 'idle',
            'transitions': {
                'idle': {'sending'},
                'sending': {'wait_response', 'success'},
                'wait_response': {'resending', 'check_response'},
                'check_response': {'resending', 'wait_response', 'success'},
                'resending': {'wait_response', 'failure'},
                'success': {'idle', 'sending'},
                'failure': {'idle', 'sending'},
            }
        })
        self.clear()

    def clear(self):
        self.busy = False
        self.resp = None
        self._ba = []
        try:
            if self._sm.current is not 'idle':
                self._sm.change_to('idle')
        except exceptions.InvalidStateError:
            self._sm.force_change_to('idle')

    def send(self, packet):
        if self.busy:
            return False
        self._sm.change_to('sending')
        command, payload = packet
        p = Protocol()
        self._ba = bytearray(p.build(command, payload))
        self._serial.write(self._ba)
        if AAE_COMMAND[command][1] is None:
            self._sm.change_to('success')
            return True

        self.tx_cmd = command
        self._sm.change_to('wait_response')
        self.start = time.clock()
        self.resend_cnt = 0
        self.busy = True # not allow another command
        return True

    def get_response(self, command, resp):
        if self._sm.current is not 'wait_response':
            return
        self.rx_cmd, self.resp = command, resp
        if (debug):
            print('Tx.get_response', command, self.resp),
        self._sm.change_to('check_response')

    def exec_(self):
        if not self.busy:
            return

        #First Decision Table
        if self._sm.current is 'wait_response':
            t1 = time.clock() - self.start
            if (t1 > 2):
                self._sm.change_to('resending')
                print('sm', self._sm.current),
        elif self._sm.current is 'check_response':
            if self.rx_cmd is not self.tx_cmd:
                self._sm.change_to('wait_response')
                return
            if self.resp is not None:
                self._sm.change_to('success')
            else:
                self._sm.change_to('wait_response')
        elif self._sm.current is 'success' or self._sm.current is 'failure':
            if self.resp is None:
                pass
                #print('judge', self._sm.current, self.tx_cmd)
            elif isinstance(self.resp, bool) or isinstance(self.resp, int):
                pass
                #print('judge', self._sm.current, self.resp)
            else: # Should be list or tuple
                if len(self.resp) < 1:
                    resp = ''
                elif isinstance(self.resp[0], int):
                    resp = ''.join('{0:02x}'.format(b) for b in self.resp)
                else: # Should be list or tuple in sub-item
                    resp = ''
                    for lst in self.resp:
                        resp += ''.join('{0:02x}'.format(b) for b in lst)
                        resp += '|'
                        #print('judge', self._sm.current, self.tx_cmd, resp)

            self.last_result = self._sm.current
            self._sm.change_to('idle')
            self.busy = False


        # Second Decision Table (have to do immediately)
        if self._sm.current is 'resending':
            if self.resend_cnt > 0:
                self._sm.change_to('failure')
            else:
                self._serial.write(self._ba)
                self.resend_cnt += 1
                self._sm.change_to('wait_response')

    last_result = 'success'