1c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang# -*- coding: utf-8 -*-
2c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
3c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang# Use of this source code is governed by a BSD-style license that can be
5c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang# found in the LICENSE file.
6c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
7c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang"""Guide the user to perform gestures. Record and validate the gestures."""
8c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
92fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwangimport fcntl
10d9b03cc8ece225087e203658ded151005eea0456Joseph Hwangimport glob
11c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport os
12c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport subprocess
13c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport sys
145a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang
15c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport common_util
167981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwangimport firmware_log
17c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport firmware_utils
18c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport fuzzy
19c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport mini_color
2069227234dabb3942f25a70004fd9347308c56a32Joseph Hwangimport mtb
21c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooneyimport touchbotII_robot_wrapper as robot_wrapper
22c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport test_conf as conf
23c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport validators
24c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
2501273ed49c5f0726b14412d6701a01c74315254aJoseph Hwangfrom firmware_utils import GestureList
2601273ed49c5f0726b14412d6701a01c74315254aJoseph Hwang
27c113079e5ee877c4e9f311653d18daf576926b37Joseph Hwangsys.path.append('../../bin/input')
28c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangimport input_device
29c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
303fb591eb36e473794ba56b633f6690f685fff6c6Joseph Hwang# Include some constants
31b0612e11b87281c1ab8ab95d3c47de2601a9df26Mindy Huangfrom firmware_constants import DEV, GV, MODE, OPTIONS, TFK
325a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang
335a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang
34c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwangclass TestFlow:
35c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    """Guide the user to perform gestures. Record and validate the gestures."""
36c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
37ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang    def __init__(self, device_geometry, device, keyboard, win, parser, output,
38bc9dba0f212df1b31ed43032c68f5e07e9ee9974Joseph Hwang                 test_version, board, firmware_version, options):
39c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.device_geometry = device_geometry
40c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.device = device
41c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.device_node = self.device.device_node
42ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        self.keyboard = keyboard
4340cb76895b4a52d85d5d2c1a25b45e49bdeb9828Joseph Hwang        self.firmware_version = firmware_version
44c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output = output
45bc9dba0f212df1b31ed43032c68f5e07e9ee9974Joseph Hwang        self.board = board
46bc9dba0f212df1b31ed43032c68f5e07e9ee9974Joseph Hwang        self.test_version = test_version
47bc9dba0f212df1b31ed43032c68f5e07e9ee9974Joseph Hwang        self.output.print_report('%s' % test_version)
48c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self._get_record_cmd()
49c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.win = win
50c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.parser = parser
51c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.packets = None
52c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_file_name = None
53c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.prefix_space = self.output.get_prefix_space()
54c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.scores = []
5534f7784f607ce1bd2916f5db6cf95a9802780a6dJoseph Hwang        self.mode = options[OPTIONS.MODE]
5642bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney        self.fngenerator_only = options[OPTIONS.FNGENERATOR]
5708eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang        self.iterations = options[OPTIONS.ITERATIONS]
58d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        self.replay_dir = options[OPTIONS.REPLAY]
59ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        self.resume_dir = options[OPTIONS.RESUME]
603bad937cb81c5c22183024c7bc67575cd099b051Joseph Hwang        self.recording = not any([bool(self.replay_dir), bool(self.resume_dir)])
614a9648f9d60fecffadb3c629222b9486bd198080Joseph Hwang        self.device_type = (DEV.TOUCHSCREEN if options[OPTIONS.TOUCHSCREEN]
624a9648f9d60fecffadb3c629222b9486bd198080Joseph Hwang                                            else DEV.TOUCHPAD)
6342bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
6442bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney        self.robot = robot_wrapper.RobotWrapper(self.board, options)
6542bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney        self.robot_waiting = False
6642bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
6708eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang        self.gv_count = float('infinity')
6801273ed49c5f0726b14412d6701a01c74315254aJoseph Hwang        gesture_names = self._get_gesture_names()
69dee21b175441da05e032ece48d774b4107569c07Charlie Mooney        order = None
70dee21b175441da05e032ece48d774b4107569c07Charlie Mooney        if self._is_robot_mode():
71dee21b175441da05e032ece48d774b4107569c07Charlie Mooney            order = lambda x: conf.finger_tips_required[x.name]
72dee21b175441da05e032ece48d774b4107569c07Charlie Mooney        self.gesture_list = GestureList(gesture_names).get_gesture_list(order)
7334f7784f607ce1bd2916f5db6cf95a9802780a6dJoseph Hwang        self._get_all_gesture_variations(options[OPTIONS.SIMPLIFIED])
7442bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
75c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.init_flag = False
762fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        self.system_device = self._non_blocking_open(self.device_node)
77c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.evdev_device = input_device.InputEvent()
78c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.screen_shot = firmware_utils.ScreenShot(self.geometry_str)
793f245993196c7cead8716ff09633a6ad49e8b006Joseph Hwang        self.mtb_evemu = mtb.MtbEvemu(device)
8042bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
81d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        self._rename_old_log_and_html_files()
8221df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        self._set_static_prompt_messages()
8309890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang        self.gesture_image_name = None
8409890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang        self.gesture_continues_flag = False
8579009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang        self.use_existent_event_file_flag = False
86c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
87c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def __del__(self):
88c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.system_device.close()
89c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
90d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang    def _rename_old_log_and_html_files(self):
91ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        """When in replay or resume mode, rename the old log and html files."""
92ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        if self.replay_dir or self.resume_dir:
93d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            for file_type in ['*.log', '*.html']:
94d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                path_names = os.path.join(self.output.log_dir, file_type)
95d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                for old_path_name in glob.glob(path_names):
96d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                    new_path_name = '.'.join([old_path_name, 'old'])
97d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                    os.rename(old_path_name, new_path_name)
98d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
995a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang    def _is_robot_mode(self):
10042bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney        return self.robot.is_robot_action_mode() or self.mode == MODE.ROBOT_SIM
1015a61f5a19dfde799a3ca407e761f400184ae5fddJoseph Hwang
10201273ed49c5f0726b14412d6701a01c74315254aJoseph Hwang    def _get_gesture_names(self):
10301273ed49c5f0726b14412d6701a01c74315254aJoseph Hwang        """Determine the gesture names based on the mode."""
1042cbb23bb88733c3e0a40f7d17681ed957b54ce27Charlie Mooney        if self.mode == MODE.QUICKSTEP:
105b0612e11b87281c1ab8ab95d3c47de2601a9df26Mindy Huang            return conf.gesture_names_quickstep
106b0612e11b87281c1ab8ab95d3c47de2601a9df26Mindy Huang        elif self.mode == MODE.NOISE:
107b0612e11b87281c1ab8ab95d3c47de2601a9df26Mindy Huang            return conf.gesture_names_noise_extended
1082cbb23bb88733c3e0a40f7d17681ed957b54ce27Charlie Mooney        elif self._is_robot_mode():
109c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooney            # The mode could be MODE.ROBOT or MODE.ROBOT_SIM.
110c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooney            # The same gesture names list is used in both modes.
111c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooney            return conf.gesture_names_robot[self.device_type]
112e943442184657cf8fbe9c33345ce6d436a0ceb1bJoseph Hwang        elif self.mode == MODE.MANUAL:
11342bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney            # Define the manual list which is gesture_names_complete:
11442bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney            # gesture_names_robot - gesture_names_equipment_required
115ab03ed85a8ca7f99608e872d0717c53d3742d031Charlie Mooney            manual_set = (set(conf.gesture_names_complete[self.device_type]) -
116ab03ed85a8ca7f99608e872d0717c53d3742d031Charlie Mooney                          set(conf.gesture_names_robot[self.device_type]))
117ab03ed85a8ca7f99608e872d0717c53d3742d031Charlie Mooney            return list(manual_set - set(conf.gesture_names_fngenerator_required))
11842bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
119bb6f69b8c90e4896dd82e0266a096674c957f7dbJoseph Hwang        elif self.mode == MODE.CALIBRATION:
120bb6f69b8c90e4896dd82e0266a096674c957f7dbJoseph Hwang            return conf.gesture_names_calibration
12101273ed49c5f0726b14412d6701a01c74315254aJoseph Hwang        else:
12242bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney            # Filter out tests that need a function generator for COMPLETE mode
12342bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney            # unless they've indicated that they have one
124b0612e11b87281c1ab8ab95d3c47de2601a9df26Mindy Huang            return [n for n in conf.gesture_names_complete[self.device_type]
12542bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney                    if (self.fngenerator_only or
12642bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney                        n not in conf.gesture_names_fngenerator_required)]
12701273ed49c5f0726b14412d6701a01c74315254aJoseph Hwang
1282fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang    def _non_blocking_open(self, filename):
1292fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        """Open the file in non-blocing mode."""
1302fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        fd = open(filename)
1312fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
1322fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        return fd
1332fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang
1342fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang    def _non_blocking_read(self, dev, fd):
1352fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        """Non-blocking read on fd."""
1362fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        try:
1372fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang            dev.read(fd)
1382fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang            event = (dev.tv_sec, dev.tv_usec, dev.type, dev.code, dev.value)
1392fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        except Exception, e:
1402fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang            event = None
1412fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        return event
1422fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang
143c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _reopen_system_device(self):
144c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Close the device and open a new one."""
145c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.system_device.close()
146c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.system_device = open(self.device_node)
1472fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        self.system_device = self._non_blocking_open(self.device_node)
148c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
14921df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang    def _set_static_prompt_messages(self):
15021df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        """Set static prompt messages."""
15121df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        # Prompt for next gesture.
15221df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        self._prompt_next = (
15321df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "Press SPACE to save this file and go to next test,\n"
15421df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "      'm'   to save this file and record again,\n"
15521df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "      'd'   to delete this file and try again,\n"
15621df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "      'x'   to discard this file and exit.")
15721df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang
15821df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        # Prompt to see test result through timeout callback.
15921df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        self._prompt_result = (
16021df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "Perform the gesture now.\n"
16121df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "See the test result on the right after finger lifted.\n"
16221df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                "Or press 'x' to exit.")
16321df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang
164a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang    def _get_prompt_abnormal_gestures(self, warn_msg):
165a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        """Prompt for next gesture."""
166a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        prompt = '\n'.join(
167a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                ["It is very likely that you perform a WRONG gesture!",
168a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                 warn_msg,
169a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                 "Press 'd'   to delete this file and try again (recommended),",
170a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                 "      SPACE to save this file if you are sure it's correct,",
171a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                 "      'x'   to discard this file and exit."])
172a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        return prompt
1738ee3e33e92f4e609c9b5fd9474e6a2263e93c79dJoseph Hwang
174c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _get_prompt_no_data(self):
175c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Prompt to remind user of performing gestures."""
176c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        prompt = ("You need to perform the specified gestures "
177c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                  "before pressing SPACE.\n")
17821df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang        return prompt + self._prompt_result
179c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
180c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _get_record_cmd(self):
181c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Get the device event record command."""
18242740f2c8a15b00c69847855bf477270f4b84cf2Charlie Mooney        # Run mtplot with settings to disable clearing the display if the robot
18342740f2c8a15b00c69847855bf477270f4b84cf2Charlie Mooney        # clicks the pad, and adding a visible click indicator in the output
1843ceb6419c90576ba7df8cee03d4b42d38d1b6d00Charlie Mooney        self.record_program = 'mtplot -s1 -c0 -m0'
185c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if not common_util.program_exists(self.record_program):
186c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            msg = 'Error: the program "%s" does not exist in $PATH.'
187c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.output.print_report(msg % self.record_program)
188c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            exit(1)
189c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
190c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        display_name = firmware_utils.get_display_name()
191c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.geometry_str = '%dx%d+%d+%d' % self.device_geometry
192c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        format_str = '%s %s -d %s -g %s'
193c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.record_cmd = format_str % (self.record_program,
194c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                                        self.device_node,
195c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                                        display_name,
196c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                                        self.geometry_str)
197c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output.print_report('Record program: %s' % self.record_cmd)
198c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
199c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _span_seq(self, seq1, seq2):
200c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Span sequence seq1 over sequence seq2.
201c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
202c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        E.g., seq1 = (('a', 'b'), 'c')
203c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang              seq2 = ('1', ('2', '3'))
204c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang              res = (('a', 'b', '1'), ('a', 'b', '2', '3'),
205c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                     ('c', '1'), ('c', '2', '3'))
206c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        E.g., seq1 = ('a', 'b')
207c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang              seq2 = ('1', '2', '3')
208c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang              res  = (('a', '1'), ('a', '2'), ('a', '3'),
209c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                      ('b', '1'), ('b', '2'), ('b', '3'))
210c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        E.g., seq1 = (('a', 'b'), ('c', 'd'))
211c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang              seq2 = ('1', '2', '3')
212c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang              res  = (('a', 'b', '1'), ('a', 'b', '2'), ('a', 'b', '3'),
213c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                      ('c', 'd', '1'), ('c', 'd', '2'), ('c', 'd', '3'))
214c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """
215c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        to_list = lambda s: list(s) if isinstance(s, tuple) else [s]
216c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        return tuple(tuple(to_list(s1) + to_list(s2)) for s1 in seq1
217c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                                                      for s2 in seq2)
218c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
219c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def span_variations(self, seq):
220c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Span the variations of a gesture."""
22176fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang        if seq is None:
22276fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang            return (None,)
22376fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang        elif isinstance(seq[0], tuple):
22476fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang            return reduce(self._span_seq, seq)
22576fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang        else:
22676fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang            return seq
227c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
228c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _stop(self):
229c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Terminate the recording process."""
230c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.record_proc.poll()
231c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        # Terminate the process only when it was not terminated yet.
232c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if self.record_proc.returncode is None:
233c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.record_proc.terminate()
234c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.record_proc.wait()
235c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output.print_window('')
236c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
237c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _get_gesture_image_name(self):
238c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Get the gesture file base name without file extension."""
239c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        filepath = os.path.splitext(self.gesture_file_name)[0]
240c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_image_name = filepath + '.png'
241c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        return filepath
242c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
243d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang    def _close_gesture_file(self):
244d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        """Close the gesture file."""
245d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        if self.gesture_file.closed:
246d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang            return
247d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang
248d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        filename = self.gesture_file.name
249d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        self.gesture_file.close()
250d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang
251d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # Strip off the header of the gesture file.
252d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        #
253d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # Input driver version is 1.0.1
254d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # Input device ID: bus 0x18 vendor 0x0 product 0x0 version 0x0
255d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # Input device name: "Atmel maXTouch Touchpad"
256d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # ...
257d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # Testing ... (interrupt to exit)
258d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        # Event: time 519.855, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID),
259d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        #                                       value 884
260d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        #
261d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        tmp_filename = filename + '.tmp'
262d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        os.rename(filename, tmp_filename)
263d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        with open(tmp_filename) as src_f:
264d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang            with open(filename, 'w') as dst_f:
265d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang                for line in src_f:
266d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang                    if line.startswith('Event:'):
267d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang                        dst_f.write(line)
268d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        os.remove(tmp_filename)
269d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang
270c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _stop_record_and_post_image(self):
271c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Terminate the recording process."""
272ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        if self.record_new_file:
273d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang            self._close_gesture_file()
274d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self.screen_shot.dump_root(self._get_gesture_image_name())
275d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self.record_proc.terminate()
276d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self.record_proc.wait()
277d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        else:
278d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self._get_gesture_image_name()
279c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.win.set_image(self.gesture_image_name)
280c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
281c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _create_prompt(self, test, variation):
282c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Create a color prompt."""
283c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        prompt = test.prompt
284c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if isinstance(variation, tuple):
285c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            subprompt = reduce(lambda s1, s2: s1 + s2,
286c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                               tuple(test.subprompt[s] for s in variation))
287c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        elif variation is None or test.subprompt is None:
288c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            subprompt = None
289c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
290c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            subprompt = test.subprompt[variation]
291c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
292c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if subprompt is None:
293c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            color_prompt = prompt
29476fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang            monochrome_prompt = prompt
295c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
296c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            color_prompt = mini_color.color_string(prompt, '{', '}', 'green')
297c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            color_prompt = color_prompt.format(*subprompt)
298c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            monochrome_prompt = prompt.format(*subprompt)
299c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
300c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        color_msg_format = mini_color.color_string('\n<%s>:\n%s%s', '<', '>',
30176fe3da7a718ff2c47f9f2d793c5fc1e863aa285Joseph Hwang                                                   'blue')
302c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        color_msg = color_msg_format % (test.name, self.prefix_space,
303c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                                        color_prompt)
304c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        msg = '%s: %s' % (test.name, monochrome_prompt)
305c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
3067981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        glog = firmware_log.GestureLog()
307ecc254fc00e6b3504d4f7d87abad937cf73a267dJoseph Hwang        glog.name = test.name
308ecc254fc00e6b3504d4f7d87abad937cf73a267dJoseph Hwang        glog.variation = variation
309ecc254fc00e6b3504d4f7d87abad937cf73a267dJoseph Hwang        glog.prompt = monochrome_prompt
3107981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang
3117981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        return (msg, color_msg, glog)
312c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
313c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _choice_exit(self):
314c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Procedure to exit."""
315c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self._stop()
316c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if os.path.exists(self.gesture_file_name):
317c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            os.remove(self.gesture_file_name)
318c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.output.print_report(self.deleted_msg)
319c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
320c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _stop_record_and_rm_file(self):
321c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Stop recording process and remove the current gesture file."""
322c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self._stop()
323c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if os.path.exists(self.gesture_file_name):
324c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            os.remove(self.gesture_file_name)
325c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.output.print_report(self.deleted_msg)
326c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
327c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _create_gesture_file_name(self, gesture, variation):
328d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        """Create the gesture file name based on its variation.
329d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
330d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        Examples of different levels of file naming:
331d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            Primary name:
332d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                pinch_to_zoom.zoom_in-lumpy-fw_11.27
333d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            Root name:
334d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                pinch_to_zoom.zoom_in-lumpy-fw_11.27-manual-20130221_050510
335d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            Base name:
336d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                pinch_to_zoom.zoom_in-lumpy-fw_11.27-manual-20130221_050510.dat
337d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        """
338c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if variation is None:
339c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            gesture_name = gesture.name
340c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
341c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            if type(variation) is tuple:
342c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                name_list = [gesture.name,] + list(variation)
343c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            else:
344c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                name_list = [gesture.name, variation]
345c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            gesture_name = '.'.join(name_list)
346c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
347d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        self.primary_name = conf.filename.sep.join([
348c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                gesture_name,
349ad3136c03a931b4c1742cdec472780be15f7e1edJoseph Hwang                self.board,
35040cb76895b4a52d85d5d2c1a25b45e49bdeb9828Joseph Hwang                conf.fw_prefix + self.firmware_version])
351d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        root_name = conf.filename.sep.join([
352d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                self.primary_name,
353cea259e9b1f95c10892179ea8594e2be73438858Joseph Hwang                self.mode,
354c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                firmware_utils.get_current_time_str()])
355d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        basename = '.'.join([root_name, conf.filename.ext])
356d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        return basename
357c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
358c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _add_scores(self, new_scores):
359c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Add the new scores of a single gesture to the scores list."""
360c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if new_scores is not None:
361c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.scores += new_scores
362c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
363c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _final_scores(self, scores):
364c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Print the final score."""
365c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        # Note: conf.score_aggregator uses a function in fuzzy module.
366c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        final_score = eval(conf.score_aggregator)(scores)
367c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output.print_report('\nFinal score: %s\n' % str(final_score))
368c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
369aaa25e0898b712793078936d112520ed7cf1a569Joseph Hwang    def _robot_action(self):
370aaa25e0898b712793078936d112520ed7cf1a569Joseph Hwang        """Control the robot to perform the action."""
37142bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney        if self._is_robot_mode() or self.robot.is_manual_noise_test_mode():
37242bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney            self.robot.configure_noise(self.gesture, self.variation)
37342bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
3743162c02ea4a2287fc77436ba28f2c9c83349b63eCharlie Mooney        if self._is_robot_mode():
3753162c02ea4a2287fc77436ba28f2c9c83349b63eCharlie Mooney            self.robot.control(self.gesture, self.variation)
376af9472281594241369d693c45722c6a8b2310840Charlie Mooney            # Once the script terminates start a timeout to clean up if one
377af9472281594241369d693c45722c6a8b2310840Charlie Mooney            # hasn't already been set to keep the test suite from hanging.
378af9472281594241369d693c45722c6a8b2310840Charlie Mooney            if not self.gesture_begins_flag:
379af9472281594241369d693c45722c6a8b2310840Charlie Mooney                self.win.register_timeout_add(self.gesture_timeout_callback,
380af9472281594241369d693c45722c6a8b2310840Charlie Mooney                                              self.gesture.timeout)
38115dcc7520cc7f821fdc8db20109d62a7011bc649Joseph Hwang
38209890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang    def _handle_user_choice_save_after_parsing(self, next_gesture=True):
383c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Handle user choice for saving the parsed gesture file."""
384c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output.print_window('')
3853bad937cb81c5c22183024c7bc67575cd099b051Joseph Hwang        if self.use_existent_event_file_flag or self.recording:
38679009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang            if self.saved_msg:
38779009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang                self.output.print_report(self.saved_msg)
38879009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang            if self.new_scores:
38979009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang                self._add_scores(self.new_scores)
39079009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang            self.output.report_html.insert_image(self.gesture_image_name)
39179009095b5857565709897cf01a23c15e8fefdb5Joseph Hwang            self.output.report_html.flush()
3921e4a6cce6769e50e5ef33dafd228c46ec6e3bc0eJoseph Hwang        # After flushing to report_html, reset the gesture_image_name so that
3931e4a6cce6769e50e5ef33dafd228c46ec6e3bc0eJoseph Hwang        # it will not be reused by next gesture variation accidentally.
3941e4a6cce6769e50e5ef33dafd228c46ec6e3bc0eJoseph Hwang        self.gesture_image_name = None
3951e4a6cce6769e50e5ef33dafd228c46ec6e3bc0eJoseph Hwang
396c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if self._pre_setup_this_gesture_variation(next_gesture=next_gesture):
397c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # There are more gestures.
398c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self._setup_this_gesture_variation()
399c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooney            self._robot_action()
400c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
401c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # No more gesture.
402c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self._final_scores(self.scores)
403c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.output.stop()
4047981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang            self.output.report_html.stop()
4051fd993155b6d1c6996daafd5789101d3342c7bc9Joseph Hwang            self.win.stop()
406c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.packets = None
407c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
408c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _handle_user_choice_discard_after_parsing(self):
409c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Handle user choice for discarding the parsed gesture file."""
410c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output.print_window('')
411c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self._setup_this_gesture_variation()
412c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooney        self._robot_action()
413c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.packets = None
414c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
415c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _handle_user_choice_exit_after_parsing(self):
416c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Handle user choice to exit after the gesture file is parsed."""
417c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self._stop_record_and_rm_file()
4187981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        self.output.stop()
4197981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        self.output.report_html.stop()
4201fd993155b6d1c6996daafd5789101d3342c7bc9Joseph Hwang        self.win.stop()
421c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
422a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang    def check_for_wrong_number_of_fingers(self, details):
423a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        flag_found = False
424a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        try:
425a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            position = details.index('CountTrackingIDValidator')
426a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        except ValueError as e:
427a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            return None
428a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang
429a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        # An example of the count of tracking IDs:
430a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        #     '    count of trackid IDs: 1'
431a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        number_tracking_ids = int(details[position + 1].split()[-1])
432a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        # An example of the criteria_str looks like:
433a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        #     '    criteria_str: == 2'
434a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        criteria = int(details[position + 2].split()[-1])
435a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        if number_tracking_ids < criteria:
436a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            print '  CountTrackingIDValidator: '
437a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            print '  number_tracking_ids: ', number_tracking_ids
438a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            print '  criteria: ', criteria
439a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            print '  number_tracking_ids should be larger!'
440a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            msg = 'Number of Tracking IDs should be %d instead of %d'
441a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            return msg % (criteria, number_tracking_ids)
442a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang        return None
443a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang
4448b3cb003be644a2b78689346628122bb5b3f2f6bCharlie Mooney    def _empty_packets_is_legal_result(self):
4458b3cb003be644a2b78689346628122bb5b3f2f6bCharlie Mooney        return ('tap' in self.gesture.name and self._is_robot_mode())
4468b3cb003be644a2b78689346628122bb5b3f2f6bCharlie Mooney
447c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _handle_user_choice_validate_before_parsing(self):
448c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Handle user choice for validating before gesture file is parsed."""
449c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        # Parse the device events. Make sure there are events.
450c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.packets = self.parser.parse_file(self.gesture_file_name)
4518b3cb003be644a2b78689346628122bb5b3f2f6bCharlie Mooney        if self.packets or self._empty_packets_is_legal_result():
452c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # Validate this gesture and get the results.
4537981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang            (self.new_scores, msg_list, vlogs) = validators.validate(
4547981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang                    self.packets, self.gesture, self.variation)
455a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang
456a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            # If the number of tracking IDs is less than the expected value,
457a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            # the user probably made a wrong gesture.
458a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            error = self.check_for_wrong_number_of_fingers(msg_list)
459a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            if error:
460a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                prompt = self._get_prompt_abnormal_gestures(error)
461a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                color = 'red'
462a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            else:
46321df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                prompt = self._prompt_next
464a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang                color = 'black'
465a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang
466d66db93c0cf3878bbef19756eb1118bacd845eb0Joseph Hwang            self.output.print_window(msg_list)
467d66db93c0cf3878bbef19756eb1118bacd845eb0Joseph Hwang            self.output.buffer_report(msg_list)
4687981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang            self.output.report_html.insert_validator_logs(vlogs)
469a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            self.win.set_prompt(prompt, color=color)
470a385567aa9e120da24561fe470a078f0d4aed365Joseph Hwang            print prompt
471c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self._stop_record_and_post_image()
472c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
473c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.win.set_prompt(self._get_prompt_no_data(), color='red')
474c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
475c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _handle_user_choice_exit_before_parsing(self):
476c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Handle user choice to exit before the gesture file is parsed."""
477d40afdf230b71e93e626c1ee9682f372940159b4Joseph Hwang        self._close_gesture_file()
4787981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        self._handle_user_choice_exit_after_parsing()
479c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
480c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _is_parsing_gesture_file_done(self):
481c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Is parsing the gesture file done?"""
482c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        return self.packets is not None
483c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
4845a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang    def _is_arrow_key(self, choice):
4855a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        """Is this an arrow key?"""
4865a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        return (choice in TFK.ARROW_KEY_LIST)
4875a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang
488ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang    def user_choice_callback(self, fd, condition):
489c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """A callback to handle the key pressed by the user.
490c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
491c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        This is the primary GUI event-driven method handling the user input.
492c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """
493ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        choice = self.keyboard.get_key_press_event(fd)
494ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        if choice:
495ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            self._handle_keyboard_event(choice)
496ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        return True
497ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang
498ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang    def _handle_keyboard_event(self, choice):
499ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang        """Handle the keyboard event."""
5005a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        if self._is_arrow_key(choice):
5015a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang            self.win.scroll(choice)
5025a90d71e179bcc3b1fac4897806db4a58a450ba4Joseph Hwang        elif self.robot_waiting:
503ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            # The user wants the robot to start its action.
504ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            if choice in (TFK.SAVE, TFK.SAVE2):
505aaa25e0898b712793078936d112520ed7cf1a569Joseph Hwang                self.robot_waiting = False
506aaa25e0898b712793078936d112520ed7cf1a569Joseph Hwang                self._robot_action()
507ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            # The user wants to exit.
508ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            elif choice == TFK.EXIT:
509ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang                self._handle_user_choice_exit_after_parsing()
510aaa25e0898b712793078936d112520ed7cf1a569Joseph Hwang        elif self._is_parsing_gesture_file_done():
511c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # Save this gesture file and go to next gesture.
512ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            if choice in (TFK.SAVE, TFK.SAVE2):
51309890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang                self._handle_user_choice_save_after_parsing()
514c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # Save this file and perform the same gesture again.
515ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            elif choice == TFK.MORE:
516c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                self._handle_user_choice_save_after_parsing(next_gesture=False)
517c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # Discard this file and perform the gesture again.
518ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            elif choice == TFK.DISCARD:
519c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                self._handle_user_choice_discard_after_parsing()
520c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # The user wants to exit.
521ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            elif choice == TFK.EXIT:
522c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                self._handle_user_choice_exit_after_parsing()
523c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # The user presses any wrong key.
524c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            else:
52521df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                self.win.set_prompt(self._prompt_next, color='red')
526c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
527ed82f0322e54196f116566309594b7e5f958fae4Joseph Hwang            if choice == TFK.EXIT:
528c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                self._handle_user_choice_exit_before_parsing()
529c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            # The user presses any wrong key.
530c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            else:
53121df1fa2829a71c8c43ea807c75595d39352b229Joseph Hwang                self.win.set_prompt(self._prompt_result, color='red')
532c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
5333fb591eb36e473794ba56b633f6690f685fff6c6Joseph Hwang    def _get_all_gesture_variations(self, simplified):
534c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Get all variations for all gestures."""
535c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        gesture_variations_list = []
5368ee3e33e92f4e609c9b5fd9474e6a2263e93c79dJoseph Hwang        self.variations_dict = {}
537c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        for gesture in self.gesture_list:
5388ee3e33e92f4e609c9b5fd9474e6a2263e93c79dJoseph Hwang            variations_list = []
539c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            variations = self.span_variations(gesture.variations)
540c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            for variation in variations:
541c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                gesture_variations_list.append((gesture, variation))
5428ee3e33e92f4e609c9b5fd9474e6a2263e93c79dJoseph Hwang                variations_list.append(variation)
5433fb591eb36e473794ba56b633f6690f685fff6c6Joseph Hwang                if simplified:
5443fb591eb36e473794ba56b633f6690f685fff6c6Joseph Hwang                    break
5458ee3e33e92f4e609c9b5fd9474e6a2263e93c79dJoseph Hwang            self.variations_dict[gesture.name] = variations_list
546c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_variations = iter(gesture_variations_list)
547c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
548c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def gesture_timeout_callback(self):
549c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """A callback watching whether a gesture has timed out."""
55009890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang        if self.replay_dir:
55109890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang            # There are event files to replay for this gesture variation.
55209890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang            if self.use_existent_event_file_flag:
55309890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang                self._handle_user_choice_validate_before_parsing()
55409890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang            self._handle_user_choice_save_after_parsing(next_gesture=True)
55509890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang            return False
55609890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang
55769227234dabb3942f25a70004fd9347308c56a32Joseph Hwang        # A gesture is stopped only when two conditions are met simultaneously:
55869227234dabb3942f25a70004fd9347308c56a32Joseph Hwang        # (1) there are no reported packets for a timeout interval, and
55969227234dabb3942f25a70004fd9347308c56a32Joseph Hwang        # (2) the number of tracking IDs is 0.
56009890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang        elif (self.gesture_continues_flag or
56169227234dabb3942f25a70004fd9347308c56a32Joseph Hwang            not self.mtb_evemu.all_fingers_leaving()):
562c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.gesture_continues_flag = False
563c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            return True
56409890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang
565c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        else:
566c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self._handle_user_choice_validate_before_parsing()
567c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.win.remove_event_source(self.gesture_file_watch_tag)
568d534df8a12153d7006dceed16cd90b54c05dd5cfJoseph Hwang            if self._is_robot_mode():
569d534df8a12153d7006dceed16cd90b54c05dd5cfJoseph Hwang                self._handle_keyboard_event(TFK.SAVE)
570c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            return False
571c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
572c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def gesture_file_watch_callback(self, fd, condition, evdev_device):
5732fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        """A callback to watch the device input."""
5742fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        # Read the device node continuously until end
5752fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        event = True
5762fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang        while event:
5772fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang            event = self._non_blocking_read(evdev_device, fd)
57869227234dabb3942f25a70004fd9347308c56a32Joseph Hwang            if event:
57969227234dabb3942f25a70004fd9347308c56a32Joseph Hwang                self.mtb_evemu.process_event(event)
5802fa046e44242b11bfe17a3acf20b74dd0407d83eJoseph Hwang
581c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_continues_flag = True
582c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if (not self.gesture_begins_flag):
583c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.gesture_begins_flag = True
584c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.win.register_timeout_add(self.gesture_timeout_callback,
5856bbab3e8ff24fd3b0c44f753cfce03225e108bb5Joseph Hwang                                          self.gesture.timeout)
586c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        return True
587c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
588c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def init_gesture_setup_callback(self, widget, event):
589c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """A callback to set up environment before a user starts a gesture."""
590c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if not self.init_flag:
591c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self.init_flag = True
592c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self._pre_setup_this_gesture_variation()
593c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang            self._setup_this_gesture_variation()
594c0d30cbdfb45cfb800689451f29fe37345c66f48Charlie Mooney            self._robot_action()
595c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
596d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang    def _get_existent_event_files(self):
597d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        """Get the existent event files that starts with the primary_name."""
598d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        primary_pathnames = os.path.join(self.output.log_dir,
599d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                                         self.primary_name + '*.dat')
600d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        self.primary_gesture_files = glob.glob(primary_pathnames)
601d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        # Reverse sorting the file list so that we could pop from the tail.
602d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        self.primary_gesture_files.sort()
603d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        self.primary_gesture_files.reverse()
604d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
605d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang    def _use_existent_event_file(self):
606d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        """If the replay flag is set in the command line, and there exists a
607d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        file(s) with the same primary name, then use the existent file(s)
608d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        instead of recording a new one.
609d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        """
610d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        if self.primary_gesture_files:
611d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self.gesture_file_name = self.primary_gesture_files.pop()
612d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            return True
613d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        return False
614d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
615c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _pre_setup_this_gesture_variation(self, next_gesture=True):
616c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        """Get gesture, variation, filename, prompt, etc."""
617d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        next_gesture_first_time = False
618c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        if next_gesture:
61908eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang            if self.gv_count < self.iterations:
62008eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang                self.gv_count += 1
62108eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang            else:
62208eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang                self.gv_count = 1
62308eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang                gesture_variation = next(self.gesture_variations, None)
62408eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang                if gesture_variation is None:
62508eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang                    return False
62608eb4422f1e718c67e29e25c3269662070716a01Joseph Hwang                self.gesture, self.variation = gesture_variation
627d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang                next_gesture_first_time = True
628d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
629d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        basename = self._create_gesture_file_name(self.gesture, self.variation)
630d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        if next_gesture_first_time:
631d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self._get_existent_event_files()
632d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
633ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        if self.replay_dir or self.resume_dir:
634d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self.use_existent_event_file_flag = self._use_existent_event_file()
635ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang
636ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        if ((not self.replay_dir and not self.resume_dir) or
637ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang                (self.resume_dir and not self.use_existent_event_file_flag)):
638d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            self.gesture_file_name = os.path.join(self.output.log_dir, basename)
639ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self.saved_msg = '(saved: %s)\n' % self.gesture_file_name
640ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self.deleted_msg = '(deleted: %s)\n' % self.gesture_file_name
641ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        else:
642ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self.saved_msg = None
643ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self.deleted_msg = None
644ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        self.new_scores = None
645c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
64642bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney        if self.robot.is_robot_action_mode() or self.robot.is_manual_noise_test_mode():
64742bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney            self.robot.turn_off_noise()
64842bc23b188cdf856d24494842909ac0e0930b257Charlie Mooney
6497981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        (msg, color_msg, glog) = self._create_prompt(self.gesture,
6507981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang                                                     self.variation)
651c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.win.set_gesture_name(msg)
6527981beea4b6dfdff43ca61e17b51ba295a342702Joseph Hwang        self.output.report_html.insert_gesture_log(glog)
6534e8330caaca32549aeb1c61903355d1efc63955dJoseph Hwang        print color_msg
654c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.output.print_report(color_msg)
655c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        return True
656c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
657c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang    def _setup_this_gesture_variation(self):
658d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        """Set up the recording process or use an existent event data file."""
659d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        if self.replay_dir:
660ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self.record_new_file = False
66109890dba0014937e0be1d75d1a46c05dd16cc6b8Joseph Hwang            self.win.register_timeout_add(self.gesture_timeout_callback, 0)
662d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang            return
663d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang
664ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        if self.resume_dir and self.use_existent_event_file_flag:
665ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self.record_new_file = False
666ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self._handle_user_choice_validate_before_parsing()
667ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            self._handle_keyboard_event(TFK.SAVE)
668ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang            return
669ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang
67045f891b832de8f1c94e1006478fb557cd341aa57Joseph Hwang        # Initiate the MtbSanityValidator. Note that this should be done each
67145f891b832de8f1c94e1006478fb557cd341aa57Joseph Hwang        # time just before recording the gesture file since it requires a
67245f891b832de8f1c94e1006478fb557cd341aa57Joseph Hwang        # snapshot of the input device before any finger touching the device.
673dfeafca16e4aacb865b51768f607a1a5a2db956cCharlie Mooney        self.gesture.mtb_sanity_validator = validators.MtbSanityValidator()
67445f891b832de8f1c94e1006478fb557cd341aa57Joseph Hwang
675d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        # Now, we will record a new gesture event file.
676d9b03cc8ece225087e203658ded151005eea0456Joseph Hwang        # Fork a new process for mtplot. Add io watch for the gesture file.
677ecb52de991febef82dc9fa08f033921e8e20bc91Joseph Hwang        self.record_new_file = True
678c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_file = open(self.gesture_file_name, 'w')
679c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.record_proc = subprocess.Popen(self.record_cmd.split(),
680c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                                            stdout=self.gesture_file)
681c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang
682c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        # Watch if data come in to the monitored file.
683c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_begins_flag = False
684c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self._reopen_system_device()
685c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang        self.gesture_file_watch_tag = self.win.register_io_add_watch(
686c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                self.gesture_file_watch_callback, self.system_device,
687c3d6c06ce24969123b90292808bdec37bc7313d7Joseph Hwang                self.evdev_device)
688