Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/viam/app/app_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from viam import logging
from viam.app._logs import _LogsStream, _LogsStreamWithIterator
from viam.proto.app import (
AddFavoriteMachineRequest,
AddFavoriteMachineResponse,
AddRoleRequest,
APIKeyWithAuthorizations,
AppServiceStub,
Expand All @@ -20,6 +22,8 @@
ChangeRoleRequest,
CheckPermissionsRequest,
CheckPermissionsResponse,
ConfigPropertyChange,
ConfigPropertyChangeResult,
CreateFragmentRequest,
CreateFragmentResponse,
CreateKeyFromExistingKeyAuthorizationsRequest,
Expand Down Expand Up @@ -58,6 +62,7 @@
DeleteRobotRequest,
DeprecateRegistryItemRequest,
DeprecateRegistryItemVersionRequest,
FavoriteMachine,
FragmentImport,
FragmentImportList,
GetDevicePushTokensRequest,
Expand Down Expand Up @@ -106,6 +111,8 @@
GetUserIDByEmailResponse,
ListAuthorizationsRequest,
ListAuthorizationsResponse,
ListFavoriteMachinesRequest,
ListFavoriteMachinesResponse,
ListFragmentsRequest,
ListFragmentsResponse,
ListKeysRequest,
Expand Down Expand Up @@ -147,13 +154,17 @@
OrgDetails,
RegistryItem,
RegistryItemStatus,
RemoveFavoriteMachineRequest,
RemoveFavoriteMachineResponse,
RemoveRoleRequest,
ResendOrganizationInviteRequest,
ResendOrganizationInviteResponse,
Robot,
RotateKeyRequest,
RotateKeyResponse,
RoverRentalRobot,
SetConfigPropertyRequest,
SetConfigPropertyResponse,
SetFirebaseConfigRequest,
SetFirebaseConfigResponse,
SharedSecret,
Expand Down Expand Up @@ -1664,6 +1675,27 @@ async def new_robot_part(self, robot_id: str, part_name: str) -> str:
response: NewRobotPartResponse = await self._app_client.NewRobotPart(request, metadata=self._metadata)
return response.part_id

async def set_config_property(
self, robot_part_id: str, property_changes: Mapping[str, ValueTypes], timeout: float | None = None
) -> list[ConfigPropertyChangeResult]:
"""Set individual values within a machine part's config by JSON path, without replacing the whole config.

Args:
robot_part_id (str): ID of the robot part to update.
property_changes (Mapping[str, ValueTypes]): A mapping of JSON paths within the config to the new value at that path.
timeout (float | None): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:
list[viam.proto.app.ConfigPropertyChangeResult]: The result of each requested property change.
"""
changes = [
ConfigPropertyChange(path=path, value=dict_to_struct({"value": value}).fields["value"])
for path, value in property_changes.items()
]
request = SetConfigPropertyRequest(robot_part_id=robot_part_id, property_changes=changes)
response: SetConfigPropertyResponse = await self._app_client.SetConfigProperty(request, metadata=self._metadata, timeout=timeout)
return list(response.property_changes)

async def delete_robot_part(self, robot_part_id: str) -> None:
"""Delete the specified machine part.

Expand Down Expand Up @@ -1902,6 +1934,41 @@ async def delete_robot(self, robot_id: str) -> None:
request = DeleteRobotRequest(id=robot_id)
await self._app_client.DeleteRobot(request, metadata=self._metadata)

async def add_favorite_machine(self, machine_id: str, timeout: float | None = None) -> None:
"""Add a machine to the current user's favorites.

Args:
machine_id (str): ID of the machine to favorite.
timeout (float | None): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.
"""
request = AddFavoriteMachineRequest(machine_id=machine_id)
_: AddFavoriteMachineResponse = await self._app_client.AddFavoriteMachine(request, metadata=self._metadata, timeout=timeout)

async def remove_favorite_machine(self, machine_id: str, timeout: float | None = None) -> None:
"""Remove a machine from the current user's favorites.

Args:
machine_id (str): ID of the machine to unfavorite.
timeout (float | None): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.
"""
request = RemoveFavoriteMachineRequest(machine_id=machine_id)
_: RemoveFavoriteMachineResponse = await self._app_client.RemoveFavoriteMachine(request, metadata=self._metadata, timeout=timeout)

async def list_favorite_machines(self, timeout: float | None = None) -> list[FavoriteMachine]:
"""List the current user's favorited machines.

Args:
timeout (float | None): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call.

Returns:
list[viam.proto.app.FavoriteMachine]: The list of favorited machines.
"""
request = ListFavoriteMachinesRequest()
response: ListFavoriteMachinesResponse = await self._app_client.ListFavoriteMachines(
request, metadata=self._metadata, timeout=timeout
)
return list(response.favorites)

async def list_fragments(
self, org_id: str, show_public: bool = True, visibilities: list[Fragment.Visibility] | None = None
) -> list[Fragment]:
Expand Down
3 changes: 2 additions & 1 deletion src/viam/components/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Mapping, Tuple, Union

from viam.proto.common import KinematicsFileFormat, Mesh
from viam.proto.common import KinematicModel, KinematicsFileFormat, Mesh

KinematicsReturn = Union[
Tuple[KinematicsFileFormat.ValueType, bytes],
Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]],
Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh], KinematicModel],
]

__all__ = [
Expand Down
15 changes: 14 additions & 1 deletion src/viam/components/arm/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,15 @@ async def is_moving(self) -> bool:
...

@abc.abstractmethod
async def get_kinematics(self, *, extra: dict[str, Any] | None = None, timeout: float | None = None, **kwargs) -> KinematicsReturn:
async def get_kinematics(
self,
*,
include_collision_meshes: bool = False,
include_visual_meshes: bool = False,
extra: dict[str, Any] | None = None,
timeout: float | None = None,
**kwargs,
) -> KinematicsReturn:
"""
Get the kinematics information associated with the arm.

Expand All @@ -435,13 +443,18 @@ async def get_kinematics(self, *, extra: dict[str, Any] | None = None, timeout:
# Get the byte contents of the file.
k_bytes = kinematics[1]

Args:
include_collision_meshes (bool): Include collision mesh bytes in the response. Otherwise meshes carry only their path.
include_visual_meshes (bool): Include visual mesh bytes in the response.

Returns:
Tuple[KinematicsFileFormat.ValueType, bytes]: A tuple containing two values; the first [0] value represents the format of the
file, either in URDF format (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF``) or
Viam's kinematic parameter format (spatial vector algebra) (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA``),
and the second [1] value represents the byte contents of the file.
If available, a third [2] value provides meshes keyed by URDF filepath.
See ``get_3d_models`` for meshes keyed by model name instead.
If available, a fourth [3] value provides the structured ``KinematicModel`` (links, joints, output frames, and properties).

For more information, see `Arm component <https://docs.viam.com/dev/reference/apis/components/arm/#getkinematics>`_.
"""
Expand Down
17 changes: 15 additions & 2 deletions src/viam/components/arm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,25 @@ async def get_status(
return struct_to_dict(response.result)

async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
self,
*,
include_collision_meshes: bool = False,
include_visual_meshes: bool = False,
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
) -> KinematicsReturn:
md = kwargs.get("metadata", self.Metadata()).proto
request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra))
request = GetKinematicsRequest(
name=self.name,
include_collision_meshes=include_collision_meshes,
include_visual_meshes=include_visual_meshes,
extra=dict_to_struct(extra),
)
response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md)
# TODO: handle empty meshes in the response to prevent silent mapping
if response.HasField("model"):
return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath, response.model)
return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath)

async def get_3d_models(
Expand Down
15 changes: 12 additions & 3 deletions src/viam/components/arm/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,22 @@ async def GetKinematics(self, stream: Stream[GetKinematicsRequest, GetKinematics
assert request is not None
arm = self.get_resource(request.name)
timeout = stream.deadline.time_remaining() if stream.deadline else None
kinematics = await arm.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
kinematics = await arm.get_kinematics(
include_collision_meshes=request.include_collision_meshes,
include_visual_meshes=request.include_visual_meshes,
extra=struct_to_dict(request.extra),
timeout=timeout,
metadata=stream.metadata,
)
model = None
if len(kinematics) == 2:
format, kinematics_data = kinematics
meshes = {}
else:
elif len(kinematics) == 3:
format, kinematics_data, meshes = kinematics
response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data, meshes_by_urdf_filepath=meshes)
else:
format, kinematics_data, meshes, model = kinematics
response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data, meshes_by_urdf_filepath=meshes, model=model)
await stream.send_message(response)

async def Get3DModels(self, stream: Stream[Get3DModelsRequest, Get3DModelsResponse]) -> None:
Expand Down
17 changes: 15 additions & 2 deletions src/viam/components/gantry/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,24 @@ async def get_status(
return struct_to_dict(response.result)

async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
self,
*,
include_collision_meshes: bool = False,
include_visual_meshes: bool = False,
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
) -> KinematicsReturn:
md = kwargs.get("metadata", self.Metadata()).proto
request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra))
request = GetKinematicsRequest(
name=self.name,
include_collision_meshes=include_collision_meshes,
include_visual_meshes=include_visual_meshes,
extra=dict_to_struct(extra),
)
response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md)
if response.HasField("model"):
return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath, response.model)
return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath)

async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]:
Expand Down
13 changes: 12 additions & 1 deletion src/viam/components/gantry/gantry.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,13 @@ async def is_moving(self) -> bool:

@abc.abstractmethod
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
self,
*,
include_collision_meshes: bool = False,
include_visual_meshes: bool = False,
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
) -> KinematicsReturn:
"""
Get the kinematics information associated with the gantry.
Expand All @@ -175,12 +181,17 @@ async def get_kinematics(
# Get the byte contents of the file.
k_bytes = kinematics[1]

Args:
include_collision_meshes (bool): Include collision mesh bytes in the response. Otherwise meshes carry only their path.
include_visual_meshes (bool): Include visual mesh bytes in the response.

Returns:
Tuple[KinematicsFileFormat.ValueType, bytes]: A tuple containing two values; the first [0] value represents the format of the
file, either in URDF format (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF``) or
Viam's kinematic parameter format (spatial vector algebra) (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA``),
and the second [1] value represents the byte contents of the file.
If available, a third [2] value provides meshes keyed by URDF filepath.
If available, a fourth [3] value provides the structured ``KinematicModel`` (links, joints, output frames, and properties).

For more information, see `Arm component <https://docs.viam.com/dev/reference/apis/components/arm/#getkinematics>`_.
"""
Expand Down
15 changes: 12 additions & 3 deletions src/viam/components/gantry/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,22 @@ async def GetKinematics(self, stream: Stream[GetKinematicsRequest, GetKinematics
assert request is not None
gantry = self.get_resource(request.name)
timeout = stream.deadline.time_remaining() if stream.deadline else None
kinematics = await gantry.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
kinematics = await gantry.get_kinematics(
include_collision_meshes=request.include_collision_meshes,
include_visual_meshes=request.include_visual_meshes,
extra=struct_to_dict(request.extra),
timeout=timeout,
metadata=stream.metadata,
)
model = None
if len(kinematics) == 2:
format, data = kinematics
meshes = {}
else:
elif len(kinematics) == 3:
format, data, meshes = kinematics
response = GetKinematicsResponse(format=format, kinematics_data=data, meshes_by_urdf_filepath=meshes)
else:
format, data, meshes, model = kinematics
response = GetKinematicsResponse(format=format, kinematics_data=data, meshes_by_urdf_filepath=meshes, model=model)
await stream.send_message(response)

async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None:
Expand Down
11 changes: 10 additions & 1 deletion src/viam/components/gripper/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,22 @@ async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeou
async def get_kinematics(
self,
*,
include_collision_meshes: bool = False,
include_visual_meshes: bool = False,
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
) -> KinematicsReturn:
md = kwargs.get("metadata", self.Metadata()).proto
request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra))
request = GetKinematicsRequest(
name=self.name,
include_collision_meshes=include_collision_meshes,
include_visual_meshes=include_visual_meshes,
extra=dict_to_struct(extra),
)
response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md)
if response.HasField("model"):
return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath, response.model)
return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath)

async def get_current_inputs(
Expand Down
7 changes: 7 additions & 0 deletions src/viam/components/gripper/gripper.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ async def is_moving(self) -> bool:
async def get_kinematics(
self,
*,
include_collision_meshes: bool = False,
include_visual_meshes: bool = False,
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
Expand All @@ -177,12 +179,17 @@ async def get_kinematics(
# Get the byte contents of the file.
k_bytes = kinematics[1]

Args:
include_collision_meshes (bool): Include collision mesh bytes in the response. Otherwise meshes carry only their path.
include_visual_meshes (bool): Include visual mesh bytes in the response.

Returns:
Tuple[KinematicsFileFormat.ValueType, bytes]: A tuple containing two values; the first [0] value represents the format of the
file, either in URDF format (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF``) or
Viam's kinematic parameter format (spatial vector algebra) (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA``),
and the second [1] value represents the byte contents of the file.
If available, a third [2] value provides meshes keyed by URDF filepath.
If available, a fourth [3] value provides the structured ``KinematicModel`` (links, joints, output frames, and properties).

For more information, see `Gripper component <https://docs.viam.com/dev/reference/apis/components/gripper/#getkinematics>`_.
"""
Expand Down
14 changes: 11 additions & 3 deletions src/viam/components/gripper/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,21 @@ async def GetKinematics(self, stream: Stream[GetKinematicsRequest, GetKinematics
assert request is not None
gripper = self.get_resource(request.name)
timeout = stream.deadline.time_remaining() if stream.deadline else None
kinematics = await gripper.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout)
kinematics = await gripper.get_kinematics(
include_collision_meshes=request.include_collision_meshes,
include_visual_meshes=request.include_visual_meshes,
extra=struct_to_dict(request.extra),
timeout=timeout,
)
model = None
if len(kinematics) == 2:
format, kinematics_data = kinematics
meshes = {}
else:
elif len(kinematics) == 3:
format, kinematics_data, meshes = kinematics
response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data, meshes_by_urdf_filepath=meshes)
else:
format, kinematics_data, meshes, model = kinematics
response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data, meshes_by_urdf_filepath=meshes, model=model)
await stream.send_message(response)

async def GetCurrentInputs(self, stream: Stream[GetCurrentInputsRequest, GetCurrentInputsResponse]) -> None:
Expand Down
Loading
Loading