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

Add stoppable solver

parent b68f8105
Loading
Loading
Loading
Loading
+267 −0
Changes for solver/adc2018solver.py: 267 added lines, 0 removed lines.
Original line number Diff line number Diff line
#!/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()