Skip to content

RSDK-4788: Add Readings to Python SDK #459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 24, 2023
4 changes: 4 additions & 0 deletions examples/server/v1/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ def __init__(
accuracy: Mapping[str, float],
):
super().__init__(name)
self.num_readings = random.randint(1, 10)
self.coordinates = coordinates
self.altitude = altitude
self.lin_vel = lin_vel
Expand All @@ -663,6 +664,9 @@ def __init__(
self.properties = properties
self.accuracy = accuracy

async def get_readings(self, **kwargs) -> Mapping[str, Any]:
return {"abcdefghij"[idx]: random.random() for idx in range(self.num_readings)}

async def get_position(self, **kwargs) -> Tuple[GeoPoint, float]:
return (self.coordinates, self.altitude)

Expand Down
6 changes: 4 additions & 2 deletions src/viam/components/movement_sensor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from grpclib.client import Channel

from viam.components.movement_sensor.movement_sensor import MovementSensor
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetReadingsRequest, GetReadingsResponse
from viam.proto.component.movementsensor import (
GetAccuracyRequest,
GetAccuracyResponse,
Expand Down Expand Up @@ -96,7 +96,9 @@ async def get_accuracy(self, *, extra: Optional[Dict[str, Any]] = None, timeout:
async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, Any]:
if extra is None:
extra = {}
return await super().get_readings(extra=extra, timeout=timeout)
request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout)
return response.readings

async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None) -> Mapping[str, ValueTypes]:
request = DoCommandRequest(name=self.name, command=dict_to_struct(command))
Expand Down
57 changes: 6 additions & 51 deletions src/viam/components/movement_sensor/movement_sensor.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import abc
import asyncio
from dataclasses import dataclass
from typing import Any, Dict, Final, List, Mapping, Optional, Tuple
from typing import Any, Dict, Final, Mapping, Optional, Tuple

from grpclib import GRPCError
from typing_extensions import Self

from viam.errors import MethodNotImplementedError, NotSupportedError
from viam.components.component_base import ComponentBase
from viam.proto.component.movementsensor import GetPropertiesResponse
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype

from ..sensor import Sensor
from . import GeoPoint, Orientation, Vector3


class MovementSensor(Sensor):
class MovementSensor(ComponentBase):
"""MovementSensor reports information about the robot's direction, position and speed.

This acts as an abstract base class for any sensors that can provide data regarding the robot's direction, position, and speed.
Expand Down Expand Up @@ -137,48 +133,7 @@ async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout:
If a sensor is not configured to have a measurement or fails to read a piece of data, it will not appear in the readings dictionary.

Returns:
Mapping[str, Any]: The readings for the MovementSensor:
{
position: GeoPoint,
altitude: float,
linear_velocity: Vector3,
angular_velocity: Vector3,
linear_acceleration: Vector3,
compass: float,
orientation: Orientation,
}
Mapping[str, Any]: The readings for the MovementSensor. Can be of any type.

"""
(pos, lv, av, la, comp, orient) = await asyncio.gather(
self.get_position(extra=extra, timeout=timeout),
self.get_linear_velocity(extra=extra, timeout=timeout),
self.get_angular_velocity(extra=extra, timeout=timeout),
self.get_linear_acceleration(extra=extra, timeout=timeout),
self.get_compass_heading(extra=extra, timeout=timeout),
self.get_orientation(extra=extra, timeout=timeout),
return_exceptions=True,
)

readings = {}

# Add returned value to the readings dictionary if value is of expected type; omit if unimplemented.
def add_reading(name: str, reading, returntype: List) -> None:
possible_error_types = (NotImplementedError, MethodNotImplementedError, NotSupportedError)
if type(reading) in returntype:
if name == "position":
readings["position"] = reading[0]
readings["altitude"] = reading[1]
else:
readings[name] = reading
return
elif isinstance(reading, possible_error_types) or (isinstance(reading, GRPCError) and "Unimplemented" in str(reading.message)):
return
raise reading

add_reading("position", pos, [tuple])
add_reading("linear_velocity", lv, [Vector3])
add_reading("angular_velocity", av, [Vector3])
add_reading("linear_acceleration", la, [Vector3])
add_reading("compass", comp, [float, int])
add_reading("orientation", orient, [Orientation])

return readings
...
21 changes: 19 additions & 2 deletions src/viam/components/movement_sensor/service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from grpclib.server import Stream

from viam.components.movement_sensor.movement_sensor import MovementSensor
from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse
from viam.proto.common import (
DoCommandRequest,
DoCommandResponse,
GetGeometriesRequest,
GetGeometriesResponse,
GetReadingsRequest,
GetReadingsResponse,
)
from viam.proto.component.movementsensor import (
GetAccuracyRequest,
GetAccuracyResponse,
Expand All @@ -22,7 +29,7 @@
MovementSensorServiceBase,
)
from viam.resource.rpc_service_base import ResourceRPCServiceBase
from viam.utils import dict_to_struct, struct_to_dict
from viam.utils import dict_to_struct, sensor_readings_native_to_value, struct_to_dict


class MovementSensorRPCService(MovementSensorServiceBase, ResourceRPCServiceBase):
Expand Down Expand Up @@ -128,3 +135,13 @@ async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometries
geometries = await sensor.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout)
response = GetGeometriesResponse(geometries=geometries)
await stream.send_message(response)

async def GetReadings(self, stream: Stream[GetReadingsRequest, GetReadingsResponse]) -> None:
request = await stream.recv_message()
assert request is not None
name = request.name
sensor = self.get_resource(name)
timeout = stream.deadline.time_remaining() if stream.deadline else None
readings = await sensor.get_readings(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
response = GetReadingsResponse(readings=sensor_readings_native_to_value(readings))
await stream.send_message(response)
6 changes: 4 additions & 2 deletions src/viam/components/power_sensor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from grpclib.client import Channel

from viam.components.power_sensor.power_sensor import PowerSensor
from viam.proto.common import DoCommandRequest, DoCommandResponse
from viam.proto.common import DoCommandRequest, DoCommandResponse, GetReadingsRequest, GetReadingsResponse
from viam.proto.component.powersensor import (
GetCurrentRequest,
GetCurrentResponse,
Expand Down Expand Up @@ -49,7 +49,9 @@ async def get_power(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Op
async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, Any]:
if extra is None:
extra = {}
return await super().get_readings(extra=extra, timeout=timeout)
request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout)
return response.readings

async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None) -> Mapping[str, ValueTypes]:
request = DoCommandRequest(name=self.name, command=dict_to_struct(command))
Expand Down
39 changes: 3 additions & 36 deletions src/viam/components/power_sensor/power_sensor.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import abc
import asyncio
from typing import Any, Dict, Final, List, Mapping, Optional, Tuple

from grpclib import GRPCError
from typing import Any, Dict, Final, Mapping, Optional, Tuple

from viam.components.component_base import ComponentBase
from viam.errors import MethodNotImplementedError, NotSupportedError
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype


Expand Down Expand Up @@ -57,35 +53,6 @@ async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout:
is_ac: bool
power: float
}
"""
(vol, cur, pow) = await asyncio.gather(
self.get_voltage(extra=extra, timeout=timeout),
self.get_current(extra=extra, timeout=timeout),
self.get_power(extra=extra, timeout=timeout),
return_exceptions=True,
)

readings = {}

# Add returned value to the readings dictionary if value is of expected type; omit if unimplemented.
def add_reading(name: str, reading, returntype: List) -> None:
possible_error_types = (NotImplementedError, MethodNotImplementedError, NotSupportedError)
if type(reading) in returntype:
if name == "voltage":
readings["voltage"] = reading[0]
readings["is_ac"] = reading[1]
elif name == "current":
readings["current"] = reading[0]
readings["is_ac"] = reading[1]
else:
readings[name] = reading
return
elif isinstance(reading, possible_error_types) or (isinstance(reading, GRPCError) and "Unimplemented" in str(reading.message)):
return
raise reading

add_reading("voltage", vol, [tuple])
add_reading("current", cur, [tuple])
add_reading("power", pow, [float])

return readings
"""
...
14 changes: 12 additions & 2 deletions src/viam/components/power_sensor/service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from grpclib.server import Stream

from viam.components.power_sensor.power_sensor import PowerSensor
from viam.proto.common import DoCommandRequest, DoCommandResponse
from viam.proto.common import DoCommandRequest, DoCommandResponse, GetReadingsRequest, GetReadingsResponse
from viam.proto.component.powersensor import (
GetCurrentRequest,
GetCurrentResponse,
Expand All @@ -12,7 +12,7 @@
PowerSensorServiceBase,
)
from viam.resource.rpc_service_base import ResourceRPCServiceBase
from viam.utils import dict_to_struct, struct_to_dict
from viam.utils import dict_to_struct, sensor_readings_native_to_value, struct_to_dict


class PowerSensorRPCService(PowerSensorServiceBase, ResourceRPCServiceBase):
Expand All @@ -22,6 +22,16 @@ class PowerSensorRPCService(PowerSensorServiceBase, ResourceRPCServiceBase):

RESOURCE_TYPE = PowerSensor

async def GetReadings(self, stream: Stream[GetReadingsRequest, GetReadingsResponse]) -> None:
request = await stream.recv_message()
assert request is not None
name = request.name
sensor = self.get_resource(name)
timeout = stream.deadline.time_remaining() if stream.deadline else None
readings = await sensor.get_readings(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata)
response = GetReadingsResponse(readings=sensor_readings_native_to_value(readings))
await stream.send_message(response)

async def GetVoltage(self, stream: Stream[GetVoltageRequest, GetVoltageResponse]) -> None:
request = await stream.recv_message()
assert request is not None
Expand Down
4 changes: 2 additions & 2 deletions src/viam/components/sensor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from grpclib.client import Channel

from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry
from viam.proto.component.sensor import GetReadingsRequest, GetReadingsResponse, SensorServiceStub
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetReadingsRequest, GetReadingsResponse
from viam.proto.component.sensor import SensorServiceStub
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.utils import ValueTypes, dict_to_struct, get_geometries, sensor_readings_value_to_native, struct_to_dict

Expand Down
11 changes: 9 additions & 2 deletions src/viam/components/sensor/service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from grpclib.server import Stream

from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse
from viam.proto.component.sensor import GetReadingsRequest, GetReadingsResponse, SensorServiceBase
from viam.proto.common import (
DoCommandRequest,
DoCommandResponse,
GetGeometriesRequest,
GetGeometriesResponse,
GetReadingsRequest,
GetReadingsResponse,
)
from viam.proto.component.sensor import SensorServiceBase
from viam.resource.rpc_service_base import ResourceRPCServiceBase
from viam.utils import dict_to_struct, sensor_readings_native_to_value, struct_to_dict

Expand Down
Loading