Skip to content

Commit dd5ecd9

Browse files
Merge branch 'main' into rsdk-7307
2 parents 00a99d5 + 69cddd2 commit dd5ecd9

22 files changed

+253
-131
lines changed

.github/workflows/docs.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ on:
1212
jobs:
1313
generate-docs:
1414
if: github.repository_owner == 'viamrobotics'
15-
runs-on: [self-hosted, x64]
16-
container:
17-
image: ghcr.io/viamrobotics/canon:amd64
15+
runs-on: ubuntu-latest
1816
steps:
19-
- name: Checkout Push/Workflow Dispatch
20-
uses: actions/checkout@v3
17+
- uses: actions/checkout@v4
18+
19+
- name: Setup Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: "3.12"
2123

2224
- name: Install Poetry
2325
uses: snok/install-poetry@v1

.github/workflows/license_finder.yml

+2-6
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,13 @@ jobs:
1010
license_finder:
1111
if: github.repository_owner == 'viamrobotics'
1212
name: Audit 3rd-Party Licenses
13-
runs-on: [x64, qemu-host]
13+
runs-on: ubuntu-latest
1414
container:
1515
image: ghcr.io/viamrobotics/canon:amd64-cache
16-
options: --platform linux/amd64
1716
timeout-minutes: 30
1817

1918
steps:
20-
- name: Check out code in rdk directory
21-
uses: actions/checkout@v2
22-
with:
23-
fetch-depth: 2
19+
- uses: actions/checkout@v4
2420

2521
- name: Install Poetry
2622
uses: snok/install-poetry@v1

.github/workflows/publish.yml

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ on:
77
jobs:
88
publish:
99
if: github.repository_owner == 'viamrobotics'
10-
runs-on: [self-hosted, x64]
11-
container:
12-
image: ghcr.io/viamrobotics/canon:amd64
10+
runs-on: ubuntu-latest
1311

1412
steps:
1513
- name: Download Release

.github/workflows/release-candidate.yml

+15-23
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,18 @@ on:
1717
jobs:
1818
prepare:
1919
if: github.repository_owner == 'viamrobotics'
20-
runs-on: [self-hosted, x64]
21-
container:
22-
image: ghcr.io/viamrobotics/canon:amd64
20+
runs-on: ubuntu-latest
2321
outputs:
2422
rc_version: ${{ steps.bump_version.outputs.rc_version }}
2523
version: ${{ steps.bump_version.outputs.version }}
2624
steps:
27-
- name: Output Event
28-
run: echo "${{ toJSON(github.event) }}"
29-
30-
- name: Install GH CLI
31-
run: |
32-
type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
33-
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
34-
&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
35-
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
36-
&& sudo apt update \
37-
&& sudo apt install gh -y
38-
3925
- name: Checkout Code
40-
uses: actions/checkout@v3
26+
uses: actions/checkout@v4
27+
28+
- name: Setup Python
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: "3.12"
4132

4233
- name: Install Poetry
4334
uses: snok/install-poetry@v1
@@ -97,9 +88,7 @@ jobs:
9788
build:
9889
needs: prepare
9990
if: github.repository_owner == 'viamrobotics'
100-
runs-on: [self-hosted, x64]
101-
container:
102-
image: ghcr.io/viamrobotics/canon:amd64
91+
runs-on: ubuntu-latest
10392
strategy:
10493
matrix:
10594
include:
@@ -126,10 +115,15 @@ jobs:
126115
whl: linux_armv7l
127116
steps:
128117
- name: Checkout Code
129-
uses: actions/checkout@v3
118+
uses: actions/checkout@v4
130119
with:
131120
ref: rc-${{ needs.prepare.outputs.version }}
132121

122+
- name: Setup Python
123+
uses: actions/setup-python@v5
124+
with:
125+
python-version: "3.12"
126+
133127
- name: Install Poetry
134128
uses: snok/install-poetry@v1
135129

@@ -158,9 +152,7 @@ jobs:
158152
release:
159153
needs: [prepare, build]
160154
if: github.repository_owner == 'viamrobotics'
161-
runs-on: [self-hosted, x64]
162-
container:
163-
image: ghcr.io/viamrobotics/canon:amd64
155+
runs-on: ubuntu-latest
164156

165157
steps:
166158
- uses: actions/download-artifact@v3

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ jobs:
9292
whl: linux_armv7l
9393
steps:
9494
- name: Checkout Code
95-
uses: actions/checkout@v3
95+
uses: actions/checkout@v4
9696
with:
9797
ref: rc-${{ needs.prepare.outputs.version }}
9898

examples/server/v1/components.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from viam.components.arm import Arm
2020
from viam.components.audio_input import AudioInput
2121
from viam.components.base import Base
22-
from viam.components.board import Board
22+
from viam.components.board import Board, TickStream
2323
from viam.components.camera import Camera
2424
from viam.components.encoder import Encoder
2525
from viam.components.gantry import Gantry
@@ -31,7 +31,7 @@
3131
from viam.components.sensor import Sensor
3232
from viam.components.servo import Servo
3333
from viam.errors import ResourceNotFoundError
34-
from viam.media import MediaStreamWithIterator
34+
from viam.streams import StreamWithIterator
3535
from viam.media.audio import Audio, AudioStream
3636
from viam.media.video import NamedImage
3737
from viam.operations import run_with_operation
@@ -155,7 +155,7 @@ async def read() -> AsyncIterator[Audio]:
155155

156156
await asyncio.sleep(self.latency.total_seconds())
157157

158-
return MediaStreamWithIterator(read())
158+
return StreamWithIterator(read())
159159

160160
async def get_properties(self) -> AudioInput.Properties:
161161
return AudioInput.Properties(
@@ -314,6 +314,11 @@ async def set_power_mode(self, **kwargs):
314314
async def write_analog(self, pin: str, value: int, *, timeout: Optional[float] = None, **kwargs):
315315
raise NotImplementedError()
316316

317+
async def stream_ticks(
318+
self, interrupts: List[Board.DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs
319+
) -> TickStream:
320+
raise NotImplementedError()
321+
317322
async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]:
318323
return GEOMETRIES
319324

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "viam-sdk"
3-
version = "0.17.0"
3+
version = "0.18.0"
44
description = "Viam Robotics Python SDK"
55
authors = [ "Naveed <naveed@viam.com>" ]
66
license = "Apache-2.0"

src/viam/components/audio_input/audio_input.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
from google.protobuf.duration_pb2 import Duration
77
from typing_extensions import Self
88

9-
from viam.media import MediaSource
9+
from viam.streams import StreamSource
1010
from viam.media.audio import Audio, AudioStream
1111
from viam.proto.component.audioinput import PropertiesResponse
1212
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype
1313

1414
from ..component_base import ComponentBase
1515

1616

17-
class AudioInput(ComponentBase, MediaSource[Audio]):
17+
class AudioInput(ComponentBase, StreamSource[Audio]):
1818
"""AudioInput represents a component that can capture audio.
1919
2020
This acts as an abstract base class for any drivers representing specific
@@ -67,7 +67,7 @@ async def stream(self, *, timeout: Optional[float] = None, **kwargs) -> AudioStr
6767
"""Stream audio samples from the audio input of the underlying robot
6868
6969
Returns:
70-
MediaStream[Audio]: The stream of audio chunks
70+
Stream[Audio]: The stream of audio chunks
7171
"""
7272
...
7373

src/viam/components/audio_input/client.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from grpclib.client import Channel
44

5-
from viam.media import MediaStream, MediaStreamWithIterator
5+
from viam.streams import Stream, StreamWithIterator
66
from viam.media.audio import Audio
77
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry
88
from viam.proto.component.audioinput import (
@@ -29,7 +29,7 @@ def __init__(self, name: str, channel: Channel):
2929
self.client = AudioInputServiceStub(channel)
3030
super().__init__(name)
3131

32-
async def stream(self, *, timeout: Optional[float] = None, **__) -> MediaStream[Audio]:
32+
async def stream(self, *, timeout: Optional[float] = None, **__) -> Stream[Audio]:
3333
async def read() -> AsyncIterator[Audio]:
3434
async with self.client.Chunks.open(timeout=timeout) as chunks_stream:
3535
await chunks_stream.send_message(
@@ -50,7 +50,7 @@ async def read() -> AsyncIterator[Audio]:
5050
audio = Audio(info=info, chunk=response.chunk)
5151
yield audio
5252

53-
return MediaStreamWithIterator(read())
53+
return StreamWithIterator(read())
5454

5555
async def get_properties(self, *, timeout: Optional[float] = None, **__) -> AudioInput.Properties:
5656
request = PropertiesRequest(name=self.name)

src/viam/components/board/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from viam.resource.registry import Registry, ResourceRegistration
66
from viam.utils import message_to_struct
77

8-
from .board import Board
8+
from .board import Board, Tick, TickStream
99
from .client import BoardClient
1010
from .service import BoardRPCService
1111

1212
__all__ = [
13-
"Board",
13+
"Board"
1414
]
1515

1616

src/viam/components/board/board.py

+30-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
from datetime import timedelta
33
from typing import Any, Dict, Final, List, Optional
44

5-
from viam.proto.component.board import PowerMode
5+
from viam.proto.component.board import PowerMode, StreamTicksResponse
66
from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Subtype
7+
from viam.streams import Stream
78

89
from ..component_base import ComponentBase
910

11+
Tick = StreamTicksResponse
12+
TickStream = Stream[Tick]
13+
1014

1115
class Board(ComponentBase):
1216
"""
@@ -372,3 +376,28 @@ async def write_analog(self, pin: str, value: int, *, timeout: Optional[float] =
372376
value (int): value to write.
373377
"""
374378
...
379+
380+
@abc.abstractmethod
381+
async def stream_ticks(
382+
self, interrupts: List[DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs
383+
) -> TickStream:
384+
"""
385+
Stream digital interrupt ticks.
386+
387+
::
388+
389+
390+
my_board = Board.from_robot(robot=robot, name="my_board")
391+
di8 = await my_board.digital_interrupt_by_name(name="8"))
392+
di11 = await my_board.digital_interrupt_by_name(name="11"))
393+
394+
Stream ticks from pins 8 and 11.
395+
ticks = my_board.stream_ticks([di8, di11])
396+
397+
Args:
398+
interrupts (List[DigitalInterrupt]) : list of digital interrupts to recieve ticks from.
399+
400+
Returns:
401+
TickStream: stream of ticks.
402+
"""
403+
...

src/viam/components/board/client.py

+34-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
from typing import Any, Dict, List, Mapping, Optional
33

44
from google.protobuf.duration_pb2 import Duration
5-
from grpclib.client import Channel
5+
from grpclib.client import Channel, Stream as ClientStream
66

7+
from viam.logging import getLogger
78
from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry
89
from viam.proto.component.board import (
910
BoardServiceStub,
@@ -22,12 +23,17 @@
2223
SetPowerModeRequest,
2324
SetPWMFrequencyRequest,
2425
SetPWMRequest,
26+
StreamTicksRequest,
27+
StreamTicksResponse,
2528
WriteAnalogRequest,
2629
)
30+
from viam.streams import StreamWithIterator
2731
from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase
2832
from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict
2933

30-
from . import Board
34+
from .board import Board, TickStream
35+
36+
LOGGER = getLogger(__name__)
3137

3238

3339
class AnalogReaderClient(Board.AnalogReader):
@@ -224,3 +230,29 @@ async def write_analog(
224230
extra = {}
225231
request = WriteAnalogRequest(name=self.name, pin=pin, value=value, extra=dict_to_struct(extra))
226232
await self.client.WriteAnalog(request, timeout=timeout)
233+
234+
async def stream_ticks(
235+
self,
236+
interrupts: List[Board.DigitalInterrupt],
237+
*,
238+
extra: Optional[Dict[str, Any]] = None,
239+
**__,
240+
) -> TickStream:
241+
if extra is None:
242+
extra = {}
243+
names = []
244+
for di in interrupts:
245+
names.append(di.name)
246+
request = StreamTicksRequest(name=self.name, pin_names=names, extra=dict_to_struct(extra))
247+
248+
async def read():
249+
tick_stream: ClientStream[StreamTicksRequest, StreamTicksResponse]
250+
async with self.client.StreamTicks.open() as tick_stream:
251+
try:
252+
await tick_stream.send_message(request, end=True)
253+
async for tick in tick_stream:
254+
yield tick
255+
except Exception as e:
256+
raise (e)
257+
258+
return StreamWithIterator(read())

src/viam/components/board/service.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from grpclib.server import Stream
2+
from h2.exceptions import StreamClosedError
23

3-
from viam.errors import MethodNotImplementedError, ResourceNotFoundError
4+
from viam.errors import ResourceNotFoundError
5+
from viam.logging import getLogger
46
from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse
57
from viam.proto.component.board import (
68
BoardServiceBase,
@@ -32,6 +34,8 @@
3234

3335
from .board import Board
3436

37+
LOGGER = getLogger(__name__)
38+
3539

3640
class BoardRPCService(BoardServiceBase, ResourceRPCServiceBase[Board]):
3741
"""
@@ -196,4 +200,21 @@ async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometries
196200
await stream.send_message(response)
197201

198202
async def StreamTicks(self, stream: Stream[StreamTicksRequest, StreamTicksResponse]) -> None:
199-
raise MethodNotImplementedError("StreamTicks").grpc_error
203+
request = await stream.recv_message()
204+
assert request is not None
205+
name = request.name
206+
board = self.get_resource(name)
207+
208+
dis = []
209+
for name in request.pin_names:
210+
dis.append(await board.digital_interrupt_by_name(name))
211+
212+
tick_stream = await board.stream_ticks(interrupts=dis, metadata=stream.metadata)
213+
async for tick in tick_stream:
214+
try:
215+
await stream.send_message(tick)
216+
except StreamClosedError:
217+
return
218+
except Exception as e:
219+
LOGGER.error(e)
220+
return

0 commit comments

Comments
 (0)