234 lines
5.4 KiB
Python
234 lines
5.4 KiB
Python
"""
|
|
Lancer avec python -m flask --debug --app server\\__init__.py run
|
|
"""
|
|
import logging
|
|
import sys
|
|
import time
|
|
|
|
from flask import Flask, request, session
|
|
import multiprocessing as mp
|
|
from api_worker.__init__ import run_instance
|
|
import uuid
|
|
from flask_cors import CORS
|
|
|
|
app = Flask("COCOSIM")
|
|
CORS(app, supports_credentials=True)
|
|
app.secret_key = bytes.fromhex('192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf')
|
|
|
|
|
|
class ProcessBase(object):
|
|
SESSION_MAX_AGE = 60 * 5
|
|
|
|
def __init__(self):
|
|
self._cmd_q = {}
|
|
self._state_q = {}
|
|
self._process = {}
|
|
self._last = {}
|
|
|
|
def known(self):
|
|
return 'id' in session and session['id'] in self._cmd_q
|
|
|
|
def update(self):
|
|
self._last[session['id']] = time.time()
|
|
|
|
def cmd_q(self):
|
|
self.update()
|
|
return self._cmd_q[session['id']]
|
|
|
|
def state_q(self):
|
|
self.update()
|
|
return self._state_q[session['id']]
|
|
|
|
def init_q(self):
|
|
self.update()
|
|
self._state_q[session['id']] = mp.Queue()
|
|
self._cmd_q[session['id']] = mp.Queue()
|
|
|
|
def init_process(self, p):
|
|
self.update()
|
|
self.cleanup()
|
|
self._process[session['id']] = p
|
|
|
|
def process(self):
|
|
return self._process[session['id']]
|
|
|
|
def purge(self, k=None):
|
|
if k is None:
|
|
k = session['id']
|
|
|
|
del self._cmd_q[k]
|
|
del self._state_q[k]
|
|
del self._process[k]
|
|
del self._last[k]
|
|
|
|
def quit(self):
|
|
self.purge()
|
|
self.cleanup()
|
|
|
|
def cleanup(self):
|
|
ctime = time.time()
|
|
to_purge = []
|
|
|
|
for k, v in self._last.items():
|
|
if ctime - v > self.SESSION_MAX_AGE:
|
|
to_purge.append(k)
|
|
|
|
for k in to_purge:
|
|
logging.info(f"Purging {k}")
|
|
self.purge(k)
|
|
|
|
def __str__(self):
|
|
return str(list(self._process.keys()))
|
|
|
|
def __len__(self):
|
|
return len(self._process.keys())
|
|
|
|
|
|
PROCESS_BASE = ProcessBase()
|
|
|
|
|
|
@app.route('/')
|
|
def hello():
|
|
PROCESS_BASE.cleanup()
|
|
return f"Hello sailor !\nProcess base : {str(PROCESS_BASE)}"
|
|
|
|
|
|
@app.route('/init')
|
|
def init():
|
|
session['id'] = uuid.uuid4().int
|
|
PROCESS_BASE.init_q()
|
|
PROCESS_BASE.init_process(mp.Process(target=run_instance, args=(session['id'], PROCESS_BASE.cmd_q(), PROCESS_BASE.state_q())))
|
|
PROCESS_BASE.process().start()
|
|
return "OK"
|
|
|
|
|
|
@app.route('/start')
|
|
def start():
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
PROCESS_BASE.cmd_q().put({'cmd': "start"})
|
|
return "OK"
|
|
|
|
|
|
@app.route('/stop')
|
|
def stop():
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q().put({'cmd': "stop"})
|
|
return "OK"
|
|
|
|
|
|
@app.route('/state')
|
|
def state():
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q().put({'cmd': "state"})
|
|
data = PROCESS_BASE.state_q().get()
|
|
return data
|
|
|
|
|
|
@app.route('/quit')
|
|
def quit():
|
|
if not PROCESS_BASE.known():
|
|
PROCESS_BASE.cleanup()
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q().put({'cmd': "quit"})
|
|
PROCESS_BASE.process().join()
|
|
PROCESS_BASE.quit()
|
|
del session['id']
|
|
logging.info("Purge effectuée")
|
|
return "OK"
|
|
|
|
|
|
@app.route('/load', methods=['POST'])
|
|
def load():
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
try:
|
|
data = request.json
|
|
PROCESS_BASE.cmd_q().put({'cmd': "load", "args": data})
|
|
except Exception:
|
|
logging.exception("ERROR")
|
|
return "Error", 418
|
|
|
|
if PROCESS_BASE.state_q().get() == "OK":
|
|
return "OK"
|
|
else:
|
|
return "Error in graph", 418
|
|
|
|
|
|
@app.route('/push/<obj>')
|
|
def push(obj):
|
|
"""
|
|
Push button
|
|
"""
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q().put({'cmd': "push", "args": obj})
|
|
return "OK"
|
|
|
|
|
|
@app.route('/release/<obj>')
|
|
def release(obj):
|
|
"""
|
|
Release button
|
|
"""
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q().put({'cmd': "release", "args": obj})
|
|
return "OK"
|
|
|
|
@app.route('/pause/<obj>')
|
|
def pause_simulation(obj):
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q.put({'cmd': "pause", "arg": obj})
|
|
return "Simulation paused"
|
|
|
|
@app.route('/resume/<obj>')
|
|
def resume_simulation(obj):
|
|
if not PROCESS_BASE.known():
|
|
return "Unknown", 401
|
|
|
|
PROCESS_BASE.cmd_q.put({'cmd': "resume", "arg": obj})
|
|
return "Simulation resumed"
|
|
|
|
def just_run():
|
|
"""
|
|
Pour lancer avec gunicorn
|
|
"""
|
|
mp.set_start_method('spawn')
|
|
app.run() # Ne pas mettre debug en mode multiprocess
|
|
|
|
|
|
def main(argv):
|
|
host = '127.0.0.1'
|
|
ssl_context = None
|
|
for arg in argv[1:]:
|
|
if arg == '-remote':
|
|
host = '0.0.0.0'
|
|
elif arg == '-secure-cookie':
|
|
app.config.update(SESSION_COOKIE_SAMESITE="None", SESSION_COOKIE_SECURE="True")
|
|
elif arg == '-ssl':
|
|
ssl_context = 'adhoc'
|
|
elif arg == '-h':
|
|
print("""
|
|
Options :
|
|
-remote : bind on 0.0.0.0 instead of 127.0.0.1
|
|
-secure-cookie : send secure cookie (for browser UI to be happy)
|
|
-ssl : enable https with adhoc certificate
|
|
""")
|
|
sys.exit(0)
|
|
else:
|
|
print(f"Unknown argument : {arg}")
|
|
sys.exit(-1)
|
|
mp.set_start_method('spawn')
|
|
app.run(debug=False, host=host, port=5555, ssl_context=ssl_context) # Ne pas mettre debug en mode multiprocess
|