The DataStore emits a realtimeUpdate signal, which gets fired whenever any of its constituent DataSources emit a realtimeUpdate signal. When a DataSource gets a realtime update, it can decode the new data and stash it into all of its DataSets. Then it fires its realtimeUpdate signal.
The PlotPage keeps track of its mode, either realtime or not, and it has a slot for receiving realtime update signals, doRealtimeUpdate(). The datastore realtimeUpdate signal is connected to the doRealtimeUpdate() slot. If not in realtime mode, then PlotPage just disconnects from the signal altogether until it enters realtime mode again. If in realtime mode, then a call to doRealtimeUpdate() updates the start and end times to the current time window and calls Replot(). Since the DataSource has already stashed the new data into its DataSets, the call to updateDataSets() in Replot() will not require any actual data retrieval.
I suppose it might be more efficient to allow realtime checks to be disabled in the DataSource. However, ideally the DataSource can automatically check whether realtime data are available, so that the PlotPage GUI can reflect that by enabling/disabling the 'realtime mode' button. I think realtime support should be built into each DataSource subclass rather than separated. In other words, avoid separating into SourceSQL and RealtimeSourceSQL. When realtime is not needed, and only if the optimization is warranted, the application can call some method on the datastore to suspend the realtime checks and the consequent realtime data collection overhead.
The DataSource realtimeUpdate signal should be generalized for the case of any data change within the DataSource. Data-driven updates of plots should not be limited to the arrival of new realtime data but instead apply to any change to the DataDomain on which DataSets depend. Likewise, the resolution for notifying of a data change should be finer than just an entire DataSource. It's possible that the dataUpdated signal could include both the DataDomain describing the changed data and the set of DataSets which changed, allowing the view or listener to determine if it is interested in the change notification. However, it would be better instead to provide a general callback interface so that views can register their interest in their particular set of datasets and domains. Then the logic for determining whether a data-driven update affects a particular view is confined to the DataSource and need not be duplicated in every implementation of a view. The view merely refreshes itself whenever the DataSource calls its callback for updated data.
This does not seem to be a complete solution though. For example, a PlotPage in realtime mode has two types of dependencies. The first is obviously all of the DataSets being plotted and their current domain. Any change to the data over the domain should trigger a replot. The second dependence is that new data outside of the current domain should also trigger an advance of the domain and replot. This needs a distinct flag in the DataDomain for identifying the kinds of updates of interest to the PlotPage, such as a NewData bit flag. If the PlotPage is not in realtime mode, then it would clear the NewData bit.
If the PlotPage registers a single callback for all of its dependent DataSets, ie, all of the DataSets being plotted in any of its Plots, then it's still possible that only a subset of those DataSets actually change and oblige the DataSource to call the callback. In the callback, the PlotPage can limit the replots to those Plots having a dependence on the changed DataSets. Here's the PlotPage callback In pseudo-code:
PlotPage:: dataUpdated (DataSetList& changed_datasets, DataDomain& changed_domain) { // If the update kind is DataAppended, then advance the // time window. if (realtimeMode && changed_domain.updateType() == DataAppended) { setStartEndTime (changed_domain.endTime() - plotPeriod, changed_domain.endTime()); } else { // It's possible only a subset of the datasets have // changed, so those are the only ones needing to be // re-filled with the current plot domain. datasource->updateDataSets(domain, changed_datasets); // Now replot // ...
The DataSet proxy idea seems like it will work well. Maybe a similar approach is needed for DataSource so that any client of a DataSource can keep its own proxy object for accessing the DataSource, and that proxy object can store the client-specific information like callbacks. Or else maybe a Plot should always use a single DataSet to maintain its list of individual variables being plotted, so the toplevel DataSet represents the conglomeration of all the variables and the update callback will be called only once if any of the datasets change.