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 @@
問題一覧
-
クライアント状況
+
システム状況
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 %}
+
+
+
クライアント
- クライアント名 |
- ステータス |
+ Client |
+ Role |
+ Status |
{% for c in clients %}
-
- {{c}} |
+
+
+ {% if c|length > 3 %}
+
+ {% endif %}
+ {{c[0]}}
+ {% if c|length > 2 %}
+ ({{c[2]}})
+ {% endif %}
+ |
+ {{c[1]}} |
|
{% 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}}】
+
-
+
処理結果
- クライアント |
+ Client (Solver) |
Status |
- {% for c in clients %}
+ {% for c in solvers %}
- {{c}} |
+
+ {% if c|length > 3 %}
+
+ {% endif %}
+ {{c[0]}}
+ {% if c|length > 2 %}
+ ({{c[2]}})
+ {% endif %}
+ |
|
{% endfor %}
@@ -33,7 +42,7 @@
{% for v in qdata.answers %}
{{v.timestamp}} |
- {{v.client}} |
+ {{v.solver}} |
{{v.nlcheck}} |
{% endfor %}