Skip to content

Commit f3b04f2

Browse files
authored
Merge pull request #490 from pikers/log_linearized_curve_overlays
Log linearized curve overlays
2 parents eb51033 + 889e920 commit f3b04f2

28 files changed

+1993
-686
lines changed

piker/data/_pathops.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ def slice_from_time(
295295
arr: np.ndarray,
296296
start_t: float,
297297
stop_t: float,
298-
step: int | None = None,
298+
step: float, # sampler period step-diff
299299

300300
) -> slice:
301301
'''
@@ -324,12 +324,6 @@ def slice_from_time(
324324
# end of the input array.
325325
read_i_max = arr.shape[0]
326326

327-
# TODO: require this is always passed in?
328-
if step is None:
329-
step = round(t_last - times[-2])
330-
if step == 0:
331-
step = 1
332-
333327
# compute (presumed) uniform-time-step index offsets
334328
i_start_t = floor(start_t)
335329
read_i_start = floor(((i_start_t - t_first) // step)) - 1
@@ -395,7 +389,7 @@ def slice_from_time(
395389
# f'diff: {t_diff}\n'
396390
# f'REMAPPED START i: {read_i_start} -> {new_read_i_start}\n'
397391
# )
398-
read_i_start = new_read_i_start - 1
392+
read_i_start = new_read_i_start
399393

400394
t_iv_stop = times[read_i_stop - 1]
401395
if (
@@ -412,7 +406,7 @@ def slice_from_time(
412406
times[read_i_start:],
413407
# times,
414408
i_stop_t,
415-
side='left',
409+
side='right',
416410
)
417411

418412
if (

piker/data/_sampling.py

+61-20
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ class Sampler:
8787
# holds all the ``tractor.Context`` remote subscriptions for
8888
# a particular sample period increment event: all subscribers are
8989
# notified on a step.
90-
# subscribers: dict[int, list[tractor.MsgStream]] = {}
9190
subscribers: defaultdict[
9291
float,
9392
list[
@@ -240,8 +239,11 @@ async def broadcast(
240239
subscribers for a given sample period.
241240
242241
'''
242+
pair: list[float, set]
243243
pair = self.subscribers[period_s]
244244

245+
last_ts: float
246+
subs: set
245247
last_ts, subs = pair
246248

247249
task = trio.lowlevel.current_task()
@@ -253,25 +255,35 @@ async def broadcast(
253255
# f'consumers: {subs}'
254256
)
255257
borked: set[tractor.MsgStream] = set()
256-
for stream in subs:
258+
sent: set[tractor.MsgStream] = set()
259+
while True:
257260
try:
258-
await stream.send({
259-
'index': time_stamp or last_ts,
260-
'period': period_s,
261-
})
262-
except (
263-
trio.BrokenResourceError,
264-
trio.ClosedResourceError
265-
):
266-
log.error(
267-
f'{stream._ctx.chan.uid} dropped connection'
268-
)
269-
borked.add(stream)
261+
for stream in (subs - sent):
262+
try:
263+
await stream.send({
264+
'index': time_stamp or last_ts,
265+
'period': period_s,
266+
})
267+
sent.add(stream)
268+
269+
except (
270+
trio.BrokenResourceError,
271+
trio.ClosedResourceError
272+
):
273+
log.error(
274+
f'{stream._ctx.chan.uid} dropped connection'
275+
)
276+
borked.add(stream)
277+
else:
278+
break
279+
except RuntimeError:
280+
log.warning(f'Client subs {subs} changed while broadcasting')
281+
continue
270282

271283
for stream in borked:
272284
try:
273285
subs.remove(stream)
274-
except ValueError:
286+
except KeyError:
275287
log.warning(
276288
f'{stream._ctx.chan.uid} sub already removed!?'
277289
)
@@ -419,7 +431,7 @@ async def maybe_open_samplerd(
419431
loglevel: str | None = None,
420432
**kwargs,
421433

422-
) -> tractor._portal.Portal: # noqa
434+
) -> tractor.Portal: # noqa
423435
'''
424436
Client-side helper to maybe startup the ``samplerd`` service
425437
under the ``pikerd`` tree.
@@ -609,6 +621,14 @@ async def sample_and_broadcast(
609621
fqsn = f'{broker_symbol}.{brokername}'
610622
lags: int = 0
611623

624+
# TODO: speed up this loop in an AOT compiled lang (like
625+
# rust or nim or zig) and/or instead of doing a fan out to
626+
# TCP sockets here, we add a shm-style tick queue which
627+
# readers can pull from instead of placing the burden of
628+
# broadcast on solely on this `brokerd` actor. see issues:
629+
# - https://github.com/pikers/piker/issues/98
630+
# - https://github.com/pikers/piker/issues/107
631+
612632
for (stream, tick_throttle) in subs.copy():
613633
try:
614634
with trio.move_on_after(0.2) as cs:
@@ -738,9 +758,6 @@ def frame_ticks(
738758
ticks_by_type[ttype].append(tick)
739759

740760

741-
# TODO: a less naive throttler, here's some snippets:
742-
# token bucket by njs:
743-
# https://gist.github.com/njsmith/7ea44ec07e901cb78ebe1dd8dd846cb9
744761
async def uniform_rate_send(
745762

746763
rate: float,
@@ -750,8 +767,22 @@ async def uniform_rate_send(
750767
task_status: TaskStatus = trio.TASK_STATUS_IGNORED,
751768

752769
) -> None:
770+
'''
771+
Throttle a real-time (presumably tick event) stream to a uniform
772+
transmissiom rate, normally for the purposes of throttling a data
773+
flow being consumed by a graphics rendering actor which itself is limited
774+
by a fixed maximum display rate.
753775
754-
# try not to error-out on overruns of the subscribed (chart) client
776+
Though this function isn't documented (nor was intentially written
777+
to be) a token-bucket style algo, it effectively operates as one (we
778+
think?).
779+
780+
TODO: a less naive throttler, here's some snippets:
781+
token bucket by njs:
782+
https://gist.github.com/njsmith/7ea44ec07e901cb78ebe1dd8dd846cb9
783+
784+
'''
785+
# try not to error-out on overruns of the subscribed client
755786
stream._ctx._backpressure = True
756787

757788
# TODO: compute the approx overhead latency per cycle
@@ -848,6 +879,16 @@ async def uniform_rate_send(
848879
# rate timing exactly lul
849880
try:
850881
await stream.send({sym: first_quote})
882+
except tractor.RemoteActorError as rme:
883+
if rme.type is not tractor._exceptions.StreamOverrun:
884+
raise
885+
ctx = stream._ctx
886+
chan = ctx.chan
887+
log.warning(
888+
'Throttled quote-stream overrun!\n'
889+
f'{sym}:{ctx.cid}@{chan.uid}'
890+
)
891+
851892
except (
852893
# NOTE: any of these can be raised by ``tractor``'s IPC
853894
# transport-layer and we want to be highly resilient

piker/data/feed.py

+3
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,9 @@ async def open_feed(
15891589
(brokermod, bfqsns),
15901590
) in zip(ctxs, providers.items()):
15911591

1592+
# NOTE: do it asap to avoid overruns during multi-feed setup?
1593+
ctx._backpressure = backpressure
1594+
15921595
for fqsn, flume_msg in flumes_msg_dict.items():
15931596
flume = Flume.from_msg(flume_msg)
15941597
assert flume.symbol.fqsn == fqsn

piker/ui/_annotate.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
Annotations for ur faces.
1919
2020
"""
21-
from typing import Callable, Optional
21+
from typing import Callable
2222

2323
from PyQt5 import QtCore, QtGui, QtWidgets
2424
from PyQt5.QtCore import QPointF, QRectF
@@ -105,7 +105,7 @@ def __init__(
105105
get_level: Callable[..., float],
106106
size: float = 20,
107107
keep_in_view: bool = True,
108-
on_paint: Optional[Callable] = None,
108+
on_paint: Callable | None = None,
109109

110110
) -> None:
111111

piker/ui/_axes.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# piker: trading gear for hackers
2-
# Copyright (C) Tyler Goodlet (in stewardship for piker0)
2+
# Copyright (C) Tyler Goodlet (in stewardship for pikers)
33

44
# This program is free software: you can redistribute it and/or modify
55
# it under the terms of the GNU Affero General Public License as published by
@@ -20,7 +20,7 @@
2020
"""
2121
from __future__ import annotations
2222
from functools import lru_cache
23-
from typing import Optional, Callable
23+
from typing import Callable
2424
from math import floor
2525

2626
import numpy as np
@@ -60,7 +60,8 @@ def __init__(
6060
**kwargs
6161
)
6262

63-
# XXX: pretty sure this makes things slower
63+
# XXX: pretty sure this makes things slower!
64+
# no idea why given we only move labels for the most part?
6465
# self.setCacheMode(QtWidgets.QGraphicsItem.DeviceCoordinateCache)
6566

6667
self.pi = plotitem
@@ -190,7 +191,7 @@ def __init__(
190191
*args,
191192
min_tick: int = 2,
192193
title: str = '',
193-
formatter: Optional[Callable[[float], str]] = None,
194+
formatter: Callable[[float], str] | None = None,
194195
**kwargs
195196

196197
) -> None:
@@ -202,8 +203,8 @@ def __init__(
202203
def set_title(
203204
self,
204205
title: str,
205-
view: Optional[ChartView] = None,
206-
color: Optional[str] = None,
206+
view: ChartView | None = None,
207+
color: str | None = None,
207208

208209
) -> Label:
209210
'''
@@ -303,8 +304,9 @@ def _indexes_to_timestrs(
303304
viz = chart._vizs[chart.name]
304305
shm = viz.shm
305306
array = shm.array
306-
times = array['time']
307-
i_0, i_l = times[0], times[-1]
307+
ifield = viz.index_field
308+
index = array[ifield]
309+
i_0, i_l = index[0], index[-1]
308310

309311
# edge cases
310312
if (
@@ -316,11 +318,13 @@ def _indexes_to_timestrs(
316318
(indexes[0] > i_0
317319
and indexes[-1] > i_l)
318320
):
321+
# print(f"x-label indexes empty edge case: {indexes}")
319322
return []
320323

321-
if viz.index_field == 'index':
322-
arr_len = times.shape[0]
324+
if ifield == 'index':
325+
arr_len = index.shape[0]
323326
first = shm._first.value
327+
times = array['time']
324328
epochs = times[
325329
list(
326330
map(

0 commit comments

Comments
 (0)