0

I have compiled a windows executable that connects to a software radio and reads out the values in its registers. When I execute this in command prompt, it returns me the following output

Running the .exe without arguments

My hardware is connected via USB to COM4, when I pass these as arguments, I get the following screen where I can choose between ranging to other radios with their ID's (100 is the Host Radio. 101, 102 103 and 104 are other radios) or can quit the application by typing 'q'.

With -u and COM4 port

If I input the ID of the radio to be ranged, I get the ranging information and this process continues until i quit by typing 'q' ranging and quitting the application

Now, Im trying to write a python script where I can call this executable, get the values and store them for further processing. I have read in other topics that this can be achieved by the library 'subprocess'. My code snippet as follows

#!/usr/bin/env python

import subprocess
import time

#subprocess.Popen([r"C:\Users\Raja\Documents\SUMO_Final\rcmSampleApp.exe"])

p = subprocess.Popen("C:/Users/Raja/Documents/SUMO_Final/rcmSampleApp.exe -u COM4", shell = True, stdin = subprocess.PIPE)


p.stdin.write('104')
time.sleep(5)
p.stdin.write('q')
p.kill()

When I execute this in .py file, the .exe loops indefintely as shown below .exe looping indefinitely inspite of p.kill()

Some answers in the forum suggest changing shell = False and terminate the process before killing it but it is not clear for me. Can someone please help?

Cheers

Raja Sattiraju
  • 1,262
  • 1
  • 20
  • 42
  • 1
    this might help you - http://stackoverflow.com/questions/37560427/sending-to-the-stdin-of-a-program-in-python3/37560878#comment62610750_37560878 – gaganso Jun 01 '16 at 15:16
  • you realize that you are already calling it with arguments right? ` -u COM4` are the arguments, if you want them to be entered by a user at runtime you can just do `Popen("C:.../rcmSampleApp.exe "+USER_ARGS)` or am I misunderstanding what you want? – Tadhg McDonald-Jensen Jun 01 '16 at 15:17
  • Yes. `-u COM4` are also arguments, But since I wont change them, I provided them statically at the start of the program. – Raja Sattiraju Jun 01 '16 at 15:23
  • I also found this [link](http://stackoverflow.com/questions/17411966/printing-stdout-in-realtime-from-a-subprocess-that-requires-stdin) useful – Raja Sattiraju Jun 01 '16 at 16:39

2 Answers2

1

flush the pipe when you write a command (and include newlines to simulate user inputs with Enter key presses, assuming program expects that), and close the pipe when you're done. It's also generally a good idea to avoid shell=True, as it introduces additional layers wrapping the command (and in circumstances other than this one, adds security and stability issues). Try:

#!/usr/bin/env python

import subprocess
import time

# Use list form of command to avoid shell=True, and raw string to allow
# safe use of "normal" looking Windows-style path
# Include universal_newlines=True automatic str decoding in Py3; it might
# fix newlines on Py2 as well, but if not, you'll need to explicitly send
# \r\n instead of just \n to match OS conventions (or use os.linesep to autoselect)
p = subprocess.Popen([r"C:\Users\Raja\Documents\SUMO_Final\rcmSampleApp.exe", "-u", "COM4"],
        stdin=subprocess.PIPE, universal_newlines=True)

p.stdin.write('104\n')  # Include newline that manual entry includes
p.stdin.flush()         # Ensure write not buffered
time.sleep(5)
p.stdin.write('q\n')    # Include newline
p.stdin.close()         # Flush and close stdin, so program knows no more input coming
p.kill()                # Depending on program, p.join() might be enough

Assuming p.join() works, you could replace the last three lines (write, close and kill/join) with just:

p.communicate('q\n')

which will perform all three tasks implicitly.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thanks a lot. Good to know about subprocess.communicate – Raja Sattiraju Jun 01 '16 at 15:31
  • @Mechanic: Yar. In this case, it's mostly just a convenient shorthand. But should you ever need to use more than one of the standard handles (read both `stdout` and `stderr`, or write `stdin` and read one or more of the others), it efficiently handles the reads and writes to remove the possibility of a deadlock due to filled pipes, and becomes absolutely critical; avoiding deadlock without it is verbose and either complicated (using `select` module to read and write in one thread) or higher overhead (using `threading` module to launch a thread(s) to do async read/write). – ShadowRanger Jun 01 '16 at 15:35
  • When I try to run it in the IDE (Spyder), I get the following error in the line `p.stdin.close()` gives `OSError: [Errno 22] Invalid argument` ´OSError: Invalid argument´ – Raja Sattiraju Jun 01 '16 at 15:53
  • @Mechanic: Does it doe the same outside the IDE? I never trust IDE environments. – ShadowRanger Jun 01 '16 at 16:05
  • It works in the command line. the error only shows up in spyder. Now if i want to access this data in the IDE, any bright ideas for this? – Raja Sattiraju Jun 01 '16 at 16:10
  • @Mechanic: If it otherwise works, only the `close` fails, you `flush`, then perform the `close` in a `try` block and ignore the error, e.g. `import errno` `p.stdin.flush()`, `try: p.stdin.close()`, `except OSError as e:`, `p.terminate()`, `if e.errno != errno.EINVAL: raise` (the `terminate` and `if` should be inside `except` block to be clear). Basically, you handle errors by forcibly terminating the subprocess, silently ignore the _known_ error, allow unknown errors to propagate. You might wrap the entire block of code (after `Popen` in that `try`/`except` if `flush` can error too). – ShadowRanger Jun 01 '16 at 16:46
1

When you use your program interactively, I assume that you type 101 Return, and then q Return.

When you start it from Python, you must simulate that Return with a \n, and you should wait the program to end instead of killing it:

p.stdin.write('104\n')
time.sleep(5)
p.stdin.write('q\n')
p.wait()

But as you only pass known output, if you just want to read and process the output you best choice is to use communicate:

out, err = p.communicate('104\nq\n')
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • When I use `out, err = p.communicate('104\nq\n')`, it works well in command line. However, when I do this in IDE, I get the following error `TypeError: 'str' does not support the buffer interface`. There is a similar question answered in another thread [printing stdout in realtime from a subprocess that requires stdin](http://stackoverflow.com/questions/17411966/printing-stdout-in-realtime-from-a-subprocess-that-requires-stdin). I changed my code to include both stdin and stdout but I get the same typeerror. – Raja Sattiraju Jun 01 '16 at 16:26
  • 1
    @Mechanic: Sounds like your command line is using Python 2, while the IDE is using Python 3. Python 2 expects `str` for `stdin`, always, while Python 3 expects `bytes` normally, or `str` when `Popen` is initialized with `universal_newlines=True`. Either pass `universal_newlines=True` to `Popen`, or change the `communicate` to `p.communicate(b'104\nq\n')`; the `b` prefix makes it a `bytes` object on Py3. On Py2.6+, the prefix is legal, but does nothing (remains `str`) unless `__future__` was used to enable the `unicode_literals`, in which case literal is `str` instead of new default `unicode`. – ShadowRanger Jun 01 '16 at 16:50
  • This works in the cmd. However when I try to do `print(out)` in the IDE, it returns me None – Raja Sattiraju Jun 01 '16 at 17:02
  • Sorry. I forgot to add `stdout = subprocess.PIPE` in the Popen. Now it works like a charm – Raja Sattiraju Jun 01 '16 at 17:05