Commit a00ebf58 authored by Kento HASEGAWA's avatar Kento HASEGAWA

Merge branch 'comm-server' into 'comm'

Merg comm-server

See merge request adc2018/adc2018-system!6
parents 60fbf844 3712e72e
...@@ -19,3 +19,7 @@ solver ...@@ -19,3 +19,7 @@ solver
# Python files # Python files
*.pyc *.pyc
__pycache__/ __pycache__/
# client-icons
client-icon/
DAS2017 ADC サーバプログラム DAS218 ADC サーバプログラム
=== ===
DAS2017 アルゴリズムデザインコンテスト用サーバプログラム DAS2018 アルゴリズムデザインコンテスト用サーバプログラム
## Description ## Description
...@@ -32,12 +32,6 @@ python3 main.py [--question XXXX] [--port XXXX] [--clients XXXX] ...@@ -32,12 +32,6 @@ python3 main.py [--question XXXX] [--port XXXX] [--clients XXXX]
<dt>-p, --port</dt> <dt>-p, --port</dt>
<dd>サーバのポート (デフォルト:5000)</dd> <dd>サーバのポート (デフォルト:5000)</dd>
<dt>-l, --line-num-th</dt>
<dd>処理を分岐させるライン数の閾値</dd>
<dt>-t, --timeout</dt>
<dd>PYNQで処理させるタイムアウト(秒)</dd>
</dl> </dl>
## Comments ## Comments
......
...@@ -28,6 +28,60 @@ questions = None ...@@ -28,6 +28,60 @@ questions = None
clients = None clients = None
current_seed = 1 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): def update_answer_list(qname):
global app_args global app_args
...@@ -50,65 +104,8 @@ def update_answer_list(qname): ...@@ -50,65 +104,8 @@ def update_answer_list(qname):
@app.before_request @app.before_request
def before_request(): def before_request():
global app_args global app_args
global questions
global clients
if questions is None: g.local_mode = (request.remote_addr == "127.0.0.1")
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]
@app.route("/post", methods=["POST"]) @app.route("/post", methods=["POST"])
def post(): def post():
...@@ -116,18 +113,24 @@ def post(): ...@@ -116,18 +113,24 @@ def post():
global questions global questions
global app_args global app_args
_client = request.form["client"]
_qname = request.form["qname"] _qname = request.form["qname"]
_answer = request.form["answer"] _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 = datetime.datetime.now()
receive_time_str = receive_time.strftime("%Y%m%d%H%M%S") receive_time_str = receive_time.strftime("%Y%m%d%H%M%S")
dat = { dat = {
"req_id": request.form['req_id'],
"client": _client, "client": _client,
"solver": _solver,
"answer": _answer, "answer": _answer,
"cputime": _cputime, "cputime": request.form['cputime'],
"timestamp": receive_time.strftime("%Y/%m/%d %H:%M:%S") "timestamp": receive_time.strftime("%Y/%m/%d %H:%M:%S")
} }
...@@ -149,7 +152,6 @@ def post(): ...@@ -149,7 +152,6 @@ def post():
pattern = r"judge += +\[(.+?), ([0-9.]+)\]" pattern = r"judge += +\[(.+?), ([0-9.]+)\]"
m = re.match(pattern, nlcheck) m = re.match(pattern, nlcheck)
print(nlcheck, m)
if m and (m.group(1) == "True"): if m and (m.group(1) == "True"):
dat["nlcheck"] = float(m.group(2)) dat["nlcheck"] = float(m.group(2))
else: else:
...@@ -192,22 +194,11 @@ def start(): ...@@ -192,22 +194,11 @@ def start():
qstr = "\n".join(_q_lines) 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 = {} res = {}
if line_num >= 0: if line_num >= 0:
# LINE_NUMが閾値未満のとき,PYNQに問題を配信して問題を解かせる # LINE_NUMが閾値未満のとき,PYNQに問題を配信して問題を解かせる
res = solve_questions(_question_name, qstr) 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"] questions[_question_name]["status"] = res["status"]
...@@ -222,22 +213,35 @@ def solve_questions(qname, qstr): ...@@ -222,22 +213,35 @@ def solve_questions(qname, qstr):
global clients global clients
global questions global questions
global current_seed 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) _url = "http://{}/start".format(host)
data = {
"client": host,
"qname": qname,
"question": qstr,
"qseed": qseed,
"req_id": req_id,
"resolver": resolver
}
try: 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) client_res = json.loads(r.text)
except Exception as e: except Exception as e:
sys.stderr.write(str(e) + "\n") sys.stderr.write(str(e) + "\n")
threads = [] threads = []
request_time = time.time() # 時刻をリクエストIDとして設定
req_id = time.time()
questions[qname]['last_req'] = req_id
for c in clients: for c in clients['solver']:
_th = threading.Thread(name=c, target=worker, args=(c, qname, qstr, current_seed, request_time)) # 問題は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() _th.start()
threads.append(_th) threads.append(_th)
current_seed += 1 current_seed += 1
...@@ -246,6 +250,22 @@ def solve_questions(qname, qstr): ...@@ -246,6 +250,22 @@ def solve_questions(qname, qstr):
return res 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"]) @app.route("/status", methods=["POST"])
def get_status(): def get_status():
_client = request.form["client"] _client = request.form["client"]
...@@ -268,8 +288,9 @@ def get_clients(): ...@@ -268,8 +288,9 @@ def get_clients():
res = OrderedDict() res = OrderedDict()
for c in clients: for c in clients['all']:
res[c] = "http://{}".format(c) client_ip = c[0]
res[client_ip] = "http://{}".format(client_ip)
return json.dumps(res) return json.dumps(res)
...@@ -289,7 +310,7 @@ def client_table(): ...@@ -289,7 +310,7 @@ def client_table():
global app_args global app_args
global clients 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") @app.route("/get_question_status")
def question_status(): def question_status():
...@@ -303,33 +324,64 @@ def question_status(): ...@@ -303,33 +324,64 @@ def question_status():
update_answer_list(qname) update_answer_list(qname)
qdata = questions[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("/") @app.route("/")
def index(): def index():
global app_args global app_args
global questions global questions
global clients
question_path = os.path.abspath(app_args["question"]) 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): def main(args):
raise NotImprementedError() 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__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description="PYNQ control panel.") 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("-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("-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("-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.") parser.add_argument("--debug", action="store_true", default=False, help="Debug mode.")
args = vars(parser.parse_args()) args = vars(parser.parse_args())
app_args = args app_args = args
init_system()
if args["debug"]: if args["debug"]:
app.debug = True app.debug = True
app.run(host='0.0.0.0', threaded=True) app.run(host='0.0.0.0', threaded=True)
...@@ -46,9 +46,13 @@ body{ ...@@ -46,9 +46,13 @@ body{
overflow-y: scroll; overflow-y: scroll;
} }
#client-control-pane table th, #client-control-pane table th.large-cell,
#client-control-pane table td{ #client-control-pane table td.large-cell{
width: 50%; width: 40%;
}
#client-control-pane table th.small-cell,
#client-control-pane table td.small-cell{
width: 20%;
} }
#question-control-pane h3{ #question-control-pane h3{
......
...@@ -74,7 +74,31 @@ var PynqManager = (function(){ ...@@ -74,7 +74,31 @@ var PynqManager = (function(){
_p.sendStop = 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(){ ...@@ -101,13 +125,19 @@ $(function(){
$(".question-row").removeClass("q-selected"); $(".question-row").removeClass("q-selected");
$tr.addClass("q-selected"); $tr.addClass("q-selected");
var qname = $tr.data("qname"); var qname = $tr.data("qname");
show_question_status(qname); // show_question_status(qname);
location.hash = "#" + qname;
return false; 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({ $.ajax({
type: "GET", type: "GET",
dataType: "html", dataType: "html",
...@@ -115,16 +145,7 @@ $(function(){ ...@@ -115,16 +145,7 @@ $(function(){
}).done(function(d){ }).done(function(d){
$("#client-control-pane").html(""); $("#client-control-pane").html("");
$("#client-control-pane").html(d); $("#client-control-pane").html(d);
pm.getStatus();
$.ajax({
type: "GET",
dataType: "json",
url: "/get_clients"
}).done(function(d){
pynqClients = d;
pm = PynqManager(pynqClients);
pm.getStatus();
});
}); });
} }
...@@ -142,15 +163,30 @@ $(function(){ ...@@ -142,15 +163,30 @@ $(function(){
var qname = $(this).data("qname"); var qname = $(this).data("qname");
pm.sendQuestion(qname, after=refresh_question_table); 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_question_table();
refresh_client_table();
$("#view-server-status-button").click(function(){ $.ajax({
refresh_client_table(); type: "GET",
return false; dataType: "json",
url: "/get_clients"
}).done(function(d){
pynqClients = d;
pm = PynqManager(pynqClients);
}); });
}); });
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
<div class="row"> <div class="row">
<div class="col-5" id="question-control-pane"> <div class="col-5" id="question-control-pane">
<h3>問題一覧</h3>&nbsp; <h3>問題一覧</h3>&nbsp;
<span><a href="#" id="view-server-status-button">クライアント状況</a></span> <span><a href="/#" id="view-server-status-button">システム状況</a></span>
<div id="question-table-wrapper"> <div id="question-table-wrapper">
<p>Loading...</p> <p>Loading...</p>
</div> </div>
......
<h3>クライアント状況</h3> <h3>システム状況</h3>
<h4>動作モード</h4>
<p>
{% if local_mode %}
Normal Mode
{% else %}
Viewer Mode
{% endif %}
</p>
<h4>クライアント</h4>
<table class="table table-bordered" id="clients-table"> <table class="table table-bordered" id="clients-table">
<tr> <tr>
<th>クライアント名</th> <th class="">Client</th>
<th>ステータス</th> <th class="">Role</th>
<th class="">Status</th>
</tr> </tr>
{% for c in clients %} {% for c in clients %}
<tr class="client-status-row" data-cname="{{c}}"> <tr class="client-status-row" data-cname="{{c[0]}}">
<td class="client-status-name">{{c}}</td> <td class="client-status-name">
{% if c|length > 3 %}
<img src="static/client-icon/{{c[3]}}" alt="{{c[2]}}" height="30" />&nbsp;
{% endif %}
{{c[0]}}
{% if c|length > 2 %}
&nbsp;({{c[2]}})
{% endif %}
</td>
<td class="">{{c[1]}}</td>
<td class="client-status-value"></td> <td class="client-status-value"></td>
</tr> </tr>
{% endfor %} {% endfor %}
......
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-4">
<h3>問題 【{{qname}}】</h3> <h3> 【{{qname}}】</h3>
<p> <p>
<button class="btn btn-primary btn-lg start-button" type="button" data-qname="{{qname}}">Start</button> <button class="btn btn-primary btn-lg start-button" type="button" data-qname="{{qname}}">Start</button>
<button class="btn btn-danger btn-lg stop-button" type="button" data-qname="all">Stop</button>
</p> </p>
</div> </div>
<div class="col-6"> <div class="col-8">
<p>処理結果</p> <p>処理結果</p>
<table class="table table-bordered"> <table class="table table-bordered">
<tr> <tr>
<th>クライアント</th> <th>Client (Solver)</th>
<th>Status</th> <th>Status</th>
</tr> </tr>
{% for c in clients %} {% for c in solvers %}
<tr> <tr>
<td>{{c}}</td> <td>
{% if c|length > 3 %}
<img src="static/client-icon/{{c[3]}}" alt="{{c[2]}}" height="30" />&nbsp;
{% endif %}
{{c[0]}}
{% if c|length > 2 %}
&nbsp;({{c[2]}})
{% endif %}
</td>
<td></td> <td></td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -33,7 +42,7 @@ ...@@ -33,7 +42,7 @@
{% for v in qdata.answers %} {% for v in qdata.answers %}
<tr> <tr>
<td>{{v.timestamp}}</td> <td>{{v.timestamp}}</td>
<td>{{v.client}}</td> <td>{{v.solver}}</td>
<td>{{v.nlcheck}}</td> <td>{{v.nlcheck}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment