diff --git a/comm/client/main.py b/comm/client/main.py index 2376135863438e1306624c6c244756a8b653b48e..e23ef87df669c010139e35cda59eae923c3aea41 100644 --- a/comm/client/main.py +++ b/comm/client/main.py @@ -50,7 +50,9 @@ def start(): "client": client_baseurl, "req_id": request.form['req_id'], "resolver": request.form['resolver'] - } + } + + print(option['req_id']) pynqrouter.start_solver(boardstr, qseed, option) diff --git a/comm/resolver/main.py b/comm/resolver/main.py index 9a6c71616a3259e6308cacc1cea79c3a252a82f3..4fcb894581616724123921c480aa9ca2e39c811b 100644 --- a/comm/resolver/main.py +++ b/comm/resolver/main.py @@ -60,8 +60,10 @@ def resolve(): with open(outpath, "r") as fp: resolved = fp.read() + print(data['req_id']) + res = { - 'answer': resolved, + 'solution': resolved, 'solved': True, 'solver': data['client'], 'resolver': client_baseurl, diff --git a/comm/server/main.py b/comm/server/main.py index 68157a4bdcef71640336aba841d9c5378f451665..4caed053b1ba2ad0a249991d042d43c93c86d0bc 100644 --- a/comm/server/main.py +++ b/comm/server/main.py @@ -17,6 +17,8 @@ import threading import time from collections import OrderedDict from flask import Flask, render_template, request, g +from gevent import pywsgi, monkey +from geventwebsocket.handler import WebSocketHandler from queue import Queue sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../solver') @@ -25,10 +27,12 @@ import BoardStr import adcclilib app = Flask(__name__) +monkey.patch_all() app_args = {} questions = None clients = None current_seed = 1 +wsq = None def load_questions(): global app_args @@ -58,10 +62,10 @@ def load_questions(): "path": v, "status": "Not solved", "answer": "", - "queue": Queue(), "board_size": board_size, "line_num": line_num, "last_req": None, + "solver": OrderedDict(), "answers": OrderedDict(), } @@ -79,7 +83,7 @@ def load_questions(): with open(v, "r") as fp: _ans_dat = fp.read() try: - questions[_name]["status"] = "Solved" + questions[_name]["status"] = "Saved" questions[_name]["answer"] = _ans_dat except KeyError as e: pass @@ -88,9 +92,14 @@ def update_answer_list(qname): global app_args global questions + global clients + questions[qname]['solver'] = OrderedDict() questions[qname]['answers'] = OrderedDict() + for c in clients['solver']: + questions[qname]['solver'][c[0]] = "-" + _folder_name = os.path.splitext(qname)[0] _answers_path = "{}/{}".format(app_args['out'], _folder_name) if os.path.isdir(_answers_path): @@ -98,30 +107,51 @@ def update_answer_list(qname): answer_log_file.sort() answer_log_file.reverse() + is_solved = False + for v2 in answer_log_file: json_name = os.path.basename(v2) with open(v2, "r") as fp: answer_log = json.load(fp) questions[qname]['answers'][json_name] = answer_log + + if answer_log['solved'] == "True": + is_solved = True if questions[qname]['answer'] == answer_log['answer']: questions[qname]['best_json'] = json_name + if (questions[qname]['last_req'] == float(answer_log['req_id'])): + if answer_log['solver'] in questions[qname]['solver']: + if answer_log['solved'] == "True": + questions[qname]['solver'][answer_log['solver']] = "Solved" + else: + questions[qname]['solver'][answer_log['solver']] = "DNS" + + if is_solved > 0: + questions[qname]['status'] = "Solved" + @app.before_request def before_request(): global app_args - g.local_mode = (request.remote_addr == "127.0.0.1") + if app_args["force_local_mode"]: + g.local_mode = True + else: + g.local_mode = (request.remote_addr in ["127.0.0.1", "::1"]) @app.route("/post", methods=["POST"]) def post(): global questions global app_args + global wsq _qname = request.form["qname"] - _answer = request.form["answer"] + _answer = request.form["solution"] _client = request.form["client"] + _req_id = request.form["req_id"] + _is_solved = request.form["solved"] if "solver" in request.form: _solver = request.form["solver"] @@ -132,45 +162,58 @@ def post(): receive_time_str = receive_time.strftime("%Y%m%d%H%M%S") dat = { - "req_id": request.form['req_id'], + "req_id": _req_id, + "qname": _qname, "client": _client, "solver": _solver, "answer": _answer, "cputime": request.form['cputime'], - "timestamp": receive_time.strftime("%Y/%m/%d %H:%M:%S") + "timestamp": receive_time.strftime("%Y/%m/%d %H:%M:%S"), + "nlcheck": -1, + "solved": _is_solved, } - # Create a temporary file to execute "nlcheck.py" - tmp_path = "{}/{}".format(app_args['out'], "tmp") - if not os.path.isdir(tmp_path): - os.makedirs(tmp_path) - tmp_file_name = "{}-{}.tmp".format(receive_time_str, _client.replace(":", ".")) - tmp_file_path = "{}/{}".format(tmp_path, tmp_file_name) - with open(tmp_file_path, "w") as fp: - fp.write(_answer) - probpath = "{}/{}".format(app_args["question"], _qname) - - # Format check - cmd = "/usr/bin/python /home/pi/adc2018/conmgr/adc2018/server/nlcheck.py --input {} --target {}".format(probpath, tmp_file_path) - print("`{}`".format(cmd)) - p = subprocess.run(cmd.strip().split(" "), stdout=subprocess.PIPE) - nlcheck = p.stdout.decode().strip() - - pattern = r"judge += +\[(.+?), ([0-9.]+)\]" - m = re.match(pattern, nlcheck) - if m and (m.group(1) == "True"): - dat["nlcheck"] = float(m.group(2)) + if _is_solved == "True": + # Create a temporary file to execute "nlcheck.py" + tmp_path = "{}/{}".format(app_args['out'], "tmp") + if not os.path.isdir(tmp_path): + os.makedirs(tmp_path) + tmp_file_name = "{}-{}.tmp".format(receive_time_str, _client.replace(":", ".")) + tmp_file_path = "{}/{}".format(tmp_path, tmp_file_name) + with open(tmp_file_path, "w") as fp: + fp.write(_answer) + probpath = "{}/{}".format(app_args["question"], _qname) + + # Format check + cmd = "/usr/bin/python /home/pi/adc2018/conmgr/adc2018/server/nlcheck.py --input {} --target {}".format(probpath, tmp_file_path) + print("`{}`".format(cmd)) + p = subprocess.run(cmd.strip().split(" "), stdout=subprocess.PIPE) + nlcheck = p.stdout.decode().strip() + + pattern = r"judge += +\[(.+?), ([0-9.]+)\]" + m = re.match(pattern, nlcheck) + if m and (m.group(1) == "True"): + dat["nlcheck"] = float(m.group(2)) + + save_path = "{}/{}".format(app_args['out'], os.path.splitext(_qname)[0]) + if not os.path.isdir(save_path): + os.makedirs(save_path) + + file_name = "{}-{}.json".format(receive_time_str, _solver.replace(":", ".")) + save_file_path = "{}/{}".format(save_path, file_name) + with open(save_file_path, "w") as fp: + json.dump(dat, fp, indent=4) else: - dat["nlcheck"] = -1 + save_path = "{}/{}".format(app_args['out'], os.path.splitext(_qname)[0]) + if not os.path.isdir(save_path): + os.makedirs(save_path) - save_path = "{}/{}".format(app_args['out'], os.path.splitext(_qname)[0]) - if not os.path.isdir(save_path): - os.makedirs(save_path) + file_name = "{}-{}.json".format(receive_time_str, _solver.replace(":", ".")) + save_file_path = "{}/{}".format(save_path, file_name) + with open(save_file_path, "w") as fp: + json.dump(dat, fp, indent=4) - file_name = "{}-{}.json".format(receive_time_str, _solver.replace(":", ".")) - save_file_path = "{}/{}".format(save_path, file_name) - with open(save_file_path, "w") as fp: - json.dump(dat, fp, indent=4) + wsq.put(dat) res = {"status": "OK"} @@ -317,6 +360,7 @@ def save_best_solution(): with open(out_file_path, 'w') as fp: fp.write(questions[qname]['answers'][_best_json]['answer']) + questions[_name]["status"] = "Saved" questions[qname]['best_json'] = _best_json res = {"status": "OK"} @@ -547,7 +591,23 @@ def question_status(): update_answer_list(qname) qdata = questions[qname] - return render_template("part_question_status.html", qname=qname, qdata=qdata, solvers=clients["solver"]) + return render_template("part_question_status.html", qname=qname, qdata=qdata, solvers=clients["solver"], localmode=g.local_mode) + +@app.route('/ws') +def ws(): + global wsq + + if request.environ.get('wsgi.websocket'): + ws = request.environ['wsgi.websocket'] + + while True: + if wsq is None: + time.sleep(1) + continue + else: + data = wsq.get() + str_data = json.dumps(data) + ws.send(str_data) @app.route("/") def index(): @@ -567,9 +627,7 @@ def init_system(): global app_args global questions global clients - - if questions is None: - load_questions() + global wsq if clients is None: @@ -591,6 +649,12 @@ def init_system(): "solver": solver, "resolver": resolver } + + if questions is None: + load_questions() + + if wsq is None: + wsq = Queue() if __name__ == "__main__": @@ -599,6 +663,7 @@ if __name__ == "__main__": parser.add_argument("-a", "--adccli", action="store", type=str, default="./adccli.json", help="Path to the ADCCLI configuration json file.") parser.add_argument("-q", "--question", action="store", type=str, default="./problems", help="Path to the question folder.") parser.add_argument("-o", "--out", action="store", type=str, default="./answers", help="Path to the output folder.") + parser.add_argument("--force-local-mode", action="store_true", default=False, help="Apply local mode view to all clients.") parser.add_argument("--debug", action="store_true", default=False, help="Debug mode.") args = vars(parser.parse_args()) app_args = args @@ -607,5 +672,9 @@ if __name__ == "__main__": if args["debug"]: app.debug = True - app.run(host='0.0.0.0', threaded=True) + + app.threaded = True + # app.run(host='0.0.0.0', threaded=True) + server = pywsgi.WSGIServer(("", 5000), app, handler_class=WebSocketHandler) + server.serve_forever() diff --git a/comm/server/static/css/pynq-manager.css b/comm/server/static/css/pynq-manager.css index 72f5dc78a1e14d5d59bb69adf29bb5575b154148..d4e326cc6b9b50057c44888b7d02978af3e84dbb 100644 --- a/comm/server/static/css/pynq-manager.css +++ b/comm/server/static/css/pynq-manager.css @@ -1,6 +1,4 @@ html, body{ - /*width: 800px;*/ - /*height: 500px;*/ font-size: 11px; overflow: hidden; } @@ -10,7 +8,7 @@ body{ /* scrollbar settings */ ::-webkit-scrollbar{ - width: 25px; + width: 20px; border: 1px solid rgba(0, 0, 50, .2); } ::-webkit-scrollbar-thumb{ @@ -18,7 +16,7 @@ body{ } #question-table-wrapper{ - height: 300px; + height: calc(100vh - 50px); overflow-y: scroll; overflow-x: hidden; } @@ -45,7 +43,7 @@ body{ } #client-control-pane{ - height: 330px; + height: calc(100vh - 20px); overflow-y: scroll; } diff --git a/comm/server/static/js/pynq-manager.js b/comm/server/static/js/pynq-manager.js index 995d93ca348f7431e90838ad183f351b16e38e39..8361e2a63018fe3ae98098683f7ccea2dcfee601 100644 --- a/comm/server/static/js/pynq-manager.js +++ b/comm/server/static/js/pynq-manager.js @@ -63,7 +63,6 @@ var PynqManager = (function(){ }).done(function(res){ var answer = res; $("#solving-question-status").text(answer["status"]); - $("#solved-client").text(answer["answer"]["client"]); if (after !== null){ after(); @@ -110,6 +109,7 @@ $(function(){ var pynqClients = {} var pm = null; + var last_req = 0; var refresh_question_table = function(){ $.ajax({ @@ -179,7 +179,7 @@ $(function(){ $("#adccli-status").text("ログアウト"); }); - pm.getStatus(); + if(pm) pm.getStatus(); }); } @@ -195,7 +195,7 @@ $(function(){ $("#client-control-pane").find(".start-button").eq(0).click(function(){ var qname = $(this).data("qname"); - pm.sendQuestion(qname, after=refresh_question_table); + pm.sendQuestion(qname); }); $("#client-control-pane").find(".stop-button").eq(0).click(function(){ pm.sendStop(); @@ -241,6 +241,19 @@ $(function(){ } }).trigger('hashchange'); + var ws = new WebSocket("ws://" + location.host + "/ws"); + ws.onmessage = function(e){ + var recv = JSON.parse(e.data.replace(/\n/g, "\\n")); + + console.log(recv); + + var hash = location.hash.replace("#", ""); + if(hash == recv['qname']){ + show_question_status(hash); + } + refresh_question_table(); + }; + refresh_question_table(); $.ajax({ @@ -250,6 +263,10 @@ $(function(){ }).done(function(d){ pynqClients = d; pm = PynqManager(pynqClients); + + if(location.hash.replace("#", "") == ""){ + pm.getStatus(); + } }); }); diff --git a/comm/server/templates/index.html b/comm/server/templates/index.html index bb8456d24c04405086c733e5a005ff0416e2a4f1..112c8f068492203682a5264cde94944aec386875 100644 --- a/comm/server/templates/index.html +++ b/comm/server/templates/index.html @@ -15,9 +15,9 @@
-
+
-
+

問題一覧

  システム状況 diff --git a/comm/server/templates/part_client_table.html b/comm/server/templates/part_client_table.html index 7386b49fabcd3f66e5f4b18208e50f950b937557..d71fe09a53bce6ddf94d3da84b0e2e6db5dff858 100644 --- a/comm/server/templates/part_client_table.html +++ b/comm/server/templates/part_client_table.html @@ -9,12 +9,13 @@ Viewer Mode {% endif %}

+{% if local_mode %}

自動運営システム

- -
+ +{% endif %}

クライアント

diff --git a/comm/server/templates/part_question_status.html b/comm/server/templates/part_question_status.html index a6f634cd7df0708d1510b6629e52078ecfc9b7f8..738df97e1d249021ae7c1ff1978b472d6ecfe8c4 100644 --- a/comm/server/templates/part_question_status.html +++ b/comm/server/templates/part_question_status.html @@ -1,6 +1,7 @@

【{{qname}}】

+ {% if localmode %}

@@ -9,6 +10,9 @@

+ {% else %} + [View Only] + {% endif %}

処理結果

@@ -28,7 +32,7 @@  ({{c[2]}}) {% endif %} -
+ {% endfor %}
{{qdata.solver[c[0]]}}
@@ -44,20 +48,22 @@ Score {% for k, v in qdata.answers.items() %} - {% if qdata.best_json == k %} + {% if (qdata.best_json == k) and (v.answer != "") %} {% else %} {% endif %} {{v.timestamp}} {{v.solver}} - {{v.nlcheck}} + + {% if v.nlcheck == -1 %} + Not solved + {% else %} + {{v.nlcheck}} + {% endif %} + {% endfor %}
-

状況:{{qdata.status}}

-

結果

-

クライアント:{{qdata.answer.client}}

-
{{qdata.answer.answer}}
diff --git a/solver/adc2018solver.py b/solver/adc2018solver.py index 9e8660096c6a55387237dd646814663896677238..588927364024ce9f4d4d2ec61a8b6c1bff98cade 100644 --- a/solver/adc2018solver.py +++ b/solver/adc2018solver.py @@ -99,7 +99,7 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): solver_thread.stopped() solver_thread = None if "host" in option: - requests.post("http://{}/post".format(option['host']), data=res) + r = requests.post("http://{}/post".format(option['host']), data=res) return res # ボード文字列から X, Y, Z を読んでくる @@ -149,7 +149,7 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): solver_thread.stopped() solver_thread = None if "host" in option: - requests.post("http://{}/post".format(option['host']), data=res) + r = requests.post("http://{}/post".format(option['host']), data=res) return res # 完了の確認 @@ -168,7 +168,7 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): mmio_led.write(0, 0) sys.stderr.write('Cannot solve it!\n') if "host" in option: - requests.post("http://{}/post".format(option['host']), data=res) + r = requests.post("http://{}/post".format(option['host']), data=res) solver_thread = None return res