TweetFollow Us on Twitter

Inter-Computer Coordination

Volume Number: 13 (1997)
Issue Number: 11
Column Tag: Programming Techniques

High-Speed Inter-Computer Coordination

by Dr. Scott B. Steinman

Combining C++, FrontierScript, LabVIEW and multiple Macs to solve a real world problem

Introduction

A few years ago, I was asked to put together an infant vision clinic that would offer not only state of the art tests for assessing the function and health of an infant's visual system, but which would also be fast, easily operated and flexible enough to readily allow future expansion. It didn't take much thinking to come to the conclusion that only the Macintosh could do it. But the programming was far from straightforward at the time, since some of the system software that I needed had only just become available and was not well documented.

This article will discuss the specific programming issues that had to be solved in order to achieve the design goals of the clinic, as well as many of the workarounds that had to be made. These issues also apply to other medical, scientific and engineering tasks. We'll see that sometimes a seemingly more roundabout design actually produces a simpler, faster and more flexible solution.

Design Decisions

The infant vision tests typically involved displaying a rapidly animated visual stimulus that moved targets on a screen or changed the target's contrast dynamically. At the same time, physiological data such as eye movement recordings or evoked potential recordings ("brainwaves") have to be obtained in response to the stimulus. Both tasks need to be executed rapidly -- the animation may be run at refresh rates up to 60 Hz, while the data acquisition may occur at up to 1000 Hz.

While speed and flexibility are always two important design goals, the system had additional restrictions that made these goals even more critical. First, the time required to conduct the full test sequence needed to be extremely short since our subjects were very young infants whose attention span is limited in duration. The examiner has to collect data extremely rapidly on any given test, then quickly proceed to the next test. Second, the same examiner who has to control the data acquisition also has the responsibility of directing the infant's attention to a visual stimulus. This not only reinforced the need for speed of testing, but also ease of operation. Finally, the software system needed to be modular and easily extended when new tests of infant vision were introduced.

I chose National Instruments' LabVIEW(tm) as the language in which to write the data recording and analysis code. LabVIEW is a true "visual" (that is, iconic) dataflow programming paradigm, much like Prograph CPX(tm). Visual programming allows rapid application development (RAD) in a fraction of the time required to write an equivalent C/C++ program (for example, see Steinman & Carver, 1995). LabVIEW also includes a huge library of data acquisition and data analysis code modules (called Virtual Instruments, or VIs for short) that may be used to build a recording system. In addition, LabVIEW's integrated user interface design tools provide a uniform intuitive "front panel" of controls that can be easily operated even by assistants.

Unfortunately, while LabVIEW is well suited for laboratory data acquisition and analysis, it is not capable of performing acquisition and analysis while simultaneously generating rapid animated visual stimulus displays. One solution that was considered was to call Macintosh Toolbox and QuickDraw routines via external C language external code modules, but these graphics routines could not be called in parallel with the data acquisition VIs. Even if these routines were called in separate programs, running all of these tasks on a single machine could still be risky since processor interrupts of the data acquisition hardware and those of the visual stimulus animation code could potentially interfere with each other -- that is, data samples could be "skipped" during graphics operations or graphics could "shear" when data samples are acquired.

We therefore chose to implement the system on two Macintosh Quadra computers -- one solely for recording and the other solely for stimulus display. This would allow each computer to perform its single specialized task at maximum speed without interfering with each other.

Both the recording program on one computer and the stimulus program on the other computer had to "know" what the other was doing at any given moment. Their operations needed to be tightly synchronized if the two-computer system were to act as a single unit. The overall operation of the laboratory software would be controlled by the examiner on the recording computer, and the second computer would act as a "slave" to the recording computer. This computer would act only in response to commands sent to it by the "master" recording computer.

This article will discuss the specific programming issues that had to be solved in order to achieve real-time inter-computer coordination while meeting the design goals of the clinic. I'll discuss many of the workarounds that needed to be made. These issues also apply to other medical, scientific and engineering tasks. We'll see that sometimes a seemingly more roundabout design actually produces a simpler, faster and more flexible solution.

Clearly, two forms of communication were required. Each would be executed independently. The first was to maintain tight control over the timing of the sequence of events during data acquisition. Digital I/O lines (NB-DIO-96, National Instruments) joined by a ribbon cable were used to transmit these signals.

The second form of communication was to transmit commands from the recording computer to the stimulus computer to "tell" it what stimulus to display and when. This would, of course, involve Apple events. These Apple events would be sent across an EtherTalk network cable that interconnected these two computers alone. In initial testing of the software system, it was found that LocalTalk was too slow. When Apple event commands were sent to the stimulus computer, the recording computer often had to wait for a reply that indicated that the command was received correctly. An Apple event could "time out" while waiting for this reply if the transmission time was too long, locking up the data acquisition when the recording computer kept waiting for the reply. The much quicker EtherTalk was the solution to this problem, as replies could be received fairly instantaneously.

Finally, two more decisions related to programming language had to be made. The first was which programming language to use for the stimulus display software. LabVIEW would not do. While LabVIEW has some capabilities for sending Apple events (with provisos to be mentioned below), it has extremely limited abilities to respond to Apple events. C++, on the other hand, is well suited for writing Apple event handlers, but also allows for rapid interrupt code for accurately-timed stimulus display animation (see Steinman and Nawrot, or Steinman for such graphics techniques).

Although Apple events would be involved in the inter-program communication between the data acquisition and stimulus display computers, they could not be used in a uniform manner throughout the software due to limitations in LabVIEW's inter-program communication code. One possible solution to this problem would be to use AppleScript, but for several reasons to be discussed below, inter-program communication was implemented with the UserLand Frontier(tm) scripting language (now available free at www.scripting.com/frontier).

It could be argued that the ethernet connection could be replaced by a simple high baud rate serial line that carried messages from one computer to the other. However, there is one disadvantage to doing so. In the current system, both the LabVIEW recording program and the C++ stimulus display program are not aware of whether they reside on two computers or on a single computer. One side effect of using Frontier as an intermediary for passing commands is that Frontier hides these details from the LabVIEW and C++ programs. This means that if the present stimulus generation and recording system could be implemented on a single computer in the future, all that would need to be done is to edit one subset of Frontier scripts -- neither the recording software nor stimulus display software would need rewriting. If a serial line had been used instead, all of the inter-program communication code would need to be completely rewritten.

Software System Components

For a better understanding of the workings of the software, the general infant testing sequence is as follows: The main program establishes the connection between the recording and stimulus computers across the EtherTalk network. The digital I/O lines are then initialized. At this point, the examiner selects a test to be run.

A command is then transmitted to the stimulus computer to launch a particular stimulus display program, whose stimulus parameters are transmitted to the display program by a second command. The examiner then clicks a button on the recording computer screen to start recording. A digital signal is sent to the stimulus computer to enable the stimulus animation, and the stimulus computer returns a digital signal to trigger data acquisition, time-locked to the stimulus display. After data collection is completed, commands are sent to the stimulus computer to stop the stimulus presentation, then quit the stimulus display program.

The software system has been designed to be both flexible and modular. It is composed of three major components: The first, implemented in LabVIEW(tm) on the recording computer, initiates an Apple event link between the two computers, initializes the data acquisition and digital I/O boards, allows the examiner to select which to run and specification of stimulus and recording parameters, as well as starting, pausing or halting data acquisition. A uniform user interface across all tests simplifies operation of the tests.

Visual stimuli are presented via the second component, a collection of very small programs written in Metrowerks CodeWarrior C++ and Mathemaesthetics Resorcerer(tm). These programs take advantage of a reusable code library of drawing and animation routines that allows both palette animation (see Baro) and frame animation (see Steinman and Nawrot, or Steinman). These programs are small because they perform only three chores: (a) receive Apple event commands, (b) display animated graphics, and (c) read and write to the digital I/O lines for synchronization with the recording software.

The third component coordinates the stimulus generation and data acquisition via Apple Open Scripting Architecture-compatible scripts in the Frontier(tm) scripting language.

Inter-Program Communication Problems and Solutions

With a combination of different programming tools, several problems that are specific to real-time simultaneous visual stimulation and data acquisition have been solved. Specifically, we will present an easy way to synchronize two Macintosh computers to work as a single laboratory device, via software commands and hardware signals passed between.

The first problem which we had to confront and solve dealt with shortcomings in LabVIEW's capacities for inter-program communication. While Apple event support is included in LabVIEW, it is mostly specialized as VIs that are used to execute other VIs, such as AESend Run VI, AESend Open, Run, Close VI, AESend Close VI, AESend Abort VI and AESend VI Active?, or responses to these commands. For programs composed of code other than its own VIs, LabVIEW is more capable of responding to commands than sending them. A few other Apple event-related VIs exist, which are geared towards starting or quitting other programs, but these are not sufficient for laboratory program control.

Fortunately, a LabVIEW VI that is often overlooked by programmers just happens to be the one VI that is critical for controlling the operation of the stimulus graphics programs by the recording computer. This VI is called AESend Do Script. As its name implies, it is specialized towards sending scripts. When such scripts are received by a target program, their text must be decoded into a series of instructions to be carried out by that program.

The next decision was to choose a specific scripting language. AppleScript presented several obstacles. The first is that while AppleScript is relatively slow, even on PowerPC-based Macintoshes. The second problem is that AppleScript was not primarily designed for sending commands quickly over a network to a second computer. To send an AppleScript or Apple event across a network, the PPC Browser must be invoked. The PPC Browser is intended to allow users to choose which computer and application should be sent a command, but it also has one shortcoming for our purposes. It provides a level of security via the User Identity dialog box. While this is useful for preventing unwanted connections across the Internet, it is a severe impediment to our design goals. Every time we need to send an AppleScript or individual Apple event to each stimulus display program, we will be faced with the PPC Browser and User Identity dialog boxes! This is not only disruptive to the examiner using the software, but will also slow down our data collection.

A third problem is that if only AppleScripts are transmitted to the stimulus computer to initiate a stimulus display, these stimulus programs must be capable of receiving and parsing the AppleScripts. This presents a very difficult task for several reasons: (1) AppleScript programming is not entirely intuitive. (2) AppleScript programming requires programming knowledge about 'aete' resources that identify the Apple events "understood" by the receiving program. (3) In the present software system, this would require adding AppleScript support to each of a dozen small stimulus display programs. While adding AppleScript is a fruitful option for single large commercial programs, the time and effort required to add AppleScript support to several small specialized laboratory programs is simply not cost effective. We have selected an alternative that is simpler, yet overcomes many of the limitations of AppleScript.

That alternative is UserLand Frontier(tm). Frontier can transmit sequences of Apple events across a network at least ten times more rapidly than AppleScript scripts, and does not require the creation of 'aete' resources.

In our system, Frontier is installed on both the recording computer and the stimulus computer. When a command must be sent from the LabVIEW program on the recording computer to a C++ visual display program on the stimulus computer, it is first sent as a script from LabVIEW to a "master" copy of Frontier on the recording computer. This Frontier application is "told" to run a script stored within its Object Database that essentially transmits the command contained in the LabVIEW script across the Ethernet network to a "slave" copy of Frontier on the second computer. This second copy of Frontier translates the command into Apple event format and relays the command to the stimulus program, where the command is carried out.

Why use such a circuitous route? Why not just send scripts directly from the LabVIEW recording program to the stimulus display program on the second computer? Two reasons have already been mentioned: (1) Frontier speeds up the transmission of commands across the network, and (2) to avoid the need to add AppleScript-parsing code to each stimulus program. Let us add a to more important reasons to use Frontier as an intermediary in passing along commands: (3) Frontier simplifies our programming task by translating the original command from LabVIEW's Do Script VI, which is in textual script format, into a form that can be handled with simple code in the stimulus programs -- Apple event handlers. (4) Because Frontier itself sends the commands across the ethernet network, the intrusive PPC Browser and User Identity dialog boxes are avoided during the time-critical portions of data collection.

When the LabVIEW recording program must find a program on the stimulus computer to which to "connect" and send commands, it connects to the "master" copy of Frontier on its own machine. Similarly, the "slave" copy of Frontier on the stimulus computer connects to stimulus programs that reside on its own computer.. These two communication paths do not invoke the PPC Browser or User Identity dialog boxes, since each connection between programs is made within a single machine. The connection between the two machines is made by Frontier before the LabVIEW program is executed. Before launching the LabVIEW recording program, a small Frontier script is run on the recording Macintosh that results in the "master" copy of Frontier on that Macintosh establishing a connection across the network to the "slave" copy of Frontier on the stimulus Macintosh and telling that computer to do something trivial such as sounding a beep. This is the only time at which the PPC Browser and User Identity dialog boxes appear. During all subsequent experimental testing, these dialog boxes never reappear even when the experimenter switches between test types and stimulus types, because the connection across the network has already been established from one copy of Frontier to the other.

Let's examine the Frontier code that sets up the inter-computer communication across the network. Listing 1 is a Frontier script entitled testConnect that first establishes the network connection between the two Macintoshes prior to the launching of the LabVIEW recording program. Each copy of Frontier contains an Object Database of frequently-used scripts and data that may be thought of as the commands that Frontier itself can execute, send or receive, as well as parameters for those commands. The testConnect script is stored in the Object Database of Frontier on the recording computer. The testConnect script simply sends a command to the stimulus computer to sound a beep. Because this is the first script to be transmitted across the EtherTalk network, it forces the PPC Browser and User Identity dialog boxes to be displayed at this time. This is beneficial because data acquisition hasn't initiated yet, so these dialogs cannot slow us the test sequence.

Listing 1: testConnect script

testConnect

Establishes interconnection with copy of Frontier on second computer. This Frontier script on the "master" recording computer transmits a trivial script to sound a beep to Frontier on the "slave" stimulus computer to force the display of the PPC Browser and User Identity dialog boxes prior to test parameter selection and data acquisition. This script is simpler than that of Listing 3 because no parameters are transmitted with the "stimulus.beep" command.

© 1997 by Scott B. Steinman. All rights reserved.

on testConnect()
    Create local variable named "netScriptStr" to construct
    text of new script

  local (netScriptStr)

    Script will instruct stimulus computer to run script
    named "speaker.beep" in Object Database of copy of
    Frontier on stimulus computer

  netScriptStr = "speaker.beep()"

    Construct script command:
    1. Create new script at location "scratchpad.netScript"
    in Object Database of Frontier on recording computer.
    This script will be to be sent to stimulus computer
    2. Tell Frontier that next few operations will work on
    the newly-created script by setting target of Frontier's
    operations to that location
    3. Clear contents at location "scratchpad.netScript"
    4. Set contents of location "scratchpad.netScript" to
    text contained in netStringStr
    5. Reset Frontier operation target to the testConnect 
    script
  
  new (scriptType,@scratchpad.netScript)
  target.set (@scratchpad.netScript)
  op.wipe()     Clear current contents of netScript
  op.setLineText (netScriptStr)
  target.clear ()

    Send script command stored at "scratchpad.netScript"
    across network to "slave" stimulus computer

  NetFrontier.runScript (@scratchpad.netScript,true)
  
    Clean up

  delete (@scratchpad.netScript)

The testConnect script starts by creating a variable named netScriptStr to contain the text of the command to be sent to the "slave" copy of Frontier. This string is simply the name of a Frontier script ("speaker.beep") within the stimulus computer's Frontier Object Database that we want executed. NetFrontier has been designed to transmit scripts stored at a specific locations in the Object Database rather than transmitting raw text strings, so we need to transfer this command string into a new script, then transmit that script. In order to do this, we create a temporary entry named "netScript" in the recording computer's Object Database's "scatchpad" area to contain this text (its Frontier data type is, appropriately enough, scriptType). We then must copy the text of the script ("speaker.beep") from the netScriptStr string into the newly created script. We are now ready to transmit the contents of this newly-created script text across the network with NetFrontier's runScript command.

How is this testConnect script called to perform these actions if the LabVIEW recording program isn't running yet? Along with its own internal scripts in the Object Database, Frontier can create stand-alone, double-clickable scripts. A stand-alone script called ConnectMacs is executed by the experimenter before launching the LabVIEW recording program. It calls the testConnect script.

Experienced Frontier programmers might notice that we have added a second parameter to the runScript NetFrontier script to allow the option of waiting or not waiting for a reply during transmission of the script to the stimulus computer. This is because by default the Apple event Frontier command that sends information from Frontier to other applications waits for a reply from the receiving application. When we send commands to show visual stimuli just as we are about to begin data acquisition, we cannot afford the luxury of waiting for an Apple event reply, as this would delay the onset of the data acquisition. It is therefore imperative to avoid a reply by using Frontier's finderEvent command instead of Apple event to send the information; finderEvent by default does not wait for a response. This command was originally intended for sending scripts from Frontier to the Macintosh System 7's scriptable Finder, but it also suites our purposes well. The modified script is shown in Listing 2.

Listing 2: runScript script

runScript

Modified runScript Frontier script. This Frontier script, part of the NetFrontier suite, sends a script across a network to control a second Macintosh computer. It has been modified to allow the option of waiting for an Apple event reply or ignoring the reply.

© 1997 by UserLand & Scott B. Steinman. All rights reserved.

on runScript (adr,waitReply)
  on callback (netAddress)
    local (data,val)
    pack (adr^,@data)
    if waitReply equals true  
        Wait for reply from Apple event
        (by default, all Apple event transmissions produce
        reply in Frontier)

      if not AppleEvent (netAddress,'netf','inst',1,
       "scratchpad.netScript",2,data)
        return (false)
      val = AppleEvent (netAddress,'netf','runs',1,
       "scratchpad.netScript")
      AppleEvent(netAddress,'netf','dele',1,
                      "scratchpad.netScript")
    else
        finderEvent command sends Apple event but 
        ignores reply

      if not finderEvent (netAddress,'netf','inst',1,
       "scratchpad.netScript",2,data)
        return (false)
      val = finderEvent(netAddress,'netf','runs',1,
       "scratchpad.netScript")
      finderEvent(netAddress,'netf','dele',1,
       "scratchpad.netScript")
      return (val)

    NetFrontier.buddyLoop (@callback)

Once the inter-computer connection is made, we launch the LabVIEW recording program and set stimulus and recording parameters prior to data collection. Setting stimulus parameters requires sending commands and data across the EtherTalk network, as dictated by scripts sent from LabVIEW to Frontier within the recording computer. But LabVIEW's script transmission VIs expect to be sending commands by connecting to another program across a network, and we don't want that to occur. We want LabVIEW to communicate only with Frontier on the same computer, and let Frontier handle the communication across the network. We therefore have to force LabVIEW to establish a "connection" directly to Frontier within the recording computer. Figure 1 displays LabVIEW's graphical code for locating this particular copy of the Frontier application, then sending a small test script to Frontier to confirm that the connect was made properly.

Figure 1. LabVIEW ConnectMacs VI.

The Find Frontier VI in Figure 2 takes advantage of a LabVIEW PPC Toolkit VI named Get Target ID, which receives the name of the application to find and returns the its target ID, a LabVIEW structure (or "cluster" in LabVIEW terminology) that stores the location of a program on a network. We restrict the search to the copy of Frontier on the same machine hosting the LabVIEW recording program. The target ID returned by this search is used in all subsequent command transmissions to Frontier.

Figure 2. LabVIEW Find Frontier VI.

Now let's discuss the specific programming steps required to send commands and data from the LabVIEW recording program to their real target--the C++ stimulus display programs. In the example code to follow, the runStimulus command will be explained. This code is used once recording and stimulus parameters have been chosen by the examiner and we are ready to initiate data acquisition in response to a visual stimulus. The LabVIEW program initializes its data acquisition VIs, then sends a runStimulus command containing stimulus settings in the form of an Apple event to Frontier. Frontier then repackages the text contained within this Apple event into a Frontier script and transmits it. Upon receipt of the restructured script, the stimulus Macintosh's copy of Frontier executes the script, extracts the parameters of the script, and sends both a command and the parameters in an Apple event to the stimulus display program. An Apple event handler in that program retrieves the stimulus settings from the Apple event parameters, and displays the stimulus after sending a sync signal via the DIO lines to let the recording Macintosh know that it is time to start acquiring data. The net result is that the stimulus program only needs to receive an Apple event and its parameters in an easy-to-extract format, rather than a textual AppleScript that requires complex code to decipher.

The first step in this process is handled by the LabVIEW recording program. Once stimulus parameters have been selected and the experimenter is ready to record data, the Send RunStim Dosc AE VI is called (Figure 3) to tell Frontier to execute a script that receives stimulus parameters and then ships them to the stimulus computer. The script to be executed on the recording computer is named Stimuli.PVEP.netRunStim, and it receives as its argument the text contained in the script transmitted by LabVIEW. The script text contains the name of the Frontier script to execute -- Stimuli.PVEP.netRunStim (a stimulus display program running a Pattern VEP visual stimulus) -- and an argument to that script containing the list of the values to which the stimulus parameters are to be set ("50 380 8").

Figure 3. LabVIEW RunStim Dosc AE VI.

The netRunStim Frontier script is shown in Listing 3. The purpose of this script is to relay the command specified within the LabVIEW script to the stimulus computer, that is, a command to prepare a stimulus display, along with the desired stimulus parameters received as a single string argument to the netRunStim script named "str". This is done by packaging the command and stimulus parameters into a form that may be transmitted across the network by NetFrontier.

Listing 3: netRunStim script

netRunStim

Frontier script within the recording computer's Object Database for interpreting the LabVIEW runStimulus script and forwarding it as a Frontier script to the stimulus computer. The command extracted from the LabVIEW script and the arguments for that command are transmitted separately by Frontier to the stimulus computer, using the NetFrontier.broadcast and NetFrontier.runScript commands, respectively. Returns true if successful, false otherwise

© 1997 by Scott B. Steinman. All rights reserved.

on netRunScript(str)

    Create local variable named "netScriptStr" to
    construct text of new script

  local (netScriptStr)
  netScriptStr = "Stimuli.PVEP.runStim()"

    Construct script command:
    1. Create new script at location "scratchpad.netScript"
    in Object Database of Frontier on recording computer.
    This script will be to be sent to stimulus computer
    2. Tell Frontier that next few commands will operate on
    the newly-created script by setting target of Frontier's
    operations to that location
    3. Clear contents at location "scratchpad.netScript"
    4. Set contents of location "scratchpad.netScript" to
    text contained in netStringStr
    5. Reset Frontier operation target to the testConnect 
    script

  new (scriptType,@scratchpad.netScript)
  target.set (@scratchpad.netScript)
  op.wipe()     Clear current contents of netScript
  op.setLineText (netScriptStr)
  target.clear ()

    Construct script argument:
    1. Delete current contents at Object Database location
      "scratchpad.scriptArgument", if any
    2. Create new string at location 
    "scratchpad.scriptArgument" to   
    hold parameters transmitted with command to run
    "Stimuli.PVEP.runStim" script.

  if defined (scratchpad.scriptArgument)
    delete (@scratchpad.scriptArgument)
  new (stringType,@scratchpad.scriptArgument)
  scratchpad.scriptArgument=str

    Send argument to stimulus computer via 
    NetFrontier.broadcast

  if not NetFrontier.broadcast (@scratchpad.netArgument)
    speaker.beep ()
    delete (@scratchpad.netScript)
    delete (@scratchpad.netArgument)
    return (false)

    Send script command to stimulus computer via
    NetFrontier.runScript

  if not NetFrontier.runScript (@scratchpad.netScript,false)
    speaker.beep ()
    delete (@scratchpad.netScript)
    delete (@scratchpad.netArgument)
    return (false)

    Clean up

  delete (@scratchpad.netScript)
  delete (@scratchpad.netArgument)
  return (true)

As in the testConnect script of Listing 1, The netRunStim script creates a local string variable to hold the text of the command that will be sent via NetFrontier to Frontier on the stimulus Macintosh. Remember that the purpose of NetFrontier is to direct a copy of Frontier on another computer to execute one of its own scripts. This is what netRunStim will do -- instruct Frontier on the stimulus Macintosh to execute a script named runStim contained in its own Object Database. A temporary script (containing the Frontier script to be executed on the stimulus computer) is constructed exactly as was done in Listing 1, but in this case the name of the script we are asking to execute on the stimulus computer is "Stimuli.PVEP.runStim". In other words, the runStim script resides in the stimulus computer's Frontier Object Database in its Stimuli.PVEP script table.

The next steps that the netRunStim script take are to send data to the stimulus computer -- data that specifies how the stimulus display will appear. This data forms the arguments to the runStim script that will be called on the stimulus computer. As we did above for the script itself, storage for the argument of the script is constructed in the "scratchpad" region of the Frontier Object Database, this time a string variable stored at the "scratchpad.netArgument" location. If a pre-existing argument string is still lingering at that location from a previous execution of this script, it is cleared. Finally, the content of the string "str" -- the stimulus parameter list "50 380 8" -- is copied directly into the string stored at "scratchpad.netArgument."

Now we are ready to transmit the script and the script argument to the stimulus computer. This is accomplished in two steps: (1) The NetFrontier broadcast command is first used to send the script argument across the network. If this transmission fails, the user is warned with a beep and all allocated storage is cleared. (2) The copy of Frontier on the stimulus computer is told to execute the script (named Stimuli.PVEP.runStim), using the script arguments just shipped, via the NetFrontier runScript command.

The copy of Frontier running on the stimulus computer must now receive this command along with its argument, decode it, and execute its own runStim script. This script is shown in Listing 4.

Listing 4: runStim script

runStim

Frontier script within the stimulus computer's Object Database for receiving script sent by the recording computer in Listing 3.The runStim script extracts the script command sent by NetFrontier.runScript and the stimulus parameters sent by NetFrontier.broadcast, then constructs an Apple event containing the stimulus parameters to the stimulus display program. Returns true if successful, false otherwise

© 1997 by Scott B. Steinman. All rights reserved.

Extract stimulus parameter strings from incoming Frontier script from recording computer. Place stimulus parameter values into local integer variables

local (sPer=long( 
        string.nthWord( scratchpad.scriptArgument,1 )))
local (diam=short( 
        string.nthWord( scratchpad.scriptArgument,2 )))
local (tPer=long( 
        string.nthWord( scratchpad.scriptArgument,3 )))

  Construct Apple event to transmit parameters to stimulus
  program. This Apple event will instruct the stimulus
  display program to modify the grating stimulus parameters
  and set up the graphical stimulus display. However, the
  stimulus will not be displayed by the stimulus program a
  until 'program ready' sync signal is received across the
  digital I/O lines from the recording computer.

  The arguments to the Frontier "Apple event" command are:
  1. 'STIM' -- the suite or group of Apple events to which
  the stimulus-running Apple event belongs
  2. 'RnSt' -- the identifier for the stimulus-running Apple
  event that tells the stimulus display program to prepare a
  stimulus display
  3. 'scyc' -- the identifier for the parameter that
  specifies the number of grating spatial cycles to display
  4. sPer -- the value of the spatial cycles parameter,
  stored in the Frontier long integer variable sPer created 
  above.
  5. 'diam' -- the identifier for the parameter that
  specifies the grating diameter
  6. diam -- the value of the diameter parameter, stored in
  the Frontier short integer variable diam created above.
  7. 'tcyc' -- the identifier for the parameter that
  specifies the number of grating temporal cycles (contrast 
  reversals) to display
  8. tPer -- the value of the temporal cycles parameter,
  stored in the Frontier long integer variable tPer created 
  above.

if not AppleEvent (Stimuli.PVEP.id,'STIM','RnSt','scyc',sPer,'diam',diam,
 'tcyc',tPer)
    If not successful, clean up and warn user with beep

  delete (@scratchpad.scriptArgument)
  speaker.beep ()
  return (false)

  Clean up

delete (@scratchpad.scriptArgument)
return (true)

The program first extracts each individual stimulus parameter contained in the script argument from a string in the scratchpad. Local variables are created in the form of named variables of specific types. The first such variable, named sPer, is a long integer to hold the first script argument, the desired grating spatial period. The grating diameter is the second of these arguments, and the grating reversal period is the third. With the stimulus parameters retrieved and separated, they can be packaged into individual Apple event parameters to be transmitted to the counterphase sinewave grating display program. Frontier's Apple event command does this for us. The Apple event command is passed the application signature of the grating stimulus program, stored in the Object Database location Stimuli.PVEP.id. The signature for the C++ sinewave grating display program is 'SinG'. Following this is the sinewave grating display program's Apple event Suite; that is, an identifier for a group of Apple events specific to that program. We use the Apple event Suite identifier 'STIM'. The following identifier is that of the sinewave grating display program's runStimulus Apple event. This identifier allows the display program to recognize the command to display a sinewave grating stimulus. All that remains is to pass the individual Apple event parameters that define the desired appearance of the sinewave. These form the three remaining pairs of arguments to the Apple event command. Each pair consists of an Apple event parameter identifier for the stimulus parameter to be used when the stimulus program decodes the Apple event, and the value of the parameter. For example, the grating spatial period is given the Apple event parameter identifier 'scyc' and the value of the Frontier sPer variable follows it.

When the Frontier Apple event command is executed, Frontier transmits a raw Apple event that will be received by the stimulus display program. This forms the last step in the chain of message-passing from LabVIEW, to Frontier on the recording computer, to the second copy of Frontier on the stimulus computer, to the stimulus display program. It is now the job of that program to decipher the incoming Apple event to determine what the stimulus display program has been asked to do by LabVIEW. In other words, the display program need not "understand" the scripting code output by LabVIEW, nor the scripts transmitted by Frontier. Such code is difficult to decipher by C++ programs. Rather, it need only understand very basic, low-level Apple events, which require a minimum of programming to decode. Our Frontier programming has allowed us to simplify our programming task for each stimulus display program to the point that each program needs only be a small skeleton program written in C++ consisting solely of Apple event-handling and stimulus generation graphics code.

The Apple event handler called when the 'RnSt' Apple event is received is shown in Listing 5. Before this code is examined, the code to set up Apple event handling must be explained. Prior to entering the event loop of the stimulus display program, the means by which the program receives high-level events like Apple events, the Apple event handler must be installed with a call to the InitAEStuff function of Listing 5.

Listing 5: AppleEvents.cp

AppleEvents.cp

// C++ code in the stimulus display program that receives and
// handles the Apple event transmitted by the runStim
// Frontier script of Listing 4. This Apple event handler
// parses the stimulus parameters from the Apple event, then
// calls the DoAETrial function to display the stimulus 
// (a sinewave grating in this case).
// Each stimulus parameter has its own Apple event keyword
// identifier to allow easy extraction from the Apple event.
// © 1997 by Scott B. Steinman. All rights reserved.

#include AppleEvents.h
#include EPPC.h
#include GestaltEqu.h
#include PPCToolbox.h
#include Processes.h

static ProcessSerialNumber  gPSN;

void InitAEStuff( void )
{
  static AEinstalls HandlersToInstall[] = {
    // Required Apple events (not shown), 
    // plus our custom Apple event.
      { 'STIM', 'RnSt',
        (AEEventHandlerUPP) AERunStimulusHandler }
    };
    OSErr   aevtErr = noErr;
    long   aLong = 0;
    Boolean   gHasAppleEvents = false;
  
    // Get process serial number of this program.
    aevtErr = GetCurrentProcess( &gPSN );
  
    // Check machine for ability to handle Apple events.
    // If not present (ie, not System 7.0 or above), exit.
    gHasAppleEvents = 
      (Gestalt( gestaltAppleEventsAttr, &aLong ) == noErr);
  
    // Installs our Apple event Handler. Whenever an Apple
    // event is received and we call AEProcessEvent, the
    // Apple event manager will check our list of handlers
    // and dispatch to our custom Apple event handler, if it 
    // exists.
  
    if ( gHasAppleEvents ) {
    // Required Apple events would also be installed here...
      aevtErr = AEInstallEventHandler( 'STIM', 'RnSt',
        (AEEventHandlerUPP) AERunStimulusHandler, 0, false );
      if ( aevtErr ) 
        ExitToShell();  // Just abort program if error occurs
      
      AESetInteractionAllowed( gInteractNow );
    } else
      ExitToShell();
}

// Apple event handler called when Apple event with
// identifier of 'RnSt' in suite 'STIM' is received.

pascal OSErr
AERunStimulusHandler( AppleEvent *messagein, 
                      AppleEvent *reply, long /* refIn */ )
{
  DescType  returnedType;
  Size      actualSize;
  OSErr    error;

  // Bring stimulus display window to front
  error = SetFrontProcess( &gPSN );
  error = AEInteractWithUser( kAEDefaultTimeout, 0,
                              (AEIdleUPP) idleProc );
  if (error != errAENoUserInteraction) {
  
    // Extract stimulus parameters from incoming 'RnSt'
    // Apple event

    // Stimulus spatial period
    error = AEGetParamPtr( messagein, (AEKeyword) 'scyc',
      typeLongInteger, &returnedType, (Ptr) &spatialPeriod,
      sizeof( spatialPeriod ), &actualSize );
    if (error) return( -1111 );  // Our own unique error code

    // Stimulus grating diameter
    error = AEGetParamPtr( messagein, (AEKeyword) 'diam',
      typeShortInteger, &returnedType, (Ptr) &diameter,
      sizeof( diameter ), &actualSize );
    if (error) return( -2222 );

    // Stimulus temporal period
    error = AEGetParamPtr( messagein, (AEKeyword) 'tcyc',
      typeLongInteger, &returnedType, (Ptr) &cycleVBlanks,
      sizeof( cycleVBlanks ), &actualSize );
    if (error) return( -3333 );
     
    DoAETrial();
  }
  else
    return( -1 );

  return( noErr );
}

void DoAETrial( void )
{
  // PlayGrating waits for digital signal before displaying
  // sinewave grating visual stimulus.
  PlayGrating();
}

InitAEStuff first determines the process serial number of this stimulus display program. The process serial number is a unique identifier for each program or task currently executing on the computer. The GetCurrentProcess Toolbox routine retrieves this serial number for us, and stores it in the global variable gPSN. After confirming with the Gestalt Manager that this machine does in fact support Apple events, we install the Apple event handlers. Here we show only the example handler and omit for the sake of brevity the four required Apple events for opening a program or program file, printing and quitting a program. Finally, we call AESetInteractionAllowed to set the program interaction mode, that is, how the user is permitted to interact with this Apple event "slave" program -- is the program run solely in the background, can it receive mouse clicks from the program user, and must the calling program be on the same computer to allow such interaction? We permit user interaction from calling programs on any computer, as most programs will.

Now we can examine the Apple event handler code itself. The first act of the example AERunStimulusHandler handler is to bring the stimulus graphics display window to the forefront by calling SetFrontProcess with the process serial number of the stimulus program, then we initiate program interaction if it was permitted. At this point, we can retrieve the stimulus parameters contained in the Apple event that will determine the appearance and behavior of the stimulus display. Each parameter is extracted from the Apple event with AEGetParamPtr, which is passed as two of its parameters an identifier or Apple event keyword that determines which parameter is retrieved, and the type of variable the parameter is. In the case of this sample code, the stimulus is a spatial sinewave grating. The first stimulus parameter stored in the Apple event to be retrieved is the spatial period of the sinewave. This parameter's keyword is 'scyc' and its type is a long integer. AEGetParamPtr stores the retrived parameter in the stimulus program's long integer variable spatialPeriod. If any error occurs in this process, the process is aborted. A similar sequence of steps is used to retrieve the grating diameter and counterphase reversal temporal period. If each parameter is extracted successfully, the DoAETrial routine is entered, which is responsible for displaying the grating stimulus. DoAETrial sends a sync signal via the digital I/O line (noted by the Boolean variable stimEnabled) to the recording computer to signal that the stimulus is ready and recording may begin.

One aspect of this inter-program control that has not yet been discussed is the launching and quitting of the stimulus display program prior to and following data acquisition. Frontier simplifies these tasks as well. NetFrontier can instruct the copy of Frontier on the stimulus computer to execute two other Frontier commands: The first is Frontier's launch command, which sends an Apple event via the Finder to launch a program. The form of this command that we use is launch.usingID(Stimuli.PVEP.id). This command finds the application whose signature is stored in a variable called "id" in the Object Database table PVEP within the Stimulus script suite. The second command, to be used after data collection, is Frontier's quit command. This command takes the form core.quit(Stimuli.PVEP.id, "no"), where the second argument is a string stating whether or not a data file is to be saved when the requested program is quit. The example code demonstrates that Frontier can be used to handle all aspects of the inter-program communication that is at the core of the laboratory electrophysiology software. Frontier launches the stimulus generation programs, signals each to display stimuli with specific stimulus parameters, then forces them to quit when we no longer need them. The sole purpose of the digital I/O lines is to ensure the tight time-locking of stimulus display and data recording.

Conclusions

In the construction of software systems, complex decisions regarding individual aspects of the system design must be made that have serious implications for the design of the remainder of the system. In the present case, the overwhelming need for program execution speed dictated the choice of a dual-computer system and the method of communication between the computers. Such a system could not have been constructed easily without combining the strengths of LabVIEW, Frontier, and C++. The use of LabVIEW shortened the program development cycle considerably. In addition, inter-computer communication of the speed and complexity used here would not have been achieved easily using C++ without the addition of Frontier. Despite the complexity of this system, the software is easy to operate by the clinician due to the intuitive user interface imposed by LabVIEW's instrument panel paradigm and the operation of all tests from a single program on the recording computer. More importantly, the system design does not sacrifice modularity and expandability. Adding a new test requires adding only a small skeleton C++ stimulus display program, a few LabVIEW VIs to select stimulus parameters, send scripts and record data, and a few Frontier scripts. These code modules are very similar in each of the recording, stimulus and intercommunication programs, in great deal due to a high degree of code reuse in LabVIEW, Frontier and C++ libraries for handling Apple events and generating animated graphics. Much of the task of creating a new set of tests simply involves duplicating, then modifying, existing code.

In the future, it may be possible to remove some of the complexity of the system once it is possible to perform all of the stimulus display and recording tasks on a single dual-monitor dual-processor computer. The inter-program communication could then be restricted to one machine, eliminating the need for NetFrontier -- scripts could be sent from LabVIEW to Frontier and directly translated into Apple events to be received by the stimulus program. Frontier can use the fast Component Manager of Power Macintoshes for Apple event transmission within a single machine, which increases the speed of inter-program communication even more. However, even with the present single-processor technology, we have demonstrated that the creation of powerful, flexible, real-time software is facilitated when it makes use of innovations found on the Macintosh computer. The same programming principles outlined in this paper may be applied to a wide range of applications.

Bibliography and References

  • Apple Computer Company, Inside Macintosh: Inter-program Communication, Addison-Wesley.
  • Baro, John A. and Hughes, Howard C. "The Display And Animation Of Full-Color Images In Real Time On The Macintosh Computer". Behavioral Research Methods, Instruments and Computers, 23 (1991), pp. 537-545.
  • Johnson, Gary W. "LabVIEW Graphical Programming: Practical Applications In Instrumentation And Control". (1994), New York: MacGraw-Hill.
  • Steinman, Scott B. "Simple Real-Time Color Frame Animation". MacTech, 9:9 (September 1993), pp. 21-35.
  • Steinman, Scott B. "Extendable Real-Time Simultaneous Data Acquisition And Stimulus Generation On The Macintosh Computer". Behavioral Research Methods, Instruments and Computers, In Press-.
  • Steinman, Scott B. and Carver, Kevin. "Visual Programming with Prograph CPX(tm)". (1995), Greenwich, Connecticut: Manning Publications / Prentice-Hall.
  • Steinman, Scott B. and Nawrot, Mark. "Real-Time Color Frame Animation For Visual Psychophysics On The Macintosh Computer". Behavioral Research Methods, Instruments and Computers, 24 (1992), pp. 439-452.

Dr. Scott Steinman is a vision scientist and Chair of the Department of Biomedical Sciences at the Southern College of Optometry. He develops software for clinical vision testing, research and education. Scott programs in C++, LabVIEW, Frontier, Prograph and Java. He has published numerous articles on computer programming, two of them previously in MacTech. You may have noticed his book, "Visual Programming with Prograph CPX", on sale in the DevDepot (do I have to hint more than this?). You can reach him at steinman@sco.edu.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.