blob: fbe79a5a4aba40cbbad6bbbd7ebe2fe6c9033fcb [file] [log] [blame]
# Copyright 2016 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.
"""Control a robot to move a device for testing specific sensors.
Description
-----------
This test controls a robot (arm) to move a device along a predefined
trajectory, to verify the functionalities of the sensors on the device.
Test Procedure
--------------
1. Robot is moved to the unloaded position.
2. Ask the operator to mount the device.
3. Robot is moved to the loaded position.
4. Robot is moved by the given trajectory.
5. Robot is moved to the unloaded position.
6. Verify the data from sensors on the device.
7. Ask the operator to unmount the device.
Dependency
----------
Depend on the class specified by the arguments `robot_fixture` and
`algorithm` to move the robot, get result from the device, and
verify the result.
Examples
--------
To use `robot.foo` to control the robot fixture, and use `algo.bar` to
calculate and verify the sensor data, add this in test list::
{
"pytest_name": "robot_movement",
"args": {
"positions": [0, 1, 2, 3, 4, 5],
"robot_fixture": "robot.foo",
"algorithm": "algo.bar"
}
}
One can also pass parameters to the classes specified in `robot_fixture` and
`algorithm`::
{
"pytest_name": "robot_movement",
"args": {
"positions": [0, 1, 2, 3, 4, 5],
"robot_fixture": "robot.foo",
"robot_fixture_args": {
"speed": 30
},
"algorithm_args": {
"trajectory_id": "bar1"
},
"algorithm": "algo.bar"
}
}
"""
import logging
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test import session
from cros.factory.test.fixture import utils as fixture_utils
from cros.factory.test.i18n import _
from cros.factory.test import server_proxy
from cros.factory.test import test_case
from cros.factory.test import test_ui
from cros.factory.utils.arg_utils import Arg
class RobotMovement(test_case.TestCase):
"""A general task that use a robot to move the device.
Two detail implementations are required for this task. One is the fixture for
controlling the robot, and the other is the algorithm for computation.
The process of this task is as following:
1. Initiate robot and ask operators to load the device.
2. Prepare DUT to start movement.
3. Move the robot arm according to the given positions.
4. After finishing movement, kick off the computation.
5. Push the results.
6. Log the files and results.
"""
ARGS = [
Arg('robot_fixture', str,
'The class name of the robot fixture under '
'``cros.factory.test.fixture``, should '
'be a subclass of ``cros.factory.test.fixture.imu.robot.Robot``.'
'E.g. robot.dummy_robot.DummyRobot'),
Arg('robot_fixture_args', dict,
'A dict of the args used for the constructor of the robot_fixture.',
default={}),
Arg('algorithm', str,
'The class name of the computing method under '
'``cros.factory.test.fixture``, should be a subclass of '
'``cros.factory.test.fixture.robot.algorithm.BaseAlgorithm``.'
'E.g. robot.dummy_algorithm.DummyAlgorithm'),
Arg('algorithm_args', dict,
'A dict of the args used for the constructor of the algorithm.',
default={}),
Arg('period_between_movement', (float, int),
'The pause period between two movements.', default=0.5),
Arg('period_after_movement', (float, int),
'The pause period after the final movements.', default=0.0),
Arg('positions', list,
'A list of position index for the robot to move.'),
Arg('upload_to_server', bool,
'If true, upload log to factory server after running.',
default=False),
]
def setUp(self):
self._dut = device_utils.CreateDUTInterface()
self._robot = fixture_utils.CreateFixture(
self.args.robot_fixture, self.args.robot_fixture_args)
self._algorithm = fixture_utils.CreateFixture(
self.args.algorithm, self.args.algorithm_args)
self._algorithm.SetLogger(session.console)
self.ui.ToggleTemplateClass('font-large', True)
def tearDown(self):
try:
self._robot.SetLED(False)
self._robot.LoadDevice(False)
self._robot.SetMotor(False)
except Exception:
pass
self._robot.Disconnect()
def Initialize(self):
"""Initializes the robot.
Intializes robot and move it to the LOAD / UNLOAD position.
"""
self.ui.SetState(_('Initializing Robot...'))
session.console.info('Intializing robot.')
self._robot.Connect()
self._robot.SetMotor(True)
def LoadDevice(self):
"""Ask operator to load DUT."""
self.ui.SetState(
_('Please load DUT onto the robot, connect all cables, '
'and press <b>SPACE</b> to continue.'))
self._robot.LoadDevice(False)
session.console.info('Wait for operators to press SPACE.')
self.ui.WaitKeysOnce(test_ui.SPACE_KEY)
session.console.info('SPACE pressed by operator.')
self.ui.SetState(_('Prepare for movement.'))
self._robot.LoadDevice(True)
def StartMoving(self):
"""Starts movement process."""
self.ui.SetState(_('Moving to start position...'))
session.console.info('Start to move.')
self._robot.SetLED(True)
self._algorithm.OnStartMoving(self._dut)
for position in self.args.positions:
session.console.info('Move to position %d.', position)
self._robot.MoveTo(position)
self.Sleep(self.args.period_between_movement)
self.Sleep(self.args.period_after_movement)
self._algorithm.OnStopMoving(self._dut)
self.ui.SetState(_('Moving to LOAD / UNLOAD position...'))
self._robot.SetLED(False)
# Shutdown and disconnect robot here to avoid robot overload during
# computing.
self._robot.LoadDevice(False)
self._robot.SetMotor(False)
self._robot.Disconnect()
def Compute(self):
"""Starts computing after the movement."""
self.ui.SetState(_('Computing...'))
session.console.info('Compute for %s', self._dut.info.serial_number)
self._algorithm.Compute(self._dut)
def PushResult(self):
"""Pushes the result to the DUT."""
self.ui.SetState(_('Pushing the result...'))
session.console.info('Pushing the result.')
self._algorithm.PullResult(self._dut)
def runTest(self):
serial_number = self._dut.info.serial_number
if not serial_number:
self.fail('Failed to get the device SN')
session.console.info('SN: %s', serial_number)
logging.info('SN: %s', serial_number)
self.Initialize()
self.LoadDevice()
self.StartMoving()
self.Compute()
self.PushResult()
if self.args.upload_to_server:
self._algorithm.UploadLog(self._dut, server_proxy.GetServerProxy())