cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Submit your abstract to the call for content for Discovery Summit Americas by April 23. Selected abstracts will be presented at Discovery Summit, Oct. 21- 24.
Discovery is online this week, April 16 and 18. Join us for these exciting interactive sessions.
Choose Language Hide Translation Bar
Craige_Hales
Super User
RunProgram

update 27apr2018: repair formatting

 

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.

Last Modified: Apr 27, 2018 10:36 AM
Comments
ian_jmp
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]} ),

Justin_Chilton
Staff

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.

Craige_Hales
Super User

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