Subscribe Bookmark
Craige_Hales

Staff

Joined:

Mar 21, 2013

RunProgram

Just got a question about using multiple RunProgram instances at the same time.  Here's an example that runs three copies of PING against three different IP addresses and records the results in three different data tables.  The example shows how to use the "parameter" value to make each RunProgram know which data table to use.  Attachment is same as inline code...

// first, we'll need containers that can be indexed from 1 to NJOBS

// and hold the references to the data tables and the RunPrograms.

datatab = Associative Array();

runprog = Associative Array();

who = {"jmp.com", "google.com", "bing.com"}; // each runProgram can do something different

NJOBS = N Items( who );

// next, a loop to make tables and launch the RunPrograms

For( iJOB = 1, iJOB <= NJOBS, iJOB++,

// make a table with a text column, then make the column wide so we can see more data

datatab[iJOB] = New Table( who[iJOB], addrows( 1 ),

New Column( "text", Character, Set Values( {"pinging..."} ) ) );

datatab[iJOB]:text << setdisplaywidth( 500 );

// launch the RunProgram.  It is particularly important to make sure the RunProgram

// object is stored in a variable that won't be overwritten...the associative array holds it.

runprog[iJOB] = RunProgram(

Executable( "PING.EXE" ), // this is the 'job' we are running several copies of, in parallel

Options( {"-n 10", who[iJOB]} ), // run for 10 seconds --- this is a PING option

Parameter( iJOB ), // remember which "instance" this is --- this goes to the ReadFunction

ReadFunction( // define a function that will get called for lots of small snippets of text

Function( {this, myInstance}, // myInstance is the iJOB

{buffer = ""}, // local to this instance. buffer up small snippets

While( this << canread, // this reads multiple snippets that may show up in a single call

          buffer = buffer || (this << read); // put the snippets together

          Wait( .01 ); // this isn't really needed except to get the snippets in a single record

); // gather as much as possible

buffer = Regex( buffer, "\s+", " ", GLOBALREPLACE ); // clean up the buffer, remove CRs and LFs

If( Trim( buffer ) != "", // see if we have something to say.  there are blanks in PING's output

          (datatab[myInstance]) << addrow( 1 ); // my table gets a new row

          (datatab[myInstance]):text = buffer; // store the cleaned up buffer

);

)

)

);

);

// if you want to, you can wait for the runprograms.  They *require* JMP to be idle to make

// the callback to the read function; the following polling loop uses wait(.1) to make sure

// JMP is mostly idle.  When the PING program terminates, it closes the STDOUT file handle

// JMP is listening to, causing the isReadEof message to return TRUE.

For( iJOB = 1, iJOB <= NJOBS, iJOB++,

While( !((runprog[iJOB]) << isReadEof), Wait( .1 ) )

);

Write( "All Done!\!n" ); // now you can proceed with the data tables

There is a limit to how many of these you can run at once.  Probably not hundreds; they uses resources on your computer that are not unlimited.

3 Comments
Staff

Many thanks!

Perhaps obvious for those who would actually use this, but on a Mac you would need:

Executable( "/sbin/ping" ),

Options( {"-c 10", who[iJOB]} ),

Thanks for posting this!

I just wanted to add that this will not work in JMP 11 (and earlier) since the optional parameter (myInstance in this case) on the ReadFunction was not added until JMP 12.

In JMP 11, you could modify the script to use unique ReadFunctions for each RunProgram instance.  This example embeds the "myInstance" variable into the local variable list, with an assignment that is resolved by the Eval( EvalExpr( ... expr() ... mechanism.

(Turn off the Translate at the top of the screen if the text below is messy.)

// first, we'll need containers that can be indexed from 1 to NJOBS

// and hold the references to the data tables and the RunPrograms.

datatab = Associative Array();

runprog = Associative Array();

who = {"jmp.com", "google.com", "bing.com"}; // each runProgram can do something different

NJOBS = N Items( who );

// next, a loop to make tables and launch the RunPrograms

For( iJOB = 1, iJOB <= NJOBS, iJOB++,

// make a table with a text column, then make the column wide so we can see more data

   datatab[iJOB] = New Table( who[iJOB], addrows( 1 ), New Column( "text", Character, Set Values( {"pinging..."} ) ) );

   datatab[iJOB]:text << setdisplaywidth( 500 );

// launch the RunProgram.  It is particularly important to make sure the RunProgram

   // object is stored in a variable that won't be overwritten...the associative array holds it.

   runprog[iJOB] = Eval(

      Eval Expr(

         RunProgram(

            Executable( "PING.EXE" ), // this is the 'job' we are running several copies of, in parallel

            Options( {"-n 10", Expr( who[iJOB] )} ), // run for 10 seconds --- this is a PING option

            // not in JMP 11: Parameter( iJOB ), // remember which "instance" this is --- this goes to the ReadFunction

            ReadFunction( // define a function that will get called for lots of small snippets of text

               Function( {this/* not in JMP 11: , myInstance*/}, // myInstance is the iJOB

                  {buffer = "",

  // the JMP 11 way...

                  myInstance = Expr( iJOB ) // evalexpr takes care of this

                  }, // local to this instance. buffer up small snippets

                  While( this << canread, // this reads multiple snippets that may show up in a single call

                     buffer = buffer || (this << read); // put the snippets together

                     Wait( .01 ); // this isn't really needed except to get the snippets in a single record

                  ); // gather as much as possible

                  buffer = Regex( buffer, "\s+", " ", GLOBALREPLACE ); // clean up the buffer, remove CRs and LFs

                  If( Trim( buffer ) != "", // see if we have something to say.  there are blanks in PING's output

                     (datatab[myInstance]) << addrow( 1 ); // my table gets a new row

                     (datatab[myInstance]):text = buffer; // store the cleaned up buffer

                  );

               )

            )

         )

      )

   );

);

// if you want to, you can wait for the runprograms.  They *require* JMP to be idle to make

// the callback to the read function; the following polling loop uses wait(.1) to make sure

// JMP is mostly idle.  When the PING program terminates, it closes the STDOUT file handle

// JMP is listening to, causing the isReadEof message to return TRUE.

For( iJOB = 1, iJOB <= NJOBS, iJOB++,

   While( !((runprog[iJOB]) << isReadEof), Wait( .1 ) )

);

Write( "All Done!\!n" ); // now you can proceed with the data tables

Article Tags