def move_to(self, x=None, y=None, z=None, check=False, translate=True):
    """
    Move to an absolute cartesian coordinate
    :param x: Cartesian millimeter of the X axis, if None then uses current position
    :param y: Cartesian millimeter of the Y axis, if None then uses current position
    :param z: Cartesian millimeter of the Z axis, if None then uses current position
    :param check: If True, asks the connected uArm device if the target coordinate is within its range of movement
    :return: self
    """
    logger.debug('move_to: x={0}, y={1}, z={2}'.format(x, y, z))
    if not self._enabled:
      self.enable_all_motors()
    new_pos = self._pos.copy()
    if x is not None:
      new_pos['x'] = x
    if y is not None:
      new_pos['y'] = y
    if z is not None:
      new_pos['z'] = z
    new_pos = round_position(new_pos)
    if translate:
      real_pos = self._remove_z_offset(new_pos)
    else:
      real_pos = new_pos.copy()
    if not self.is_simulating():
      if check and not self.can_move_to(**real_pos):
        raise RuntimeError(
          'Coordinate not reachable by uArm: {0}'.format(new_pos))
      speed_mm_per_min = self._speed * 60
      self.set_position(relative=False, speed=speed_mm_per_min, **real_pos)

    self._pos = new_pos.copy()
    return self
 def update_position(self, check=False):
   """
   Retrieve the current XYZ coordinate position from the connected uArm device
   :return: self
   """
   logger.debug('update_position')
   if self.is_simulating():
     return self
   pos = self.get_position(wait=True)
   is_n = pos is None
   is_l = isinstance(pos, list)
   if is_n or not is_l or not len(pos) or not isinstance(pos[0], float):
     logger.debug('Not able to read position, out of bounds')
     return self
   new_pos = round_position(self._apply_z_offset({
     'x': pos[0],
     'y': pos[1],
     'z': pos[2]
   }))
   distance = 0
   check = check and self._enabled  # no need to check if motors are disabled
   if check:
     old_pos = self._pos.copy()
     distance, _, _ = cartesian_to_polar(
       **subtract_positions(new_pos, old_pos))
   self._pos = new_pos
   if check and distance > UARM_SKIPPED_DISTANCE_THRESHOLD:
     raise RuntimeError(
       'Detected {0}mm skipped: target={1} - actual={2}'.format(
         round(distance, 1), old_pos, new_pos))
   logger.debug('New Position: {0}'.format(self._pos))
   return self
 def can_move_relative(self, x=None, y=None, z=None):
   logger.debug('can_move_relative: x={0}, y={1}, z={2}'.format(x, y, z))
   new_pos = self._pos.copy()
   if x is not None:
     new_pos['x'] = x + new_pos['x']
   if y is not None:
     new_pos['y'] = y + new_pos['y']
   if z is not None:
     new_pos['z'] = z + new_pos['z']
   new_pos = round_position(new_pos)
   return self.can_move_to(**new_pos)
 def can_move_to(self, x=None, y=None, z=None):
   logger.debug('can_move_to: x={0}, y={1}, z={2}'.format(x, y, z))
   if self.is_simulating():
     return True  # no way to test during simulation
   new_pos = self._pos.copy()
   if x is not None:
     new_pos['x'] = x
   if y is not None:
     new_pos['y'] = y
   if z is not None:
     new_pos['z'] = z
   new_pos = round_position(new_pos)
   # Send coordinates to uArm to see if they are within the limit.
   # This must be done on the device itself, because of it's weird
   # coordinate system and load-carrying ability at different positions.
   new_pos = self._remove_z_offset(new_pos)
   unreachable = self.check_pos_is_limit(list(new_pos.values()))
   return not bool(unreachable)
 def move_relative(self, x=None, y=None, z=None, check=False):
   """
   Move to a relative cartesian coordinate, away from it's current coordinate
   :param x: Cartesian millimeter of the X axis, if None then uses current position
   :param y: Cartesian millimeter of the Y axis, if None then uses current position
   :param z: Cartesian millimeter of the Z axis, if None then uses current position
   :param check: If True, asks the connected uArm device if the target coordinate is within its range of movement
   :return: self
   """
   logger.debug('move_relative: x={0}, y={1}, z={2}'.format(x, y, z))
   rel_pos = self._pos.copy()
   if x is not None:
     rel_pos['x'] = x + self._pos['x']
   if y is not None:
     rel_pos['y'] = y + self._pos['y']
   if z is not None:
     rel_pos['z'] = z + self._pos['z']
   rel_pos = round_position(rel_pos)
   # using only absolute movements, because accelerations do not seem to take
   # affect when using relative movements (not sure if firmware or API issue)
   self.move_to(check=check, **rel_pos)
   return self