Example: running UNIX command from processing to execute python scripts

This is not a question.

I wanted to use the AxiDraw with processing.
There are some libraries out there but none had a nice trajectory planning build in.


So I followed the the install steps for the AxiDraw python API and AxiCLI


Next I needed to be able to call any of those commands and script from within processing.
Here is my resulting code that works both with calling any of AxiDraw’s python examples and/or the axicli commands.

//code from here: https://forum.processing.org/one/topic/running-bash-script-from-processing.html
/*
 * RUN UNIX COMMANDS IN PROCESSING
 * Jeff Thompson
 * Adapted and annotated from code by Wharfie
 * http://www.computing.net/answers/unix/run-unix-command-thr-java-program-/5887.html
 * July 2011
 *
 * Runs Unix commands from within Processing.  This can be *super* helpful for
 * doing complex operations on folders such as copying or compressing multiple 
 * files.  A simple command can make quick work of what would otherwise be a 
 * cumbersome task in Processing and will likely be much quicker than any Java 
 * implementation of the same process.
 *
 * You will likely need to specify the full path where you want to work, unless your
 * location is relative to your sketch.  For example: /Users/jeffthompson/Desktop
 *
 * Suggestions to try:
 *   whoami                            prints username currently logged on
 *   ls                                this lists all files in the particular location
 *   wc -w filename.extension          this counts words in a particular file
 *   cp sourcefile.ext destfile.txt    makes a copy of a file to a new one
 *   ./yourBashScript.sh               run a bash script (allowing nested and more complex commands)
 *
 * For more ideas, look at the excellent SS64 site: http://ss64.com/bash
 * 
 * www.jeffreythompson.org
 */
 
 //addition from here: https://forum.processing.org/one/topic/unexpected-token-import-17-5-2013.html
import java.io.InputStreamReader;

void setup() {

  // what command to run
  String commandToRun = "/usr/local/bin/python3 axicli.py /Users/stephanschulz/Documents/axidraw-master/cli/AxiDraw_trivial.svg";
  //String commandToRun = "/usr/local/bin/python3 axicli.py version";

  //String commandToRun = "/usr/local/bin/python3 -V";
  //String commandToRun = "/usr/local/bin/python3 /Users/stephanschulz/Documents/axidraw-master/cli/examples_python/plot.py";
  
  //String commandToRun = "ls";
  // String commandToRun = "wc -w sourcefile.extension";
  // String commandToRun = "cp sourcefile.extension destinationfile.extension";
  // String commandToRun = "./yourBashScript.sh";

  File workingDir = new File("/Users/stephanschulz/Documents/Processing/shell/test_axicli/");   // where to do it - should be full path
  String returnedValues;                                                                     // value to return any results

  // give us some info:
  println("Running command: " + commandToRun);
  println("Location:        " + workingDir);
  println("---------------------------------------------\n");

  // run the command!
  try {

    // complicated!  basically, we have to load the exec command within Java's Runtime
    // exec asks for 1. command to run, 2. null which essentially tells Processing to 
    // inherit the environment settings from the current setup (I am a bit confused on
    // this so it seems best to leave it), and 3. location to work (full path is best)
    Process p = Runtime.getRuntime().exec(commandToRun, null, workingDir);

    // variable to check if we've received confirmation of the command
    int i = p.waitFor();

    // if we have an output, print to screen
    if (i == 0) {

      // BufferedReader used to get values back from the command
      BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));

      // read the output from the command
      while ( (returnedValues = stdInput.readLine ()) != null) {
        println(returnedValues);
      }
    }

    // if there are any error messages but we can still get an output, they print here
    else {
      BufferedReader stdErr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
      println("error:");
      // if something is returned (ie: not null) print the result
      while ( (returnedValues = stdErr.readLine ()) != null) {
        println(returnedValues);
      }
    }
  }

  // if there is an error, let us know
  catch (Exception e) {
    println("Error running command!");  
    println(e);
  }

  // when done running command, quit
  println("\n---------------------------------------------");
  println("DONE!");
  exit();
}
1 Like

But I am noticing that I am not always able to get the return messages from those UNIX commands.

String commandToRun = "/usr/local/bin/python3 -V"; works fine.
String commandToRun = "/usr/local/bin/python3 axicli.py version"; works fine.

But trying to get for example an estimated plot duration does not work through processing, but works in terminal.

String commandToRun = "/usr/local/bin/python3 axicli.py " +_filePath + _fileName + " -Tvg 1 -o " +_filePath + "outputfile.svg";

???

here a most minimal example that successfully prints the axicli version in the console but does not print out the values when asking for --report_time

I do not think this has much to do with the axicli api but rather with how the above code interacts with the UNIX calls?

The UNIX call is done here in a thread in order to not block the rest of the processing sketch.

//addition from here: https://forum.processing.org/one/topic/unexpected-token-import-17-5-2013.html
import java.io.InputStreamReader;

void setup() {

  size(900, 500);
}

void draw() {
  background(210);

  circle(mouseX, mouseY, 20);

fill(0);
  text("key a == get axicli version", 10, 10);
  text("key t == get axicli svg plotting duration", 10, 25);
  text("see console for results",10,40);
}

void keyPressed() {
  String sketchpath = sketchPath("");

  if (key == 't') {
    String commandToRun = "/usr/local/bin/python3 axicli.py "+sketchpath+"data/AxiDraw_trivial.svg -v --report_time";
    new ThreadedMethodWithParameter((String) commandToRun);
  }
  if (key == 'a') {
    String commandToRun = "/usr/local/bin/python3 axicli.py version";
    new ThreadedMethodWithParameter((String) commandToRun);
  }
}

//https://discourse.processing.org/t/how-can-i-use-thread-with-functions-that-has-inputs/12964/2

class ThreadedMethodWithParameter extends Thread {
  final String commandToRun;

  ThreadedMethodWithParameter(final String _command) {
    commandToRun = _command;
    start();
  }

  @Override void run() {
    println("thread run UNIX command: "+commandToRun);
    String sketchpath = sketchPath("");
    File workingDir = new File(sketchpath+"/axidraw_api");   // where to do it - should be full path
    String returnedValues;                                                                     // value to return any results

    // give us some info:
    println("Running command: " + commandToRun);
    println("Location:        " + workingDir);
    println("---------------------------------------------\n");

    // run the command!
    try {

      // complicated!  basically, we have to load the exec command within Java's Runtime
      // exec asks for 1. command to run, 2. null which essentially tells Processing to 
      // inherit the environment settings from the current setup (I am a bit confused on
      // this so it seems best to leave it), and 3. location to work (full path is best)
      Process p = Runtime.getRuntime().exec(commandToRun, null, workingDir);

      // variable to check if we've received confirmation of the command
      int ii = p.waitFor();

      // if we have an output, print to screen
      if (ii == 0) {

        // BufferedReader used to get values back from the command
        BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));

        // read the output from the command
        while ( (returnedValues = stdInput.readLine ()) != null) {
          println(returnedValues);
        }
      }

      // if there are any error messages but we can still get an output, they print here
      else {
        BufferedReader stdErr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        println("error:");
        // if something is returned (ie: not null) print the result
        while ( (returnedValues = stdErr.readLine ()) != null) {
          println(returnedValues);
        }
      }
    }

    // if there is an error, let us know
    catch (Exception e) {
      println("Error running command!");  
      println(e);
    }

    // when done running command, quit
    println("\n---------------------------------------------");
    println("DONE!");
  }
}