Basic Scenario

DataSourceFactory knows about available kinds of DataSources, perhaps from the command-line or a config file and some defaults it can search (like attempting to connect to known database addresses on plane and at home). Each datasource is identified by a string like netcdf:/data/rf01.cdf or sql:user,password@datahost:port, where the particular DataSource subclass knows how to parse the string to open itself.

The GUI requests the default list of datasource specifiers (DataStore::getDefaultDataSourceSpecs()) from the DataStore.

The user selects a datasource to open.

The GUI tells DataStore to open that datasource (DataStore::openDataSource()). The DataStore calls DataSourceFactory::create() and adds the returned DataSource to itself. It queries the new DataSource for its list of DataSets (DataSource::getDataSets()) and merges that list with its current list from any other DataSources. This updates the list of available variables.

When a DataSource is opened, the particular DataSource backend creates its own subclasses of DataSet for each of its variables, but does not fill them with any data (unless that proves to be a worthwhile optimization). For example, the SourceNetCDF instance creates a DataSetNetCDF for each of the variables in the netcdf file. Among other things, this allows SourceNetCDF to get at the varid of a DataSet when it gets passed in through updateDataSets(). The idea of DataSet subclasses has not actually been needed yet, so currently all DataSources use a single DataSetBack class.

After opening a new DataSource, the GUI can refresh the list of available variables by calling getDataSets() on the datastore.

The GUI also updates the metadata display by requesting new metadata from the datastore with getGlobalMetadata(). The DataStore forwards that request to the individual datasources (or perhaps only to a 'primary datasource') and merges the info from all the datasources where it makes sense. The returned metadata should include the combined time span of the datasources so the GUI can choose a reasonable default time span for the plots.

Each datasource backend retrieves its metadata in a backend-specific way.

Now the GUI can display current metadata and variable choices, and it can can set the plot time to a reasonable default based on the timespan returned in the metadata.

The user uses GUI controls to add a variable to a plot, which causes the chosen DataSet to be added to the Plot with Plot::addDataSet().

The plots need to be refreshed, so the GUI calls PlotPage::Replot(). Replot() passes all of the DataSets being plotted on the page to the datastore->updateDataSets() method.

The datastore passes the DataSets to their DataSources, and each DataSource backend can verify that the DataSet is current or else retrieve the requested window of data and add it to the DataSet. In the case of a SourceSQL, if any data need to be requested to fill in a DataSet it can assemble a single SQL statement given the set of DataSets needing updating.

When updateDataSets() returns in PlotPage::Replot(), all of the Plots can be replotted, and each Plot will get the current and correct window of data from its DataSet.

If the user changes other features of the plots, such as the time window, the PlotPage::Replot() method updates the plots in the same way. If the time window has changed, each DataSet will be filled with the new time range. If the DataSet happened to already have that time range cached, then no extra data requests would be necessary. If a Plot change does not affect the data, such as a color change, then the updateDataSets() call could be skipped.