diff --git a/comm/client/main.py b/comm/client/main.py index d44b3254878421ea5c49c83e1c6c9810121af462..022c5a3b29b80962ebfe255908caa3816fe8d646 100644 --- a/comm/client/main.py +++ b/comm/client/main.py @@ -17,65 +17,17 @@ from urllib.parse import urlparse sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../solver') import BoardStr -import pynqrouter +import adc2018solver as pynqrouter app = Flask(__name__) args = {} -pynq_thread = None +# pynq_thread = None client_baseurl = "" -# Reference: https://teratail.com/questions/52593 -class StoppableThread(threading.Thread): - def __init__(self, target, qname, qstr, qseed): - super(StoppableThread, self).__init__(target=target) - self._th_stop = threading.Event() - self._qname = qname - self._qstr = qstr - self._qseed = int(qseed) - self._answer = None - - def run(self): - global client_baseurl - global pynq_thread - global args - - # Main funciton (the solver should be placed here) - boardstr = BoardStr.conv_boardstr(self._qstr.split('\n'), 'random', self._qseed) - result = pynqrouter.solve(boardstr, self._qseed) - if result['solved']: - answer = result['solution'] - else: - answer = None - pynq_thread = None - return - - res = { - "client": client_baseurl, - "qname": self._qname, - "answer": answer, - "cputime": result['elapsed'] - } - self._answer = answer - r = requests.post("http://{}/post".format(args["host"]), data=res) - - if args["verbose"]: - print(res) - - pynq_thread = None - - def stop(self): - self._th_stop.set() - - def stopped(self): - return self._th_stop.isSet() - - def get_answer(self): - return self._answer - @app.route('/start', methods=["POST"]) def start(): - global pynq_thread + # global pynq_thread global args global client_baseurl @@ -84,13 +36,19 @@ def start(): if args["verbose"]: print(request.form) - if pynq_thread is None: + if pynqrouter.solver_thread is not None: + pynqrouter.stop_solver() + + if pynqrouter.solver_thread is None: qstr = request.form["question"] qname = request.form["qname"] qseed = request.form["qseed"] - pynq_thread = StoppableThread(target=StoppableThread, qname=qname, qstr=qstr, qseed=qseed) + + boardstr = BoardStr.conv_boardstr(qstr.split('\n'), 'random', int(qseed)) + option = {"name": qname, "host": args['host'], "client": client_baseurl} - pynq_thread.start() + pynqrouter.start_solver(boardstr, qseed, option) + # pynq_thread = pynqrouter.start_solver(boardstr, qseed, option) # 実行だけ開始する ans["status"] = "Processing" @@ -106,26 +64,27 @@ def start(): @app.route('/stop') def stop(): - global pynq_thread + # global pynq_thread - if pynq_thread is None: + if pynqrouter.solver_thread is None: ans = {"status": "No threads"} else: - pynq_thread.stop() + # pynq_thread.stop() + pynqrouter.stop_solver() ans = {"status": "Stopped"} - pynq_thread = None + # pynq_thread = None return json.dumps(ans) @app.route("/status") def status(): - global pynq_thread + # global pynq_thread res_mes = "" - if pynq_thread is None: + if pynqrouter.solver_thread is None: res_mes = "Ready" else: res_mes = "Working" diff --git a/solver/adc2018solver.py b/solver/adc2018solver.py new file mode 100644 index 0000000000000000000000000000000000000000..faabcaa77a6f62012b022732faed39936cb3f3ff --- /dev/null +++ b/solver/adc2018solver.py @@ -0,0 +1,267 @@ +#!/opt/python3.6/bin/python3.6 +# -*- coding: utf-8 -*- + +""" +PYNQ 上で pynqrouter を実行する。 +""" + +from pynq import Overlay +from pynq import PL +from pynq import MMIO + +import argparse +import requests +import sys +import time +import threading + +import BoardStr + + +# Settings -- pynqrouter +IP = 'SEG_pynqrouter_0_Reg' +OFFSET_BOARD = 65536 # 0x10000 ~ 0x1ffff +OFFSET_SEED = 131072 # 0x20000 +OFFSET_STATUS = 131080 # 0x20008 +MAX_X = 72 +MAX_Y = 72 +MAX_Z = 8 +BITWIDTH_Z = 3 +# Settings -- LED +IP_LED = 'SEG_axi_gpio_0_Reg' + +solver_thread = None + +class StoppableThread(threading.Thread): + + def __init__(self, target, args=()): + super(StoppableThread, self).__init__(target=target, args=args) + self._status = 'running' + def stop(self): + if self._status == 'running': + self._status = 'stopping' + def stopped(self): + self._status = 'stopped' + def is_running(self): + return (self._status == 'running') + def is_stopping(self): + return (self._status == 'stopping') + def is_stopped(self): + return (self._status == 'stopped') + +def start_solver(boardstr, seed, option): + global solver_thread + + if solver_thread is None: + solver_thread = StoppableThread(target=solve, args=(boardstr, int(seed), False, option)) + solver_thread.start() + +def stop_solver(): + global solver_thread + + if solver_thread is not None: + if solver_thread.is_running(): + solver_thread.stop() + solver_thread.join() + solver_thread = None + +def solve(boardstr, seed=12345, zero_padding=False, option=dict()): + global solver_thread + + print('boardstr:') + print(boardstr) + print('seed:') + print(seed) + print('') + + # ボード文字列から X, Y, Z を読んでくる + size_x = (ord(boardstr[1]) - ord('0')) * 10 + (ord(boardstr[2]) - ord('0')) + size_y = (ord(boardstr[4]) - ord('0')) * 10 + (ord(boardstr[5]) - ord('0')) + size_z = (ord(boardstr[7]) - ord('0')) + + # Overlay 読み込み + OL = Overlay('pynqrouter_lines128_length256.bit') + OL.download() + print(OL.ip_dict) + print('Overlay loaded!') + + # MMIO 接続 (pynqrouter) + mmio = MMIO(int(PL.ip_dict[IP][0], 16), int(PL.ip_dict[IP][1], 16)) + + # MMIO 接続 & リセット (LED) + mmio_led = MMIO(int(PL.ip_dict[IP_LED][0], 16), int(PL.ip_dict[IP_LED][1], 16)) + mmio_led.write(0, 0) + + # 入力データをセット + imem = pack(boardstr) + for i in range(len(imem)): + mmio.write(OFFSET_BOARD + (i * 4), imem[i]) + + mmio.write(OFFSET_SEED, seed) + + # スタート + # ap_start (0 番地の 1 ビット目 = 1) + mmio.write(0, 1) + print('Start!') + time_start = time.time() + + # ap_done (0 番地の 2 ビット目 = 2) が立つまで待ってもいいが + # done は一瞬だけ立つだけのことがあるから + # ap_idle (0 番地の 3 ビット目 = 4) を待ったほうが良い + iteration = 0 + while (mmio.read(0) & 4) == 0: + # 動いてるっぽく見えるようにLチカさせる + iteration += 1 + if iteration == 10000: + mmio_led.write(0, 3) + elif 20000 <= iteration: + mmio_led.write(0, 12) + iteration = 0 + if (solver_thread is not None) and (not solver_thread.is_running()): + solver_thread.stopped() + solver_thread = None + return { 'solved': False, 'solution': '', 'elapsed': -1.0 } + + # 完了の確認 + print('Done!') + print('control:', mmio.read(0)) + time_done = time.time() + elapsed = time_done - time_start + print('elapsed:', elapsed) + print('') + + # 状態の取得 + status = int(mmio.read(OFFSET_STATUS)) + print('status:', status) + if status != 0: + # 解けなかったらLEDを消す + mmio_led.write(0, 0) + sys.stderr.write('Cannot solve it!\n') + res = {'client': option['client'], 'qname': option['name'], 'answer': '', 'cputime': -1} + if "host" in option: + requests.post("http://{}/post".format(option['host']), data=res) + solver_thread = None + return { 'solved': False, 'solution': '', 'elapsed': -1.0 } + print('Solved!') + + # 解けたらLEDを全部つける + mmio_led.write(0, 15) + + # 出力 + omem = [] + for i in range(len(imem)): + omem.append(mmio.read(OFFSET_BOARD + (i * 4))) + boards = unpack(omem) + + # 回答の生成 + solution = ('SIZE ' + str(size_x) + 'X' + str(size_y) + 'X' + str(size_z) + '\n') + for z in range(size_z): + solution += ('LAYER ' + str(z + 1) + '\n') + for y in range(size_y): + for x in range(size_x): + if x != 0: + solution += ',' + i = ((x * MAX_X + y) << BITWIDTH_Z) | z + if zero_padding: + solution += '{0:0>2}'.format(boards[i]) # 2桁の0詰め + else: + solution += str(boards[i]) # 普通に表示 + solution += '\n' + + if solver_thread is not None: + 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 + return { 'solved': True, 'solution': solution, 'elapsed': elapsed } + +def main(): + parser = argparse.ArgumentParser(description="Solver with pynqrouter") + + # 入出力 + parser.add_argument('-i', '--input', action='store', nargs='?', default=None, type=str, + help='Path to input problem file') + parser.add_argument('-b', '--boardstr', action='store', nargs='?', default=None, type=str, + help='Problem boardstr (if you want to solve directly)') + parser.add_argument('-o', '--output', action='store', nargs='?', default=None, type=str, + help='Path to output answer file') + + # ソルバオプション + parser.add_argument('-t', '--terminals', action='store', nargs='?', default='initial', type=str, + help='Terminals order (initial, edgefirst, or random) (default: initial)') + parser.add_argument('-s', '--seed', action='store', nargs='?', default=12345, type=int, + help='Random seed') + + # 出力オプション + parser.add_argument('-p', '--print', action='store_true', default=False, + help='Enable to print the solution') + parser.add_argument('-z', '--zero-padding', action='store_true', default=False, + help='Enable to do zero-padding') + + args = parser.parse_args() + + if args.input is not None: + # 問題ファイルの読み込み + with open(args.input, 'r') as f: + lines = f.readlines() + + # 問題ファイルを boardstr に変換 + boardstr = BoardStr.conv_boardstr(lines, args.terminals, args.seed) + + elif args.boardstr is not None: + boardstr = args.boardstr + + else: + sys.stderr.write('Specify at least "input" or "boardstr"!\n') + sys.exit(1) + + result = solve(boardstr, args.seed) + if result['solved'] == False: + sys.exit(1) + + # 表示 & ファイル出力 + if args.print: + print('') + print('SOLUTION') + print('========') + print(result['solution']) + if args.output is not None: + with open(args.output, 'w') as f: + f.write(result['solution']) + + +def pack(_str): + """ + ボードストリング文字列をMMIOメモリにパックする + """ + _mem = [ 0 for _ in range(MAX_X * MAX_Y * MAX_Z // 4) ] + for i in range(len(_str)): + _index = i // 4; + _offset = i % 4; + if _offset == 0: + _mem[_index] = _mem[_index] | (ord(_str[i])) + elif _offset == 1: + _mem[_index] = _mem[_index] | (ord(_str[i]) << 8) + elif _offset == 2: + _mem[_index] = _mem[_index] | (ord(_str[i]) << 16) + elif _offset == 3: + _mem[_index] = _mem[_index] | (ord(_str[i]) << 24) + return _mem + + +def unpack(_mem): + """ + MMIOメモリからボード情報をアンパックする + """ + _boards = [ 0 for _ in range(MAX_X * MAX_Y * MAX_Z) ] + for i in range(len(_mem)): + _boards[4 * i ] = (_mem[i]) & 255 + _boards[4 * i + 1] = (_mem[i] >> 8) & 255 + _boards[4 * i + 2] = (_mem[i] >> 16) & 255 + _boards[4 * i + 3] = (_mem[i] >> 24) & 255 + return _boards + + +if __name__ == '__main__': + main()