blob: aa0eb0f6f2a102e5439138a857ddde228abddfb2 [file] [log] [blame]
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A wrapper for robot manipulation with Touchbot II."""
import os
import re
import shutil
import sys
import time
import common_util
import test_conf as conf
from firmware_constants import GV, MODE, OPTIONS
# Define the robot control script names.
SCRIPT_NOISE = 'generate_noise.py'
SCRIPT_LINE = 'line.py'
SCRIPT_TAP = 'tap.py'
SCRIPT_CLICK = 'click.py'
SCRIPT_ONE_STATIONARY_FINGER = 'one_stationary_finger.py'
SCRIPT_STATIONARY_WITH_TAPS = 'stationary_finger_with_taps_around_it.py'
SCRIPT_QUICKSTEP = 'quickstep.py'
# A script to "reset" the robot by having it move safely to a spot just above
# the center of the touchpad.
SCRIPT_RESET = 'reset.py'
# Define constants for coordinates.
# Normally, a gesture is performed within [START, END].
# For tests involved with RangeValidator which intends to verify
# the min/max reported coordinates, use [OFF_START, OFF_END] instead
# so that the gestures are performed off the edge.
START = 0.1
CENTER = 0.5
END = 0.9
OFF_START = -0.05
OFF_END = 1.05
ABOVE_CENTER = 0.25
BELOW_CENTER = 0.75
LEFT_TO_CENTER = 0.25
RIGHT_TO_CENTER = 0.75
OUTER_PINCH_SPACING = 70
INNER_PINCH_SPACING = 25
PHYSICAL_CLICK_SPACING = 20
PHYSICAL_CLICK_FINGER_SIZE = 2
TWO_CLOSE_FINGER_SPACING = 17
TWO_FINGER_SPACING = 25
class RobotWrapperError(Exception):
"""An exception class for the robot_wrapper module."""
pass
class RobotWrapper:
"""A class to wrap and manipulate the robot library."""
def __init__(self, board, options):
self._board = board
self._mode = options[OPTIONS.MODE]
self._is_touchscreen = options[OPTIONS.TOUCHSCREEN]
self._fngenerator_only = options[OPTIONS.FNGENERATOR]
self._robot_script_dir = self._get_robot_script_dir()
self._noise_script_dir = os.path.join(self._get_robot_script_dir(),
SCRIPT_NOISE)
# Each get_control_command method maps to a script name.
self._robot_script_name_dict = {
self._get_control_command_line: SCRIPT_LINE,
self._get_control_command_rapid_taps: SCRIPT_TAP,
self._get_control_command_single_tap: SCRIPT_TAP,
self._get_control_command_drumroll: SCRIPT_TAP,
self._get_control_command_click: SCRIPT_CLICK,
self._get_control_command_one_stationary_finger:
SCRIPT_ONE_STATIONARY_FINGER,
self._get_control_command_pinch: SCRIPT_LINE,
self._get_control_command_stationary_with_taps:
SCRIPT_STATIONARY_WITH_TAPS,
self._get_control_command_quickstep: SCRIPT_QUICKSTEP,
}
# Each gesture maps to a get_control_command method
self._method_of_control_command_dict = {
conf.NOISE_LINE: self._get_control_command_line,
conf.NOISE_STATIONARY: self._get_control_command_single_tap,
conf.NOISE_STATIONARY_EXTENDED: self._get_control_command_single_tap,
conf.ONE_FINGER_TRACKING: self._get_control_command_line,
conf.ONE_FINGER_TO_EDGE: self._get_control_command_line,
conf.ONE_FINGER_SWIPE: self._get_control_command_line,
conf.ONE_FINGER_TAP: self._get_control_command_single_tap,
conf.ONE_FINGER_TRACKING_FROM_CENTER:
self._get_control_command_line,
conf.RAPID_TAPS: self._get_control_command_rapid_taps,
conf.TWO_FINGER_TRACKING: self._get_control_command_line,
conf.TWO_FINGER_SWIPE: self._get_control_command_line,
conf.TWO_FINGER_TAP: self._get_control_command_single_tap,
conf.TWO_CLOSE_FINGERS_TRACKING: self._get_control_command_line,
conf.RESTING_FINGER_PLUS_2ND_FINGER_MOVE:
self._get_control_command_one_stationary_finger,
conf.FIRST_FINGER_TRACKING_AND_SECOND_FINGER_TAPS:
self._get_control_command_one_stationary_finger,
conf.FINGER_CROSSING:
self._get_control_command_one_stationary_finger,
conf.PINCH_TO_ZOOM: self._get_control_command_pinch,
conf.DRUMROLL: self._get_control_command_drumroll,
conf.TWO_FAT_FINGERS_TRACKING: self._get_control_command_line,
conf.ONE_FINGER_PHYSICAL_CLICK: self._get_control_command_click,
conf.TWO_FINGER_PHYSICAL_CLICK: self._get_control_command_click,
conf.THREE_FINGER_PHYSICAL_CLICK: self._get_control_command_click,
conf.FOUR_FINGER_PHYSICAL_CLICK: self._get_control_command_click,
conf.STATIONARY_FINGER_NOT_AFFECTED_BY_2ND_FINGER_TAPS:
self._get_control_command_stationary_with_taps,
conf.FAT_FINGER_MOVE_WITH_RESTING_FINGER:
self._get_control_command_one_stationary_finger,
conf.DRAG_LATENCY:
self._get_control_command_quickstep,
}
self._line_dict = {
GV.LR: (START, CENTER, END, CENTER),
GV.RL: (END, CENTER, START, CENTER),
GV.TB: (CENTER, START, CENTER, END),
GV.BT: (CENTER, END, CENTER, START),
GV.BLTR: (START, END, END, START),
GV.TRBL: (END, START, START, END),
GV.BRTL: (END, END, START, START),
GV.TLBR: (START, START, END, END),
GV.CUR: (CENTER, CENTER, END, START),
GV.CUL: (CENTER, CENTER, START, START),
GV.CLR: (CENTER, CENTER, END, END),
GV.CLL: (CENTER, CENTER, START, END),
# Overshoot for this one-finger gesture only: ONE_FINGER_TO_EDGE
GV.CL: (CENTER, CENTER, OFF_START, CENTER),
GV.CR: (CENTER, CENTER, OFF_END, CENTER),
GV.CT: (CENTER, CENTER, CENTER, OFF_START),
GV.CB: (CENTER, CENTER, CENTER, OFF_END),
}
# The angle wrt the pad that the fingers should take when doing a 2f
# gesture along these lines, or doing 2f taps at different angles.
self._angle_dict = {
GV.LR: -45,
GV.RL: -45,
GV.TB: 45,
GV.BT: 45,
GV.TLBR: 90,
GV.BLTR: 0,
GV.TRBL: 0,
GV.HORIZONTAL: -45,
GV.VERTICAL: 45,
GV.DIAGONAL: 0,
}
# The stationary finger locations corresponding the line specifications
# for the finger crossing tests
self._stationary_finger_dict = {
GV.LR: (CENTER, ABOVE_CENTER),
GV.RL: (CENTER, BELOW_CENTER),
GV.TB: (RIGHT_TO_CENTER, CENTER),
GV.BT: (LEFT_TO_CENTER, CENTER),
GV.BLTR: (RIGHT_TO_CENTER, BELOW_CENTER),
GV.TRBL: (LEFT_TO_CENTER, ABOVE_CENTER),
}
self._speed_dict = {
GV.SLOW: 10,
GV.NORMAL: 20,
GV.FAST: 30,
GV.FULL_SPEED: 100,
}
# The frequencies of noise in Hz.
self._frequency_dict = {
GV.LOW_FREQUENCY: 5000, # 5kHz
GV.MED_FREQUENCY: 500000, # 500kHz
GV.HIGH_FREQUENCY: 1000000, # 1MHz
}
# Add the list of extended frequency values to the dict.
freq_value_dict = {freq: int(freq.replace('Hz', '')) for freq in GV.EXTENDED_FREQUENCIES}
self._frequency_dict = dict(self._frequency_dict.items() + freq_value_dict.items())
# The waveforms of noise.
self._waveform_dict = {
GV.SQUARE_WAVE: 'SQUARE',
GV.SINE_WAVE: 'SINE',
}
# The amplitude of noise in Vpp.
self._amplitude_dict = {
GV.HALF_AMPLITUDE: 10,
GV.MAX_AMPLITUDE: 20,
}
self._location_dict = {
# location parameters for one-finger taps
GV.TL: (START, START),
GV.TR: (END, START),
GV.BL: (START, END),
GV.BR: (END, END),
GV.TS: (CENTER, START),
GV.BS: (CENTER, END),
GV.LS: (START, CENTER),
GV.RS: (END, CENTER),
GV.CENTER: (CENTER, CENTER),
}
self.fingertips = [None, None, None, None]
self._build_robot_script_paths()
self._get_device_spec()
def _get_device_spec(self):
if not self.is_robot_action_mode():
return
# First check if there is already a device spec in this directory
if (os.path.isfile('%s.p' % self._board) and
os.path.isfile('%s_min.p' % self._board)):
return
# Next, check if maybe there is a spec in the touchbotII directory
spec_path = os.path.join(self._robot_script_dir,
'device_specs/%s.p' % self._board)
spec_min_path = os.path.join(self._robot_script_dir,
'device_specs/%s_min.p' % self._board)
if os.path.isfile(spec_path) and os.path.isfile(spec_min_path):
shutil.copy(spec_path, '.')
shutil.copy(spec_min_path, '.')
return
# If both of those fail, then generate a new device spec
self._calibrate_device(self._board)
def _get_fingertips(self, tips_to_get):
if self.fingertips != [None, None, None, None]:
print 'Error, there are still fingertips on'
sys.exit(1)
for size in conf.ALL_FINGERTIP_SIZES:
fingers = [1 if f == size else 0 for f in tips_to_get]
print 'size: %d\tfingers: %s' % (size, str(fingers))
if fingers == [0, 0, 0, 0]:
continue
script = os.path.join(self._robot_script_dir,
'manipulate_fingertips.py')
para = (script, fingers[0], fingers[1], fingers[2], fingers[3],
size, str(True))
cmd = 'python %s %d %d %d %d %d %s' % para
if self._execute_control_command(cmd):
print 'Error getting the figertips!'
print 'Please make sure all the fingertips are in the correct'
print 'positions before continuing.'
print 'WARNING: failure to do this correctly could cause'
print ' permanent damage to the robot!'
print
print 'The fingers it was attempting to get were as follows:'
print tips_to_get
print '(Starting with the front right finger and going counter'
print 'clockwise from there. Finger sizes are 0-3)'
self._wait_for_user_input('GO', 'ABORT')
self.fingertips = tips_to_get
def _return_fingertips(self):
""" Return all the fingertips to the nest, one size at a time.
This function uses the self.fingertips member variable to know which
finger has which tip size, then returns them all to the nest
"""
for size in conf.ALL_FINGERTIP_SIZES:
# See which (if any) of the fingers currently have this size tip
fingers = [1 if f == size else 0 for f in self.fingertips]
if fingers == [0, 0, 0, 0]:
continue
script = os.path.join(self._robot_script_dir,
'manipulate_fingertips.py')
para = (script, fingers[0], fingers[1], fingers[2], fingers[3],
size, str(False))
cmd = 'python %s %d %d %d %d %d %s' % para
if self._execute_control_command(cmd):
print 'Error returning the figertips!'
print 'Please make sure all the fingertips are in their correct'
print 'spots in the nest before continuing.'
print 'WARNING: failure to do this correctly could cause'
print ' permanent damage to the robot!'
self._wait_for_user_input('GO', 'ABORT')
self.fingertips = [None, None, None, None]
def _wait_for_user_input(self, continue_cmd, stop_cmd):
user_input = None
while user_input not in [continue_cmd, stop_cmd]:
if user_input:
print 'Sorry, but "%s" was incorrect' % user_input
user_input = raw_input('Type "%s" to continue or "%s" to stop: ' %
(continue_cmd, stop_cmd))
if user_input == stop_cmd:
raise RobotWrapperError('Operator aborted after error')
def _calibrate_device(self, board):
""" Have the operator show the robot where the device is."""
calib_script = os.path.join(self._robot_script_dir,
'calibrate_for_new_device.py')
calib_cmd = 'python %s %s' % (calib_script, board)
self._execute_control_command(calib_cmd)
def is_manual_noise_test_mode(self):
return (self._mode in [MODE.NOISE, MODE.COMPLETE]
and self._fngenerator_only)
def is_robot_action_mode(self):
"""Is it in robot action mode?
In the robot action mode, it actually invokes the robot control script.
"""
if self.is_manual_noise_test_mode():
return False
return self._mode in [MODE.ROBOT, MODE.QUICKSTEP, MODE.NOISE]
def _raise_error(self, msg):
"""Only raise an error if it is in the robot action mode."""
if self.is_robot_action_mode():
raise RobotWrapperError(msg)
def _get_robot_script_dir(self):
"""Get the directory of the robot control scripts."""
for lib_path in [conf.robot_lib_path, conf.robot_lib_path_local]:
cmd = ('find %s -maxdepth 1 -type d -name %s' %
(lib_path, conf.python_package))
path = common_util.simple_system_output(cmd)
if path:
robot_script_dir = os.path.join(path, conf.gestures_sub_path)
if os.path.isdir(robot_script_dir):
return robot_script_dir
return ''
def _get_num_taps(self, gesture):
"""Determine the number of times to tap."""
matches = re.match('[^0-9]*([0-9]*)[^0-9]*', gesture)
return int(matches.group(1)) if matches else None
def _reverse_coord_if_is_touchscreen(self, coordinates):
"""Reverse the coordinates if the device is a touchscreen.
E.g., the original coordinates = (0.1, 0.9)
After reverse, the coordinates = (1 - 0.1, 1 - 0.9) = (0.9, 0.1)
@param coordinates: a tuple of coordinates
"""
return (tuple(1.0 - c for c in coordinates) if self._is_touchscreen else
coordinates)
def _get_control_command_pinch(self, robot_script, gesture, variation):
# Depending on which direction you're zooming, change the order of the
# finger spacings.
if GV.ZOOM_IN in variation:
starting_spacing = INNER_PINCH_SPACING
ending_spacing = OUTER_PINCH_SPACING
else:
starting_spacing = OUTER_PINCH_SPACING
ending_spacing = INNER_PINCH_SPACING
# Keep the hand centered on the pad, and make the fingers move
# in or out with only two opposing fingers on the pad.
para = (robot_script, self._board,
CENTER, CENTER, 45, starting_spacing,
CENTER, CENTER, 45, ending_spacing,
0, 1, 0, 1, self._speed_dict[GV.SLOW], 'basic')
return 'python %s %s_min.p %f %f %d %d %f %f %d %d %d %d %d %d %f %s' % para
def _get_control_command_quickstep(self, robot_script, gesture, variation):
"""have the robot do the zig-zag gesture for latency testing."""
para = (robot_script, self._board, self._speed_dict[GV.FULL_SPEED])
return 'python %s %s_min.p %d' % para
def _dimensions(self, device_spec):
device_script = os.path.join(self._robot_script_dir, 'device_size.py')
cmd = 'python %s %s' % (device_script, device_spec)
results = common_util.simple_system_output(cmd)
dimensions = results.split()
return float(dimensions[0]), float(dimensions[1])
def _adjust_for_target_distance(self, line, stationary_finger, distance):
""" Given a point and a line in relative coordinates move the point
so that is exactly 'distance' millimeters away from the line for the
current board. Sometimes the robot needs to move two fingers very
close and this is problematic in relative coordinates as the board
dimensions change. This function allows the test to compensate and
get a more accurate test.
"""
# First convert all the values into absolute coordinates by using
# the calibrated device spec to see the dimensions of the pad
h, w = self._dimensions("%s_min.p" % self._board)
abs_line = (line[0] * w, line[1] * h, line[2] * w, line[3] * h)
x, y = (stationary_finger[0] * w, stationary_finger[1] * h)
# Find a point near the stationary finger that is distance
# away from the line at the closest point
dx = abs_line[2] - abs_line[0]
dy = abs_line[3] - abs_line[1]
if dx == 0: # vertical line
if x > abs_line[0]:
x = abs_line[0] + distance
else:
x = abs_line[0] - distance
elif dy == 0: # horizontal line
if y > abs_line[1]:
y = abs_line[1] + distance
else:
y = abs_line[1] - distance
else:
# First, find the closest point on the line to the point
m = dy / dx
b = abs_line[1] - m * abs_line[0]
m_perp = -1.0 / m
b_perp = y - m_perp * x
x_intersect = (b_perp - b) / (m - m_perp)
y_intersect = m * x_intersect + b
current_distance_sq = ((x_intersect - x) ** 2 +
(y_intersect - y) ** 2)
scale = distance / (current_distance_sq ** 0.5)
x = x_intersect - (x_intersect - x) * scale
y = y_intersect - (y_intersect - y) * scale
#Convert this absolute point back into relative coordinates
x_rel = x / w
y_rel = y / h
return (x_rel, y_rel)
def _get_control_command_one_stationary_finger(self, robot_script, gesture,
variation):
line = speed = finger_gap_mm = None
num_taps = 0
# The stationary finger should be in the bottom left corner for resting
# finger tests, and various locations for finger crossing tests
stationary_finger = (START, END)
for element in variation:
if element in GV.GESTURE_DIRECTIONS:
line = self._line_dict[element]
if 'finger_crossing' in gesture:
stationary_finger = self._stationary_finger_dict[element]
finger_gap_mm = conf.FINGER_CROSSING_GAP_MM
elif 'second_finger_taps' in gesture:
stationary_finger = self._location_dict[GV.BL]
speed = self._speed_dict[GV.SLOW]
num_taps = 3
elif 'fat_finger' in gesture:
stationary_finger = self._stationary_finger_dict[element]
speed = self._speed_dict[GV.SLOW]
finger_gap_mm = conf.FAT_FINGER_AND_STATIONARY_FINGER_GAP_MM
elif element in GV.GESTURE_SPEED:
speed = self._speed_dict[element]
if line is None or speed is None:
msg = 'Cannot derive the line/speed parameters from %s %s.'
self._raise_error(msg % (gesture, variation))
# Adjust the positioning to get a precise gap between finger tips
# if this gesture requires it
if finger_gap_mm:
min_space_mm = (conf.FINGERTIP_DIAMETER_MM[self.fingertips[0]] +
conf.FINGERTIP_DIAMETER_MM[self.fingertips[2]]) / 2
stationary_finger = self._adjust_for_target_distance(
line, stationary_finger,
min_space_mm + finger_gap_mm)
line = self._reverse_coord_if_is_touchscreen(line)
start_x, start_y, end_x, end_y = line
stationary_x, stationary_y = stationary_finger
para = (robot_script, self._board, stationary_x, stationary_y,
start_x, start_y, end_x, end_y, speed, num_taps)
cmd = 'python %s %s_min.p %f %f %f %f %f %f %s --taps=%d' % para
return cmd
def _get_control_command_line(self, robot_script, gesture, variation):
"""Get robot control command for gestures using robot line script."""
line_type = 'swipe' if bool('swipe' in gesture) else 'basic'
line = speed = finger_angle = None
for element in variation:
if element in GV.GESTURE_DIRECTIONS:
line = self._line_dict[element]
finger_angle = self._angle_dict.get(element, None)
elif element in GV.GESTURE_SPEED:
speed = self._speed_dict[element]
if not speed:
if line_type is 'swipe':
speed = self._speed_dict[GV.FAST]
if 'two_close_fingers' in gesture or 'fat' in gesture:
speed = self._speed_dict[GV.NORMAL]
if line is None or speed is None:
msg = 'Cannot derive the line/speed parameters from %s %s.'
self._raise_error(msg % (gesture, variation))
line = self._reverse_coord_if_is_touchscreen(line)
start_x, start_y, end_x, end_y = line
if 'two' in gesture:
if 'close_fingers' in gesture:
finger_spacing = TWO_CLOSE_FINGER_SPACING
finger_angle += 45
fingers = (1, 1, 0, 0)
else:
finger_spacing = TWO_FINGER_SPACING
fingers = (0, 1, 0, 1)
if finger_angle is None:
msg = 'Unable to determine finger angle for %s %s.'
self._raise_error(msg % (gesture, variation))
else:
finger_spacing = 17
fingers = (0, 1, 0, 0)
finger_angle = 0
# Generate the CLI command
para = (robot_script, self._board,
start_x, start_y, finger_angle, finger_spacing,
end_x, end_y, finger_angle, finger_spacing,
fingers[0], fingers[1], fingers[2], fingers[3],
speed, line_type)
cmd = 'python %s %s.p %f %f %d %d %f %f %d %d %d %d %d %d %f %s' % para
if self._get_noise_command(gesture, variation):
# A one second pause to give the touchpad time to calibrate
DELAY = 1 # 1 seconds
cmd = '%s %d' % (cmd, DELAY)
return cmd
def _get_control_command_stationary_with_taps(self, robot_script, gesture,
variation):
""" There is only one variant of this gesture, so there is only one
command to generate. This is the command for tapping around a
stationary finger on the pad.
"""
return 'python %s %s.p 0.5 0.5' % (robot_script, self._board)
def _get_control_command_rapid_taps(self, robot_script, gesture, variation):
num_taps = self._get_num_taps(gesture)
return self._get_control_command_taps(robot_script, gesture,
variation, num_taps)
def _get_control_command_single_tap(self, robot_script, gesture, variation):
# Get the single tap command
cmd = self._get_control_command_taps(robot_script, gesture, variation, 1)
if self._get_noise_command(gesture, variation):
# Add the noise command and a pause to the tap
TEST_DURATION = 3 # 3 seconds
cmd = '%s %d' % (cmd, TEST_DURATION)
return cmd
def _get_control_command_drumroll(self, robot_script, gesture, variation):
"""Get robot control command for the drumroll gesture. There is only
one so there is no need for complicated parsing here.
"""
return ('python %s %s.p 0.5 0.5 45 20 0 1 0 1 50 drumroll' %
(robot_script, self._board))
def _get_control_command_taps(self, robot_script, gesture,
variation, num_taps):
"""Get robot control command for tap gestures. This includes rapid tap
tests as well as 1 and 2 finger taps at various locations on the pad.
"""
if num_taps is None:
msg = 'Cannot determine the number of taps to do from %s.'
self._raise_error(msg % gesture)
# The tap commands have identical arguments as the click except with
# two additional arguments at the end. As such we generate the 'click'
# command and add these on to make it work as a tap.
cmd = self._get_control_command_click_tap(robot_script, gesture,
variation)
control_cmd = '%s %d tap' % (cmd, num_taps)
return control_cmd
def _get_control_command_click(self, robot_script, gesture, variation):
"""Get the robot control command for a physical click gesture"""
cmd = self._get_control_command_click_tap(robot_script, gesture,
variation)
control_cmd = '%s %d' % (cmd, PHYSICAL_CLICK_FINGER_SIZE)
return control_cmd
def _get_control_command_click_tap(self, robot_script, gesture, variation):
"""Get robot control arguments that are common between pysical click
and tapping gestures
"""
location = None
angle = 45
for element in variation:
if element in self._location_dict:
location = self._location_dict[element]
if 'two' in gesture and element in self._angle_dict:
angle = self._angle_dict[element]
# All non-one finger taps are simply perfomed in the middle of the pad
if 'one' not in gesture and location is None:
location = self._location_dict[GV.CENTER]
if location is None:
msg = 'Cannot determine the location parameters from %s %s.'
self._raise_error(msg % (gesture, variation))
fingers = [1, 0, 0, 0]
if 'two' in gesture:
fingers = [0, 1, 0, 1]
elif 'three' in gesture:
fingers = [0, 1, 1, 1]
elif 'four' in gesture:
fingers = [1, 1, 1, 1]
location_str = ' '.join(
map(str, self._reverse_coord_if_is_touchscreen(location)))
para = (robot_script, self._board, location_str, angle,
PHYSICAL_CLICK_SPACING,
fingers[0], fingers[1], fingers[2], fingers[3])
control_cmd = 'python %s %s.p %s %d %d %d %d %d %d' % para
return control_cmd
def _build_robot_script_paths(self):
"""Build the robot script paths."""
# Check if the robot script dir could be found.
if not self._robot_script_dir:
script_path = os.path.join(conf.robot_lib_path, conf.python_package,
conf.gestures_sub_path)
msg = 'Cannot find robot script directory in "%s".'
self._raise_error(msg % script_path)
# Build the robot script path dictionary
self._robot_script_dict = {}
for method in self._robot_script_name_dict:
script_name = self._robot_script_name_dict.get(method)
# Check if the control script actually exists.
robot_script = os.path.join(self._robot_script_dir, script_name)
if not os.path.isfile(robot_script):
msg = 'Cannot find the robot control script: %s'
self._raise_error(msg % robot_script)
self._robot_script_dict[method] = robot_script
def _get_control_command(self, gesture, variation):
"""Get robot control command based on the gesture and variation."""
script_method = self._method_of_control_command_dict.get(gesture)
if not script_method:
self._raise_error('Cannot find "%s" gesture in '
'_method_of_control_command_dict.' % gesture)
robot_script = self._robot_script_dict.get(script_method)
if not robot_script:
msg = 'Cannot find "%s" method in _robot_script_dict.'
self._raise_error(msg % script_method)
return script_method(robot_script, gesture, variation)
def _execute_control_command(self, control_cmd):
"""Execute a control command."""
print 'Executing: "%s"' % control_cmd
if self.is_robot_action_mode():
# Pausing to give everything time to settle
time.sleep(0.5)
return common_util.simple_system(control_cmd)
return 0
def _reset_with_safety_clearance(self, destination):
reset_script = os.path.join(self._robot_script_dir, SCRIPT_RESET)
para = (reset_script, self._board, destination,
self._speed_dict[GV.FAST])
self._execute_control_command('python %s %s.p %s %d' % para)
def turn_off_noise(self):
off_cmd = 'python %s OFF' % self._noise_script_dir
common_util.simple_system(off_cmd)
def _execute_noise_command(self, noise_cmd):
if self.is_robot_action_mode() or self.is_manual_noise_test_mode():
return common_util.simple_system(noise_cmd)
def _get_noise_command(self, gesture, variation):
if not gesture or not variation:
return None
waveform = frequency = amplitude = None
for element in variation:
if element.endswith('Hz'):
frequency = int(element[:-2])
elif element in GV.NOISE_FREQUENCY:
frequency = self._frequency_dict[element]
elif element in GV.NOISE_WAVEFORM:
waveform = self._waveform_dict[element]
elif element in GV.NOISE_AMPLITUDE:
amplitude = self._amplitude_dict[element]
if waveform and frequency and amplitude:
return 'python %s %s %d %d' % (self._noise_script_dir,
waveform, frequency, amplitude)
return None
def configure_noise(self, gesture, variation):
if not gesture or not variation:
return None
if not isinstance(variation, tuple):
variation = (variation,)
noise_cmd = self._get_noise_command(gesture.name, variation)
if noise_cmd:
self._execute_noise_command(noise_cmd)
def control(self, gesture, variation):
"""Have the robot perform the gesture variation."""
tips_needed = conf.finger_tips_required[gesture.name]
if self.fingertips != tips_needed:
self._reset_with_safety_clearance('nest')
self._return_fingertips()
self._get_fingertips(tips_needed)
self._reset_with_safety_clearance('pad')
if not isinstance(variation, tuple):
variation = (variation,)
try:
print gesture.name, variation
control_cmd = self._get_control_command(gesture.name, variation)
self._execute_control_command(control_cmd)
except RobotWrapperError as e:
print gesture.name, variation
print 'RobotWrapperError: %s' % str(e)
sys.exit(1)