diff --git a/.gitignore b/.gitignore index 0ebe4bcbc542cd5011608aebbb1eb8f08911c764..d4eb4b6c035cba27c21db393dd75b8ad8a5da0b5 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ solver # Python files *.pyc __pycache__/ + +# client-icons +client-icon/ + 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 56d07ec5c7b949cd3c74bb7fe87701e70fcde47b..b151ca3a2cd22e48c589d1144291645041286555 100644 --- a/comm/server/main.py +++ b/comm/server/main.py @@ -28,6 +28,60 @@ questions = None clients = None current_seed = 1 +def load_questions(): + global app_args + global questions + + 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 + def update_answer_list(qname): global app_args @@ -50,65 +104,8 @@ def update_answer_list(qname): @app.before_request def before_request(): 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() - - 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, - "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 - - if clients is None: - - with open(app_args["client"], "r") as fp: - _clients = fp.readlines() - - clients = [v.rstrip() for v in _clients] + g.local_mode = (request.remote_addr == "127.0.0.1") @app.route("/post", methods=["POST"]) def post(): @@ -116,18 +113,24 @@ 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": _cputime, + "cputime": request.form['cputime'], "timestamp": receive_time.strftime("%Y/%m/%d %H:%M:%S") } @@ -149,7 +152,6 @@ def post(): pattern = r"judge += +\[(.+?), ([0-9.]+)\]" m = re.match(pattern, nlcheck) - print(nlcheck, m) if m and (m.group(1) == "True"): dat["nlcheck"] = float(m.group(2)) else: @@ -192,22 +194,11 @@ 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) - outpath = "{}/T03_A{}.txt".format(app_args["out"], qnumber) - res = {} if line_num >= 0: # LINE_NUMが閾値未満のとき,PYNQに問題を配信して問題を解かせる res = solve_questions(_question_name, qstr) - # 整形ルーティング (再配線) する - #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(" ")) - # 最終結果だけを保存 questions[_question_name]["status"] = res["status"] @@ -222,22 +213,35 @@ def solve_questions(qname, qstr): global clients global questions global current_seed - global app_args - def worker(host, qname, qstr, qseed, request_time): + 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, "request_time": request_time}) + r = requests.post(_url, data=data) client_res = json.loads(r.text) except Exception as e: sys.stderr.write(str(e) + "\n") threads = [] - request_time = time.time() + # 時刻をリクエストIDとして設定 + req_id = time.time() + questions[qname]['last_req'] = req_id - for c in clients: - _th = threading.Thread(name=c, target=worker, args=(c, qname, qstr, current_seed, request_time)) + 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 @@ -246,6 +250,22 @@ def solve_questions(qname, qstr): return res +@app.route("/stop", methods=["POST"]) +def stop(): + _client = request.form["client"] + _url = _client + "/stop" + + 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") + + res = {"status": client_res} + + return json.dumps(res) + @app.route("/status", methods=["POST"]) def get_status(): _client = request.form["client"] @@ -268,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) @@ -289,7 +310,7 @@ 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(): @@ -303,33 +324,64 @@ def question_status(): update_answer_list(qname) qdata = questions[qname] - return render_template("part_question_status.html", qname=qname, qdata=qdata, clients=clients) + 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("-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 dd2cd54eb79a785ef5034aa5f195275997b9d3fc..4ef3532a257a1d441b833a5441b583fb907aa4e7 100644 --- a/comm/server/static/css/pynq-manager.css +++ b/comm/server/static/css/pynq-manager.css @@ -46,9 +46,13 @@ body{ 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 82deab0ce4d0088c3a9dc0b48732de4dbf8f5611..903fac2d808810ba4d8788e2bebb1c6fc3f0a606 100644 --- a/comm/server/static/js/pynq-manager.js +++ b/comm/server/static/js/pynq-manager.js @@ -74,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]); + } } @@ -101,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", @@ -115,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(); }); } @@ -142,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 f669e445c573018a8cf8e874d1b09529f129a161..e46a98dccbec283024c7d703e347f519dd76ff9d 100644 --- a/comm/server/templates/part_question_status.html +++ b/comm/server/templates/part_question_status.html @@ -1,20 +1,29 @@
-
-

問題 【{{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 clients %} + {% for c in solvers %} - + {% endfor %} @@ -33,7 +42,7 @@ {% for v in qdata.answers %} - + {% endfor %}
クライアントClient (Solver) Status
{{c}} + {% if c|length > 3 %} + {{c[2]}}  + {% endif %} + {{c[0]}} + {% if c|length > 2 %} +  ({{c[2]}}) + {% endif %} +
{{v.timestamp}}{{v.client}}{{v.solver}} {{v.nlcheck}}