diff --git a/.gitignore b/.gitignore index 6f6995dea78dd6e08e41d7e4d03a7e5e8a71fc80..d4eb4b6c035cba27c21db393dd75b8ad8a5da0b5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ .DS_Store *.txt +*.tmp +*.json # Object files *.o @@ -17,3 +19,7 @@ solver # Python files *.pyc __pycache__/ + +# client-icons +client-icon/ + diff --git a/comm/client/main.py b/comm/client/main.py index 92249f31b3c84871ed12e694a4a4edd8a7b0237e..2376135863438e1306624c6c244756a8b653b48e 100644 --- a/comm/client/main.py +++ b/comm/client/main.py @@ -21,7 +21,6 @@ import adc2018solver as pynqrouter app = Flask(__name__) args = {} -# pynq_thread = None client_baseurl = "" @app.route('/start', methods=["POST"]) @@ -45,10 +44,15 @@ def start(): qseed = request.form["qseed"] boardstr = BoardStr.conv_boardstr(qstr.split('\n'), 'random', int(qseed)) - option = {"name": qname, "host": args['host'], "client": client_baseurl} + option = { + "name": qname, + "host": args['host'], + "client": client_baseurl, + "req_id": request.form['req_id'], + "resolver": request.form['resolver'] + } pynqrouter.start_solver(boardstr, qseed, option) - # pynq_thread = pynqrouter.start_solver(boardstr, qseed, option) # 実行だけ開始する ans["status"] = "Processing" @@ -64,8 +68,6 @@ def start(): @app.route('/stop') def stop(): - # global pynq_thread - if pynqrouter.solver_thread is None: ans = {"status": "No threads"} else: @@ -73,15 +75,11 @@ def stop(): pynqrouter.stop_solver() ans = {"status": "Stopped"} - # pynq_thread = None - return json.dumps(ans) @app.route("/status") def status(): - # global pynq_thread - res_mes = "" if pynqrouter.solver_thread is None: @@ -121,3 +119,4 @@ if __name__ == "__main__": if args["debug"]: app.debug = True app.run(host='0.0.0.0', port=args["port"], threaded=True) + diff --git a/comm/resolver/README.md b/comm/resolver/README.md new file mode 100644 index 0000000000000000000000000000000000000000..f83c279cda6a03fa9b297c741b9ca0d97ea6c463 --- /dev/null +++ b/comm/resolver/README.md @@ -0,0 +1,29 @@ +DAS2018 ADC クライアント(リゾルバ)プログラム +=== + +DAS2018 アルゴリズムデザインコンテスト用クライアントプログラム + +## Description + +解答データをソルバから受信し,整形した結果をサーバへ返すプログラム. + +## Requirements + +- Python 3.5以上(くらい) +- Flask + +## Usage + +``` +python3 main.py [--port XXXX] [--host XXXX] +``` + +### Options + +
+
-H, --host
+
サーバホストのアドレス (デフォルト:192.168.5.1:5000)
+ +
-p, --port
+
使用するポート (デフォルト:5000)
+
diff --git a/comm/resolver/main.py b/comm/resolver/main.py new file mode 100644 index 0000000000000000000000000000000000000000..9a6c71616a3259e6308cacc1cea79c3a252a82f3 --- /dev/null +++ b/comm/resolver/main.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +""" +This script provides a PYNQ client. +This is intended to run on the client server (PYNQ). +""" + +import argparse +import json +import os +import platform +import queue +import requests +import subprocess +import sys +import threading +import time +from flask import Flask, render_template, request, g +from urllib.parse import urlparse + +sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../solver') +import BoardStr +# import adc2018solver as pynqrouter + +app = Flask(__name__) +args = {} +client_baseurl = "" +resolve_queue = None + +def resolve(): + """ + queueを受け取りそこに入ってきた答えを整形し,ホストに送信します. + """ + global args + global client_baseurl + global resolve_queue + + while True: + if resolve_queue is None: + time.sleep(1) + continue + else: + data = resolve_queue.get() + + if args['verbose']: + print("Processing queued data...") + print(data) + + outpath = "./works/resolved.txt" + probpath = "./problems/{}".format(data['qname']) + tmppath = "./works/answer.txt" + + with open(tmppath, "w") as fp: + fp.write(data['answer']) + + # Resolver + cmd = "/home/pi/adc2017/pynq-router/resolver/solver --reroute --output {} {} {}".format(outpath, probpath, tmppath) + subprocess.call(cmd.strip().split(" ")) + + resolved = "" + with open(outpath, "r") as fp: + resolved = fp.read() + + res = { + 'answer': resolved, + 'solved': True, + 'solver': data['client'], + 'resolver': client_baseurl, + 'client': client_baseurl, + 'cputime': data['cputime'], + 'qname': data['qname'], + 'req_id': data['req_id'] + } + # 結果をホストに返す + r = requests.post("http://{}/post".format(args['host']), data=res) + + if args['verbose']: + print("Done.") + print(res) + +@app.route('/post', methods=["POST"]) +def start(): + + # global pynq_thread + global args + global client_baseurl + global resolve_queue + + if args["verbose"]: + print(request.form) + + data = { + 'client': request.form['client'], + 'qname': request.form['qname'], + 'answer': request.form['solution'], + 'cputime': request.form['cputime'], + 'req_id': request.form['req_id'] + } + + resolve_queue.put(data) + + ans = {"status": "Queued"} + + if args["verbose"]: + print(ans) + + return json.dumps(ans) + +@app.route('/stop') +def stop(): + + global resolve_queue + + if resolve_queue.empty() is None: + ans = {"status": "Nothing queued"} + else: + # pynq_thread.stop() + while not resolve_queue.empty(): + resolve_queue.get() + ans = {"status": "Stopped"} + + return json.dumps(ans) + +@app.route("/status") +def status(): + + res_mes = "" + + if resolve_queue.empty(): + res_mes = "Ready" + else: + res_mes = "Working" + + res = {"status": res_mes} + return json.dumps(res) + +@app.route("/") +def index(): + return platform.node() + +@app.before_request +def before_request(): + global client_baseurl + + _url = request.url + parse = urlparse(_url) + client_baseurl = parse.netloc + +if __name__ == "__main__": + + # Check if this script runs as "root" user + # if os.getuid() != 0: + # raise Exception("Must run as root") + # sys.exit(1) + + parser = argparse.ArgumentParser(description="PYNQ client (for resolver).") + parser.add_argument("-p", "--port", action="store", type=int, default=5000, help="Port") + parser.add_argument("-H", "--host", action="store", type=str, default="192.168.5.1:5000", help="Host address") + parser.add_argument("--debug", action="store_true", default=False, help="Debug mode.") + parser.add_argument("-v", "--verbose", action="store_true", default=False, help="Verbose.") + args = vars(parser.parse_args()) + + # Start resolver thread + resolve_queue = queue.Queue() + th = threading.Thread(target=resolve, args=()) + th.setDaemon(True) + th.start() + + if args["debug"]: + app.debug = True + app.run(host='0.0.0.0', port=args["port"], threaded=True) + diff --git a/comm/server/README.md b/comm/server/README.md index b76c13b42c35730fd13400c746fe58c27af597ef..661ced123163fd205eca92eb9d6617db88fb5f4e 100644 --- a/comm/server/README.md +++ b/comm/server/README.md @@ -1,7 +1,7 @@ -DAS2017 ADC サーバプログラム +DAS218 ADC サーバプログラム === -DAS2017 アルゴリズムデザインコンテスト用サーバプログラム +DAS2018 アルゴリズムデザインコンテスト用サーバプログラム ## Description @@ -32,12 +32,6 @@ python3 main.py [--question XXXX] [--port XXXX] [--clients XXXX]
-p, --port
サーバのポート (デフォルト:5000)
- -
-l, --line-num-th
-
処理を分岐させるライン数の閾値
- -
-t, --timeout
-
PYNQで処理させるタイムアウト(秒)
## Comments diff --git a/comm/server/main.py b/comm/server/main.py index 845e070b8c2e9e7ee2b8bc0732d45acc7a3a8c30..b151ca3a2cd22e48c589d1144291645041286555 100644 --- a/comm/server/main.py +++ b/comm/server/main.py @@ -5,6 +5,7 @@ This is intended to run on the host server (Raspberry Pi). """ import argparse +import datetime import glob import json import os @@ -27,79 +28,143 @@ questions = None clients = None current_seed = 1 -@app.before_request -def before_request(): +def load_questions(): global app_args global questions - global clients - if questions is None: - question_path = os.path.abspath(app_args["question"]) - question_list = glob.glob("{}/NL_*.txt".format(question_path)) - question_list.sort() + question_path = os.path.abspath(app_args["question"]) + question_list = glob.glob("{}/NL_*.txt".format(question_path)) + question_list.sort() + + questions = OrderedDict() + + for v in question_list: + # 問題の情報を読み込む + with open(v, "r") as fp: + _q_lines = fp.readlines() + board_size = "" + line_num = -1 + for l in _q_lines: + if "SIZE" in l: + board_size = l.strip().split()[1] + if "LINE_NUM" in l: + line_num = int(l.strip().split()[1]) + break + + _name = os.path.basename(v) + questions[_name] = { + "path": v, + "status": "Not solved", + "answer": "", + "queue": Queue(), + "board_size": board_size, + "line_num": line_num, + "last_req": None, + "answers": [], + } + + update_answer_list(_name) + + # 既に回答されているものを読み込む + answer_path = os.path.abspath(app_args["out"]) + answer_list = glob.glob("{}/T03_A*.txt".format(answer_path)) + + for v in answer_list: + _ans_name = os.path.basename(v) + m = re.search(r"T03_A([0-9A-Za-z]+)\.txt", _ans_name) + if m: + _name = "NL_Q{}.txt".format(m.group(1)) + with open(v, "r") as fp: + _ans_dat = fp.read() + try: + questions[_name]["status"] = "Solved" + questions[_name]["answer"] = _ans_dat + except KeyError as e: + pass - questions = OrderedDict() +def update_answer_list(qname): - for v in question_list: - # 問題の情報を読み込む - with open(v, "r") as fp: - _q_lines = fp.readlines() - board_size = "" - line_num = -1 - for l in _q_lines: - if "SIZE" in l: - board_size = l.strip().split()[1] - if "LINE_NUM" in l: - line_num = int(l.strip().split()[1]) - break - - _name = os.path.basename(v) - questions[_name] = { - "path": v, - "status": "Not solved", - "answer": {}, - "queue": Queue(), - "board_size": board_size, - "line_num": line_num - } - - # 既に回答されているものを読み込む - answer_path = os.path.abspath(app_args["out"]) - answer_list = glob.glob("{}/T03_A*.txt".format(answer_path)) - - for v in answer_list: - _ans_name = os.path.basename(v) - m = re.search(r"T03_A([0-9A-Za-z]+)\.txt", _ans_name) - if m: - _name = "NL_Q{}.txt".format(m.group(1)) - with open(v, "r") as fp: - _ans_dat = fp.read() - try: - questions[_name]["status"] = "Solved" - questions[_name]["answer"] = _ans_dat - except KeyError as e: - pass + global app_args + global questions - if clients is None: + questions[qname]['answers'] = [] - with open(app_args["client"], "r") as fp: - _clients = fp.readlines() + _folder_name = os.path.splitext(qname)[0] + _answers_path = "{}/{}".format(app_args['out'], _folder_name) + if os.path.isdir(_answers_path): + answer_log_file = glob.glob("{}/*.json".format(_answers_path)) + answer_log_file.sort() + answer_log_file.reverse() + + for v2 in answer_log_file: + with open(v2, "r") as fp: + answer_log = json.load(fp) + questions[qname]['answers'].append(answer_log) + +@app.before_request +def before_request(): + global app_args - clients = [v.rstrip() for v in _clients] + g.local_mode = (request.remote_addr == "127.0.0.1") @app.route("/post", methods=["POST"]) def post(): global questions + global app_args - _client = request.form["client"] _qname = request.form["qname"] _answer = request.form["answer"] - _cputime = request.form["cputime"] + _client = request.form["client"] + + if "solver" in request.form: + _solver = request.form["solver"] + else: + _solver = _client + + receive_time = datetime.datetime.now() + receive_time_str = receive_time.strftime("%Y%m%d%H%M%S") + + dat = { + "req_id": request.form['req_id'], + "client": _client, + "solver": _solver, + "answer": _answer, + "cputime": request.form['cputime'], + "timestamp": receive_time.strftime("%Y/%m/%d %H:%M:%S") + } + + # 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)) + else: + dat["nlcheck"] = -1 - dat = {"client": _client, "answer": _answer, "cputime": _cputime} + save_path = "{}/{}".format(app_args['out'], os.path.splitext(_qname)[0]) + if not os.path.isdir(save_path): + os.makedirs(save_path) - questions[_qname]["queue"].put(dat) + file_name = "{}-{}.json".format(receive_time_str, _client.replace(":", ".")) + save_file_path = "{}/{}".format(save_path, file_name) + with open(save_file_path, "w") as fp: + json.dump(dat, fp, indent=4) res = {"status": "OK"} @@ -129,121 +194,77 @@ def start(): qstr = "\n".join(_q_lines) - qnumber = _question_name.replace(".txt", "").replace("NL_Q", "") - probpath = "{}/{}".format(app_args["question"], _question_name) - tmppath = "{}/T03_A{}_tmp.txt".format(app_args["out"], qnumber) - infopath = "{}/T03_A{}_info.txt".format(app_args["out"], qnumber) - outpath = "{}/T03_A{}.txt".format(app_args["out"], qnumber) - res = {} if line_num >= 0: - if line_num < app_args["line_num_th"]: - # LINE_NUMが閾値未満のとき,PYNQに問題を配信して問題を解かせる - res = solve_questions(_question_name, qstr) - # 一旦回答をテンポラリファイルに保存 - with open(tmppath, "w") as fp: - fp.write(res["answer"]["answer"]) - else: - # LINE_NUMが閾値以上のとき,PYNQでは解けないのでRaspberry Piに解かせる - # 文字数多くなるとコマンドラインで載りきらないからパイプで渡す - #boardstr = BoardStr.conv_boardstr(_q_lines, 'random', current_seed) - cmd = "/opt/python3.6/bin/python3.6 /home/pi/pynq-router/solver/gen_boardstr.py -t random -s {} {} | /home/pi/pynq-router/sw_huge/sim - {} {}".format(current_seed, probpath, current_seed, tmppath) - print("`{}`".format(cmd)) - time_start = time.time() - subprocess.call(cmd.strip(), shell=True) - time_done = time.time() - elapsed = time_done - time_start - res["answer"] = {} - res["answer"]["client"] = "192.168.4.1" - res["answer"]["answer"] = "Solved on Raspberry Pi!" - res["answer"]["cputime"] = elapsed - res["status"] = "Solved on Raspberry Pi" - current_seed += 1 - - # CPU time - print("CPU_time:{}".format(res["answer"]["cputime"])) - with open(infopath, "w") as f: - f.write("CPU_time:{}\n".format(res["answer"]["cputime"])) - f.write("memory:551250\n") - - # 回答をファイルに保存するとしたらここで処理する - # 整形ルーティング (再配線) する - #cmd = "/home/pi/pynq-router/resolver/solver --reroute --output {} {} {}".format(outpath, probpath, tmppath) - cmd = "/home/pi/adc2017/pynq-router/resolver/solver --reroute --output {} {} {}".format(outpath, probpath, tmppath) - print("`{}`".format(cmd)) - subprocess.call(cmd.strip().split(" ")) - - # たまに整形ルーティングが失敗する - # そのときは tmp を答えファイルとしてコピーする - #if not os.path.exists(outpath) and os.path.exists(tmppath): - # cmd = "/bin/cp {} {}".format(tmppath, outpath) - # subprocess.call(cmd.strip().split(" ")) - - # 回答ファイルが正しく出力されないときは,正しく解けなかったとき - if not os.path.exists(outpath): - res["status"] = "DNF" - else: - # Format check - #cmd = "/usr/bin/python /home/pi/conmgr/adc2017/server/nlcheck.py --input {} --target {}".format(probpath, outpath) - cmd = "/usr/bin/python /home/pi/adc2018/conmgr/adc2018/server/nlcheck.py --input {} --target {}".format(probpath, outpath) - print("`{}`".format(cmd)) - subprocess.call(cmd.strip().split(" ")) + # LINE_NUMが閾値未満のとき,PYNQに問題を配信して問題を解かせる + res = solve_questions(_question_name, qstr) # 最終結果だけを保存 questions[_question_name]["status"] = res["status"] - questions[_question_name]["answer"] = res["answer"] return json.dumps(res) def solve_questions(qname, qstr): + """ + このメソッドでは,問題データをソルバのクライアントに送りつける. + 結果は/postに送られてくるので,結果の集計はそちらで行う. + """ global clients global questions global current_seed - global app_args - def worker(host, qname, qstr, qseed, q): + def worker(host, qname, qstr, qseed, req_id, resolver): _url = "http://{}/start".format(host) + data = { + "client": host, + "qname": qname, + "question": qstr, + "qseed": qseed, + "req_id": req_id, + "resolver": resolver + } try: - r = requests.post(_url, data={"client": host, "qname": qname, "question": qstr, "qseed": qseed}) + r = requests.post(_url, data=data) client_res = json.loads(r.text) - q.put(client_res) except Exception as e: sys.stderr.write(str(e) + "\n") threads = [] - q = Queue() - for c in clients: - _th = threading.Thread(name=c, target=worker, args=(c, qname, qstr, current_seed, q)) + # 時刻をリクエストIDとして設定 + req_id = time.time() + questions[qname]['last_req'] = req_id + + for c in clients['solver']: + # 問題はSolverに送る + client_addr = c[0] + idx_of_resolver = current_seed % len(clients['resolver']) + resolver = clients['resolver'][idx_of_resolver][0] + _th = threading.Thread(name=client_addr, target=worker, args=(client_addr, qname, qstr, current_seed, req_id, resolver)) _th.start() threads.append(_th) current_seed += 1 - # PYNQが解き終わるまで待つ(ここでは最大10秒) - cnt = 0 - while cnt < app_args["timeout"] and questions[qname]["queue"].qsize() < 1: - time.sleep(1) - cnt += 1 + res = {"status": "Processed"} - res = {"status": "Done", "answers": [], "answer": ""} + return res - while not questions[qname]["queue"].empty(): - _r = questions[qname]["queue"].get() - res["answers"].append(_r) +@app.route("/stop", methods=["POST"]) +def stop(): + _client = request.form["client"] + _url = _client + "/stop" - # res["answers"]に,回答を得られたものの結果が,返ってきた順に入る. - # 解の品質等を決めて最終的な回答を与える場合はここで処理する(今はとりあえず最初の答え) - # TODO: 答えが無い場合の処理 - if len(res["answers"]) > 0: - res["answer"] = res["answers"][0] - else: - res["answer"] = { "client": "None", "answer": "" } - res["status"] = "DNF" + try: + r = requests.get(_url) + client_res = json.loads(r.text)["status"] + except Exception as e: + client_res = "Connection error" + sys.stderr.write(str(e) + "\n") - #print(res) + res = {"status": client_res} - return res + return json.dumps(res) @app.route("/status", methods=["POST"]) def get_status(): @@ -267,8 +288,9 @@ def get_clients(): res = OrderedDict() - for c in clients: - res[c] = "http://{}".format(c) + for c in clients['all']: + client_ip = c[0] + res[client_ip] = "http://{}".format(client_ip) return json.dumps(res) @@ -288,45 +310,78 @@ def client_table(): global app_args global clients - return render_template("part_client_table.html", clients=clients) + return render_template("part_client_table.html", clients=clients["all"], local_mode=g.local_mode) @app.route("/get_question_status") def question_status(): global app_args global questions + global clients qname = request.args.get("qname", "") + + update_answer_list(qname) qdata = questions[qname] - return render_template("part_question_status.html", qname=qname, qdata=qdata) + return render_template("part_question_status.html", qname=qname, qdata=qdata, solvers=clients["solver"]) @app.route("/") def index(): global app_args global questions - global clients question_path = os.path.abspath(app_args["question"]) - return render_template("index.html", questions=questions, question_path=question_path, clients=clients) + return render_template("index.html", questions=questions, question_path=question_path) def main(args): raise NotImprementedError() +def init_system(): + + global app_args + global questions + global clients + + if questions is None: + load_questions() + + if clients is None: + + with open(app_args["client"], "r") as fp: + _clients = fp.readlines() + + all_clients = [v.rstrip().split() for v in _clients] + + solver = [] + resolver = [] + for v in all_clients: + if v[1].lower() == "solver": + solver.append(v) + elif v[1].lower() == "resolver": + resolver.append(v) + + clients = { + "all": all_clients, + "solver": solver, + "resolver": resolver + } + if __name__ == "__main__": parser = argparse.ArgumentParser(description="PYNQ control panel.") parser.add_argument("-c", "--client", action="store", type=str, default=None, required=True, help="Client list.") 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("-l", "--line-num-th", action="store", type=int, default=128, help="Line number threshold.") - parser.add_argument("-t", "--timeout", action="store", type=int, default=300, help="Timeout.") parser.add_argument("--debug", action="store_true", default=False, help="Debug mode.") args = vars(parser.parse_args()) app_args = args + init_system() + if args["debug"]: app.debug = True app.run(host='0.0.0.0', threaded=True) + diff --git a/comm/server/static/css/pynq-manager.css b/comm/server/static/css/pynq-manager.css index a909fe9c4bae24fec6dcf98f643d25250e0d2571..4ef3532a257a1d441b833a5441b583fb907aa4e7 100644 --- a/comm/server/static/css/pynq-manager.css +++ b/comm/server/static/css/pynq-manager.css @@ -43,12 +43,16 @@ body{ #client-control-pane{ height: 330px; - overflow: scroll; + overflow-y: scroll; } -#client-control-pane table th, -#client-control-pane table td{ - width: 50%; +#client-control-pane table th.large-cell, +#client-control-pane table td.large-cell{ + width: 40%; +} +#client-control-pane table th.small-cell, +#client-control-pane table td.small-cell{ + width: 20%; } #question-control-pane h3{ diff --git a/comm/server/static/js/pynq-manager.js b/comm/server/static/js/pynq-manager.js index 5bd3a9116d0b250c31d92fffb590c9726b834cef..903fac2d808810ba4d8788e2bebb1c6fc3f0a606 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-result").text(answer["answer"]["answer"]); $("#solved-client").text(answer["answer"]["client"]); if (after !== null){ @@ -75,7 +74,31 @@ var PynqManager = (function(){ _p.sendStop = function(){ - console.log("Not implemented!"); + for (var key in this.clients){ + (function(_key, _client){ + var statusObj = $(".client-status-row[data-cname='"+_key+"']").find(".client-status-value").eq(0) + $.ajax({ + type: "POST", + url: "/stop", + dataType: "json", + data: { + "client": _client + } + }).done(function(res){ + var answer = res["status"]; + var message = ""; + + if (answer) { + message = answer; + }else{ + message = "Illegal response: " + res; + } + statusObj.text(message); + }).fail(function(){ + statusObj.text("Connection error"); + }); + })(key, this.clients[key]); + } } @@ -102,13 +125,19 @@ $(function(){ $(".question-row").removeClass("q-selected"); $tr.addClass("q-selected"); var qname = $tr.data("qname"); - show_question_status(qname); + // show_question_status(qname); + location.hash = "#" + qname; return false; }); + + var hash = location.hash.replace("#", ""); + if(hash != ""){ + $(".question-row[data-qname='" + hash + "']").addClass("q-selected"); + } }); } - var refresh_client_table = function(){ + var show_client_table = function(){ $.ajax({ type: "GET", dataType: "html", @@ -116,16 +145,7 @@ $(function(){ }).done(function(d){ $("#client-control-pane").html(""); $("#client-control-pane").html(d); - - $.ajax({ - type: "GET", - dataType: "json", - url: "/get_clients" - }).done(function(d){ - pynqClients = d; - pm = PynqManager(pynqClients); - pm.getStatus(); - }); + pm.getStatus(); }); } @@ -143,15 +163,30 @@ $(function(){ var qname = $(this).data("qname"); pm.sendQuestion(qname, after=refresh_question_table); }); + $("#client-control-pane").find(".stop-button").eq(0).click(function(){ + pm.sendStop(); + }); }); } + $(window).on('hashchange', function(){ + var hash = location.hash.replace("#", ""); + if(hash == ""){ + show_client_table(); + }else{ + show_question_status(hash); + } + }).trigger('hashchange'); + refresh_question_table(); - refresh_client_table(); - $("#view-server-status-button").click(function(){ - refresh_client_table(); - return false; + $.ajax({ + type: "GET", + dataType: "json", + url: "/get_clients" + }).done(function(d){ + pynqClients = d; + pm = PynqManager(pynqClients); }); }); diff --git a/comm/server/templates/index.html b/comm/server/templates/index.html index ea6802291df850455a9d292f39a14090ab3435c4..bb8456d24c04405086c733e5a005ff0416e2a4f1 100644 --- a/comm/server/templates/index.html +++ b/comm/server/templates/index.html @@ -20,7 +20,7 @@

問題一覧

  - クライアント状況 + システム状況

Loading...

diff --git a/comm/server/templates/part_client_table.html b/comm/server/templates/part_client_table.html index a176eed0dc582ad7de97bfc7d8fe6d49a044fb9d..6211db7a81feb30a171696ac0c605c2153922d12 100644 --- a/comm/server/templates/part_client_table.html +++ b/comm/server/templates/part_client_table.html @@ -1,12 +1,33 @@ -

クライアント状況

+

システム状況

+ +

動作モード

+

+{% if local_mode %} +Normal Mode +{% else %} +Viewer Mode +{% endif %} +

+ +

クライアント

- - + + + {% for c in clients %} - - + + + {% endfor %} diff --git a/comm/server/templates/part_question_status.html b/comm/server/templates/part_question_status.html index ad90ab689bfe190dc4c1410b188a1eee4197b75d..e46a98dccbec283024c7d703e347f519dd76ff9d 100644 --- a/comm/server/templates/part_question_status.html +++ b/comm/server/templates/part_question_status.html @@ -1,7 +1,54 @@ -

問題 【{{qname}}】

-

- -

+
+
+

【{{qname}}】

+

+ + +

+
+
+

処理結果

+
クライアント名ステータスClientRoleStatus
{{c}}
+ {% if c|length > 3 %} + {{c[2]}}  + {% endif %} + {{c[0]}} + {% if c|length > 2 %} +  ({{c[2]}}) + {% endif %} + {{c[1]}}
+ + + + + {% for c in solvers %} + + + + + {% endfor %} +
Client (Solver)Status
+ {% if c|length > 3 %} + {{c[2]}}  + {% endif %} + {{c[0]}} + {% if c|length > 2 %} +  ({{c[2]}}) + {% endif %} +
+
+
+ +
+

処理結果一覧

+ + + + + + + {% for v in qdata.answers %} + + + + + + {% endfor %} +
TimestampClientScore
{{v.timestamp}}{{v.solver}}{{v.nlcheck}}
+
+

状況:{{qdata.status}}

結果

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

diff --git a/solver/adc2018solver.py b/solver/adc2018solver.py index b93f4619bf1ac7934bccc03729f82adbdc601fc2..9e8660096c6a55387237dd646814663896677238 100644 --- a/solver/adc2018solver.py +++ b/solver/adc2018solver.py @@ -78,6 +78,15 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): print(seed) print('') + res = { + 'client': option['client'], + 'qname': option['name'], + 'solution': '', + 'cputime': -1.0, + 'req_id': option['req_id'], + 'solved': False + } + # LINE数を数えてコンフィグするbitstreamを分岐 line_num = boardstr.count('L') if line_num < 127: @@ -89,7 +98,9 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): else: solver_thread.stopped() solver_thread = None - return {'solved': False, 'solution': '', 'elapsed': -1.0} + if "host" in option: + requests.post("http://{}/post".format(option['host']), data=res) + return res # ボード文字列から X, Y, Z を読んでくる size_x = (ord(boardstr[1]) - ord('0')) * 10 + (ord(boardstr[2]) - ord('0')) @@ -137,7 +148,9 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): if (solver_thread is not None) and (not solver_thread.is_running()): solver_thread.stopped() solver_thread = None - return { 'solved': False, 'solution': '', 'elapsed': -1.0 } + if "host" in option: + requests.post("http://{}/post".format(option['host']), data=res) + return res # 完了の確認 print('Done!') @@ -154,11 +167,11 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): # 解けなかったらLEDを消す mmio_led.write(0, 0) sys.stderr.write('Cannot solve it!\n') - res = {'client': option['client'], 'qname': option['name'], 'answer': '', 'cputime': -1} if "host" in option: requests.post("http://{}/post".format(option['host']), data=res) solver_thread = None - return { 'solved': False, 'solution': '', 'elapsed': -1.0 } + return res + print('Solved!') # 解けたらLEDを全部つける @@ -185,13 +198,24 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): solution += str(boards[i]) # 普通に表示 solution += '\n' + res = { + 'client': option['client'], + 'qname': option['name'], + 'solution': solution, + 'cputime': elapsed, + 'req_id': option['req_id'], + 'solved': True + } + if "resolver" in option: + r = requests.post("http://{}/post".format(option['resolver']), data=res) + elif "host" in option: + r = requests.post("http://{}/post".format(option['host']), data=res) + if solver_thread is not None: solver_thread.stopped() - res = {'client': option['client'], 'qname': option['name'], 'answer': solution, 'cputime': elapsed} - if "host" in option: - r = requests.post("http://{}/post".format(option['host']), data=res) solver_thread = None - return { 'solved': True, 'solution': solution, 'elapsed': elapsed } + + return res def main(): parser = argparse.ArgumentParser(description="Solver with pynqrouter")