diff --git a/docs/examples/example.ipynb b/docs/examples/example.ipynb
index 9367bd021..94c38e2ef 100644
--- a/docs/examples/example.ipynb
+++ b/docs/examples/example.ipynb
@@ -296,7 +296,7 @@
"from typing_extensions import Self\n",
"\n",
"from viam.proto.app.robot import ComponentConfig\n",
- "from viam.proto.common import ResourceName\n",
+ "from viam.proto.common import Mesh, ResourceName\n",
"from viam.resource.base import ResourceBase\n",
"from viam.resource.types import Model, ModelFamily\n",
"\n",
@@ -543,14 +543,14 @@
"# modular-arm/src/my_modular_arm.py\n",
"import asyncio\n",
"import os\n",
- "from typing import Any, ClassVar, Dict, Mapping, Optional, Tuple\n",
+ "from typing import Any, ClassVar, Dict, Mapping, Optional, Tuple, Union\n",
"from typing_extensions import Self\n",
"\n",
"from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose\n",
"from viam.module.module import Module\n",
"from viam.operations import run_with_operation\n",
"from viam.proto.app.robot import ComponentConfig\n",
- "from viam.proto.common import ResourceName\n",
+ "from viam.proto.common import Mesh, ResourceName\n",
"from viam.resource.base import ResourceBase\n",
"from viam.resource.registry import Registry, ResourceCreatorRegistration\n",
"from viam.resource.types import Model, ModelFamily\n",
@@ -615,7 +615,7 @@
" async def is_moving(self) -> bool:\n",
" return not self.is_stopped\n",
"\n",
- " async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:\n",
+ " async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Union[Tuple[KinematicsFileFormat.ValueType, bytes], Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]]:\n",
" return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics\n",
"\n",
"async def main():\n",
@@ -672,176 +672,7 @@
{
"data": {
"text/html": [
- "
# my-python-robot/my_cool_arm.py\n",
- "\n",
- "import asyncio\n",
- "import json\n",
- "from typing import Any, Dict, Optional, Tuple\n",
- "\n",
- "from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose\n",
- "from viam.operations import run_with_operation\n",
- "\n",
- "\n",
- "class MyCoolArm(Arm):\n",
- " # Subclass the Viam Arm component and implement the required functions\n",
- "\n",
- " def __init__(self, name: str):\n",
- " # Starting position\n",
- " self.position = Pose(\n",
- " x=0,\n",
- " y=0,\n",
- " z=0,\n",
- " o_x=0,\n",
- " o_y=0,\n",
- " o_z=0,\n",
- " theta=0,\n",
- " )\n",
- "\n",
- " # Starting joint positions\n",
- " self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])\n",
- " self.is_stopped = True\n",
- "\n",
- " # Minimal working kinematics model\n",
- " self.kinematics = json.dumps(\n",
- " {\n",
- " "name": "MyArm",\n",
- " "links": [{"id": "base", "parent": "world", "translation": {"x": 0, "y": 0, "z": 0}}],\n",
- " "joints": [\n",
- " {"id": "waist", "type": "revolute", "parent": "base", "axis": {"x": 0, "y": 0, "z": 1}, "max": 359, "min": -359}\n",
- " ],\n",
- " }\n",
- " ).encode("utf-8")\n",
- " super().__init__(name)\n",
- "\n",
- " async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:\n",
- " return self.position\n",
- "\n",
- " @run_with_operation\n",
- " async def move_to_position(\n",
- " self,\n",
- " pose: Pose,\n",
- " extra: Optional[Dict[str, Any]] = None,\n",
- " **kwargs,\n",
- " ):\n",
- " operation = self.get_operation(kwargs)\n",
- "\n",
- " self.is_stopped = False\n",
- " self.position = pose\n",
- "\n",
- " # Simulate the length of time it takes for the arm to move to its new position\n",
- " for x in range(10):\n",
- " await asyncio.sleep(1)\n",
- "\n",
- " # Check if the operation is cancelled and, if it is, stop the arm's motion\n",
- " if await operation.is_cancelled():\n",
- " await self.stop()\n",
- " break\n",
- "\n",
- " self.is_stopped = True\n",
- "\n",
- " async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions:\n",
- " return self.joint_positions\n",
- "\n",
- " @run_with_operation\n",
- " async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs):\n",
- " operation = self.get_operation(kwargs)\n",
- "\n",
- " self.is_stopped = False\n",
- " self.joint_positions = positions\n",
- "\n",
- " # Simulate the length of time it takes for the arm to move to its new joint position\n",
- " for x in range(10):\n",
- " await asyncio.sleep(1)\n",
- "\n",
- " # Check if the operation is cancelled and, if it is, stop the arm's motion\n",
- " if await operation.is_cancelled():\n",
- " await self.stop()\n",
- " break\n",
- "\n",
- " self.is_stopped = True\n",
- "\n",
- " async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):\n",
- " self.is_stopped = True\n",
- "\n",
- " async def is_moving(self) -> bool:\n",
- " return not self.is_stopped\n",
- "\n",
- " async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:\n",
- " return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics\n",
- " \n"
+ "# my-python-robot/my_cool_arm.py\n\nimport asyncio\nimport json\nfrom typing import Any, Dict, Optional, Tuple\n\nfrom viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose\nfrom viam.operations import run_with_operation\n\n\nclass MyCoolArm(Arm):\n # Subclass the Viam Arm component and implement the required functions\n\n def __init__(self, name: str):\n # Starting position\n self.position = Pose(\n x=0,\n y=0,\n z=0,\n o_x=0,\n o_y=0,\n o_z=0,\n theta=0,\n )\n\n # Starting joint positions\n self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])\n self.is_stopped = True\n\n # Minimal working kinematics model\n self.kinematics = json.dumps(\n {\n "name": "MyArm",\n "links": [{"id": "base", "parent": "world", "translation": {"x": 0, "y": 0, "z": 0}}],\n "joints": [\n {"id": "waist", "type": "revolute", "parent": "base", "axis": {"x": 0, "y": 0, "z": 1}, "max": 359, "min": -359}\n ],\n }\n ).encode("utf-8")\n super().__init__(name)\n\n async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:\n return self.position\n\n @run_with_operation\n async def move_to_position(\n self,\n pose: Pose,\n extra: Optional[Dict[str, Any]] = None,\n **kwargs,\n ):\n operation = self.get_operation(kwargs)\n\n self.is_stopped = False\n self.position = pose\n\n # Simulate the length of time it takes for the arm to move to its new position\n for x in range(10):\n await asyncio.sleep(1)\n\n # Check if the operation is cancelled and, if it is, stop the arm's motion\n if await operation.is_cancelled():\n await self.stop()\n break\n\n self.is_stopped = True\n\n async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions:\n return self.joint_positions\n\n @run_with_operation\n async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs):\n operation = self.get_operation(kwargs)\n\n self.is_stopped = False\n self.joint_positions = positions\n\n # Simulate the length of time it takes for the arm to move to its new joint position\n for x in range(10):\n await asyncio.sleep(1)\n\n # Check if the operation is cancelled and, if it is, stop the arm's motion\n if await operation.is_cancelled():\n await self.stop()\n break\n\n self.is_stopped = True\n\n async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):\n self.is_stopped = True\n\n async def is_moving(self) -> bool:\n return not self.is_stopped\n\n async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Union[Tuple[KinematicsFileFormat.ValueType, bytes], Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]]:\n return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics\n \n"
],
"text/plain": [
""
@@ -1037,7 +868,7 @@
"from typing_extensions import Self\n",
"\n",
"from viam.proto.app.robot import ComponentConfig\n",
- "from viam.proto.common import ResourceName\n",
+ "from viam.proto.common import Mesh, ResourceName\n",
"from viam.resource.base import ResourceBase\n",
"from viam.resource.types import Model, ModelFamily\n",
"\n",
diff --git a/docs/examples/my_cool_arm.py b/docs/examples/my_cool_arm.py
index 1fcdb11e9..889b3cb66 100644
--- a/docs/examples/my_cool_arm.py
+++ b/docs/examples/my_cool_arm.py
@@ -2,11 +2,11 @@
import asyncio
import json
-from typing import Any, Dict, List, Optional, Tuple
+from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose
from viam.operations import run_with_operation
-from viam.proto.common import Capsule, Geometry, Sphere
+from viam.proto.common import Capsule, Geometry, Mesh, Sphere
class MyCoolArm(Arm):
@@ -100,7 +100,9 @@ async def is_moving(self) -> bool:
async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]:
return self.geometries
- async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ async def get_kinematics(
+ self, extra: Optional[Dict[str, Any]] = None, **kwargs
+ ) -> Union[Tuple[KinematicsFileFormat.ValueType, bytes], Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]]:
return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics
async def close(self):
diff --git a/examples/server/v1/components.py b/examples/server/v1/components.py
index 1f1c18ee9..4bc0de836 100644
--- a/examples/server/v1/components.py
+++ b/examples/server/v1/components.py
@@ -19,7 +19,6 @@
from PIL import Image
from viam.components.arm import Arm
-from viam.components.audio_in import AudioIn, AudioResponse
from viam.components.audio_out import AudioOut
from viam.components.base import Base
from viam.components.board import Board, TickStream
@@ -48,6 +47,7 @@
ResponseMetadata,
Sphere,
Vector3,
+ Mesh,
)
from viam.proto.component.arm import JointPositions
from viam.proto.component.encoder import PositionType
@@ -73,7 +73,7 @@ def __init__(self, name: str):
)
self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])
self.is_stopped = True
- self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
super().__init__(name)
async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:
@@ -101,71 +101,12 @@ async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):
async def is_moving(self):
return not self.is_stopped
- async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]:
return self.kinematics
async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
return GEOMETRIES
-class ExampleAudioIn(AudioIn):
- def __init__(self, name: str):
- super().__init__(name)
- self.sample_rate = 44100
- self.num_channels = 2
- self.supported_codecs = ["pcm16"]
- self.chunk_count = 0
- self.latency = timedelta(milliseconds=20)
- self.volume_scale = 0.2
- self.frequency_hz = 440
-
- async def get_audio(
- self, codec: str, duration_seconds: float, previous_timestamp_ns: int, *, timeout: Optional[float] = None, **kwargs
- ) -> AudioIn.AudioStream:
- async def read() -> AsyncIterator[AudioIn.AudioResponse]:
- # Generate chunks based on duration
- chunk_duration_ms = 100 # 100ms per chunk
- chunks_to_generate = max(1, int((duration_seconds * 1000) / chunk_duration_ms))
-
- for i in range(chunks_to_generate):
- # Generate audio data (sine wave pattern)
- chunk_data = b""
- samples_per_chunk = int(self.sample_rate * (chunk_duration_ms / 1000))
-
- for sample in range(samples_per_chunk):
- # Calculate the timing in seconds of this audio sample
- time_offset = (i * chunk_duration_ms / 1000) + (sample / self.sample_rate)
- # Generate one 16-bit PCM audio sample for a sine wave
- # 32767 scales the value from (-1,1) to full 16 bit signed range (-32768,32767)
- amplitude = int(32767 * self.volume_scale * math.sin(2 * math.pi * self.frequency_hz * time_offset))
-
- # Convert to 16-bit PCM stereo
- sample_bytes = amplitude.to_bytes(2, byteorder="little", signed=True)
- chunk_data += sample_bytes * self.num_channels
-
- chunk_start_time = previous_timestamp_ns + (i * chunk_duration_ms * 1000000) # Convert ms to ns
- chunk_end_time = chunk_start_time + (chunk_duration_ms * 1000000)
-
- audio_chunk = AudioInChunk(
- audio_data=bytes(chunk_data),
- audio_info=AudioInfo(codec=codec, sample_rate_hz=int(self.sample_rate), num_channels=self.num_channels),
- sequence=i,
- start_timestamp_nanoseconds=chunk_start_time,
- end_timestamp_nanoseconds=chunk_end_time,
- )
- audio_response = AudioResponse(audio=audio_chunk)
- yield audio_response
-
- await asyncio.sleep(self.latency.total_seconds())
-
- return StreamWithIterator(read())
-
- async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> AudioIn.Properties:
- """Return the audio input device properties."""
- return AudioIn.Properties(supported_codecs=self.supported_codecs, sample_rate_hz=self.sample_rate, num_channels=self.num_channels)
-
- async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
- return GEOMETRIES
-
class ExampleAudioOut(AudioOut):
def __init__(self, name: str):
@@ -546,8 +487,8 @@ async def is_moving(self):
async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
return GEOMETRIES
- async def get_kinematics(self, *, extra=None, timeout=None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
- return (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_UNSPECIFIED, b"abc")
+ async def get_kinematics(self, *, extra=None, timeout=None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]:
+ return (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_UNSPECIFIED, b"abc", {})
class ExampleGripper(Gripper):
@@ -555,7 +496,7 @@ def __init__(self, name: str):
self.opened = False
self.is_stopped = True
self.holding_something = False
- self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
super().__init__(name)
async def open(self, extra: Optional[Dict[str, Any]] = None, **kwargs):
@@ -581,7 +522,7 @@ async def is_moving(self):
async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
return GEOMETRIES
- async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]:
return self.kinematics
diff --git a/examples/server/v1/server.py b/examples/server/v1/server.py
index 188bfe904..4a31c5137 100644
--- a/examples/server/v1/server.py
+++ b/examples/server/v1/server.py
@@ -8,7 +8,6 @@
from .components import (
ExampleAnalog,
ExampleArm,
- ExampleAudioIn,
ExampleAudioOut,
ExampleBase,
ExampleBoard,
@@ -30,7 +29,6 @@
async def run(host: str, port: int, log_level: int):
my_arm = ExampleArm("arm0")
- my_audio_in = ExampleAudioIn("audio_in0")
my_audio_out = ExampleAudioOut("audio_out0")
my_base = ExampleBase("base0")
my_board = ExampleBoard(
@@ -77,7 +75,6 @@ async def run(host: str, port: int, log_level: int):
server = Server(
resources=[
my_arm,
- my_audio_in,
my_audio_out,
my_base,
my_board,
diff --git a/src/viam/app/app_client.py b/src/viam/app/app_client.py
index 1f2dfc231..d2d2674c5 100644
--- a/src/viam/app/app_client.py
+++ b/src/viam/app/app_client.py
@@ -165,6 +165,8 @@
UpdateRobotResponse,
UploadModuleFileRequest,
Visibility,
+ FragmentImport,
+ FragmentImportList,
)
from viam.proto.app import Fragment as FragmentPB
from viam.proto.app import FragmentHistoryEntry as FragmentHistoryEntryPB
@@ -736,6 +738,7 @@ async def update_organization(
public_namespace: Optional[str] = None,
region: Optional[str] = None,
cid: Optional[str] = None,
+ default_fragments: Optional[List[FragmentImport]] = None,
) -> Organization:
"""Updates organization details.
@@ -768,6 +771,11 @@ async def update_organization(
region=region,
cid=cid,
name=name,
+ default_fragments=(
+ FragmentImportList(fragments=default_fragments)
+ if default_fragments is not None
+ else None
+ ),
)
response: UpdateOrganizationResponse = await self._app_client.UpdateOrganization(request, metadata=self._metadata)
return response.organization
diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py
index 8cc2d9aa8..d255995e9 100644
--- a/src/viam/app/data_client.py
+++ b/src/viam/app/data_client.py
@@ -1535,6 +1535,7 @@ async def binary_data_capture_upload(
tags: Optional[List[str]] = None,
dataset_ids: Optional[List[str]] = None,
data_request_times: Optional[Tuple[datetime, datetime]] = None,
+ mime_type: Optional[str] = None,
) -> str:
"""Upload binary sensor data.
@@ -1573,6 +1574,7 @@ async def binary_data_capture_upload(
dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects
denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor.
+ mime_type (Optional[str]): Optional mime type of the data.
Raises:
GRPCError: If an invalid part ID is passed.
@@ -1603,6 +1605,7 @@ async def binary_data_capture_upload(
method_parameters=method_parameters,
tags=tags,
dataset_ids=dataset_ids,
+ mime_type=mime_type or "",
)
if file_extension:
metadata.file_extension = file_extension if file_extension[0] == "." else f".{file_extension}"
@@ -1721,6 +1724,7 @@ async def streaming_data_capture_upload(
data_request_times: Optional[Tuple[datetime, datetime]] = None,
tags: Optional[List[str]] = None,
dataset_ids: Optional[List[str]] = None,
+ mime_type: Optional[str] = None,
) -> str:
"""Uploads the metadata and contents of streaming binary data.
@@ -1753,6 +1757,7 @@ async def streaming_data_capture_upload(
denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor.
tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
+ mime_type (Optional[str]): Optional mime type of the data.
Raises:
GRPCError: If an invalid part ID is passed.
@@ -1773,6 +1778,7 @@ async def streaming_data_capture_upload(
file_extension=file_ext if file_ext[0] == "." else f".{file_ext}",
tags=tags,
dataset_ids=dataset_ids,
+ mime_type=mime_type or "",
)
sensor_metadata = SensorMetadata(
time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None,
@@ -1802,6 +1808,7 @@ async def file_upload(
file_extension: Optional[str] = None,
tags: Optional[List[str]] = None,
dataset_ids: Optional[List[str]] = None,
+ mime_type: Optional[str] = None,
) -> str:
"""Upload arbitrary file data.
@@ -1832,6 +1839,7 @@ async def file_upload(
isn't provided. Files with a ``.jpeg``, ``.jpg``, or ``.png`` extension will be saved to the **Images** tab.
tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
+ mime_type (Optional[str]): Optional mime type of the data.
Raises:
GRPCError: If an invalid part ID is passed.
@@ -1866,6 +1874,7 @@ async def file_upload_from_path(
method_parameters: Optional[Mapping[str, Any]] = None,
tags: Optional[List[str]] = None,
dataset_ids: Optional[List[str]] = None,
+ mime_type: Optional[str] = None,
) -> str:
"""Upload arbitrary file data.
@@ -1890,6 +1899,7 @@ async def file_upload_from_path(
method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use.
tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data.
dataset_ids (Optional[List[str]]): Optional list of datasets to add the data to.
+ mime_type (Optional[str]): Optional mime type of the data.
Raises:
GRPCError: If an invalid part ID is passed.
@@ -1917,6 +1927,7 @@ async def file_upload_from_path(
file_extension=file_extension if file_extension else "",
tags=tags,
dataset_ids=dataset_ids,
+ mime_type=mime_type or "",
)
response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data if data else bytes()))
return response.binary_data_id
diff --git a/src/viam/components/__init__.py b/src/viam/components/__init__.py
index e69de29bb..8a1abd54a 100644
--- a/src/viam/components/__init__.py
+++ b/src/viam/components/__init__.py
@@ -0,0 +1,12 @@
+from typing import Mapping, Tuple, Union
+
+from viam.proto.common import KinematicsFileFormat, Mesh
+
+KinematicsReturn = Union[
+ Tuple[KinematicsFileFormat.ValueType, bytes],
+ Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]],
+]
+
+__all__ = [
+ "KinematicsReturn",
+]
diff --git a/src/viam/components/arm/__init__.py b/src/viam/components/arm/__init__.py
index 60278e410..fcc9ce871 100644
--- a/src/viam/components/arm/__init__.py
+++ b/src/viam/components/arm/__init__.py
@@ -1,3 +1,4 @@
+from viam.components import KinematicsReturn
from viam.proto.common import KinematicsFileFormat, Pose
from viam.proto.component.arm import JointPositions
from viam.resource.registry import Registry, ResourceRegistration
@@ -10,6 +11,7 @@
"Arm",
"JointPositions",
"KinematicsFileFormat",
+ "KinematicsReturn",
"Pose",
]
diff --git a/src/viam/components/arm/arm.py b/src/viam/components/arm/arm.py
index 5569bfafb..0c262f7ff 100644
--- a/src/viam/components/arm/arm.py
+++ b/src/viam/components/arm/arm.py
@@ -1,10 +1,11 @@
import abc
-from typing import Any, Dict, Final, Optional, Tuple
+from typing import Any, Dict, Final, Optional
from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT
+from .. import KinematicsReturn
from ..component_base import ComponentBase
-from . import JointPositions, KinematicsFileFormat, Pose
+from . import JointPositions, Pose
class Arm(ComponentBase):
@@ -195,7 +196,7 @@ async def is_moving(self) -> bool:
@abc.abstractmethod
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> KinematicsReturn:
"""
Get the kinematics information associated with the arm.
@@ -217,6 +218,7 @@ async def get_kinematics(
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.
For more information, see `Arm component `_.
"""
diff --git a/src/viam/components/arm/client.py b/src/viam/components/arm/client.py
index 37ad25e08..523eb8b31 100644
--- a/src/viam/components/arm/client.py
+++ b/src/viam/components/arm/client.py
@@ -1,7 +1,8 @@
-from typing import Any, Dict, List, Mapping, Optional, Tuple
+from typing import Any, Dict, List, Mapping, Optional
from grpclib.client import Channel
+from viam.components import KinematicsReturn
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetKinematicsRequest, GetKinematicsResponse
from viam.proto.component.arm import (
ArmServiceStub,
@@ -19,7 +20,7 @@
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict
-from . import Arm, KinematicsFileFormat, Pose
+from . import Arm, Pose
class ArmClient(Arm, ReconfigurableResourceRPCClientBase):
@@ -113,11 +114,11 @@ async def do_command(
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> KinematicsReturn:
md = kwargs.get("metadata", self.Metadata()).proto
request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md)
- return (response.format, response.kinematics_data)
+ 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]:
md = kwargs.get("metadata", self.Metadata())
diff --git a/src/viam/components/arm/service.py b/src/viam/components/arm/service.py
index 56e903daf..b4d3d7f5f 100644
--- a/src/viam/components/arm/service.py
+++ b/src/viam/components/arm/service.py
@@ -109,8 +109,13 @@ 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
- format, kinematics_data = await arm.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
- response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data)
+ kinematics = await arm.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
+ if len(kinematics) == 2:
+ format, kinematics_data = kinematics
+ meshes = {}
+ else:
+ format, kinematics_data, meshes = kinematics
+ response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data, meshes_by_urdf_filepath=meshes)
await stream.send_message(response)
async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None:
diff --git a/src/viam/components/gantry/client.py b/src/viam/components/gantry/client.py
index 1d300bbf9..8a3fe2bf0 100644
--- a/src/viam/components/gantry/client.py
+++ b/src/viam/components/gantry/client.py
@@ -1,14 +1,14 @@
-from typing import Any, Dict, List, Mapping, Optional, Tuple
+from typing import Any, Dict, List, Mapping, Optional
from grpclib.client import Channel
+from viam.components import KinematicsReturn
from viam.proto.common import (
DoCommandRequest,
DoCommandResponse,
Geometry,
GetKinematicsRequest,
GetKinematicsResponse,
- KinematicsFileFormat,
)
from viam.proto.component.gantry import (
GantryServiceStub,
@@ -119,11 +119,11 @@ async def do_command(
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> KinematicsReturn:
md = kwargs.get("metadata", self.Metadata()).proto
request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md)
- return (response.format, response.kinematics_data)
+ 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]:
md = kwargs.get("metadata", self.Metadata())
diff --git a/src/viam/components/gantry/gantry.py b/src/viam/components/gantry/gantry.py
index 6d552fbe5..59a07d293 100644
--- a/src/viam/components/gantry/gantry.py
+++ b/src/viam/components/gantry/gantry.py
@@ -1,9 +1,9 @@
import abc
-from typing import Any, Dict, Final, List, Optional, Tuple
+from typing import Any, Dict, Final, List, Optional
-from viam.components.arm import KinematicsFileFormat
from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT
+from .. import KinematicsReturn
from ..component_base import ComponentBase
@@ -159,7 +159,7 @@ async def is_moving(self) -> bool:
@abc.abstractmethod
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> KinematicsReturn:
"""
Get the kinematics information associated with the gantry.
@@ -181,6 +181,7 @@ async def get_kinematics(
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.
For more information, see `Arm component `_.
"""
diff --git a/src/viam/components/gantry/service.py b/src/viam/components/gantry/service.py
index 7036a0517..70f99a01d 100644
--- a/src/viam/components/gantry/service.py
+++ b/src/viam/components/gantry/service.py
@@ -115,8 +115,13 @@ 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
- format, data = await gantry.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
- response = GetKinematicsResponse(format=format, kinematics_data=data)
+ kinematics = await gantry.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
+ if len(kinematics) == 2:
+ format, data = kinematics
+ meshes = {}
+ else:
+ format, data, meshes = kinematics
+ response = GetKinematicsResponse(format=format, kinematics_data=data, meshes_by_urdf_filepath=meshes)
await stream.send_message(response)
async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None:
diff --git a/src/viam/components/gripper/client.py b/src/viam/components/gripper/client.py
index 359a862b3..2b8319b05 100644
--- a/src/viam/components/gripper/client.py
+++ b/src/viam/components/gripper/client.py
@@ -1,7 +1,8 @@
-from typing import Any, Dict, List, Mapping, Optional, Tuple
+from typing import Any, Dict, List, Mapping, Optional
from grpclib.client import Channel
+from viam.components import KinematicsReturn
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetKinematicsRequest, GetKinematicsResponse
from viam.proto.component.gripper import (
GrabRequest,
@@ -17,7 +18,6 @@
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict
-from . import KinematicsFileFormat
from .gripper import Gripper
@@ -101,8 +101,8 @@ async def get_kinematics(
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> KinematicsReturn:
md = kwargs.get("metadata", self.Metadata()).proto
request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md)
- return (response.format, response.kinematics_data)
+ return (response.format, response.kinematics_data, response.meshes_by_urdf_filepath)
diff --git a/src/viam/components/gripper/gripper.py b/src/viam/components/gripper/gripper.py
index 60883bbac..b49b4aa60 100644
--- a/src/viam/components/gripper/gripper.py
+++ b/src/viam/components/gripper/gripper.py
@@ -1,11 +1,11 @@
import abc
from dataclasses import dataclass
-from typing import Any, Dict, Final, Optional, Tuple
+from typing import Any, Dict, Final, Optional
from viam.components.component_base import ComponentBase
from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT
-from . import KinematicsFileFormat
+from .. import KinematicsReturn
class Gripper(ComponentBase):
@@ -161,7 +161,7 @@ async def get_kinematics(
extra: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
**kwargs,
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> KinematicsReturn:
"""
Get the kinematics information associated with the gripper.
@@ -183,6 +183,7 @@ async def get_kinematics(
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.
For more information, see `Gripper component `_.
"""
diff --git a/src/viam/components/gripper/service.py b/src/viam/components/gripper/service.py
index 571992b0d..f40b02df3 100644
--- a/src/viam/components/gripper/service.py
+++ b/src/viam/components/gripper/service.py
@@ -105,6 +105,11 @@ 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
- format, kinematics_data = await gripper.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout)
- response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data)
+ kinematics = await gripper.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout)
+ if len(kinematics) == 2:
+ format, kinematics_data = kinematics
+ meshes = {}
+ else:
+ format, kinematics_data, meshes = kinematics
+ response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data, meshes_by_urdf_filepath=meshes)
await stream.send_message(response)
diff --git a/src/viam/gen/app/v1/app_pb2.py b/src/viam/gen/app/v1/app_pb2.py
index cd137c5de..e54d0f293 100644
--- a/src/viam/gen/app/v1/app_pb2.py
+++ b/src/viam/gen/app/v1/app_pb2.py
@@ -704,4 +704,4 @@
_globals['_MACHINEPICKERCUSTOMIZATIONS']._serialized_start = 36026
_globals['_MACHINEPICKERCUSTOMIZATIONS']._serialized_end = 36150
_globals['_APPSERVICE']._serialized_start = 38142
- _globals['_APPSERVICE']._serialized_end = 49144
\ No newline at end of file
+ _globals['_APPSERVICE']._serialized_end = 49144
diff --git a/tests/mocks/components.py b/tests/mocks/components.py
index e435db614..7e658f095 100644
--- a/tests/mocks/components.py
+++ b/tests/mocks/components.py
@@ -13,7 +13,7 @@
from google.protobuf.timestamp_pb2 import Timestamp
from viam.components.arm import Arm, JointPositions, KinematicsFileFormat
-from viam.components.audio_in import AudioIn, AudioResponse
+from viam.components.audio_in import AudioIn
from viam.components.audio_out import AudioOut
from viam.components.base import Base
from viam.components.board import Board, Tick
@@ -33,8 +33,8 @@
from viam.components.switch import Switch
from viam.errors import ResourceNotFoundError
from viam.media.video import CameraMimeType, NamedImage, ViamImage
-from viam.proto.common import AudioInfo, Capsule, Geometry, GeoPoint, Orientation, Pose, PoseInFrame, ResponseMetadata, Sphere, Vector3
-from viam.proto.component.audioin import AudioChunk as Chunk
+from viam.proto.common import AudioInfo, Capsule, Geometry, GeoPoint, Orientation, Pose, PoseInFrame, ResponseMetadata, Sphere, Vector3, Mesh
+from viam.proto.component.audioin import AudioChunk, GetAudioResponse
from viam.proto.component.board import PowerMode
from viam.proto.component.encoder import PositionType
from viam.streams import StreamWithIterator
@@ -51,7 +51,7 @@ def __init__(self, name: str):
self.position = Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)
self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])
self.is_stopped = True
- self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
self.geometries = GEOMETRIES
self.extra = None
self.timeout: Optional[float] = None
@@ -100,7 +100,7 @@ async def is_moving(self) -> bool:
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]:
self.extra = extra
self.timeout = timeout
return self.kinematics
@@ -117,60 +117,48 @@ async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Option
class MockAudioIn(AudioIn):
def __init__(self, name: str, properties: AudioIn.Properties):
super().__init__(name)
- self.geometries = GEOMETRIES
self.properties = properties
+ self.geometries = GEOMETRIES
self.timeout: Optional[float] = None
self.extra: Optional[Dict[str, Any]] = None
async def get_audio(
- self,
- codec: str,
- duration_seconds: float,
- previous_timestamp_ns: int,
- *,
- extra: Optional[Dict[str, Any]] = None,
- timeout: Optional[float] = None,
- **kwargs,
- ):
- async def read() -> AsyncIterator[AudioResponse]:
- # Generate mock audio chunks
- for i in range(2):
- chunk_data = f"audio_chunk_{i}".encode("utf-8")
- timestamp_start = previous_timestamp_ns + i * 1000000000 # 1 second intervals in nanoseconds
- timestamp_end = timestamp_start + 1000000000
-
- audio_chunk = Chunk(
- audio_data=chunk_data,
- audio_info=AudioInfo(
- codec=codec, sample_rate_hz=self.properties.sample_rate_hz, num_channels=self.properties.num_channels
- ),
- sequence=i,
- start_timestamp_nanoseconds=timestamp_start,
- end_timestamp_nanoseconds=timestamp_end,
+ self, codec: str, duration_seconds: float, previous_timestamp_ns: int, *, timeout: Optional[float] = None, **kwargs
+ ) -> StreamWithIterator[GetAudioResponse]:
+ self.timeout = timeout
+
+ async def read() -> AsyncIterator[GetAudioResponse]:
+ for sequence in range(2):
+ start_ts = previous_timestamp_ns + (sequence + 1) * 1_000_000_000
+ end_ts = start_ts + 1_000_000_000
+ yield GetAudioResponse(
+ audio=AudioChunk(
+ audio_data=b"mock-audio-data",
+ audio_info=AudioInfo(
+ codec=codec,
+ sample_rate_hz=self.properties.sample_rate_hz,
+ num_channels=self.properties.num_channels,
+ ),
+ start_timestamp_nanoseconds=start_ts,
+ end_timestamp_nanoseconds=end_ts,
+ sequence=sequence,
+ )
)
- audio_response = AudioResponse(audio=audio_chunk, request_id="mock_request")
- yield audio_response
-
- self.extra = extra
- self.timeout = timeout
return StreamWithIterator(read())
- async def get_properties(
- self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> AudioIn.Properties:
- self.extra = extra
+ async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> AudioIn.Properties:
self.timeout = timeout
return self.properties
- async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]:
+ async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]:
+ return {"command": command}
+
+ async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs):
self.extra = extra
self.timeout = timeout
return self.geometries
- async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]:
- return {"command": command}
-
class MockAudioOut(AudioOut):
def __init__(self, name: str, properties: AudioOut.Properties):
@@ -518,7 +506,7 @@ def __init__(self, name: str, position: List[float], lengths: List[float]):
self.position = position
self.lengths = lengths
self.is_stopped = True
- self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
self.extra = None
self.homed = True
self.speeds = Optional[List[float]]
@@ -567,7 +555,7 @@ async def is_moving(self) -> bool:
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]:
self.extra = extra
self.timeout = timeout
return self.kinematics
@@ -599,7 +587,7 @@ class MockGripper(Gripper):
def __init__(self, name: str):
self.opened = False
self.geometries = GEOMETRIES
- self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
self.holding_something = False
self.extra = None
self.is_stopped = True
@@ -642,7 +630,7 @@ async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeou
async def get_kinematics(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
- ) -> Tuple[KinematicsFileFormat.ValueType, bytes]:
+ ) -> Tuple[KinematicsFileFormat.ValueType, bytes, Mapping[str, Mesh]]:
self.extra = extra
self.timeout = timeout
return self.kinematics
diff --git a/tests/test_arm.py b/tests/test_arm.py
index 9a2bfd07c..ac57357ec 100644
--- a/tests/test_arm.py
+++ b/tests/test_arm.py
@@ -35,7 +35,7 @@ class TestArm:
arm = MockArm(name="arm")
pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20)
joint_pos = JointPositions(values=[1, 8, 2])
- kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
async def test_move_to_position(self):
await self.arm.move_to_position(self.pose)
@@ -92,7 +92,7 @@ def setup_class(cls):
cls.service = ArmRPCService(cls.manager)
cls.pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20)
cls.joint_pos = JointPositions(values=[1, 8, 2])
- cls.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ cls.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
async def test_move_to_position(self):
async with ChannelFor([self.service]) as channel:
@@ -155,7 +155,7 @@ async def test_get_kinematics(self):
client = ArmServiceStub(channel)
request = GetKinematicsRequest(name=self.name)
response: GetKinematicsResponse = await client.GetKinematics(request)
- assert (response.format, response.kinematics_data) == self.kinematics
+ assert (response.format, response.kinematics_data, response.meshes_by_urdf_filepath) == self.kinematics
async def test_get_geometries(self):
async with ChannelFor([self.service]) as channel:
@@ -182,7 +182,7 @@ def setup_class(cls):
cls.service = ArmRPCService(cls.manager)
cls.pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20)
cls.joint_pos = JointPositions(values=[1, 8, 2])
- cls.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ cls.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
async def test_move_to_position(self):
async with ChannelFor([self.service]) as channel:
diff --git a/tests/test_gantry.py b/tests/test_gantry.py
index 728f1926a..e22501df1 100644
--- a/tests/test_gantry.py
+++ b/tests/test_gantry.py
@@ -72,9 +72,10 @@ async def test_extra(self):
assert self.gantry.extra == extra
async def test_get_kinematics(self):
- format, data = await self.gantry.get_kinematics()
+ format, data, meshes = await self.gantry.get_kinematics()
assert format == self.gantry.kinematics[0]
assert data == self.gantry.kinematics[1]
+ assert meshes == self.gantry.kinematics[2]
async def test_timeout(self):
assert self.gantry.timeout is None
@@ -258,9 +259,10 @@ async def test_extra(self):
async def test_get_kinematics(self):
async with ChannelFor([self.service]) as channel:
client = GantryClient(self.gantry.name, channel)
- format, data = await client.get_kinematics(timeout=1.1)
+ format, data, meshes = await client.get_kinematics(timeout=1.1)
assert format == self.gantry.kinematics[0]
assert data == self.gantry.kinematics[1]
+ assert meshes == self.gantry.kinematics[2]
assert self.gantry.timeout == loose_approx(1.1)
async def test_get_geometries(self):
diff --git a/tests/test_gripper.py b/tests/test_gripper.py
index 38499b724..22b88ea2e 100644
--- a/tests/test_gripper.py
+++ b/tests/test_gripper.py
@@ -87,7 +87,7 @@ async def test_get_geometries(self, gripper: MockGripper):
assert geometries == GEOMETRIES
async def test_get_kinematics(self, gripper: MockGripper):
- kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
kd = await gripper.get_kinematics(extra={"1": "2"})
assert kd == kinematics
assert gripper.extra == {"1": "2"}
@@ -162,10 +162,10 @@ async def test_get_geometries(self, gripper: MockGripper, service: GripperRPCSer
async def test_get_kinematics(self, gripper: MockGripper, service: GripperRPCService):
async with ChannelFor([service]) as channel:
client = GripperServiceStub(channel)
- kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
request = GetKinematicsRequest(name=gripper.name)
response: GetKinematicsResponse = await client.GetKinematics(request)
- assert (response.format, response.kinematics_data) == kinematics
+ assert (response.format, response.kinematics_data, response.meshes_by_urdf_filepath) == kinematics
class TestClient:
@@ -226,7 +226,7 @@ async def test_get_geometries(self, gripper: MockGripper, service: GripperRPCSer
async def test_get_kinematics(self, gripper: MockGripper, service: GripperRPCService):
async with ChannelFor([service]) as channel:
client = GripperClient(gripper.name, channel)
- kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02")
+ kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02", {})
kd = await client.get_kinematics(extra={"1": "2"})
assert kd == kinematics
assert gripper.extra == {"1": "2"}
diff --git a/tests/test_registry.py b/tests/test_registry.py
index ad1b70583..72ed8223f 100644
--- a/tests/test_registry.py
+++ b/tests/test_registry.py
@@ -24,7 +24,6 @@ def __init__(self, name: str, channel: Channel):
def test_components_register_themselves_correctly():
assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "arm") in Registry.REGISTERED_APIS()
- assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "audio_in") in Registry.REGISTERED_APIS()
assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "audio_out") in Registry.REGISTERED_APIS()
assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "base") in Registry.REGISTERED_APIS()
assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "board") in Registry.REGISTERED_APIS()