USING AN API AS A DEVELOPER PLATFORM

Increase programmers' productivity by using an application-specific system

Jeffrey M. Parker

Jeffrey Parker is a software design engineer at National Instruments Inc., 12109 Technology Blvd., Austin, TX 78727-6204. He is working on open architecture and advanced data display features for LabVIEW.


Modern computer systems consist of underlying hardware with several layers of operating systems, virtual machines of different architectures, programming systems, and finally, "applications." The role of the systems software engineer is to produce higher-level layers for use by the applications software engineer, who adds the outermost application layer. Part of the ubiquitous software production bottleneck is because applications software engineers too often are working with programming systems that are too primitive and too far removed from their application areas. Dealing with the idiosyncrasies of the end user and of applications domain is enough of a challenge by itself. Applications software engineering shouldn't be complicated by mapping the application into an unsuitable programming system for the lack of something better.

At one time, the question of advanced programming systems was moot. Memory limitations, low CPU performance, crude display technology, and so forth, dictated that a programming system required as few resources as possible. Today, that computer hardware and software technology have evolved to the point where blazingly fast CPUs, megabytes of memory, and dazzling graphics are available even on relatively low-cost personal computers. Today, more resources can be devoted to higher-level, easy-to-use programming systems.

The properties of a programming system germane to the task of applications software production are as follows: the computation model; the programming metaphor and tools; and the basic program building blocks of the virtual machine upon which the application is to be built. Typically, a programming system is built as an extension of the underlying machine. The computation model, programming metaphor, and building blocks are chosen with more emphasis on how well they fit on top of the machine, rather than how well they support a given application area.

An alternative approach is to focus on a particular application area. This produces a software-development system whose computational model, programming metaphor, and primitive building blocks are tailored to that application area. This approach leverages the efforts of the applications software programmer, as shown in Figure 1, page 30.

Consider the computer-aided test-and-measurement application area. In this area, computers are used as tools for controlling and collecting data from electronic instruments and from plug-in data acquisition cards. In this area, computers are used for processing and displaying the data in various ways.

The test-and-measurement domain has some peculiarities that make test-and-measurement programming different from, say, writing an operating system or an accounting package. Test setups change frequently and software needs to be easily modifiable. A need exists for low-level software components (such as hardware drivers) as well as for some high-level components (such as the ability to graph data easily).

Test-and-measurement programs are usually not written by professional programmers. Instead, a scientist, engineer, or technician -- an instrumentation user -- does the programming, and these people don't necessarily think the way professional programmers do. Concepts such as control settings, block diagrams of test setups, and signals flowing between instruments are familiar and natural to the instrumentation user. Concepts like variables, designing in pseudocode, and sequentially executed program statements may be foreign to them.

LabVIEW, from National Instruments in Austin, Texas, is an application-specific programming system that runs on the Macintosh. Its computation model, programming metaphor, and program building blocks were chosen with the test-and-measurement application area in mind. The remainder of this article makes use of the test-and-measurement application area and of LabVIEW, as examples will illustrate. The idea of creating a higher-level programming system by tailoring its properties -- computation model, programming metaphor, and building blocks -- to fit a particular application area.

Computational Models -- Control Flow and its Alternatives

The computation model defines how computation proceeds in a computer. The most prevalent computation model is the control-flow model. Here, the computer's instructions and data reside in global memory and instructions are executed under the control of a program counter. Obviously, many applications can be mapped onto the control flow model, but that mapping isn't always natural. The easiest programming to build is one that mimics the underlying hardware, and that's the main reason for the popularity of the control-flow model. The von Neumann global memory-centralized processor architecture is still the most prevalent hardware architecture.

Many alternative models of computation are better suited to certain application domains. Two well-known examples are the functional model of computation and logic programming, both of which were developed and refined to satisfy the needs of the AI domain.

Hardware support is becoming available for many computation models. The benefits of a different computation model to an application area are often so great that it pays to build the new computation model in software on top of hardware based on a different model. This is true in the test-and-measurement area. The concept of signals flowing through functional blocks that perform transformations on them is familiar to test-and-measurement users. A natural extension is to think of data as flowing through a computer program. For this reason, LabVIEW's computation model is a virtual dataflow machine -- virtual in the sense that it is implemented on top of the von Neumann architecture of the Macintosh 680xx processor. A dataflow machine does not rely on a central processor sequentially executing instructions that manipulate global data under the control of a program counter. Rather, a dataflow machine executes instructions when their data is available. When an instruction has all of its data inputs, it can execute (or "fire") and produce a result at its output(s), which may then allow another instruction that is waiting for that output to fire, and so on. At a given time, if multiple instructions have all of their input data available, they can fire in arbitrary order. If more than one processor is available, multiple instructions can make use of the additional processors to run at the same time.

Programming Metaphors: Text Languages vs. Graphical Programming

The programming metaphor determines what form a program takes, how its data is defined, and how the program is to be constructed. In other words, the programmer metaphor determines how the user manipulates the computation model.

Most computation models are still using the programming metaphor that was introduced in the 1960s: the programming language based on a context-free grammar with textual tokens. Text-based programming languages have many strong points. These languages are a concise representation of abstract programming concepts, and can be made unambiguous. They're adaptable to a wide variety of underlying virtual machine architectures, and developing tools to work with them is easy. Text languages are flexible and portable. These advantages ensure that the text-based programming languages will be with us for a long time.

Text-based programming metaphors have some disadvantages, as well. Note that most of the advantages just mentioned are due to the ease with which a text-based programming metaphor can be built on top of the underlying computational model. When starting from the perspective of the application domain and looking inward to the computation model, a new set of criteria becomes more important. How well does the programming metaphor fit underneath, and relate to, that application domain? From this viewpoint, the shortcomings of a text-based metaphor become clear.

A major shortcoming of text-based programming metaphors is the difference in level of abstraction from the problem to be solved in the application domain. These metaphors may be a good specification of the computation machine on which the application will execute, but they're usually not a good description of the application itself. The application programmer who uses these metaphors must abruptly "shift gears" between thinking about the application in terms of the problem domain and in terms of implementation on the computation model.

Text-based programming languages are usually not much help in the early stages of designing a program. Most software designs begin (or should begin) with figures and diagrams of various kinds -- dataflow diagrams, state diagrams, flowcharts, and so on. The human mind is visually oriented. It captures and understands complex relationships more quickly when they are presented pictorially. Consider a simple problem from the test-and-measurement domain. An engineer wants to use an analog-to-digital converter to acquire some data, scale it to a different range, and plot it on a graph. The design would probably be worked out in the form of a block diagram, such as the one in Figure 2, this page.

Given this design, the first step in writing a program to implement it in a programming language such as C might be to recast the block diagram in high-level pseudocode:

  BEGIN   AcquireData(D);   ScaleData(D, K1, K2);   AddTimebase(D, X0, FX);   PlotGraph(D);
END

Even at this relatively high level of abstraction, the ability to see what the program is doing has been lost. While mapping the pseudo code into compileable C code, the programmer becomes further immersed in the syntax and semantics of a programming metaphor that has more in common with the machine than the application. Relationships that were explicit in the diagram become implicit in the text code. For example, the fact that the data array is being transformed by successive operations is obvious from the diagram, but hidden in the pseudocode. Based on data dependency, the order of execution is clear from the diagram, but only implied in the pseudocode. And such problems are amplified as the four lines of pseudocode are expanded into tens or hundreds of lines of C.

The drawbacks of the dichotomy between design process and program implementation are compounded when, changes in the program's design must be made during the development or maintenance of an application. When the application design process is on a different conceptual plane than the program implementation, all design iterations must make the long round trip through implementation at a different level of abstraction. This is particularly troublesome when the nature of the application itself calls for a rapid-prototyping development style and, therefore, significant iteration at the design level.

To illustrate, consider the test engineer who wants to plot the data both before and after scaling in the data acquisition example -- a seemingly trivial change. Modifying the block diagram is easy enough (as shown in Figure 3, below). To change the C language implementation, the programmer must shift gears and delve back into a different model. It is important to recognize, for example, that D is a shared storage location. The order of computation is important. If the plotting function as a side effect modifies D, then a copy of D must be made. Uncertainty about the side effects of what appear to be simple changes in the design may result in unreasonable resistance to beneficial changes.

Yet another drawback of conventional text programming languages is that source code is usually poor documentation of the program's design. Other internal documentation must be generated, if the program is to be maintained.

Since diagrams are often a better representation of an application and its design process, why not produce executable code directly from some sort of diagram? That is the idea behind graphical programming metaphors. Using a graphical editor (the counterpart of a text editor), the programmer draws a diagram which, if syntactically correct, can be directly executed. A graphical programming metaphor that is carefully chosen to fit the application area can greatly leverage the efforts of the application software engineer. This is accomplished by minimizing the abruptness of the transition between the design of the solution and its implementation as a program.

Programming by Diagram

In LabVIEW, the programming metaphor takes the form of a graphical dataflow programming language called G. In LabVIEW terminology, a G graphical program is referred to as a "block diagram." This metaphor was chosen for two reasons: block diagrams are a familiar concept in the test-and-measurement world; and dataflow programs are well-suited to a graphical representation, that is, nodes or instructions, connected by arcs or data paths. The primitives of G consist of icons that represent sources and sinks for data (called "terminals"), and icons that represent "instructions" (or units of execution). The LabVIEW programmer constructs a block diagram by connecting data outputs on the primitive icons to data inputs on other primitive icons.

Figure 4 left, illustrates the G program for the data-acquisition example. Note the similarity to the block diagram of Figure 2, where data conceptually flows along the lines connecting the various blocks. (Also, note the ease with which the graphical program can be modified to produce the system of Figure 3). In the LabVIEW block diagram, data flows between dataflow instructions. These instructions vary in complexity from simple (addition and multiplication) to complex (acquire an array of data, add a time-base for plotting). The larger block in the center of the LabVIEW program (the one that resembles a pad of paper) is an iteration structure called a for loop. It iterates over the elements of the array, performing the scaling operations on each element. The interior of the for loop is a dataflow sub-graph that executes once per iteration of the loop. The result array is accumulated over the iterations and becomes the output of the for loop.

Data Declaration: the Front Panel

The test-and-measurement programmer defines data in LabVIEW by using another familiar metaphor: the front panel of an electronic instrument. Data resides in objects called "controls" that the LabVIEW programmer arranges in a window called the "front panel." The kind of control determines the type of the data it contains, just as with physical instruments (toggle switches represent Boolean data and digital readouts represent numeric data). LabVIEW also has a string primitive data type, and a corresponding front-panel control for entering and displaying text. Aggregate data types --arrays and structures ("clusters") -- can also be represented. A special display style, the graph, displays a special case of aggregate data type (an array of X-Y points). Figure 5, page 31, shows LabVIEW's basic and aggregate control styles.

The method for entering data values is consistent with the control metaphor. Values are entered by "operating" the control (the mouse or the keyboard). Thus, Boolean switches are "flipped" by clicking on them with the mouse, and the value in a digital display is changed by typing in its new value.

Front Panel + Block Diagram = Virtual Instrument

Another important metaphor in the LabVIEW programming system concerns the relationship between the front panel window (where data is specified, entered, and displayed) and the block diagram window (where the G program is created). Figure 6, right, shows the front panel window and block diagram window for the data acquisition example. Each control or indicator in the front panel window has a corresponding terminal icon on the block diagram, so that its data may be introduced into the G program. In Figure 6, the controls and indicators in the front panel window labelled "Scale Factor," "Offset," and "Plot Of Scaled Data" correspond to the similarly labelled terminals in the block diagram window.

This is analogous to a real electronic instrument. If you look at its controls and indicators from the front-panel side, you are seeing its user interface. If you remove the outer case of the instrument and look at the front panel from the back side, you see the terminals that allow the controls and indicators to be connected to the circuit.

Together, a front panel user interface and a "circuit card" in the form of a G-language block diagram program make up LabVIEW's basic module, which is called a "virtual instrument."

LabVIEW's Graphical Editor and Execution Executive

Graphical programs are constructed with a graphical editor. A good metaphor is just as important for this programming tool as it is in the graphical programming language itself. LabVIEW's graphical editor extends the electronic instrument metaphor where appropriate, and draws on the metaphors of the Macintosh as well. The standard Macintosh window and menu interface is employed, as is the idea of direct manipulation of graphical objects made popular by the Macintosh Finder's desktop metaphor.

The LabVIEW programmer builds front panels and block diagrams by choosing objects (such as controls and dataflow instructions) from menus, which causes them to appear as icons in the appropriate window. They can then be copied, deleted, and positioned as desired by using the usual Macintosh pointing, clicking, and dragging mouse operations. Pulldown menus are generally reserved for options that are more global in nature. Attributes of objects are accessed by using popup menus that supplement the pulldown menus. Popup menus in LabVIEW are context-sensitive (that is the menu is specific to the object that is popped up on).

The cursor takes on the shape of various "tools" to indicate what operations may be performed on objects. The open hand is used for moving, resizing, and copying objects. The pointing finger is used to operate front-panel controls. The spool of wire is used to connect icons on the block diagram. The I-beam cursor is used to create text labels in the front panel and block diagram windows. The magnifying glass is used to obtain context-sensitive help by clicking on front panel or block diagram objects.

LabVIEW's run-time executive also has an instrument panel metaphor. This metaphor is used to start and stop the execution of a virtual instrument, and control debugging functions (such as single-stepping through dataflow instructions).

Figure 7, page 33, illustrates some of the features of LabVIEW's graphical editor and run-time executive.

Program Building Blocks

In addition to the computation model and programming metaphor, the final property that pertains to programming systems is the granularity and reusability of the building blocks from which the programmer creates programs.

How Primitive Are the Program Primitives?

The granularity of program building blocks is determined by the computation model and programming metaphor, which, when combined, provide the programmer with a set of primitive constructs. Like the programming metaphor itself, these primitives can be tailored to fit the underlying machine, in which case they are general, but perhaps not particularly powerful. Or, they can be chosen with the application domain in mind, in which case they may trade some generality for power and ease of use in a particular area. This tailoring of primitive operations to the application domain is the major contribution of the so-called "fourth generation" languages (such as database application generators, where the language's primitive operations do in one statement what might take several lines to do in a "conventional" programming language).

In test-and-measurement applications, as in database applications, some complex operations are performed often enough that they should be provided as primitives by the programming system. LabVIEW, like fourth-generation languages, supplies some higher-level constructs, in addition to the primitives that give LabVIEW its general-purpose programming language flexibility. These higher-level constructs fall into three categories: special front-panel controls and indicators, higher-level primitive dataflow "instructions," and libraries of prebuilt virtual instruments.

Test-and-measurement applications programmers need more flexibility in front-panel data input and display than is offered by the basic control and indicator styles of Figure 5. This is for a variety of reasons:

Figure 8 this page, illustrates several of the display styles for Boolean and numeric data provided by LabVIEW as system primitives.

LabVIEW provides a wide variety of primitive dataflow instructions for manipulating data of all types. Some of these primitives are identical to what you would expect to find in any programming language. Basic arithmetic operations (such as add, subtract, multiply, and divide), Boolean operations (AND, OR, NOT), and basic string operations (such as concatenation) are provided. Also provided in LabVIEW's toolkit are primitives for communicating with hardware through internal ports and busses, and specialized string functions that are useful for building and parsing strings for command-based automated test instruments.

Finally, several libraries of virtual instruments are produced in-house and distributed with LabVIEW because they satisfy special needs within the test-and-measurement domain. A high-level mathematical library provides virtual instruments for digital signal processing and statistics. A library of virtual instrument drivers provides access to National Instruments' data acquisition plug-in cards. A variety of instrument drivers provide a virtual instrument layer on top of the usual command-based remote programming interface of automated test instruments.

Reusability: Why Reinvent The Wheel?

Unlike other disciplines, in present-day software engineering, products built from reusable components are the exception rather than the rule. Reusability depends on several factors. Does the programming metaphor facilitate the construction of hierarchical, reusable components? Are the components packaged so they can be easily reused? Finally, what about cataloging and documentation? How difficult is it to find something to reuse and given a component, how difficult is it to figure out what it does? Most programming systems provide a way to construct modules, and some also provide good packaging and browsing capabilities (for example, the object-browsing capabilities of Smalltalk). In most systems, software reusability suffers because of packaging and documentation problems.

LabVIEW addresses the software reusability issue by allowing its basic modules (virtual instruments) to be encapsulated and used as dataflow instructions in the block diagram programs of other virtual instruments.

Three steps are involved in encapsulating a virtual instrument (see Figure 9 this page). First, the virtual instrument must be given a graphical identifier (an icon that is created with a builtin icon editor). Next, its front panel interface must be supplemented with a programmatic interface that allows it to be connected into another block diagram program. Since the controls and indicators on the front panel already define the data interface to the block diagram program, they are associated with areas on the icon to define the programmatic interface. Finally, the virtual instrument must be stored in a way that allows it to be retrieved for use in another block diagram program. Each virtual instrument is stored in a separate file and retrieved by name.

From the standpoint of software reusability, virtual instruments are excellent modules. The concept of a "software integrated circuit" has received a lot of press, but not a lot of practice. LabVIEW's virtual instruments are quite literally software integrated circuits, with a well-defined "pinout" for connecting them into a graphical program. The storage granularity of a virtual instrument (one per file) simplifies the logistics of reusability. No library manager is needed. A virtual instrument carries its source code, user interface, and documentation in one package. The LabVIEW programmer can open the virtual instrument's block diagram to see how it works, try it out by operating it interactively from its front panel, and then use it programatically as a submodule in another virtual instrument's block diagram.

Conclusion

Are high-level, domain-specific programming environments effective? Judging from the success of LabVIEW and other products that fit the high-level, domain-specific model (such as the 4th Dimension and Helix databases, Visual Interactive Programming, and HyperCard), the answer is "yes." Since LabVIEW was released as a product in the fall of 1986, it has gathered an enthusiastic following. Its users have reported that they are, indeed, able to more rapidly build test-and-measurement applications because of LabVIEW's advanced programming metaphor and built-in components.

Two years of feedback from LabVIEW users has revealed what users need and expect in a product like LabVIEW. Much of what has been learned (especially in the area of execution performance and editing functionality) has been incorporated into Version 2.0.

From the developer's point of view, advanced programming systems probably look like a lot of work to implement -- and they are.

The fact is, the systems software engineer does have to spend some of that memory, and some of those processor cycles, and quite a bit of his or her time, to make the benefits of easier-to-use programming systems available. Advances in hardware technology mean more resources are available to spend. The key concept here is that they're being spent, not wasted. With high-level programming systems comes leveraged increase in software engineering productivity.