Skip to content
Closed
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
103 changes: 101 additions & 2 deletions docs/api/sensor_streams/advanced_streams.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,34 @@ class MLModel(Module):

```

## Getting Values Synchronously

Sometimes you don't want a stream, you just want to call a function and get the latest value.

If you are doing this periodically as a part of a processing loop, it is very likely that your code will be much cleaner and safer using actual reactivex pipeline. So bias towards checking our [reactivex quick guide](reactivex.md) and [official docs](https://rxpy.readthedocs.io/)

(TODO we should actually make this example actually executable)

```python skip
self.color_image.observable().pipe(
# takes the best image from a stream every 200ms,
# ensuring we are feeding our detector with highest quality frames
quality_barrier(lambda x: x["quality"], target_frequency=0.2),

## Getting Values Synchronously
# converts Image into Person detections
ops.map(detect_person),

# converts Detection2D to Twist pointing in the direction of a detection
ops.map(detection2d_to_twist),

# emits the latest value every 50ms making our control loop run at 20hz
# despite detections running at 200ms
ops.sample(0.05),
).subscribe(self.twist.publish) # shoots off the Twist out of the module
```

Sometimes you don't want a stream, you just want to call a function and get the latest value. We provide two approaches:

If you'd still like to switch to synchronous fetching, we provide two approaches, `getter_hot()` and `getter_cold()`

| | `getter_hot()` | `getter_cold()` |
|------------------|--------------------------------|----------------------------------|
Expand All @@ -138,6 +158,85 @@ Sometimes you don't want a stream, you just want to call a function and get the
| **Resources** | Keeps connection open | Opens/closes each call |
| **Use when** | Frequent reads, need latest | Occasional reads, save resources |

<details>
<summary>diagram source</summary>

```pikchr fold output=assets/getter_hot_cold.svg
color = white
fill = none

H_Title: box "getter_hot()" rad 5px fit wid 170% ht 170%

Sub: box "subscribe" rad 5px fit wid 170% ht 170% with .n at H_Title.s + (0, -0.5in)
arrow from H_Title.s to Sub.n
arrow right from Sub.e
Cache: box "Cache" rad 5px fit wid 170% ht 170%

# blocking box around subscribe->cache (one-time setup)
Blk0: box dashed color 0x5c9ff0 with .nw at Sub.nw + (-0.1in, 0.25in) wid (Cache.e.x - Sub.w.x + 0.2in) ht 0.7in rad 5px
text "blocking" italic with .n at Blk0.n + (0, -0.05in)

arrow right from Cache.e
Getter: box "getter" rad 5px fit wid 170% ht 170%

arrow from Getter.e right 0.3in then down 0.25in then right 0.2in
G1: box invis "call()" color 0x8cbdf2 fit wid 150%
arrow right 0.4in from G1.e
box invis "instant" fit wid 150%

arrow from Getter.e right 0.3in then down 0.7in then right 0.2in
G2: box invis "call()" color 0x8cbdf2 fit wid 150%
arrow right 0.4in from G2.e
box invis "instant" fit wid 150%

text "always subscribed" italic with .n at Blk0.s + (0, -0.1in)


# === getter_cold section ===
C_Title: box "getter_cold()" rad 5px fit wid 170% ht 170% with .nw at H_Title.sw + (0, -1.6in)

arrow down 0.3in from C_Title.s
ColdGetter: box "getter" rad 5px fit wid 170% ht 170%

# Branch to first call
arrow from ColdGetter.e right 0.3in then down 0.3in then right 0.2in
Cold1: box invis "call()" color 0x8cbdf2 fit wid 150%
arrow right 0.4in from Cold1.e
Sub1: box invis "subscribe" fit wid 150%
arrow right 0.4in from Sub1.e
Wait1: box invis "wait" fit wid 150%
arrow right 0.4in from Wait1.e
Val1: box invis "value" fit wid 150%
arrow right 0.4in from Val1.e
Disp1: box invis "dispose " fit wid 150%

# blocking box around first row
Blk1: box dashed color 0x5c9ff0 with .nw at Cold1.nw + (-0.1in, 0.25in) wid (Disp1.e.x - Cold1.w.x + 0.2in) ht 0.7in rad 5px
text "blocking" italic with .n at Blk1.n + (0, -0.05in)

# Branch to second call
arrow from ColdGetter.e right 0.3in then down 1.2in then right 0.2in
Cold2: box invis "call()" color 0x8cbdf2 fit wid 150%
arrow right 0.4in from Cold2.e
Sub2: box invis "subscribe" fit wid 150%
arrow right 0.4in from Sub2.e
Wait2: box invis "wait" fit wid 150%
arrow right 0.4in from Wait2.e
Val2: box invis "value" fit wid 150%
arrow right 0.4in from Val2.e
Disp2: box invis "dispose " fit wid 150%

# blocking box around second row
Blk2: box dashed color 0x5c9ff0 with .nw at Cold2.nw + (-0.1in, 0.25in) wid (Disp2.e.x - Cold2.w.x + 0.2in) ht 0.7in rad 5px
text "blocking" italic with .n at Blk2.n + (0, -0.05in)
```

</details>

<!--Result:-->
![output](assets/getter_hot_cold.svg)


**Prefer `getter_cold()`** when you can afford to wait and warmup isn't expensive. It's simpler (no cleanup needed) and doesn't hold resources. Only use `getter_hot()` when you need instant reads or the source is expensive to start.

### `getter_hot()` - Background subscription, instant reads
Expand Down
71 changes: 71 additions & 0 deletions docs/api/sensor_streams/assets/getter_hot_cold.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading