Skip to content
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

RSDK-5936: change return type for get readings #506

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
12 changes: 8 additions & 4 deletions docs/examples/example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -272,13 +272,14 @@
"\n",
"from viam.components.sensor import Sensor\n",
"from viam.logging import getLogger\n",
"from viam.utils import SensorReading\n",
"\n",
"LOGGER = getLogger(__name__)\n",
"\n",
"\n",
"class MySensor(Sensor):\n",
" # Subclass the Viam Sensor component and implement the required functions\n",
" async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:\n",
" async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:\n",
" with open(\"/proc/net/wireless\") as wifi_stats:\n",
" content = wifi_stats.readlines()\n",
" wifi_signal = [x for x in content[2].split(\" \") if x != \"\"]\n",
Expand Down Expand Up @@ -368,6 +369,8 @@
"# ADD `Sequence` FROM `typing`.\n",
"from typing import Sequence\n",
"\n",
"from viam.utils import SensorReading\n",
"\n",
"class MySensor(Sensor):\n",
" # ADD A VALIDATOR FUNCTION \n",
" @classmethod\n",
Expand Down Expand Up @@ -395,7 +398,7 @@
" multiplier = 1.0\n",
" self.multiplier = multiplier\n",
"\n",
" async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:\n",
" async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:\n",
" with open(\"/proc/net/wireless\") as wifi_stats:\n",
" content = wifi_stats.readlines()\n",
" result = [x for x in content[2].split(\" \") if x != \"\"]\n",
Expand Down Expand Up @@ -1031,10 +1034,11 @@
"\n",
"from viam.components.sensor import Geometry, Sensor\n",
"from viam.rpc.server import Server\n",
"from viam.utils import SensorReading\n",
"\n",
"\n",
"class MySensor(Sensor):\n",
" async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:\n",
" async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:\n",
" with open(\"/proc/net/wireless\") as wifi_stats:\n",
" content = wifi_stats.readlines()\n",
" wifi_signal = [x for x in content[2].split(\" \") if x != \"\"]\n",
Expand Down Expand Up @@ -1485,7 +1489,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4 (main, Jun 20 2023, 17:23:00) [Clang 14.0.3 (clang-1403.0.22.14.1)]"
"version": "3.11.6 (main, Oct 2 2023, 20:46:14) [Clang 14.0.3 (clang-1403.0.22.14.1)]"
},
"vscode": {
"interpreter": {
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/module_step2.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from viam.resource.base import ResourceBase
from viam.resource.registry import Registry, ResourceCreatorRegistration
from viam.resource.types import Model, ModelFamily
from viam.utils import SensorReading

LOGGER = getLogger(__name__)

Expand All @@ -24,7 +25,7 @@ def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, Resour
sensor = cls(config.name)
return sensor

async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:
async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:
with open("/proc/net/wireless") as wifi_stats:
content = wifi_stats.readlines()
wifi_signal = [x for x in content[2].split(" ") if x != ""]
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/module_step2_optional.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from viam.resource.base import ResourceBase
from viam.resource.registry import Registry, ResourceCreatorRegistration
from viam.resource.types import Model, ModelFamily
from viam.utils import SensorReading

LOGGER = getLogger(__name__)

Expand All @@ -35,7 +36,7 @@ def validate_config(cls, config: ComponentConfig) -> Sequence[str]:
raise Exception("Multiplier cannot be 0.")
return []

async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:
async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:
with open("/proc/net/wireless") as wifi_stats:
content = wifi_stats.readlines()
result = [x for x in content[2].split(" ") if x != ""]
Expand Down
3 changes: 2 additions & 1 deletion docs/examples/module_step3.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from viam.resource.base import ResourceBase
from viam.resource.registry import Registry, ResourceCreatorRegistration
from viam.resource.types import Model, ModelFamily
from viam.utils import SensorReading

LOGGER = getLogger(__name__)

Expand All @@ -25,7 +26,7 @@ def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, Resour
sensor = cls(config.name)
return sensor

async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:
async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:
with open("/proc/net/wireless") as wifi_stats:
content = wifi_stats.readlines()
wifi_signal = [x for x in content[2].split(" ") if x != ""]
Expand Down
5 changes: 3 additions & 2 deletions examples/server/v1/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from viam.proto.component.arm import JointPositions
from viam.proto.component.audioinput import AudioChunk, AudioChunkInfo, SampleFormat
from viam.proto.component.encoder import PositionType
from viam.utils import SensorReading

GEOMETRIES = [
Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), sphere=Sphere(radius_mm=2)),
Expand Down Expand Up @@ -645,7 +646,7 @@ def __init__(
self.properties = properties
self.accuracy = accuracy

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

async def get_position(self, **kwargs) -> Tuple[GeoPoint, float]:
Expand Down Expand Up @@ -693,7 +694,7 @@ def __init__(self, name: str):
self.num_readings = random.randint(1, 10)
super().__init__(name)

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

async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_module/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from viam.resource.base import ResourceBase
from viam.resource.registry import Registry, ResourceCreatorRegistration
from viam.resource.types import Model, ModelFamily
from viam.utils import ValueTypes
from viam.utils import SensorReading, ValueTypes

LOGGER = getLogger(__name__)

Expand Down Expand Up @@ -39,7 +39,7 @@ def validate_config(cls, config: ComponentConfig) -> Sequence[str]:
raise Exception("Multiplier cannot be 0.")
return []

async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, Any]:
async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:
return {"signal": 1 * self.multiplier}

async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]:
Expand Down
7 changes: 4 additions & 3 deletions src/viam/components/movement_sensor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
MovementSensorServiceStub,
)
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict
from viam.utils import SensorReading, ValueTypes, dict_to_struct, get_geometries, sensor_readings_value_to_native, struct_to_dict

from . import GeoPoint, Orientation, Vector3

Expand Down Expand Up @@ -93,12 +93,13 @@ async def get_accuracy(self, *, extra: Optional[Dict[str, Any]] = None, timeout:
response: GetAccuracyResponse = await self.client.GetAccuracy(request, timeout=timeout)
return response.accuracy

async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, Any]:
async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, SensorReading]:
if extra is None:
extra = {}
request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout)
return response.readings

return sensor_readings_value_to_native(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
5 changes: 4 additions & 1 deletion src/viam/components/movement_sensor/movement_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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 viam.utils import SensorReading

from . import GeoPoint, Orientation, Vector3

Expand Down Expand Up @@ -129,7 +130,9 @@ async def get_accuracy(
"""
...

async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Mapping[str, Any]:
async def get_readings(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
) -> Mapping[str, SensorReading]:
"""Obtain the measurements/data specific to this sensor.
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.

Expand Down
6 changes: 3 additions & 3 deletions src/viam/components/power_sensor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
PowerSensorServiceStub,
)
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.utils import ValueTypes, dict_to_struct, struct_to_dict
from viam.utils import SensorReading, ValueTypes, dict_to_struct, sensor_readings_value_to_native, struct_to_dict


class PowerSensorClient(PowerSensor, ReconfigurableResourceRPCClientBase):
Expand Down Expand Up @@ -46,12 +46,12 @@ async def get_power(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Op
response: GetPowerResponse = await self.client.GetPower(request, timeout=timeout)
return response.watts

async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, Any]:
async def get_readings(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, SensorReading]:
if extra is None:
extra = {}
request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra))
response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout)
return response.readings
return sensor_readings_value_to_native(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
5 changes: 4 additions & 1 deletion src/viam/components/power_sensor/power_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from viam.components.component_base import ComponentBase
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype
from viam.utils import SensorReading


class PowerSensor(ComponentBase):
Expand Down Expand Up @@ -41,7 +42,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, **kwargs) -> Mapping[str, Any]:
async def get_readings(
self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs
) -> Mapping[str, SensorReading]:
"""Obtain the measurements/data specific to this sensor.
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.

Expand Down
6 changes: 4 additions & 2 deletions src/viam/components/sensor/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
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
from viam.utils import SensorReading, ValueTypes, dict_to_struct, get_geometries, sensor_readings_value_to_native, struct_to_dict

from .sensor import Sensor

Expand All @@ -20,7 +20,9 @@ def __init__(self, name: str, channel: Channel):
self.client = SensorServiceStub(channel)
super().__init__(name)

async def get_readings(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None) -> Mapping[str, Any]:
async def get_readings(
self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None
) -> Mapping[str, SensorReading]:
if extra is None:
extra = {}
request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra))
Expand Down
3 changes: 2 additions & 1 deletion src/viam/components/sensor/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Any, Mapping, Optional

from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype
from viam.utils import SensorReading

from ..component_base import ComponentBase

Expand All @@ -20,7 +21,7 @@ class Sensor(ComponentBase):
@abc.abstractmethod
async def get_readings(
self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs
) -> Mapping[str, Any]:
) -> Mapping[str, SensorReading]:
"""
Obtain the measurements/data specific to this sensor.

Expand Down
4 changes: 2 additions & 2 deletions src/viam/services/sensors/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, Subtype
from viam.services.service_client_base import ServiceClientBase
from viam.utils import ValueTypes, dict_to_struct, sensor_readings_value_to_native, struct_to_dict
from viam.utils import SensorReading, ValueTypes, dict_to_struct, sensor_readings_value_to_native, struct_to_dict


class SensorsClient(ServiceClientBase, ReconfigurableResourceRPCClientBase):
Expand All @@ -34,7 +34,7 @@ async def get_sensors(self, *, extra: Optional[Mapping[str, Any]] = None, timeou

async def get_readings(
self, sensors: List[ResourceName], *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None
) -> Mapping[ResourceName, Mapping[str, Any]]:
) -> Mapping[ResourceName, Mapping[str, SensorReading]]:
"""Get the readings from the specific sensors provided

Args:
Expand Down
7 changes: 5 additions & 2 deletions src/viam/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
ValueTypes = Union[bool, SupportsBytes, SupportsFloat, List, Mapping, str, None]
"""Types that can be encoded into a protobuf `Value`"""

SensorReading = Union[ValueTypes, Vector3, GeoPoint, Orientation]
"""Types that can be returned from a sensor"""


def primitive_to_value(v: ValueTypes) -> Value:
"""
Expand Down Expand Up @@ -172,7 +175,7 @@ async def get_geometries(
return [geometry for geometry in response.geometries]


def sensor_readings_native_to_value(readings: Mapping[str, Any]) -> Mapping[str, Any]:
def sensor_readings_native_to_value(readings: Mapping[str, Any]) -> Mapping[str, Value]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

prim_readings = dict(readings)
for key, reading in readings.items():
if isinstance(reading, Vector3):
Expand All @@ -190,7 +193,7 @@ def sensor_readings_native_to_value(readings: Mapping[str, Any]) -> Mapping[str,
return {key: primitive_to_value(value) for (key, value) in prim_readings.items()}


def sensor_readings_value_to_native(readings: Mapping[str, Value]) -> Mapping[str, Any]:
def sensor_readings_value_to_native(readings: Mapping[str, Value]) -> Mapping[str, SensorReading]:
prim_readings: Dict[str, Any] = {key: value_to_primitive(value) for (key, value) in readings.items()}
for key, reading in prim_readings.items():
if isinstance(reading, Mapping):
Expand Down
4 changes: 2 additions & 2 deletions tests/mocks/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
from viam.proto.component.audioinput import AudioChunk, AudioChunkInfo, SampleFormat
from viam.proto.component.board import PowerMode
from viam.proto.component.encoder import PositionType
from viam.utils import ValueTypes
from viam.utils import SensorReading, ValueTypes

GEOMETRIES = [
Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), sphere=Sphere(radius_mm=2)),
Expand Down Expand Up @@ -991,7 +991,7 @@ def __init__(self, name: str, result: Mapping[str, Any] = {"a": 0, "b": {"foo":

async def get_readings(
self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs
) -> Mapping[str, Any]:
) -> Mapping[str, SensorReading]:
self.extra = extra
self.timeout = timeout
return self.readings
Expand Down
8 changes: 2 additions & 6 deletions tests/test_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,11 @@ async def test_get_digital_interrupt_value(self, board: MockBoard, service: Boar
async with ChannelFor([service]) as channel:
client = BoardServiceStub(channel)

request = GetDigitalInterruptValueRequest(
board_name=board.name, digital_interrupt_name="dne"
)
request = GetDigitalInterruptValueRequest(board_name=board.name, digital_interrupt_name="dne")
with pytest.raises(GRPCError, match=r".*Status.NOT_FOUND.*"):
await client.GetDigitalInterruptValue(request)

request = GetDigitalInterruptValueRequest(
board_name=board.name, digital_interrupt_name="interrupt1"
)
request = GetDigitalInterruptValueRequest(board_name=board.name, digital_interrupt_name="interrupt1")
response: GetDigitalInterruptValueResponse = await client.GetDigitalInterruptValue(request)
assert response.value == 0

Expand Down
6 changes: 3 additions & 3 deletions tests/test_movement_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
MovementSensorServiceStub,
)
from viam.resource.manager import ResourceManager
from viam.utils import dict_to_struct, primitive_to_value, struct_to_dict
from viam.utils import dict_to_struct, sensor_readings_value_to_native, struct_to_dict

from . import loose_approx
from .mocks.components import GEOMETRIES, MockMovementSensor
Expand Down Expand Up @@ -291,7 +291,7 @@ async def test_get_readings(self, movement_sensor: MockMovementSensor, service:
request = GetReadingsRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS))
assert movement_sensor.extra is None
response: GetReadingsResponse = await client.GetReadings(request, timeout=8.90)
assert response.readings == {key: primitive_to_value(value) for (key, value) in READINGS.items()}
assert sensor_readings_value_to_native(response.readings) == READINGS
assert movement_sensor.extra == EXTRA_PARAMS
assert movement_sensor.timeout == loose_approx(8.90)

Expand Down Expand Up @@ -402,7 +402,7 @@ async def test_get_readings(self, movement_sensor: MockMovementSensor, service:
client = MovementSensorClient(movement_sensor.name, channel)
assert movement_sensor.extra is None
value = await client.get_readings(extra=EXTRA_PARAMS, timeout=2.34)
assert value == {key: primitive_to_value(value) for (key, value) in READINGS.items()}
assert value == READINGS
assert movement_sensor.extra == EXTRA_PARAMS
assert movement_sensor.timeout == loose_approx(2.34)

Expand Down
Loading
Loading