## Hrishi Hiraskar ## 23 October 2016 import gevent import time import os import threading from gevent import monkey from gevent.pywsgi import WSGIServer from flask import Flask, request, Response, render_template, send_from_directory from werkzeug import secure_filename monkey.patch_all(aggressive=False) import subprocess app = Flask(__name__, static_folder='webapp/') # This is the path to the upload directory app.config['UPLOAD_FOLDER'] = 'uploads/' # These are the extension that we are accepting to be uploaded app.config['ALLOWED_EXTENSIONS'] = set(['zcos', 'xcos']) # Delay time to look for new line (in s) LOOK_DELAY = 0.1 # States of the line INITIALIZATION = 0 ENDING = 1 DATA = 2 NOLINE = -1 # Scilab dir, can't run absolute paths SCI = "../../../../scilab/scilab_master_old/scilab/" # List to store figure IDs figure_list = [] # List to store filenames of files xcos_file_list = [] class line_and_state: # Class to store the line and its state line = None state = NOLINE def __init__(self, line, state): self.line = line self.state = state def set(self, line_state): self.line = line_state[0] self.state = line_state[1] return False def get_line(self): return self.line def get_state(self): return self.state def parse_line(line): # Function to parse the line # Returns tuple of figure ID and state # state = INITIALIZATION if new figure is created # ENDING if current fig end # DATA otherwise line_words = line.split(' ') if line_words[2] == "Initialization": # New figure created # Get fig id figure_id = int(line_words[-1]) return (figure_id, INITIALIZATION) elif line_words[2] == "Ending": # Current figure end # Get fig id figure_id = int(line_words[-1]) return (figure_id, ENDING) else: # Current figure coordinates figure_id = int(line_words[2]) return (figure_id, DATA) def get_line_and_state(file): # Function to get a new line from file # This also parses the line and appends new figures to figure List global figure_list line = file.readline() if not line: return (None, NOLINE) parse_result = parse_line(line) figure_id = parse_result[0] state = parse_result[1] if state == INITIALIZATION: # New figure created # Add figure ID to list figure_list.append(figure_id) return (None, INITIALIZATION) elif state == ENDING: # End of figure # Remove figure ID from list figure_list.remove(figure_id) return (None, ENDING) return (line, DATA) def event_stream(xcos_file_id): global figure_list # If no id is sent, return if(len(xcos_file_id)==0): return xcos_file_id = int(xcos_file_id) xcos_file_dir = os.getcwd() + '/uploads/' xcos_file_name = xcos_file_list[xcos_file_id] # Get previously running scilab process IDs proc = subprocess.Popen("pgrep scilab", stdout=subprocess.PIPE, shell=True) # out will contain output of command, the list of process IDs of scilab (out, err) = proc.communicate() _l = len(out) # Run xcos file command = ["./"+SCI+"bin/scilab-adv-cli", "-nogui", "-noatomsautoload", "-nb", "-nw", "-e", "loadXcosLibs();importXcosDiagram('" + xcos_file_dir + xcos_file_name + "');xcos_simulate(scs_m,4);mode(2);quit()"] scilab_proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False); # Wait till xcos is launched while len(out) == _l: # If length of out equals _l, # it means scilab hasn't launched yet # Wait gevent.sleep(LOOK_DELAY) # Get process IDs of scilab instances proc = subprocess.Popen("pgrep scilab", stdout=subprocess.PIPE, shell=True) # out will contain output of command, the list of process IDs of scilab (out, err) = proc.communicate() # out will contain output of command, the list of process IDs of scilab # Get the latest process ID of scilab pid = out.split()[-1] # Define function to kill scilab(if still running) and remove files def kill_scilab(): # Kill scilab by it's pid subprocess.Popen(["kill","-9",pid]) # Remove log file subprocess.Popen(["rm",log_dir+log_name]) # Remove xcos file subprocess.Popen(["rm",xcos_file_dir+xcos_file_name]) # Log file directory # As the scilab process is spawned by this script # the log directory is same as that of this script log_dir = "" # Log file name log_name = "scilab-log-"+pid+".txt" # Open the log file log_file = open(log_dir + log_name, "w+") # Kill scilab-adv-cli, if running and get it's output # If the simulation is error free, no output is generated scilab_proc.kill() (scilab_out, scilab_err) = scilab_proc.communicate(); # Check for empty diagram if "Empty diagram" in scilab_out: yield "event: ERROR\ndata: Empty diagram\n\n" kill_scilab(); return # Some other error elif len(scilab_out) > 0: yield "event: ERROR\ndata: " + scilab_out + "\n\n" kill_scilab(); return # Start sending log line = line_and_state(None, NOLINE) while (line.set(get_line_and_state(log_file)) or line.get_state() != ENDING or len(figure_list) > 0): # Get the line and loop until the state is ENDING and figure_list empty if line.get_state() != DATA: gevent.sleep(LOOK_DELAY) else: yield "event: log\ndata: "+line.get_line()+"\n\n" # Reset line, so server won't send same line twice line = line_and_state(None, NOLINE) # Finished Sending Log kill_scilab() # Notify Client yield "event: DONE\ndata: None\n\n" # Route that will process the file upload @app.route('/upload', methods=['POST']) def upload(): # Get the file file = request.files['file'] # Check if the file is not null if file: # Make the filename safe, remove unsupported chars client_id = len(xcos_file_list) filename = str(client_id)+".xcos" # Move the file form the temporal folder to # the upload folder we setup file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) xcos_file_list.append(filename) return str(client_id) else: return "error" @app.route('/SendLog') def sse_request(): # Set response method to event-stream return Response(event_stream(request.args.get('id', '')), mimetype='text/event-stream') @app.route('/') def static_file(path): return app.send_static_file(path) @app.route('/') def page(): return app.send_static_file('index.html') if __name__ == '__main__': # Set server address 127.0.0.1:8001/ http_server = WSGIServer(('127.0.0.1', 8001), app) http_server.serve_forever()