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

ENH: ArchiveTimePlot Fetch Data on X-Axis Change #1172

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
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
91 changes: 63 additions & 28 deletions pydm/widgets/archiver_time_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ def __init__(
init_y_channels: List[str] = [],
background: str = "default",
optimized_data_bins: int = 2000,
cache_data: bool = True,
show_all: bool = True,
):
super(PyDMArchiverTimePlot, self).__init__(
parent=parent,
Expand All @@ -779,21 +781,42 @@ def __init__(
bottom_axis=DateAxisItem("bottom"),
)
self.optimized_data_bins = optimized_data_bins
self._show_all = False # Show all plotted data after archiver fetch
# self._show_all = show_all # Show all plotted data after archiver fetch
self._starting_timestamp = time.time() # The timestamp at which the plot was first rendered
self._min_x = self._starting_timestamp - DEFAULT_TIME_SPAN
self._prev_x = self._min_x # Holds the minimum x-value of the previous update of the plot
self._archive_request_queued = False
self.setTimeSpan(DEFAULT_TIME_SPAN)

def updateXAxis(self, update_immediately: bool = False) -> None:
self.cache_data = False
# self.cache_data = cache_data

@property
def cache_data(self):
return self._cache_data

@cache_data.setter
def cache_data(self, retain: bool):
self._cache_data = retain
if retain:
try:
self.plotItem.sigXRangeChanged.disconnect(self.updateXAxis)
self.plotItem.sigXRangeChangedManually.disconnect(self.updateXAxis)
except TypeError:
pass
else:
self.plotItem.sigXRangeChanged.connect(self.updateXAxis)
self.plotItem.sigXRangeChangedManually.connect(self.updateXAxis)

def updateXAxis(self, update_immediately: bool = False, *_) -> None:
"""Manages the requests to archiver appliance. When the user pans or zooms the x axis to the left,
a request will be made for backfill data"""
if len(self._curves) == 0 or self.auto_scroll_timer.isActive():
if len(self._curves) == 0:
return

min_x = self.plotItem.getAxis("bottom").range[0] # Gets the leftmost timestamp displayed on the x-axis
max_x = self.plotItem.getAxis("bottom").range[1]
max_point = max([curve.max_x() for curve in self._curves])
if min_x == 0: # This is zero when the plot first renders
self._max_x = time.time()
self._min_x = self._max_x - DEFAULT_TIME_SPAN
Expand All @@ -803,30 +826,40 @@ def updateXAxis(self, update_immediately: bool = False) -> None:
self._min_x = self._min_x - self.getTimeSpan()
self._archive_request_queued = True
self.requestDataFromArchiver()
self.plotItem.setXRange(
time.time() - DEFAULT_TIME_SPAN, time.time(), padding=0.0, update=update_immediately
)
elif min_x < self._min_x and not self.plotItem.isAnyXAutoRange():
# This means the user has manually scrolled to the left, so request archived data
self._min_x = min_x
self.setTimeSpan(max_point - min_x)
if not self._archive_request_queued:
# Letting the user pan or scroll the plot is convenient, but can generate a lot of events in under
# a second that would trigger a request for data. By using a timer, we avoid this burst of events
# and consolidate what would be many requests to archiver into just one.
self._archive_request_queued = True
QTimer.singleShot(1000, self.requestDataFromArchiver)
# Here we only update the x-axis if the user hasn't asked for autorange and they haven't zoomed in (as
# detected by the max range showing on the plot being less than the data available)
elif not self.plotItem.isAnyXAutoRange() and max_x >= max_point - 10:
if min_x > (self._prev_x + 15) or min_x < (self._prev_x - 15):
# The plus/minus 15 just makes sure we don't do this on every update tick of the graph
blocked = self.plotItem.blockSignals(True)
self.plotItem.setXRange(time.time() - DEFAULT_TIME_SPAN, time.time(), padding=0.0)
self.plotItem.blockSignals(blocked)
elif not self._cache_data:
if min_x != self._min_x or max_x != self._max_x:
# The timerange of the x-axis changed; user wants only archive data for the new range
self._min_x = min_x
self._max_x = max_x
self.setTimeSpan(max_x - min_x)
if not self._archive_request_queued:
self._archive_request_queued = True
QTimer.singleShot(1000, self.requestDataFromArchiver)
elif not self.plotItem.isAnyXAutoRange():
max_point = max([curve.max_x() for curve in self._curves])
if min_x < self._min_x:
# User has manually scrolled to the left, so request archived data
self._min_x = min_x
self.setTimeSpan(max_point - min_x)
else:
# Keep the plot moving with a rolling window based on the current timestamp
self.plotItem.setXRange(
max_point - self.getTimeSpan(), max_point, padding=0.0, update=update_immediately
)
if not self._archive_request_queued:
self._archive_request_queued = True
QTimer.singleShot(1000, self.requestDataFromArchiver)
elif max_x >= max_point - 10:
# Only update the x-axis if autorange is disabled and user hasn't zoomed in
if min_x > (self._prev_x + 15) or min_x < (self._prev_x - 15):
# The plus/minus 15 just makes sure we don't do this on every update tick of the graph
self.setTimeSpan(max_point - min_x)
else:
# Keep the plot moving with a rolling window based on the current timestamp
blocked = self.plotItem.blockSignals(True)
self.plotItem.setXRange(
max_point - self.getTimeSpan(), max_point, padding=0.0, update=update_immediately
)
self.plotItem.blockSignals(blocked)

self._prev_x = min_x

def requestDataFromArchiver(self, min_x: Optional[float] = None, max_x: Optional[float] = None) -> None:
Expand All @@ -851,8 +884,10 @@ def requestDataFromArchiver(self, min_x: Optional[float] = None, max_x: Optional
if curve.use_archive_data:
if max_x is None:
max_x = curve.min_x()
if not self._cache_data:
max_x = min(max_x, self._max_x)
requested_seconds = max_x - min_x
if requested_seconds <= 5:
if requested_seconds <= MIN_TIME_SPAN:
continue # Avoids noisy requests when first rendering the plot
# Max amount of raw data to return before using optimized data
max_data_request = int(0.80 * self.getArchiveBufferSize())
Expand Down Expand Up @@ -903,7 +938,7 @@ def createCurveItem(self, *args, **kwargs) -> ArchivePlotCurveItem:
def archive_data_received(self):
"""Take any action needed when this plot receives new data from archiver appliance"""
self._archive_request_queued = False
if self.auto_scroll_timer.isActive():
if self.auto_scroll_timer.isActive() or not self._show_all:
return

max_x = max([curve.max_x() for curve in self._curves])
Expand Down
Loading