Commit ca2e8c1b authored by Kento HASEGAWA's avatar Kento HASEGAWA

Merge branch 'comm' into 'master'

Add support for resolver and improve UI

See merge request adc2018/adc2018-system!9
parents 4cffa912 5e4c621e
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
.DS_Store .DS_Store
*.txt *.txt
*.tmp
*.json
# Object files # Object files
*.o *.o
...@@ -17,3 +19,7 @@ solver ...@@ -17,3 +19,7 @@ solver
# Python files # Python files
*.pyc *.pyc
__pycache__/ __pycache__/
# client-icons
client-icon/
...@@ -21,7 +21,6 @@ import adc2018solver as pynqrouter ...@@ -21,7 +21,6 @@ import adc2018solver as pynqrouter
app = Flask(__name__) app = Flask(__name__)
args = {} args = {}
# pynq_thread = None
client_baseurl = "" client_baseurl = ""
@app.route('/start', methods=["POST"]) @app.route('/start', methods=["POST"])
...@@ -45,10 +44,15 @@ def start(): ...@@ -45,10 +44,15 @@ def start():
qseed = request.form["qseed"] qseed = request.form["qseed"]
boardstr = BoardStr.conv_boardstr(qstr.split('\n'), 'random', int(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) pynqrouter.start_solver(boardstr, qseed, option)
# pynq_thread = pynqrouter.start_solver(boardstr, qseed, option)
# 実行だけ開始する # 実行だけ開始する
ans["status"] = "Processing" ans["status"] = "Processing"
...@@ -64,8 +68,6 @@ def start(): ...@@ -64,8 +68,6 @@ def start():
@app.route('/stop') @app.route('/stop')
def stop(): def stop():
# global pynq_thread
if pynqrouter.solver_thread is None: if pynqrouter.solver_thread is None:
ans = {"status": "No threads"} ans = {"status": "No threads"}
else: else:
...@@ -73,15 +75,11 @@ def stop(): ...@@ -73,15 +75,11 @@ def stop():
pynqrouter.stop_solver() pynqrouter.stop_solver()
ans = {"status": "Stopped"} ans = {"status": "Stopped"}
# pynq_thread = None
return json.dumps(ans) return json.dumps(ans)
@app.route("/status") @app.route("/status")
def status(): def status():
# global pynq_thread
res_mes = "" res_mes = ""
if pynqrouter.solver_thread is None: if pynqrouter.solver_thread is None:
...@@ -121,3 +119,4 @@ if __name__ == "__main__": ...@@ -121,3 +119,4 @@ if __name__ == "__main__":
if args["debug"]: if args["debug"]:
app.debug = True app.debug = True
app.run(host='0.0.0.0', port=args["port"], threaded=True) app.run(host='0.0.0.0', port=args["port"], threaded=True)
DAS2018 ADC クライアント(リゾルバ)プログラム
===
DAS2018 アルゴリズムデザインコンテスト用クライアントプログラム
## Description
解答データをソルバから受信し,整形した結果をサーバへ返すプログラム.
## Requirements
- Python 3.5以上(くらい)
- Flask
## Usage
```
python3 main.py [--port XXXX] [--host XXXX]
```
### Options
<dl>
<dt>-H, --host</dt>
<dd>サーバホストのアドレス (デフォルト:192.168.5.1:5000)</dd>
<dt>-p, --port</dt>
<dd>使用するポート (デフォルト:5000)</dd>
</dl>
#!/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)
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
......
This diff is collapsed.
...@@ -43,12 +43,16 @@ body{ ...@@ -43,12 +43,16 @@ body{
#client-control-pane{ #client-control-pane{
height: 330px; height: 330px;
overflow: 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{
......
...@@ -63,7 +63,6 @@ var PynqManager = (function(){ ...@@ -63,7 +63,6 @@ var PynqManager = (function(){
}).done(function(res){ }).done(function(res){
var answer = res; var answer = res;
$("#solving-question-status").text(answer["status"]); $("#solving-question-status").text(answer["status"]);
$("#solved-result").text(answer["answer"]["answer"]);
$("#solved-client").text(answer["answer"]["client"]); $("#solved-client").text(answer["answer"]["client"]);
if (after !== null){ if (after !== null){
...@@ -75,7 +74,31 @@ var PynqManager = (function(){ ...@@ -75,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]);
}
} }
...@@ -102,13 +125,19 @@ $(function(){ ...@@ -102,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",
...@@ -116,16 +145,7 @@ $(function(){ ...@@ -116,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();
});
}); });
} }
...@@ -143,15 +163,30 @@ $(function(){ ...@@ -143,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 %}
......
<h3>問題 【{{qname}}】</h3> <div class="row">
<p> <div class="col-4">
<button class="btn btn-primary btn-lg start-button" type="button" data-qname="{{qname}}">Start</button> <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-danger btn-lg stop-button" type="button" data-qname="all">Stop</button>
</p>
</div>
<div class="col-8">
<p>処理結果</p>
<table class="table table-bordered">
<tr>
<th>Client (Solver)</th>
<th>Status</th>
</tr>
{% for c in solvers %}
<tr>
<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>
</tr>
{% endfor %}
</table>
</div>
</div>
<div>
<h4>処理結果一覧</h4>
<table class="table table-bordered table-striped">
<tr>
<th>Timestamp</th>
<th>Client</th>
<th>Score</th>
</tr>
{% for v in qdata.answers %}
<tr>
<td>{{v.timestamp}}</td>
<td>{{v.solver}}</td>
<td>{{v.nlcheck}}</td>
</tr>
{% endfor %}
</table>
</div>
<p>状況:<span id="solving-question-status">{{qdata.status}}</span></p> <p>状況:<span id="solving-question-status">{{qdata.status}}</span></p>
<h4>結果</h4> <h4>結果</h4>
<p>クライアント:<span id="solved-client">{{qdata.answer.client}}</span></p> <p>クライアント:<span id="solved-client">{{qdata.answer.client}}</span></p>
......
...@@ -78,6 +78,15 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): ...@@ -78,6 +78,15 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()):
print(seed) print(seed)
print('') print('')
res = {
'client': option['client'],
'qname': option['name'],
'solution': '',
'cputime': -1.0,
'req_id': option['req_id'],
'solved': False
}
# LINE数を数えてコンフィグするbitstreamを分岐 # LINE数を数えてコンフィグするbitstreamを分岐
line_num = boardstr.count('L') line_num = boardstr.count('L')
if line_num < 127: if line_num < 127:
...@@ -89,7 +98,9 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): ...@@ -89,7 +98,9 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()):
else: else:
solver_thread.stopped() solver_thread.stopped()
solver_thread = None 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 を読んでくる # ボード文字列から X, Y, Z を読んでくる
size_x = (ord(boardstr[1]) - ord('0')) * 10 + (ord(boardstr[2]) - ord('0')) 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()): ...@@ -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()): if (solver_thread is not None) and (not solver_thread.is_running()):
solver_thread.stopped() solver_thread.stopped()
solver_thread = None 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!') print('Done!')
...@@ -154,11 +167,11 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): ...@@ -154,11 +167,11 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()):
# 解けなかったらLEDを消す # 解けなかったらLEDを消す
mmio_led.write(0, 0) mmio_led.write(0, 0)
sys.stderr.write('Cannot solve it!\n') sys.stderr.write('Cannot solve it!\n')
res = {'client': option['client'], 'qname': option['name'], 'answer': '', 'cputime': -1}
if "host" in option: if "host" in option:
requests.post("http://{}/post".format(option['host']), data=res) requests.post("http://{}/post".format(option['host']), data=res)
solver_thread = None solver_thread = None
return { 'solved': False, 'solution': '', 'elapsed': -1.0 } return res
print('Solved!') print('Solved!')
# 解けたらLEDを全部つける # 解けたらLEDを全部つける
...@@ -185,13 +198,24 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()): ...@@ -185,13 +198,24 @@ def solve(boardstr, seed=12345, zero_padding=False, option=dict()):
solution += str(boards[i]) # 普通に表示 solution += str(boards[i]) # 普通に表示
solution += '\n' 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: if solver_thread is not None:
solver_thread.stopped() 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 solver_thread = None
return { 'solved': True, 'solution': solution, 'elapsed': elapsed }
return res
def main(): def main():
parser = argparse.ArgumentParser(description="Solver with pynqrouter") parser = argparse.ArgumentParser(description="Solver with pynqrouter")
......
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