Commit a00ebf58 authored by Kento HASEGAWA's avatar Kento HASEGAWA
Browse files

Merge branch 'comm-server' into 'comm'

Merg comm-server

See merge request adc2018/adc2018-system!6
parents 60fbf844 3712e72e
Loading
Loading
Loading
Loading
+4 −0
Changes for .gitignore: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -19,3 +19,7 @@ solver
# Python files
*.pyc
__pycache__/

# client-icons
client-icon/
+2 −8
Changes for comm/server/README.md: 2 added lines, 8 removed lines.
Original line number Diff line number Diff line
DAS2017 ADC サーバプログラム
DAS218 ADC サーバプログラム
===

DAS2017 アルゴリズムデザインコンテスト用サーバプログラム
DAS2018 アルゴリズムデザインコンテスト用サーバプログラム

## Description

@@ -32,12 +32,6 @@ python3 main.py [--question XXXX] [--port XXXX] [--clients XXXX]

    <dt>-p, --port</dt>
    <dd>サーバのポート (デフォルト:5000)</dd>

    <dt>-l, --line-num-th</dt>
    <dd>処理を分岐させるライン数の閾値</dd>

    <dt>-t, --timeout</dt>
    <dd>PYNQで処理させるタイムアウト(秒)</dd>
</dl>

## Comments
+107 −55
Changes for comm/server/main.py: 107 added lines, 55 removed lines.
Original line number Diff line number Diff line
@@ -28,32 +28,10 @@ questions = None
clients = None
current_seed = 1

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():
def load_questions():
    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()
@@ -81,6 +59,7 @@ def before_request():
            "queue": Queue(),
            "board_size": board_size,
            "line_num": line_num,
            "last_req": None,
            "answers": [],
            }

@@ -103,12 +82,30 @@ def before_request():
            except KeyError as e:
                pass

    if clients is None:
def update_answer_list(qname):

        with open(app_args["client"], "r") as fp:
            _clients = fp.readlines()
    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

        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)
+7 −3
Changes for comm/server/static/css/pynq-manager.css: 7 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -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{
+52 −16
Changes for comm/server/static/js/pynq-manager.js: 52 added lines, 16 removed lines.
Original line number Diff line number Diff line
@@ -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,17 +145,8 @@ $(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();
        });
        });
    }

    var show_question_status = function(qname){
@@ -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);
    });

});
Loading