Control Executive proposal

Overview & background

One component of the refactoring plan is to provide a high level "control executive".  By this is meant an API to control most of the operations that are currently under the control of the VAPOR GUI, while avoiding any actual GUI code.  Here are some thoughts about what should be doable from the Control Executive:

The above capabilities primarily are currently in the following classes:

Proposed Control Executive Design:

First, split the above classes into parts that can remain in the GUI and parts that should be represented in the Control Executive.

Once the functionality has been split as above, the next step is to clean up and organize the Control Executive API to facilitate development of applications that will use the API.  This organization should consist of three sections:  State management (the Params API) , Rendering (Render, GLWindow, Manip methods that perform rendering), and ControlExecutive (including VizWinMgr and Session)

Control Executive API

The proposed API is available here. Doxygen source for the documentation is attached here.

(AN) The above API has been modified here with some additional methods and some changes to how Params and Renderers are referenced. Doxygen source is attached here.

The following items are not currently addressed by the ControlExecutive class:

  1. ErrorHandling: All of the ControlExecutive methods return a success or failure status. However, no methods have been provided for returning an error code or error message. ## (AN) I propose an ErrorHandler class be defined, which UI's can use to be notified when the Control Executive has an error condition.
  2. OpenGL state mgt: In the proposed API the UI is responsible for establishing the OpenGL drawing context (e.g. creating a drawing window) on behalf of the ControlExecutive. We need to flesh out what other OpenGL responsibilities/expectations the UI and ControlExecutive have wrt OpenGL.  Not sure what you are asking.  Does this require an implementation?
  3. Timestep specification: How are time steps specified?## The timestep is specified in the AnimationParams class
  4. Key framing:## This is also specified in the AnimationParams class
  5. Manipulators## (AN) I propose a ManipParams class that will include all the settings associated with the current mouse mode.  UI's can translate mouse events into changes in the ManipParams that applies to that window.
  6. Image capture## (AN) I added an "EnableCapture" method to the API doc
  7. Probing: How does the UI query data values selected by the probe?## (AN) The UI can convert the probe mouse coordinates into user coordinates (using Extents etc. obtained from DataMgr).  Then it can use the RegularGrid to evaluate a variable at the specified position.

Example Use Cases

Case 1

// A single visualization window with a single renderer, single instance

ControlExecutive *cntrl_exec = new ControlExecutive();

// The UI has created an OpenGL window. It now informs the ControlExecutive
// that it is available and ready for rendering

int viz = cntrl_exec->NewVisualizer(oglinfo);

// Tell the CE which files to load. LoadData() returns a pointer to a class
// object that can be used to query metadata info

const DataInfo *data_info = cntrl_exec->LoadData(file(s));

// Create a new DVR params bound to the single OpenGL window we have

// Note that it is adjusted to

// ensure the settings are compatible with the data that has been loaded.

DVRParams* rp = (DvrParams*)cntrl_exec->NewParams("DVR",viz);

//Create a renderer instance suitable for the DVR Params

int inst = ctrl_exec->NewRenderer(viz, "DVR", rp);

// Get a handle for renderer instances Params object and then change
// some parameters. SHOULD CHECK ERROR STATUS
// when setting params. If error occurs on a single SetValue(),

//the value will not be changed and an error return code will be returned.



// Make sure the dvr instance is active and tell the CE to enable
// this renderer, and render everything.

cntrl_exec->ActivateRender(viz, "DVR", inst, true);


Case 2

// Here we have  two iso renderer instances, both bound to the same window

ControlExecutive *cntrl_exec = new ControlExecutive();

int viz = cntrl_exec->NewVisualizer(oglinfo);

const DataInfo *data_info = cntrl->LoadData(file(s));

// Create two iso Params instances

IsoParams* rp1 = (IsoParams*)cntrl_exec->NewParams("Iso",viz);

IsoParams* rp2 = (IsoParams*)cntrl_exec->NewParams("Iso",viz);

// Set some params on rp1 and rp2.  Put these changes as one entry in the command queue, so

//we can undo them if there's an error.  Note that if we did not invoke StartCommand(), then each Params

//state change would be inserted as a separate entry in the queue

ctrl_exec->StartCommand(rp1, "Iso setup");


int rc = ctrl_exec->EndCommand(rp1);

// if rc is nonzero, an error occurred and state was reverted to prior to the StartCommand()

if (rc){

  //provide an error message here


ctrl_exec->StartCommand(rp2, "Iso setup");


rc = ctrl_exec->EndCommand(rp2);

if (rc){

  //provide an error message here


// obtain renderer instances for the Iso Params

int inst1 = ctrl_exec->NewRenderer(viz, "Iso", rp1);

int inst2 = ctrl_exec->NewRenderer(viz, "Iso", rp2);

// Make sure both iso's are active and tell them to render
cntrl_exec->ActivateRender(viz,"Iso",inst1, true);

Case 3

// Here we have two visualizer windows with one iso renderer per window

ControlExecutive *cntrl_exec = new ControlExecutive();

// Create two windows
int viz1 = cntrl_exec->NewVisualizer(oglinfo);
int viz2 = cntrl_exec->NewVisualizer(oglinfo);

const DataInfo *data_info = cntrl->LoadData(file(s));

// Create one iso instance for each of the two windows

IsoParams* rp1 = (IsoParams*)cntrl_exec->NewParams("Iso",viz1);

IsoParams* rp2 = (IsoParams*)cntrl_exec->NewParams("Iso",viz2);

// Set iso params



//Create renderers

int inst1 = ctrl_exec->NewRenderer(viz1, "Iso", rp1);

int inst2 = ctrl_exec->NewRenderer(viz2, "Iso", rp2);

// Activate renderers and call Paint for *both* windows

cntrl_exec->ActivateRender(viz1,"Iso",inst1, true);


// Change viewing params and re-render. Both windows use global view params

//Push and pop on the matrix stack is only performed during rendering, not here
//Note that the visualizer params are by default global; i.e. apply to both viz1 and viz2

//The following two lines are not needed except when the viewpoint params are not already in "global" state

cntrl_exec->GetLocalParams(viz1, "Viewpoint")->SetGlobal(true);

cntrl_exec->GetLocalParams(viz2, "Viewpoint")->SetGlobal(true);

ViewpointParams* vpp = (ViewpointParams*) cntrl_exec->GetGlobalParams("Viewpoint");

Viewpoint* vp = vpp->GetViewpoint();




Case 4

// Handing an undo

// The UI should check to find out which params instance
// has changed, and then update its state to reflect change.
// identify the window, instance, type that changed

int instance, viz;

string type;

Params* p = cntrl_exec->Undo(&instance,&viz,type);

//The UI will need to insert code here to update its state for (instance, viz, type)


//Note: changes that occur always can be identified as a modification of a single params instance.

Prototype Implementation proposal

Goal:  A simple prototype that validates the Control Executive API, that can be used as a framework for filling in full application capabilities.  The prototype will eliminate all legacy code that we are not intending to continue supporting in 3.0.

Implementation plan overview:

  1. Create a 3.0 branch from the 2.2.4 tree
  2. Leave vdf and common libraries intact (for now)
  3. Implement Control Executive as a class (initially) in the Render lib.  Hook it up to Params and Render libraries (after following changes are implemented).
  4. In Params lib, remove most of the code (i.e. at least remove it from the build) except for the following changes:
    1. Keep Params, RenderParams, ParamsBase classes but strip out legacy code.
    2. Reimplement RegionParams, ViewpointParams, AnimationParams based on XML representation
    3. Keep Box, ArrowParams (but strip out or replace legacy code)
    4. Add Undo/Redo queue management to Params class
    5. Add new Params classes for management of state that is currently not in Params, such as Manips, renderer enablement, instance management.
    6. Load/save session files
    7. Add error checking, reversion of invalid settings on SetValues
    8. Avoid DataStatus usage (rely on DataMgr for VDC information; any necessary functions should eventually be implemented in VDF lib.
  5. In Render lib:
    1. Retain Renderer class, plus ArrowRenderer.  Clean out legacy code.
    2. Retain Manip class
    3. Strip Qt Dependence out of GLWindow class.  Retain ability to maintain Renderer instances via OpenGL calls.
    4. Eliminate dependence on Trackball:  The trackball will be in the GUI and will act through the ViewpointParams.
  6. Implement a Qt-based GUI
    1. Clean up the EventRouters that correspond to Params classes that are being used in this prototype, retain only functionality that is required for setting values in params and connecting them to GUI events.
    2. Redesign current EventRouter event model, simplify and avoid spurious events.
    3. Replace VizWin with a stand-alone QGLWidget
    4. Purge legacy code from VizWinMgr.  Identify (and reimplement) the functionality of VizWinMgr that is needed in GUI (some functionality such as start-up configuration should be moved to Params).