blob: aa884f6132370b56e72260d860dc194960b41f1c [file] [log] [blame]
"""
Copyright (c) 2019, OptoFidelity OY
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the OptoFidelity OY.
4. Neither the name of the OptoFidelity OY nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from operator import add, sub
from .tnt_client import TnTClient, TnTClientObject, RequestError, TnTDutPoint
from .tnt_robot_client import TnTRobotClient
from . import logger
import base64
log = logger.get_logger(__name__)
class TnTDUTClient(TnTClientObject):
""" This class implements methods to control TnT Server DUT resources. """
def __init__(self, name, workspace=TnTClientObject.default_workspace, host="127.0.0.1", port=8000):
TnTClientObject.__init__(self, host, port, workspace, 'dut', name)
self._data = None
self._parsed_data = None
try:
self._refresh_data()
except RequestError as err:
raise RuntimeError("Could not initialize DUT client: " + str(err))
self._robot = None
def _get_dut_data(self, name):
self._refresh_data()
return self._parsed_data[name]
def _refresh_data(self):
"""
Updates DUT information from the server.
"""
self._data = self._GET("")["properties"]
self._parsed_data = {}
self._parsed_data["width"] = self._data["width"]
self._parsed_data["height"] = self._data["height"]
tl = self._data["top_left"]
tr = self._data["top_right"]
bl = self._data["bottom_left"]
tl = [tl["x"], tl["y"], tl["z"]]
tr = [tr["x"], tr["y"], tr["z"]]
bl = [bl["x"], bl["y"], bl["z"]]
# Because there's no point to implement full vector class at this time:
tl_tr = map(sub, tr, tl)
tl_bl = map(sub, bl, tl)
br = map(add, tl, tl_tr)
br = map(add, br, tl_bl)
self._parsed_data["position"] = {
"top_left": tl,
"top_right": tr,
"bottom_left": bl,
"bottom_right": br,
}
self._parsed_data["top_left"] = tl
self._parsed_data["top_right"] = tr
self._parsed_data["bottom_left"] = bl
self._parsed_data["bottom_right"] = br
self._parsed_data["orientation"] = self._data["orientation"]
self._parsed_data["base_distance"] = self._data["base_distance"]
@property
def width(self):
""" Width of a DUT (X axis). """
return self._get_dut_data("width")
@property
def height(self):
""" Height of a DUT (Y axis). """
return self._get_dut_data("height")
@property
def position(self):
"""
Corner positions of the DUT in its relative coordinate system context.
Property is in dictionary from {"top_left": top_left, "top_right": top_right,
"bottom_left": bottom_left, "bottom_right": bottom_right}.
"""
return self._get_dut_data("position")
@property
def top_left(self):
""" Position of top left corner of the DUT. """
return self._get_dut_data("top_left")
@property
def top_right(self):
""" Position of top right corner of the DUT. """
return self._get_dut_data("top_right")
@property
def bottom_left(self):
""" Position of bottom left corner of the DUT. """
return self._get_dut_data("bottom_left")
@property
def bottom_right(self):
""" Position of bottom right corner of the DUT. """
return self._get_dut_data("bottom_right")
@property
def tl(self):
""" Shortcut for top_left. """
return self.top_left
@property
def tr(self):
""" Shortcut for top_right. """
return self.top_right
@property
def bl(self):
""" Shortcut for bottom_left. """
return self.bottom_left
@property
def br(self):
""" Shortcut for bottom_right. """
return self.bottom_right
@property
def orientation(self):
"""
Orientation of the DUT i.e. the basis vectors of DUT's transform matrix.
Given as dictionary {'i': x_basis_vector, 'j': y_basis_vector, 'k': z_basis_vector}.
"""
return self._get_dut_data("orientation")
@property
def robot(self):
""" Returns robot object which DUT is using for position queries and some movements. """
if self._robot is None:
raise ValueError("Active robot object not set.")
return self._robot
@robot.setter
def robot(self, value):
""" Sets robot object which DUT should use for position queries and some movements. """
if not isinstance(value, TnTRobotClient):
raise ValueError("Robot object must be of type TnTRobotClient.")
self._robot = value
@property
def base_distance(self):
"""
DUT base distance.
"""
return self._get_dut_data("base_distance")
def get_robot_position(self):
"""
Get the current robot position in DUT coordinates.
:return: Robot position in DUT coordinates.
"""
if self._robot is None:
raise ValueError("Active robot object not set.")
return self._robot.get_position(self.name)
def jump(self, x, y, z, jump_height=None):
"""
Performs a jump with given parameters.
In case jump_height is not given, robot jumps to maximum height along robot z axis.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param z: Target z coordinate on DUT.
:param jump_height: Height of the jump from DUT surface along DUT z-direction (default: jump to robot maximum height).
"""
if x is None or y is None or z is None:
raise ValueError("Parameters x, y, and z must be provided.")
params = {
"x": x,
"y": y,
"z": z
}
if jump_height is not None:
params["jump_height"] = jump_height
self._PUT("gestures/jump", params)
def move(self, x, y, z, tilt=None, azimuth=None):
"""
Moves in to given DUT position via straight path.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param z: Target z coordinate on DUT.
:param tilt: Tilt angle in DUT frame (default: 0).
:param azimuth: Azimuth angle in DUT frame (default: 0).
"""
if self._robot is None:
raise ValueError("Active robot object not set.")
self._robot.move(x=x, y=y, z=z, tilt=tilt, azimuth=azimuth, context=self.name)
def path(self, points):
"""
Moves through a path defined by given points in DUT context
:param points: List of TnTDutPoint objects that define points / poses in DUT context.
"""
params = {"poses": [p.to_dict() for p in points]}
self._PUT("gestures/path", params)
def swipe(self, x1, y1, x2, y2, tilt1=None, tilt2=None, azimuth1=None, azimuth2=None, clearance=0, radius=6):
"""
Performs swipe gesture in DUT context.
Robot accelerates and decelerates along an arc of given radius before and after touching the DUT.
:param x1: Start x coordinate on DUT.
:param y1: Start y coordinate on DUT.
:param x2: End x coordinate on DUT.
:param y2: End y coordinate on DUT.
:param tilt1: Start tilt angle in DUT frame.
:param tilt2: End tilt angle in DUT frame.
:param azimuth1: Start azimuth angle in DUT frame.
:param azimuth2: End azimuth angle in DUT frame.
:param clearance: Z coordinate on DUT when running swipe (default: 0).
:param radius: Swipe acceleration arc radius.
"""
params = {
"x1": x1,
"y1": y1,
"x2": x2,
"y2": y2,
"clearance": clearance,
"radius": radius
}
if tilt1 is not None:
params["tilt1"] = tilt1
if azimuth1 is not None:
params["azimuth1"] = azimuth1
if tilt2 is not None:
params["tilt2"] = tilt2
if azimuth2 is not None:
params["azimuth2"] = azimuth2
self._PUT("gestures/swipe", params)
def drag(self, x1, y1, x2, y2, z=None, tilt1=None, tilt2=None, azimuth1=None, azimuth2=None, clearance=0,
predelay=0, postdelay=0):
"""
Performs a drag gesture in DUT context.
:param x1: Start x coordinate on DUT.
:param y1: Start y coordinate on DUT.
:param x2: End x coordinate on DUT.
:param y2: End y coordinate on DUT.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt1: Start tilt angle in DUT frame.
:param tilt2: End tilt angle in DUT frame.
:param azimuth1: Start azimuth angle in DUT frame.
:param azimuth2: End azimuth angle in DUT frame.
:param clearance: Z coordinate on DUT when running drag (default: 0).
:param predelay: Delay between touchdown and move in seconds.
:param postdelay: Delay between the end of movement and touchup in seconds.
"""
params = {
"x1": x1,
"y1": y1,
"x2": x2,
"y2": y2,
"clearance": clearance,
"predelay": predelay,
"postdelay": postdelay
}
if z is not None:
params["z"] = z
if tilt1 is not None:
params["tilt1"] = tilt1
if azimuth1 is not None:
params["azimuth1"] = azimuth1
if tilt2 is not None:
params["tilt2"] = tilt2
if azimuth2 is not None:
params["azimuth2"] = azimuth2
send = self._PUT
send("gestures/drag", params)
def drag_force(self, x1, y1, x2, y2, force, z=None, tilt1=None, tilt2=None, azimuth1=None, azimuth2=None):
"""
Performs a drag gesture with force in DUT context.
:param x1: Start x coordinate on DUT.
:param y1: Start y coordinate on DUT.
:param x2: End x coordinate on DUT.
:param y2: End y coordinate on DUT.
:param force: Grams of force to apply when running on DUT surface.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt1: Start tilt angle in DUT frame.
:param tilt2: End tilt angle in DUT frame.
:param azimuth1: Start azimuth angle in DUT frame.
:param azimuth2: End azimuth angle in DUT frame.
"""
params = {
"x1": x1,
"y1": y1,
"x2": x2,
"y2": y2,
"force": force
}
if z is not None:
params["z"] = z
if tilt1 is not None:
params["tilt1"] = tilt1
if azimuth1 is not None:
params["azimuth1"] = azimuth1
if tilt2 is not None:
params["tilt2"] = tilt2
if azimuth2 is not None:
params["azimuth2"] = azimuth2
send = self._PUT
send("gestures/drag_force", params)
def tap(self, x, y, z=None, tilt=None, azimuth=None, clearance=0, duration=None):
"""
Performs a tap gesture in DUT context.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt: Tilt angle in DUT frame (default: 0).
:param azimuth: Azimuth angle in DUT frame (default: 0).
:param clearance: Z coordinate on DUT on tap down (default: 0).
:param duration: How long to keep finger down in seconds (default: 0s).
"""
params = {
"x": x,
"y": y,
"clearance": clearance
}
if z is not None:
params["z"] = z
if duration is not None:
params["duration"] = duration
if tilt is not None:
params["tilt"] = tilt
if azimuth is not None:
params["azimuth"] = azimuth
self._PUT("gestures/tap", params)
def watchdog_tap(self, x: float, y: float, z: float = None, tilt: float = 0, azimuth: float = 0,
clearance: float = 0, duration: float = 0):
"""
Performs a tap designed for Watchdog measurement with given parameters.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt: Tilt angle in DUT frame (default: 0).
:param azimuth: Azimuth angle in DUT frame (default: 0).
:param clearance: (optional) distance from DUT surface during movement
:param duration: How long to keep finger down in seconds (default: 0s).
:return: "ok" / error
"""
params = {
"x": x,
"y": y,
"tilt": tilt,
"azimuth": azimuth,
"clearance": clearance,
"duration": duration
}
if z is not None:
params["z"] = z
self._PUT("gestures/watchdog_tap", params)
def double_tap(self, x, y, z=None, tilt=None, azimuth=None, clearance=0, duration=None, interval=None):
"""
Performs a double tap gesture in DUT context.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt: Tilt angle in DUT frame (default: 0).
:param azimuth: Azimuth angle in DUT frame (default: 0).
:param clearance: Z coordinate on DUT on tap down (default: 0).
:param duration: How long to keep finger down in seconds (default: 0s).
:param interval: How long to wait between taps (default: 0s).
"""
params = {
"x": x,
"y": y,
"clearance": clearance
}
if z is not None:
params["z"] = z
if tilt is not None:
params["tilt"] = tilt
if azimuth is not None:
params["azimuth"] = azimuth
if duration is not None:
params["duration"] = duration
if interval is not None:
params["interval"] = interval
self._PUT("gestures/double_tap", params)
def multi_tap(self, points, lift=2, clearance=0):
"""
Performs multiple tap gestures in DUT context according to given points.
Start and end of multi-tap sequence is the base distance of the DUT.
:param points: List of TnTDUTPoint objects indicating where to tap. Z coordinates of the points
are ignored and lift and clearance are used for tap movement.
:param lift: Distance of how high the effector is raised from the DUT between the taps.
:param clearance: Z coordinate on DUT on tap down (default: 0).
"""
# Make one path
poselist = [{
"x": points[0].x,
"y": points[0].y,
"z": self.base_distance # safe approach height
}]
for p in points:
poselist += [{
"x": p.x,
"y": p.y,
"z": lift
},{
"x": p.x,
"y": p.y,
"z": clearance
},{
"x": p.x,
"y": p.y,
"z": lift,
}]
poselist += [{
"x": points[-1].x,
"y": points[-1].y,
"z": self.base_distance # safe height
}]
params = {
"poses": poselist
}
self._PUT("gestures/path", params)
def press(self, x, y, force, z=None, tilt=None, azimuth=None, duration=None, press_depth=-1):
"""
Performs a press gesture in DUT context.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param force: Force in grams, to be activated after moving to lower position.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt: Tilt angle in DUT frame (default: 0).
:param azimuth: Azimuth angle in DUT frame (default: 0).
:param duration: How long to keep specified force active in seconds (default: 0s).
:param press_depth: Distance from DUT surface during press, negative values being below/through DUT surface.
Tip will be pressed to the desired depth if the force limit is not reached before that.
If the force limit is reached before desired depth, the movement will stop there. (default: -1mm).
"""
params = {
"x": x,
"y": y,
"z": z,
}
if tilt is not None:
params["tilt"] = tilt
if azimuth is not None:
params["azimuth"] = azimuth
if force is not None:
params["force"] = force
if duration is not None:
params["duration"] = duration
if press_depth is not None:
params['press_depth'] = press_depth
self._PUT("gestures/press", params)
def circle(self, x, y, r, n=1, angle=0, z=None, tilt=None, azimuth=None, clearance=0, clockwise=False):
"""
Performs a circle gesture in DUT context.
:param x: Circle center x coordinate on DUT.
:param y: Circle center y coordinate on DUT.
:param r: Circle radius.
:param n: Number of revolutions. Floats can be used.
:param angle: Start circle angle in degrees. For 0 angle, x is x0+r and y is y0.
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param tilt: Tilt angle in DUT frame (default: 0).
:param azimuth: Azimuth angle in DUT frame (default: 0).
:param clearance: Z coordinate on DUT when moving in circle (default: 0).
:param clockwise: True to move clockwise, false to move counter clockwise.
"""
params = {
"x": x,
"y": y,
"r": r,
"n": n,
"angle": angle,
"clearance": clearance,
"clockwise": clockwise,
}
if z is not None:
params["z"] = z
if tilt is not None:
params["tilt"] = tilt
if azimuth is not None:
params["azimuth"] = azimuth
self._PUT("gestures/circle", params)
def list_buttons(self):
"""
List buttons of the current DUT.
:return: List of TnTDutPhysicalButtonClient objects.
"""
all_buttons = TnTClient(host=self.host, port=self.port, workspace=self.default_workspace).dutphysicalbuttons()
child_buttons = []
for button in all_buttons:
if button.parent == self.name:
child_buttons.append(button)
return child_buttons
def press_button(self, button_name):
"""
Press DUT button.
:param button_name: Name of button to press.
"""
buttons = self.list_buttons()
for button in buttons:
if button.name == button_name:
return button.press()
def screenshot(self, camera_id="Camera1", **kwargs):
"""
Capture screenshot.
:param camera_id: Id of camera to use to take a screenshot.
:param kwargs: extra parameters to pass to camera like exposure, gain
:return: image name <str>
"""
params = {
"camera_id": camera_id
}
response = self._POST("screenshot", params)
log.debug('screenshot response {}{d}'.format(response, d=dir(response)))
if response and "name" in response.keys():
return response["name"]
else:
return None
def find_objects(self, filename, min_score=None, crop_left=None, crop_upper=None,
crop_right=None, crop_lower=None, crop_unit=None):
"""
Find an object model (.shm) from the currently visible portion of the screen.
:example:
Response body:
success -- Any matches found [True | False]
screenshot -- Address of the screenshot
results -- Array of Result objects
Result object:
score -- Match score [0.0 .. 1.0]
topLeftX -- Bounding box top left x coordinate
topLeftY -- Bounding box top left x coordinate
bottomRightX -- Bounding box bottom right x coordinate
bottomRightY -- Bounding box bottom right y coordinate
centerX -- Bounding box center x coordinate
centerY -- Bounding box center y coordinate
shape -- Name of the shape file
Example return value:
{
"success": True
"screenshot": "localhost:8000/TnTAPI/Screenshots/1.jpg"
"results":
[{
"topLeftX": 4.21,
"topLeftY": 2.37,
"bottomRightX": 6.23,
"bottomRightY": 3.54,
"centerX": 5.22,
"centerY": 2.96,
"shape": "shapeName.shm",
"score": 0.781
}]
}
:param filename: Name of file containing object model.
:param min_score: Minimum accepted confidence score of result [0.0 .. 1.0] (optional).
:param crop_left: Left coordinate for cropping rectangle.
:param crop_upper: Upper coordinate for cropping rectangle.
:param crop_right: Right coordinate for cropping rectangle.
:param crop_lower: Lower coordinate for cropping rectangle.
:param crop_unit: Unit of crop coordinates (per/mm/pix).
:return: Dictionary with keys: "success", "screenshot", "results".
Response body:
success -- Any matches found [True | False]
screenshot -- Address of the screenshot
results -- Array of Result objects
Result object:
score -- Match score [0.0 .. 1.0]
topLeftX -- Bounding box top left x coordinate
topLeftY -- Bounding box top left x coordinate
bottomRightX -- Bounding box bottom right x coordinate
bottomRightY -- Bounding box bottom right y coordinate
centerX -- Bounding box center x coordinate
centerY -- Bounding box center y coordinate
shape -- Name of the shape file
Example return value:
{
"success": True
"screenshot": "localhost:8000/TnTAPI/Screenshots/1.jpg"
"results":
[{
"topLeftX": 4.21,
"topLeftY": 2.37,
"bottomRightX": 6.23,
"bottomRightY": 3.54,
"centerX": 5.22,
"centerY": 2.96,
"shape": "shapeName.shm",
"score": 0.781
}]
}
"""
params = {
"filename": filename,
}
if min_score is not None:
params["min_score"] = min_score
if crop_left is not None:
params["crop_left"] = crop_left
if crop_right is not None:
params["crop_right"] = crop_right
if crop_upper is not None:
params["crop_upper"] = crop_upper
if crop_lower is not None:
params["crop_lower"] = crop_lower
if crop_unit is not None:
params["crop_unit"] = crop_unit
return self._POST("find_objects", params)
def search_text(self, pattern, regexp=False, language='English', min_score=1.0, case_sensitive=True,
crop_left=None, crop_upper=None, crop_right=None, crop_lower=None, crop_unit=None):
"""
Search text pattern from the visible portion of the DUT screen.
:param pattern: Text or pattern to find. Search all by default.
:param regexp: Use pattern as a regexp. [True | False (default)]
:param language: OCR language. [English (default)|Finnish]
:param min_score: Minimum score (confidence value). 0.0 - 1.0. Default is 1.0. Value over 0.6 means the sequences are close matches.
:param case_sensitive: Should the comparision be done case sensitive or not. [True (default) | False].
:param crop_left: Left coordinate for cropping rectangle.
:param crop_right: Right coordinate for cropping rectangle.
:param crop_upper: Upper coordinate for cropping rectangle.
:param crop_lower: Lower coordinate for cropping rectangle.
:param crop_unit: Unit of crop coordinates (per/mm/pix).
:return: Dictionary with keys "success", and "results".
Example return:
{
"success": True
"results":
[{
"topLeftX": 4.21,
"topLeftY": 2.37,
"bottomRightX": 6.23,
"bottomRightY": 3.54,
"centerX": 5.22,
"centerY": 2.96,
"score": 0.781
}]
}
"""
params = {
"pattern": pattern,
"regexp": regexp,
"language": language,
"min_score": min_score,
"case_sensitive": case_sensitive
}
if crop_left is not None:
params["crop_left"] = crop_left
if crop_right is not None:
params["crop_right"] = crop_right
if crop_upper is not None:
params["crop_upper"] = crop_upper
if crop_lower is not None:
params["crop_lower"] = crop_lower
if crop_unit is not None:
params["crop_unit"] = crop_unit
return self._POST("search_text", params)
def show_image(self, image: bytes):
"""
Show bytes image on DUT screen.
:param image: Image as bytes. None will empty the screen
:return: "ok" / error
"""
if image is not None:
data = base64.encodebytes(image).decode("ascii")
else:
data = ""
return self._PUT("show_image", {"image": data})
def info(self):
"""
Get raw info dictionary from DUT.
Contains information about DUT model, display resolution, etc.
:return: dict
"""
return self._GET("info")
def touches(self):
"""
Gets list of touches since last call of this function.
If you want to clear the touches buffer, call this function and discard the results.
Current Android application supports the following fields:
x Touch x-coordinate. Touch resolution is the same as screen pixel resolution.
y Touch y-coordinate. Touch resolution is the same as screen pixel resolution.
pressure Touch pressure, float value in range 0..1, device dependent and not any standard unit.
id Touch id. Sequential number where every new finger to touch the screen
takes the first free positive number as id.
action Numbered enumeration where:
0 = touch start
1 = touch end
2 = touch move / stays pressed at point
3 = touch cancelled
orientation Stylus event; angle in radians where 0 == north, -pi/2 = west, pi/2 = east
azimuth Stylus event; angle in radians where 0 == normal to surface and M_PI/2 is flat to surface.
distance Stylus event; 0.0 indicates direct contact and larger values
indicate increasing distance from the surface.
:return: (dict) where key 'fields' is a list of touch field names.
and where key 'touches' is a list of touches. One touch is an array of values.
"""
r = self._GET("touches")
return r
def filter_points(self, points, region, margin=0):
"""
Filter a list of points that are inside of DUT shape, given region and margin.
:param points: List of (x, y) points, millimeters.
:param region: Name of the region.
:param margin: Margin inwards the given region, millimeters.
:return: Filtered list of (x, y) points.
"""
p = {"points": points, "region_name": region, "margin": margin}
r = self._PUT("filter_points", p)
return r
def filter_lines(self, lines, region, margin=0):
"""
Filter list of (x1, y1, x2, y2) lines with given region and margin.
The filter will cut lines to pieces that fit inside the region with given margin.
One given line can result to none or several lines.
:param lines: List of (x1, y1, x2, y2) lines, in millimeters.
:param region: Name of the filter region.
:param margin: Margin inwards the region, millimeters.
:return: List of list of (x1, y1, x2, y2) lines (each given line results to a list of lines).
"""
p = {"lines": lines, "region_name": region, "margin": margin}
r = self._PUT("filter_lines", p)
return r
def region_contour(self, region, num_points):
"""
Return the given region as a list of (x, y) points.
"contour" as in how OpenCV names the approximation of a shape as a point list:
'Contours can be explained simply as a curve joining all the continuous points (along the boundary)'
Can be used to OpenCV shape analysis or any other shape-analysis or geometric operation.
:param region: Name of the region.
:param num_points: Number of points to use on contour.
:return: List of (x, y) contour points, in millimeters.
"""
r = self._GET("region_contour", {"region_name": region, "num_points": num_points})
return r
def svg_data(self):
"""
Return the svg file specified for the dut. If there is
no specified file, return empty string
:return: svg file or empty string
"""
r = self._GET("svg_data")
return r
"""
Google specific gestures
"""
def pinch(self, x, y, d1, d2, azimuth, z=None, clearance=0):
"""
Performs a pinch gesture in DUT context.
:param x: Target x coordinate on DUT. Target is the middle position between fingers.
:param y: Target y coordinate on DUT. Target is the middle position between fingers.
:param d1: Distance between fingers at the beginning.
:param d2: Distance between fingers at the end.
:param azimuth: Azimuth angle during the pinch.
:param z: Overrides base distance.
:param clearance: Distance from DUT surface during gesture.
"""
params = {
"x": x,
"y": y,
"d1": d1,
"d2": d2,
"azimuth": azimuth,
"clearance": clearance
}
if z is not None:
params.update({"z": z})
self._PUT("gestures/pinch", params)
def drumroll(self, x, y, azimuth, separation, tap_count, tap_duration, clearance=0):
"""
Taps tap_count times with two fingers, one finger at a time, starting with finger 1 (left finger).
Tapping is done with given azimuth angle and finger separation.
Tapping is done tap_duration seconds, to which period the tap_count number of taps is layed out evenly.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param azimuth: Azimuth angle during the gesture.
:param separation: Separation between the fingers during the gesture, millimeters.
:param tap_count: Number of taps to perform.
:param tap_duration: Duration of all taps together, seconds.
:param clearance: Distance from DUT surface during gesture.
"""
params = {
"x": x,
"y": y,
"azimuth": azimuth,
"separation": separation,
"tap_count": tap_count,
"tap_duration": tap_duration,
"clearance": clearance
}
self._PUT("gestures/drumroll", params)
def compass(self, x, y, azimuth1, azimuth2, separation, z=None, clearance=0):
"""
Compass movement.
Primary finger (the left finger) stays at x, y position while the other finger rotates around it.
Rotation starts at azimuth angle a1 and ends at azimuth angle a2.
Rotation will be done to shortest route direction.
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param azimuth1: Start azimuth angle.
:param azimuth2: End azimuth angle.
:param separation: Distance between fingers during the gesture
:param z: Target z coordinate on DUT when hovering before and after gesture (default: DUT's base_distance).
:param clearance: Distance from DUT surface during gesture.
"""
params = {
"x": x,
"y": y,
"azimuth1": azimuth1,
"azimuth2": azimuth2,
"separation": separation,
"clearance": clearance
}
if z is not None:
params.update({"z": z})
self._PUT("gestures/compass", params)
def compass_tap(self, x, y, azimuth1, azimuth2, separation, tap_azimuth_step, z=None,
tap_with_stationary_finger=False, clearance=0):
"""
Compass movement with tapping.
Primary finger (the left finger) stays at x, y position while the other finger rotates around it.
Rotation starts at azimuth angle a1 and ends at azimuth angle a2.
Rotation will be done to shortest route direction.
Tapping is done with selected finger. (moving finger by default)
:param x: Target x coordinate on DUT.
:param y: Target y coordinate on DUT.
:param azimuth1: Start azimuth angle.
:param azimuth2: End azimuth angle.
:param separation: Distance between fingers during the gesture.
:param tap_azimuth_step: Angle in degrees between taps.
:param z: Target z coordinate on DUT when hovering before and after gesture and in-between taps (default: DUT's base_distance).
:param tap_with_stationary_finger: Stationary or moving finger does the tapping.
:param clearance: Distance from DUT surface during gesture.
"""
params = {
"x": x,
"y": y,
"azimuth1": azimuth1,
"azimuth2": azimuth2,
"separation": separation,
"tap_azimuth_step": tap_azimuth_step,
"tap_with_stationary_finger": tap_with_stationary_finger,
"clearance": clearance
}
if z is not None:
params.update({"z": z})
self._PUT("gestures/compass_tap", params)
def touch_and_tap(self, touch_x: float, touch_y: float, tap_x: float, tap_y: float, z: float = None,
number_of_taps=1, tap_predelay=0, tap_duration=0, tap_interval=0, clearance: float=0):
"""
Performs a gesture where primary finger is touching a location and secondary finger is doing a taping gesture
at another location.
You can define the x, y coordinates for both fingers separately.
:param touch_x: Touching finger x, millimeters.
:param touch_y: Touching finger y, millimeters.
:param tap_x: Tapping finger x, millimeters.
:param tap_y: Tapping finger y, millimeters.
:param z: Target z coordinate on DUT when hovering before and after gesture and in-between taps (default: DUT's base_distance).
:param number_of_taps: Number of taps to perform. 1 for single tap, 2 for double tap, ...
:param tap_predelay: Duration to touch with primary finger before tapping with secondary finger.
:param tap_duration: Time to wait during the tap finger down, seconds.
:param tap_interval: Time interval between the taps, seconds. Only affects number_of_taps >= 2
:param clearance: (optional) distance from DUT surface during movement
:return: "ok" / error
"""
params = {"touch_x": touch_x,
"touch_y": touch_y,
"tap_x": tap_x,
"tap_y": tap_y,
"number_of_taps": number_of_taps,
"tap_predelay": tap_predelay,
"tap_duration": tap_duration,
"tap_interval": tap_interval,
"clearance": clearance}
if z is not None:
params.update({"z": z})
return self._PUT("gestures/touch_and_tap", params)
def line_tap(self, x1: float, y1: float, x2: float, y2: float, tap_distances: list, separation: float=None,
azimuth: float=0, z: float=None, clearance: float=0):
"""
Moves the robot along a line between x1, y1 and x2, y2 and taps the surface with primary finger
at given distances relative to the x1, y1 starting position.
:param x1: Line start x, millimeters.
:param y1: Line start y, millimeters.
:param x2: Line end x, millimeters.
:param y2: Line end y, millimeters.
:param tap_distances: Tap locations as in distances from the beginning of the line, millimeters.
:param separation: Distance between finger centers, millimeters. If not defined then default distance is used.
:param azimuth: Azimuth angle to use during the line.
:param z: Target z coordinate on DUT when hovering before and after gesture and in-between taps.
(default: DUT's base_distance).
:param clearance: (optional) Distance from DUT surface during movement.
:return: "ok" / error
"""
params = {"x1": x1,
"y1": y1,
"x2": x2,
"y2": y2,
"tap_distances": tap_distances,
"azimuth": azimuth,
"clearance": clearance}
if separation is not None:
params.update({"separation": separation})
if z is not None:
params.update({"z": z})
return self._PUT("gestures/line_tap", params)