-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RSDK-9623: Add Discover Service and GetModelsFromModules to Python (#827
) Co-authored-by: Naveed Jooma <[email protected]>
- Loading branch information
1 parent
5634681
commit 21ff90f
Showing
7 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from viam.resource.registry import Registry, ResourceRegistration | ||
from viam.services.discovery.service import DiscoveryRPCService | ||
|
||
from .client import DiscoveryClient | ||
from .discovery import Discovery | ||
|
||
__all__ = [ | ||
"DiscoveryClient", | ||
"Discovery", | ||
] | ||
|
||
Registry.register_api(ResourceRegistration(Discovery, DiscoveryRPCService, lambda name, channel: DiscoveryClient(name, channel))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from typing import Any, List, Mapping, Optional | ||
|
||
from grpclib.client import Channel | ||
|
||
from viam.proto.app.robot import ComponentConfig | ||
from viam.proto.common import DoCommandRequest, DoCommandResponse | ||
from viam.proto.service.discovery import ( | ||
DiscoverResourcesRequest, | ||
DiscoverResourcesResponse, | ||
DiscoveryServiceStub, | ||
) | ||
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase | ||
from viam.utils import ValueTypes, dict_to_struct, struct_to_dict | ||
|
||
from .discovery import Discovery | ||
|
||
|
||
class DiscoveryClient(Discovery, ReconfigurableResourceRPCClientBase): | ||
""" | ||
Connect to the Discovery service, which allows you to discover resources on a machine. | ||
""" | ||
|
||
client: DiscoveryServiceStub | ||
|
||
def __init__(self, name: str, channel: Channel): | ||
super().__init__(name) | ||
self.channel = channel | ||
self.client = DiscoveryServiceStub(channel) | ||
|
||
async def discover_resources( | ||
self, | ||
*, | ||
extra: Optional[Mapping[str, Any]] = None, | ||
timeout: Optional[float] = None, | ||
**kwargs, | ||
) -> List[ComponentConfig]: | ||
md = kwargs.get("metadata", self.Metadata()).proto | ||
request = DiscoverResourcesRequest( | ||
name=self.name, | ||
extra=dict_to_struct(extra), | ||
) | ||
response: DiscoverResourcesResponse = await self.client.DiscoverResources(request, timeout=timeout, metadata=md) | ||
return list(response.discoveries) | ||
|
||
async def do_command( | ||
self, | ||
command: Mapping[str, ValueTypes], | ||
*, | ||
timeout: Optional[float] = None, | ||
**kwargs, | ||
) -> Mapping[str, ValueTypes]: | ||
md = kwargs.get("metadata", self.Metadata()).proto | ||
request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) | ||
response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) | ||
return struct_to_dict(response.result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import abc | ||
from typing import Final, List, Mapping, Optional | ||
|
||
from viam.proto.app.robot import ComponentConfig | ||
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, API | ||
from viam.utils import ValueTypes | ||
|
||
from ..service_base import ServiceBase | ||
|
||
|
||
class Discovery(ServiceBase): | ||
""" | ||
Discovery represents a Discovery service. | ||
This acts as an abstract base class for any drivers representing specific | ||
discovery implementations. This cannot be used on its own. If the ``__init__()`` function is | ||
overridden, it must call the ``super().__init__()`` function. | ||
""" | ||
|
||
API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] | ||
RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "discovery" | ||
) | ||
|
||
@abc.abstractmethod | ||
async def discover_resources( | ||
self, | ||
*, | ||
extra: Optional[Mapping[str, ValueTypes]] = None, | ||
timeout: Optional[float] = None, | ||
) -> List[ComponentConfig]: | ||
"""Get all component configs of discovered resources on a machine | ||
:: | ||
my_discovery = DiscoveryClient.from_robot(machine, "my_discovery") | ||
# Get the discovered resources | ||
result = await my_discovery.discover_resources( | ||
"my_discovery", | ||
) | ||
discoveries = result.discoveries | ||
Args: | ||
name (str): The name of the discover service | ||
Returns: | ||
List[ComponentConfig]: A list of ComponentConfigs that describe | ||
the components found by a discover service | ||
""" | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
from grpclib.server import Stream | ||
|
||
from viam.proto.common import DoCommandRequest, DoCommandResponse | ||
from viam.proto.service.discovery import ( | ||
DiscoverResourcesRequest, | ||
DiscoverResourcesResponse, | ||
UnimplementedDiscoveryServiceBase, | ||
) | ||
from viam.resource.rpc_service_base import ResourceRPCServiceBase | ||
from viam.utils import dict_to_struct, struct_to_dict | ||
|
||
from .discovery import Discovery | ||
|
||
|
||
class DiscoveryRPCService(UnimplementedDiscoveryServiceBase, ResourceRPCServiceBase): | ||
""" | ||
gRPC service for a Discovery service | ||
""" | ||
|
||
RESOURCE_TYPE = Discovery | ||
|
||
async def DiscoverResources(self, stream: Stream[DiscoverResourcesRequest, DiscoverResourcesResponse]) -> None: | ||
request = await stream.recv_message() | ||
assert request is not None | ||
discovery = self.get_resource(request.name) | ||
extra = struct_to_dict(request.extra) | ||
timeout = stream.deadline.time_remaining() if stream.deadline else None | ||
result = await discovery.discover_resources( | ||
extra=extra, | ||
timeout=timeout, | ||
) | ||
response = DiscoverResourcesResponse( | ||
discoveries=result, | ||
) | ||
await stream.send_message(response) | ||
|
||
async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: | ||
request = await stream.recv_message() | ||
assert request is not None | ||
discovery = self.get_resource(request.name) | ||
timeout = stream.deadline.time_remaining() if stream.deadline else None | ||
result = await discovery.do_command(struct_to_dict(request.command), timeout=timeout) | ||
await stream.send_message(DoCommandResponse(result=dict_to_struct(result))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import pytest | ||
from grpclib.testing import ChannelFor | ||
|
||
from viam.proto.app.robot import ComponentConfig | ||
from viam.proto.common import ( | ||
DoCommandRequest, | ||
DoCommandResponse, | ||
) | ||
from viam.proto.service.discovery import ( | ||
DiscoverResourcesRequest, | ||
DiscoverResourcesResponse, | ||
DiscoveryServiceStub, | ||
) | ||
from viam.resource.manager import ResourceManager | ||
from viam.services.discovery import DiscoveryClient | ||
from viam.services.discovery.service import DiscoveryRPCService | ||
from viam.utils import dict_to_struct, struct_to_dict | ||
|
||
from .mocks.services import MockDiscovery | ||
|
||
DISCOVERIES = [ComponentConfig()] | ||
|
||
|
||
DISCOVERY_SERVICE_NAME = "discovery1" | ||
|
||
|
||
@pytest.fixture(scope="function") | ||
def discovery() -> MockDiscovery: | ||
return MockDiscovery( | ||
DISCOVERY_SERVICE_NAME, | ||
) | ||
|
||
|
||
@pytest.fixture(scope="function") | ||
def service(discovery: MockDiscovery) -> DiscoveryRPCService: | ||
rm = ResourceManager([discovery]) | ||
return DiscoveryRPCService(rm) | ||
|
||
|
||
class TestDiscovery: | ||
async def test_discover_resources(self, discovery: MockDiscovery): | ||
extra = {"foo": "discovery"} | ||
response = await discovery.discover_resources(extra=extra) | ||
assert discovery.extra == extra | ||
assert response == DISCOVERIES | ||
|
||
async def test_do(self, discovery: MockDiscovery): | ||
command = {"command": "args"} | ||
response = await discovery.do_command(command) | ||
assert response["cmd"] == command | ||
|
||
|
||
class TestService: | ||
async def test_discover_resources(self, discovery: MockDiscovery, service: DiscoveryRPCService): | ||
async with ChannelFor([service]) as channel: | ||
client = DiscoveryServiceStub(channel) | ||
extra = {"cmd": "discovery"} | ||
request = DiscoverResourcesRequest(name=discovery.name, extra=dict_to_struct(extra)) | ||
response: DiscoverResourcesResponse = await client.DiscoverResources(request) | ||
assert discovery.extra == extra | ||
assert response.discoveries == DISCOVERIES | ||
|
||
async def test_do(self, discovery: MockDiscovery, service: DiscoveryRPCService): | ||
async with ChannelFor([service]) as channel: | ||
client = DiscoveryServiceStub(channel) | ||
command = {"command": "args"} | ||
request = DoCommandRequest(name=discovery.name, command=dict_to_struct(command)) | ||
response: DoCommandResponse = await client.DoCommand(request) | ||
assert struct_to_dict(response.result)["cmd"] == command | ||
|
||
|
||
class TestClient: | ||
async def test_discover_resources(self, discovery: MockDiscovery, service: DiscoveryRPCService): | ||
async with ChannelFor([service]) as channel: | ||
client = DiscoveryClient(DISCOVERY_SERVICE_NAME, channel) | ||
extra = {"foo": "discovery"} | ||
response = await client.discover_resources(name=DISCOVERY_SERVICE_NAME, extra=extra) | ||
assert response == DISCOVERIES | ||
assert discovery.extra == extra | ||
|
||
async def test_do(self, service: DiscoveryRPCService): | ||
async with ChannelFor([service]) as channel: | ||
client = DiscoveryClient(DISCOVERY_SERVICE_NAME, channel) | ||
command = {"command": "args"} | ||
response = await client.do_command(command) | ||
assert response["cmd"] == command |