0

I am working on a small java app that needs to start a python script and interact with it. The python script is to run in the background and wait for commands. After each command I expect a response which will be forwarded back to the java app.

I have used the examples here and here to open the python script.

My question is how do I, without re-running the python script hook into it and run my commands?

public void startProcess()
{
    try {
        p = Runtime.getRuntime().exec("python " + scriptPath);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public String executeCommand(String cmd)
{
    String consoleResponse = "";

    try {
        // how do I perform something similar to p.exec(cmd)

        BufferedReader stdInput = new BufferedReader(new
                 InputStreamReader(p.getInputStream()));

        BufferedReader stdError = new BufferedReader(new
                 InputStreamReader(p.getErrorStream()));

        // read the output from the command
        System.out.println("Here is the standard output of the command:\n");
        while ((consoleResponse += stdInput.readLine()) != null) {
        }

        // read any errors from the attempted command
        System.out.println("Here is the standard error of the command (if any):\n");
        while ((consoleResponse = stdError.readLine()) != null) {
        }

    } catch (IOException e) {
        e.printStackTrace();
    }

    return consoleResponse;
}

EDIT: The python script is for BACpypes. The script does 3 things. WhoIs: gets a list of all devices connected over bacnet ReadHexFile: reads in a text file to be sent to all devices on the network SendFile: sends the file to all devices.

I am not experienced with python and feel it would be simpler to keep all this data in one script.

I suppose one option is to break each command into its own script and pass the data to the java application.

Michael Miner
  • 964
  • 2
  • 17
  • 39
  • What arguments are given to the python script? Also does the python script already listen for input while running, and if so how? – xtratic Jul 26 '18 at 14:27
  • The python script does sit and wait for input. I am not sure how, but this is the script it is based on. https://github.com/JoelBender/bacpypes/blob/master/samples/WhoIsIAm.py – Michael Miner Jul 26 '18 at 14:28
  • If you do not know what input the script is listening for then you won't be able to communicate with it. You'll need to find out the communication method first, either find documentation on the script, run the script to see if it says it needs arguments or input, or read the script and look for inputs or listeners. – xtratic Jul 26 '18 at 14:34
  • It seems like the hex file might be the only input, since it sounds like the script is detecting devices on the network on its own and sending the hex file. – xtratic Jul 26 '18 at 14:36
  • The script does nothing until it gets a 'whois' command. If you enter no commands the script just spins – Michael Miner Jul 26 '18 at 14:37
  • how does the script receive the "whois" command? you don't run `python path\to\script.py whois` so is it receiving that from stdin? – xtratic Jul 26 '18 at 14:42
  • Originally I run the script from command line. Then I type whois and the script responds – Michael Miner Jul 26 '18 at 14:48
  • I've edited my answer, does it help you? – xtratic Jul 26 '18 at 16:34

1 Answers1

0

how do I, without re-running the python script hook into it and run my commands?

You would need to make the single Python script keep listening for the new input or requests (back and forth communication), but I think that would be a slight pain and also makes your python script less clear than the standard input -> process -> output flow.

What is the reason for avoiding running multiple Python scripts?


To write input to your scripts stdin, do something like this:

public static void main(String[] args) throws IOException, InterruptedException {
    ProcessBuilder pb = new ProcessBuilder("python", "path\\to\\script.py");
    Process pr = pb.start();

    try (BufferedWriter writerToProc = new BufferedWriter(
            new OutputStreamWriter(pr.getOutputStream()));
            BufferedReader readerOfProc = new BufferedReader(
                    new InputStreamReader(pr.getInputStream()));
            BufferedReader errorsOfProc = new BufferedReader(
                    new InputStreamReader(pr.getErrorStream()))) {

        writerToProc.write("WhoIs\n");
        writerToProc.write("ReadHexFile\n"); // is this the syntax?
        writerToProc.write("SendFile 'path\to\file.txt'\n");
        writerToProc.flush();

        StringBuilder procOutput = new StringBuilder();
        boolean gaveUp = false;
        long waitTime = 10 * 1_000; // 10 seconds
        long lastRead = System.currentTimeMillis();
        for(;;) {
             final long currTime = System.currentTimeMillis();
             final int available = readerOfProc.available();
             if(available > 0){
                 // TODO read the available bytes without blocking
                 byte[] bytes = new byte[available];
                 readerOfProc.read(bytes);
                 procOutput.append(new String(bytes));

                 // maybe check this input for an EOF code
                 // your python task should write EOF when it has finished
                 lastRead = currTime;
             } else if((currTime - lastRead) > waitTime){
                 gaveUp = true;
                 break;
             }
        }


        // readerOfProc.lines().forEach((l) -> System.out.println(l));
        // errorsOfProc.lines().forEach((l) -> System.out.println(l));
    }
}
xtratic
  • 4,600
  • 2
  • 14
  • 32
  • I am very inexperienced in python. The python script was mostly given to me. I just did a few edits to the script for file parsing. The script does three things. Find a list of devices. Loads in a text file. Sends the file to the devices. All this data is stored in the script and does not seem easily sent to numerous scripts – Michael Miner Jul 26 '18 at 14:20
  • Correct me if I am wrong but your latest edit would run a new instance of the script each time through – Michael Miner Jul 26 '18 at 14:48
  • Yes `Runtime.getRuntime().exec(new String[]{"python", scriptPath, ...` will run a new instance of the script. – xtratic Jul 26 '18 at 15:28
  • unfortunately I can not run my script that way. The script does not take arguments to start, it simply waits for them afterwards. – Michael Miner Jul 26 '18 at 15:29
  • Yep, got it. Another comment of mine says you'll need to write to the Processes standard input stream. Gimme a minute and I'll write the code to do that. – xtratic Jul 26 '18 at 15:32
  • This is mostly working for me. I perform the whois and I can get the console output just fine. I am trying to test my other python functions. If I call ReadHexFile with no path I am supposed to get an error message. Doing this from the script I get the error message. Doing this from java I do not get that message. Any ideas as to why? – Michael Miner Jul 26 '18 at 17:28
  • Without seeing your java and python code, no, sorry, I don't know why. Glad that I was able to help you send input though and good luck with the rest. – xtratic Jul 26 '18 at 17:53
  • The issue was I was missing a "\n" character. Now that that is in. I can start the process. Get the BufferedWriter as needed and write whatever I want – Michael Miner Jul 26 '18 at 19:06
  • Let me add one more question. Lets say my network is slow. It takes 5 seconds for the console to get 5 devices. I have 50 devices. In this answer is there a way to say, if we have not seen new data in 10 seconds we are good to go? – Michael Miner Jul 26 '18 at 21:12
  • @MichaelMiner Try the code I have included in my edit. I have not been able to test it, but the general idea is that it should time-out after 10 seconds if it was unable to read anything from the input stream. – xtratic Jul 26 '18 at 23:51
  • Unfortunately I found a solution similar to that. My issue with the 10 second timeout is that the file transfer takes 4 minutes. I want hoping you'd know of a more responsive solution. – Michael Miner Jul 27 '18 at 00:01
  • As long as it got some data 10 seconds ago, it will keep trying to read. If the file transfer takes 4 minutes then that's an issue on the python side of things. Like I mentioned before, you may want python to write `EOF` when it is finished transferring the file, so you can know that the transfer is done. That combined with a timeout should work decently. – xtratic Jul 27 '18 at 12:19