Add robot support for 3, 4, and 5 finger probes.

This required a pretty substantial code overhaul, but the touchbot
class is better for it.  Essentially the largest probes are difficult
for a number of reasons:
  1. They're bigger, so you have to offset their positions
  2. They're heavier, so you have to be extremely careful when moving
     to keep them from falling off.
  3. They tended to cause oscillations in the PID controllers of the
     fingers.

This CL enables these large multi-finger fingertips by re-doing the way
we calibrate the touchpad.  Now the robot goes to a known flat location
and presses its fingertips against them to guarantee that they are
correctly zeroed to the same height.  This required all of the nest
locations to be recalibrated, which is why this change looks so much
bigger than it way.

Second, this CL sets up the ability to perfectly sync the movement
of both fingers.  Previously to avoid bus collisions the fingers were
signaled one at a time, which resulted in a slight delay between them
starting to move.  Now, we can disable bus-responses, broadcast the
command to both simultaneously, and then re-enable bus-responses.  This
keeps them in sync, and prevents collisions, but is a bit slow.  The
fingers take ~1second to load the new config, so I had to go to some
trouble to make sure it still does the old-style moves when it can.

Finally, as a result of making these somewhat fundamental changes, a
few minor code changes and re-designs are included that facilitated
the new functionalities easier.

BUG=chromium:474709
TEST=manual testing.

Change-Id: I9c98503b75bbc9eb7943d3b70e0d2a3f5ce74064
Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/272558
Reviewed-by: Dennis Kempin <denniskempin@chromium.org>
diff --git a/gesture_interpreter.py b/gesture_interpreter.py
index b837017..03a44e3 100644
--- a/gesture_interpreter.py
+++ b/gesture_interpreter.py
@@ -22,6 +22,9 @@
 FAT_FINGERTIP = '1round_14mm'
 FAT_SECONDARY_FINGERTIP = '2round_14mm'
 NOISE_TESTING_FINGERTIP = '1round_12mm'
+THREE_FINGER_FINGERTIP = 'three_fingers'
+FOUR_FINGER_FINGERTIP = 'four_fingers'
+FIVE_FINGER_FINGERTIP = 'five_fingers'
 
 BUFFER_SIZE = 0.1
 OVERSHOOT_DISTANCE = 0.05
@@ -77,6 +80,19 @@
     tests.GV.DIAGONAL: 45,
 }
 
+ONE_FINGERTIP_TAP_TESTS = [
+  tests.ONE_FINGER_TAP,
+  tests.THREE_FINGER_TAP,
+  tests.FOUR_FINGER_TAP,
+  tests.FIVE_FINGER_TAP,
+]
+
+TAP_TEST_FINGERTIPS = {
+  tests.ONE_FINGER_TAP: STANDARD_FINGERTIP,
+  tests.THREE_FINGER_TAP: THREE_FINGER_FINGERTIP,
+  tests.FOUR_FINGER_TAP: FOUR_FINGER_FINGERTIP,
+  tests.FIVE_FINGER_TAP: FIVE_FINGER_FINGERTIP,
+}
 
 SINGLE_FINGER_LINE_TESTS = [
     tests.ONE_FINGER_TO_EDGE,
@@ -121,36 +137,46 @@
 def PerformCorrespondingGesture(test, variation_number, robot, device_spec):
   variation = test.variations[variation_number]
   fn = None
+
   if test.name == tests.NOISE_STATIONARY:
     fn = lambda: _PerformStationaryNoiseTest(variation, robot, device_spec)
-  elif test.name == tests.ONE_FINGER_TAP:
-    fn = lambda: _PerformOneFingerTapTest(variation, robot, device_spec)
+
+  elif test.name in ONE_FINGERTIP_TAP_TESTS:
+    fingertip = robot.fingertips[TAP_TEST_FINGERTIPS[test.name]]
+    vertical_offset = 0
+    if test.name != tests.ONE_FINGER_TAP:
+      vertical_offset = robot.LARGE_FINGERTIP_VERTICAL_OFFSET
+    fn = lambda: _PerformOneFingertipTapTest(variation, robot, device_spec,
+                                             fingertip, vertical_offset)
   elif test.name == tests.TWO_FINGER_TAP:
     fn = lambda: _PerformTwoFingerTapTest(variation, robot, device_spec)
+
   elif test.name in SINGLE_FINGER_LINE_TESTS:
     pause = 1 if test.name == tests.ONE_FINGER_TRACKING_FROM_CENTER else 0
     is_swipe = (test.name == tests.ONE_FINGER_SWIPE)
     fn = lambda: _PerformOneFingerLineTest(variation, robot, device_spec,
                                            pause, is_swipe)
+
   elif test.name in TWO_FINGER_LINE_TESTS:
     spacing = 5
     fingertips = [robot.fingertips[STANDARD_FINGERTIP],
                   robot.fingertips[STANDARD_SECONDARY_FINGERTIP]]
     is_swipe = (test.name == tests.TWO_FINGER_SWIPE)
-
     if test.name == tests.TWO_CLOSE_FINGERS_TRACKING:
       spacing = 0
     elif test.name == tests.TWO_FAT_FINGERS_TRACKING:
       spacing = 10
       fingertips = [robot.fingertips[FAT_FINGERTIP],
                     robot.fingertips[FAT_SECONDARY_FINGERTIP]]
-
     fn = lambda: _PerformTwoFingerLineTest(variation, robot, device_spec,
                                            fingertips, spacing, is_swipe)
+
   elif test.name == tests.RESTING_FINGER_PLUS_2ND_FINGER_MOVE:
     fn = lambda: _PerformRestingFingerTest(variation, robot, device_spec)
+
   elif test.name == tests.PINCH_TO_ZOOM:
     fn = lambda: _PerformPinchTest(variation, robot, device_spec)
+
   elif test.name == tests.DRAG_THUMB_EDGE:
     fn = lambda: _PerformThumbEdgeTest(variation, robot, device_spec)
 
@@ -168,11 +194,12 @@
   robot.Tap(device_spec, [fingertip], tap_position, touch_time_s=4)
 
 
-def _PerformOneFingerTapTest(variation, robot, device_spec):
+def _PerformOneFingertipTapTest(variation, robot, device_spec, fingertip,
+                                vertical_offset):
   location, = variation
   tap_position = LOCATION_COORDINATES[location]
-  fingertip = robot.fingertips[STANDARD_FINGERTIP]
-  robot.Tap(device_spec, [fingertip], tap_position)
+  robot.Tap(device_spec, [fingertip], tap_position,
+            vertical_offset=vertical_offset)
 
 
 def _PerformTwoFingerTapTest(variation, robot, device_spec):