#!/usr/bin/env python3 """ This script provides a PYNQ control panel. This is intended to run on the host server (Raspberry Pi). """ import argparse import datetime import glob import json import os import re import requests import subprocess import sys import threading import time from collections import OrderedDict from flask import Flask, render_template, request, g from queue import Queue sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../solver') import BoardStr app = Flask(__name__) app_args = {} 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, "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 global questions questions[qname]['answers'] = [] _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 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"] receive_time = datetime.datetime.now() receive_time_str = receive_time.strftime("%Y%m%d%H%M%S") dat = { "client": _client, "answer": _answer, "cputime": _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) print(nlcheck, m) if m and (m.group(1) == "True"): dat["nlcheck"] = float(m.group(2)) 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) 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"} return json.dumps(res) @app.route("/start", methods=["POST"]) def start(): global app_args global questions global current_seed _question_name = request.form["qname"] _q = questions[_question_name] questions[_question_name]["status"] = "Processing" with open(_q["path"], "r") as fp: _q_lines = fp.readlines() line_num = -1 for l in _q_lines: if "LINE_NUM" in l: line_num = int(l.strip().split()[1]) break 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"] 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, request_time): _url = "http://{}/start".format(host) try: r = requests.post(_url, data={"client": host, "qname": qname, "question": qstr, "qseed": qseed, "request_time": request_time}) client_res = json.loads(r.text) except Exception as e: sys.stderr.write(str(e) + "\n") threads = [] request_time = time.time() for c in clients: client_addr = c[0] _th = threading.Thread(name=client_addr, target=worker, args=(client_addr, qname, qstr, current_seed, request_time)) _th.start() threads.append(_th) current_seed += 1 res = {"status": "Processed"} return res @app.route("/status", methods=["POST"]) def get_status(): _client = request.form["client"] _url = _client + "/status" 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("/get_clients") def get_clients(): global clients res = OrderedDict() for c in clients: client_ip = c[0] res[client_ip] = "http://{}".format(client_ip) return json.dumps(res) @app.route("/get_question_table") def question_table(): global app_args global questions question_path = os.path.abspath(app_args["question"]) return render_template("part_question_table.html", questions=questions, question_path=question_path) @app.route("/get_client_table") def client_table(): global app_args global clients return render_template("part_client_table.html", clients=clients, 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, clients=clients) @app.route("/") def index(): global app_args global questions global clients question_path = os.path.abspath(app_args["question"]) print(g.local_mode) return render_template("index.html", questions=questions, question_path=question_path, clients=clients) 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() clients = [v.rstrip().split() for v in _clients] 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("--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)