Skip to content

Commit

Permalink
Merge pull request #2455 from architecture-building-systems/2349-susp…
Browse files Browse the repository at this point in the history
…end-simulations

2349 suspend simulations
  • Loading branch information
daren-thomas committed Nov 15, 2019
2 parents 049b9a6 + caad852 commit 01d4939
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 14 deletions.
2 changes: 1 addition & 1 deletion cea/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "2.25.1"
__version__ = "2.25.2"


class ConfigError(Exception):
Expand Down
19 changes: 19 additions & 0 deletions cea/interfaces/dashboard/server/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from flask_restplus import Namespace, Resource, fields, reqparse
from flask import request, current_app
from cea.interfaces.dashboard.tools.routes import kill_job, worker_processes


__author__ = "Daren Thomas"
Expand All @@ -24,6 +25,7 @@
JOB_STATE_STARTED = 1
JOB_STATE_SUCCESS = 2
JOB_STATE_ERROR = 3
JOB_STATE_CANCELED = 4

job_info_model = api.model('JobInfo', {
'id': fields.Integer,
Expand All @@ -49,6 +51,7 @@ def next_id():
# this is the first job...
return 1


# FIXME: replace with database or similar solution
class JobInfo(object):
"""Store all the information required to run a job"""
Expand Down Expand Up @@ -113,6 +116,8 @@ def post(self, jobid):
job = jobs[jobid]
job.state = JOB_STATE_SUCCESS
job.error = None
if job.id in worker_processes:
del worker_processes[job.id]
current_app.socketio.emit("cea-worker-success", api.marshal(job, job_info_model))
return job

Expand All @@ -124,5 +129,19 @@ def post(self, jobid):
job = jobs[jobid]
job.state = JOB_STATE_ERROR
job.error = request.data
if job.id in worker_processes:
del worker_processes[job.id]
current_app.socketio.emit("cea-worker-error", api.marshal(job, job_info_model))
return job


@api.route("/cancel/<int:jobid>")
class JobCanceled(Resource):
@api.marshal_with(job_info_model)
def post(self, jobid):
job = jobs[jobid]
job.state = JOB_STATE_CANCELED
job.error = "Canceled by user"
kill_job(jobid)
current_app.socketio.emit("cea-worker-canceled", api.marshal(job, job_info_model))
return job
39 changes: 26 additions & 13 deletions cea/interfaces/dashboard/tools/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,34 @@
)

# maintain a list of all subprocess.Popen objects created
worker_processes = []
worker_processes = {} # jobid -> subprocess.Popen


def shutdown_worker_processes():
"""When shutting down the flask server, make sure any subprocesses are also terminated. See issue #2408."""
for popen in worker_processes:
# using code from here: https://stackoverflow.com/a/4229404/2260
# to terminate child processes too
print("SHUTDOWN: killing child processes of {pid}".format(pid=popen.pid))
for jobid in worker_processes.keys():
kill_job(jobid)


def kill_job(jobid):
"""Kill the processes associated with a jobid"""
if not jobid in worker_processes:
return

popen = worker_processes[jobid]
# using code from here: https://stackoverflow.com/a/4229404/2260
# to terminate child processes too
print("killing child processes of {jobid} ({pid})".format(jobid=jobid, pid=popen.pid))
try:
process = psutil.Process(popen.pid)
children = process.children(recursive=True)
for child in children:
print("-- killing child {pid}".format(pid=child.pid))
child.kill()
process.kill()
except psutil.NoSuchProcess:
return
children = process.children(recursive=True)
for child in children:
print("-- killing child {pid}".format(pid=child.pid))
child.kill()
process.kill()
del worker_processes[jobid]


@blueprint.route("/")
Expand All @@ -42,17 +55,17 @@ def route_index():
def route_workers():
"""Return a list of worker processes"""
processes = []
for worker in worker_processes:
for worker in worker_processes.values():
processes.append(worker.pid)
processes.extend(child.pid for child in psutil.Process(worker.pid).children(recursive=True))
return jsonify(sorted(processes))


@blueprint.route('/start/<jobid>', methods=['POST'])
@blueprint.route('/start/<int:jobid>', methods=['POST'])
def route_start(jobid):
"""Start a ``cea-worker`` subprocess for the script. (FUTURE: add support for cloud-based workers"""
print("tools/route_start: {jobid}".format(**locals()))
worker_processes.append(subprocess.Popen(["python", "-m", "cea.worker", jobid]))
worker_processes[jobid] = subprocess.Popen(["python", "-m", "cea.worker", "{jobid}".format(jobid=jobid)])
return jsonify(jobid)


Expand Down
11 changes: 11 additions & 0 deletions setup/cityenergyanalyst.nsi
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# NSIS script for creating the City Energy Analyst installer

; include logic library
!include 'LogicLib.nsh'

; include the modern UI stuff
!include "MUI2.nsh"
Expand Down Expand Up @@ -175,6 +177,15 @@ Section "Base Installation" Base_Installation_Section
nsExec::ExecToLog '"$INSTDIR\Dependencies\Python\python.exe" -m pip install -U --force-reinstall pip'
DetailPrint "Pip installing CityEnergyAnalyst==${VER}"
nsExec::ExecToLog '"$INSTDIR\Dependencies\Python\python.exe" -m pip install -U cityenergyanalyst==${VER}'

# make sure cea was installed
Pop $0
DetailPrint 'pip install cityenergyanalyst==${VER} returned $0'
${If} "$0" != "0"
Abort "Could not install CityEnergyAnalyst ${VER} - see Details"
${EndIf}


DetailPrint "Pip installing Jupyter"
nsExec::ExecToLog '"$INSTDIR\Dependencies\Python\python.exe" -m pip install --force-reinstall jupyter ipython'

Expand Down

0 comments on commit 01d4939

Please sign in to comment.