BCDA-APS / epics-bluesky-vm

Simulate a beam line using EPICS IOCs, GUIs, Bluesky and related components.
Other
2 stars 1 forks source link

Show interim progress during subprocess call (starting IOCs) #11

Closed prjemian closed 3 years ago

prjemian commented 3 years ago

When instrument.iocs.check_iocs is run from blueskyStarter.sh, it can take a long time to complete. During this time, no output is shown to the console. Consider showing a progress bar or the interim output from the subprocess call. At least print a message that the step may take a minute or more to complete.

prjemian commented 3 years ago

quick demo of tqdm (thanks):

In [1]: from tqdm import tqdm

In [2]: import sys, time

In [3]: def doodad():
   ...:     time.sleep(1)
   ...: 

In [4]: with tqdm(file=sys.stdout) as pbar:
   ...:     for i in range(10):
   ...:         doodad()
   ...:         pbar.update(1)
   ...: 
10it [00:10,  1.00s/it]
prjemian commented 3 years ago

This would be sufficient.

prjemian commented 3 years ago

Pay attention to this example: Adding Tqdm to python subprocesses

import sys
import subprocess

from tqdm import tqdm

def create_test_bash_script():
    """
    Create a bash script that generates numbers 1 to 2000
    This is just for illustration purpose to simulate a long running bash command
    """
    with open('hello', 'w') as bash_file:
        bash_file.write('''\
    #!/bin/bash
    # Tested using bash version 4.1.5
    for ((i=1;i<=2000;i++));
    do
        # your-unix-command-here
        echo $i
    done
    ''')

def run_task(cmd):

    try:
        # create a default tqdm progress bar object, unit='B' defines a String that will be used to define the unit of each iteration in our case bytes
        with tqdm(unit='B', unit_scale=True, miniters=1, desc="run_task={}".format(cmd)) as t:
            # subprocess.PIPE gets the output of the child process
            process = subprocess.Popen(cmd, shell=True, bufsize=1, universal_newlines=True, stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)

            # print subprocess output line-by-line as soon as its stdout buffer is flushed in Python 3:
            out = []
            for line in process.stdout:
                out.append(line)
                # Update the progress, since we do not have a predefined iterator
                # tqdm doesnt know before hand when to end and cant generate a progress bar
                # hence elapsed time will be shown, this is good enough as we know
                # something is in progress
                t.update()
                # forces stdout to "flush" the buffer
                sys.stdout.flush()

            # We explicitly close stdout
            process.stdout.close()

            # wait for the return code
            return_code = process.wait()

            # if return code is not 0 this means our script errored out
            if return_code != 0:
                raise subprocess.CalledProcessError(return_code, cmd)

            print("".join(out))  # TODO: consider filtering out incomplete lines

    except subprocess.CalledProcessError as e:
        sys.stderr.write(
            "common::run_command() : [ERROR]: output = {}, error code = {}\n".format(e.output, e.returncode))

create_test_bash_script()

# run your terminal command using below
run_task('chmod 755 hello && ./hello')
prjemian commented 3 years ago

Instead, implemented #23