GUPPI Controller Semantics

Overview

Introduction

In summer 2008, the GUPPI controller shifted into a formalized "agent-based" design, where all objects in the controller inherited an abstract agent class with strictly defined common methods and arguments (emphasis on stricter class definitions, subclassing was nothing new). Originally intended to support an unforgiving SOAP WSDL across programming languages, this shift facilitated cleaner code, which in turn yielded higher code reuse and less cruft (woot -- software engineering FTW!).

The current agent model is incomplete with respect to current discussions to identify GUPPI manager parameters and to support the GBT config tool and Astrid. Primarily, in the current design, it's not immediately obvious how to implement high-level commands in the manager (e.g. a command to plot the ADC histogram to a known PNG path), nor is it obvious how to implement parameters which in turn trickle down to lower level attribues (e.g. setting the number of channels in the manager in turn affects the DAQ/NCHAN parameter and the BEE2 FPGA personality configurations).

Controller Responsibilities

(from MR6C108)

The GUPPI Controller shall:
  • provide read and write access to all on-board parameters
  • provide instrument start and stop commands
  • run independently of the hardware boards, with the exception of the on-board interfaces of TinySH and BORPH
  • integrate with the GUPPI Data Acquisition (GDAQ) module by
    • providing all parameters and external information needed by GDAQ
    • reading status, diagnostic, error and logging messages from GDAQ
    • providing timely start and stop commands
  • be compatible with future on-going integration with the GBT M&C system

Controller Stakeholders

The following stakeholders (a.k.a "clients") use the GUPPI Controller:
  • the GUPPI command interpreter (i.e. the "expert level interface")
  • the GBT GUPPI Manager
  • expert-level Python scripts (mostly from ScottRansom and PaulDemorest)

For cleanliness and maintability, it's best that as much functionality with respect to GUPPI attributes and methods is included in the controller, so that all stakeholders use the same set of code. In other words, "if the controller can do it, it should" (- RonDuPlain).

Controller Framework

Common Themes

  • Deal with arguments entirely as strings.
    • This is easy to ship over the wire (currently via SOAP)
    • We have yet to see a case where we can't type-cast it where it's implemented.
    • Common types are very useful for simplifying interfaces.

Building Blocks

What are the building blocks (or "primitives" if you prefer) for the controller?

Building Block Example Description In Use? Notes
boolean string 'true' 'true' or 'false' yes string for consistency in SOAP return values
parameter 'BEE2/FPGA1/FFT_SHIFT' string value representing some GUPPI attribute yes  
profile 'BEE2/b2_GOUT_U2_4K_800_A_NR_fpga2_2008_Sep_15_1400.bof' string name of some GUPPI configuration yes  
routine 'plot_adc_hist' string name of a prepackaged routine, available based on loaded profiles no  

Core Methods

What core methods are seen frequently throughout the controller?

Core Method Usage Description In Use? Notes
get [list of values] = get([list of parameters]) get parameter values yes  
set [list of boolean strings] = set([list of parameters], [list of values]) set parameter values yes  
load [list of boolean strings] = load([list of profiles]) load profiles yes  
unload [list of boolean strings] = unload([list of profiles]) unload profiles yes  
call [list of boolean strings] = call([list of routines]) run a series of routines no should mutable routine parameters be given through call or via special parameters?

All methods map an input list of strings to an output list of strings, in a one-for-one mapping.
  • [parameter values] = get([parameters])
  • [boolean strings] = set([parameters], [values])
  • [boolean strings] = load([profiles])
  • [boolean strings] = unload([profiles])

Lists of boolean strings indicate success or failure for setting each parameter, or loading or unloading each profile, respectively.

The 'index' Keyword

Some methods accept the 'index' keyword as a special case, which requests an index of available arguments.
  • [list of parameters user can get] = get(['index'])
  • [list of profiles user can load] = load(['index'])
  • [list of profiles user can unload] = unload(['index'])

In other words, the 'index' keyword answers the following questions:
  • get(['index']) - "What [parameters] can the user get?"
  • load(['index']) - "What [profiles] can the user load?"
  • unload(['index']) - "What [profiles] can the user unload?"

This currently breaks the one-for-one mapping. That may be undesirable, but it makes for cleaner code. The 'index' keyword entered the scene when soaplib (Python) wouldn't accept default arguments over the wire (nor would it accept None or an empty string). So, the controller needed something as a placeholder -- 'index' is it.

Implementing These Semantics, or Where We Need to Center Our Discussion

Currently, parameters and profiles are passed around between the Controller, the Demux, and various "low-level agents" which plug into the Demux (currently Bee2Agent and DaqAgent). (Note that all classes below inherit the Agent class.)

                                                                           +--------------------+
             |                                                         |-> |      Bee2Agent     |
manager      |     +--------------------+     +--------------------+   |   +--------------------+
interpreter  | <-> |     Controller     | <-> |        Demux       | <-| 
scripts      |     +--------------------+     +--------------------+   |   +--------------------+
             |                                                         |-> |      DaqAgent      |
                                                                           +--------------------+

The current question: What types of building blocks and core methods can we add to the mix in order to provide high-level functions and keywords/parameters without breaking everything?

Main.RonDuPlain's thoughts on moving forward...

This is somewhat of a brain dump, so bear with me.

My GUPPI Controller values:
  • stick to building blocks; if something can't be built on these building blocks, then maybe the building blocks should be changed -- exceptions should be few, have very good reasons, and be clearly noted
  • implement it once, and either use the Controller code directly (by Python import mechanisms or some sort of wrapper like swig) or call it over SOAP
  • retain the expert's power to tinker and override any high level functions (with full expectation that frequent overrides will trigger changes to the controller's code)

I see 2 fundamental paradigms for the Controller:
  1. plumbing - providing structure for the building blocks listed above, and making them work in general
  2. adding functionality - extending these building blocks to mean something for the current GUPPI design, or for that matter, any GUPPI-/CICADA-like system (there could potentially be several in the near future)

(No, these are not major revelations, but I would like to cleanly separate the two.)

Once the basic building blocks are in place, it should all come down to how we implement parameters, profiles, and tasks at a design-specific level.

I'm considering a plugin framework, where we build design-specific agents (either explicitly or through config files). These plugins in turn provide additional parameters, parameters which impact multiple demux parameters, and profiles which load the necessary configuration. We already have a sort of low-level plugin framework for the Demux. Give the Demux a DaqAgent and a Bee2Agent, and it will glue them together into a common API. It's fairly easy to fit in a new agent at that level.

What if we gave the Demux various high-level (dare I say "top-level") agents? These agents could be given permission to rearrange, add, and remove low-level agents (those for boards and DAQ).

This would look something like:

                                              +--------------------+
                                              |     PluginAgent    |
                                              +--------------------+
                                                         |                 +--------------------+
             |                                           v             |-> |      Bee2Agent     |
manager      |     +--------------------+     +--------------------+   |   +--------------------+
interpreter  | <-> |     Controller     | <-> |        Demux       | <-| 
scripts      |     +--------------------+     +--------------------+   |   +--------------------+
             |                                                         |-> |      DaqAgent      |
                                                                           +--------------------+

I think of these as "top-level" agents because I see them as introducing parameters and profiles at the global scope of the Demux (which in turn is available to the Controller). The Bee2Agent provides parameters like 'BEE2/FPGA1/FFT_SHIFT' in the 'BEE2' namespace, and the DaqAgent provides parameters like 'DAQ/SRC_NAME' in the 'DAQ' namespace. As an example, I envision a PluginAgent adding parameters such as 'OFFSET0' (note that there is no namespace qualifier -- it's "global" in scope, with respect to Demux) which in turn sets 'DAQ/OFFSET0' and 'BEE2/FPGA2/OFFSET_I'

We will have many parameters at the global/root namespace (especially with respect to at all of the "manager-level" parameters in recent discussion), that I think it lends itself to plugging in agents here. I suppose I envision that once the manager, Controller, and Demux are stable and mature, all work will go into the agents which Demux calls, without any modifications to Demux itself. This would also be true for supporting the IBOB and ROACH boards in the future.

There's still an issue: what do we do about executing tasks which do not simply involve get/set/load/unload? For example, how will we implement arm(), init(), and functions like the plotting the ADC histogram? For that, I suggest a routine primitive, which is available in the Controller based on loaded profiles. Ideally, these routines are pre-packaged based on design and as such, are somewhat inflexible.

How to handle mutable parameters in this scheme? A call operation runs a routine, but what if the routine accepts parameters. Should the parameter be given in call or as individual sets. I support individual set calls for special parameters which come with routines. I see Controller routines as being pre-packaged and somewhat abstract, a way to activate what's already available.

I'm open to ideas on how to build a flexible command interface...

Main.PatrickBrandt's thoughts on moving forward...

(Patrick, do you have an alternative proposal?)

Examples

Add some, about anything.

Needed Wiki Updates

Pages to update/verify when semantics reach critical mass

Pages to update/verify when the implementations reach critical mass

-- RonDuPlain - 13 Oct 2008
who is looking for feedback from PatrickBrandt and AmyShelton.
Topic revision: r3 - 2009-10-27, ChrisClark
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding NRAO Public Wiki? Send feedback