"""試算の結果(results.json)から、公開用の試算ページ(serving-robot.html)を作る(v2.7)。 使い方: python3 build_page_v27.py results.json page_v27.template.html <出力先> --into <公開中の serving-robot.html> v2.7:料理をロボット優先にした(2分待ってもロボットが来なければスタッフが運ぶ)。全条件を回し直した。 --into のとき、const D の1行と見出しの版に加えて、前提の一覧の「配膳の分担」の1行も入れ替える。 v2.6:試算のモデルは v2.4 のまま。店の形を直した版に入れ替え(③⑥⑦のレジの向き・会計の列・⑦の細い通路0.95m)、⑦だけ回し直した。 公開中のページには、後から手で足した「費用の欄」があるので、ページ全体を置き換えず、 作ったページの「const D = …;」の1行と見出しの版だけを公開中のページに入れる(--into 公開中のページ)。 v2.5:試算のモデルは v2.4 のまま、店の形(layouts/)を直した版に入れ替え、② 定番の郊外型だけ回し直した。 店の形の版(generation)がそろっているか、試算に使った卓までの距離が店の形の距離と同じかを確かめてから作る。 results.json は haizen_sim.py(v2.4)を総当たりで回した結果。v2.4 で入店待ちの終わりを卓に着いたときにし、 「下げ膳にも使う」でロボットがスタッフと一緒に卓へ向かい、載せ終えるまで待つ形に直した。 v2.3 で足したのは、標準ファミレスの中の「図面のある店」(fam_p0〜fam_p5)と、その平面図(D.plans)。 平面図は layouts/ の店の形(正は p2m-site の public/sim/layouts/)から、描くのに要る所だけを取り出す。 """ import json, os, sys import haizen_sim as h res_path, tpl_path, out = sys.argv[1], sys.argv[2], sys.argv[3] R = json.load(open(res_path, encoding="utf-8")) keys = ["store", "staff", "robots", "arrivals_per_h", "mode", "trays", "fragile_by", "dish_wait_mean", "dish_wait_p90", "cold_share", "entry_wait_mean", "full_wait_mean", "staff_wait_mean", "turn_mean", "walk_km_per_staff", "staff_util", "robot_util", "robot_share", "robot_bus_share", "collects", "rescues", "pauses", "fragile_robot", "groups"] rows = [] for r in R["rows"]: row = [round(r[k], 3) if i >= 7 else r[k] for i, k in enumerate(keys)] row.append(1 if r["staff_wait_mean"] >= h.NG_MIN else 0) # 丸める前の値で決める rows.append(row) # 図面のある店:画面で使う名前と平面図 stores = R["stores"] plans = {} q = lambda a: [round(v, 2) for v in a] for k, s in stores.items(): if not s.get("layout"): continue s["short"] = s["label"] s["label"] = f"標準ファミレス({s['short']})" s["dist_mean"] = round(sum(s["dist_list"]) / len(s["dist_list"]), 1) s.pop("dist_list", None) # ページには要らない(試算に使った距離は results.json と layouts/ にある) f = [x for x in os.listdir(h.LAYOUT_DIR) if x.startswith(s["layout"] + "_") and x.endswith(".json") and "_analysis" not in x][0] L = json.load(open(os.path.join(h.LAYOUT_DIR, f), encoding="utf-8"))["layout"] W, Dd = L["outer"]["w"], L["outer"]["d"] plans[k] = { "w": W, "d": Dd, "outline": L.get("outline") or [[0, 0], [W, 0], [W, Dd], [0, Dd]], "rooms": [q(r["rect"]) + [r["kind"], r["label"]] for r in L["rooms"] if r["kind"] != "outside"], "walls": [q(w["rect"]) for w in L["walls"]], "tables": [q([t["x"], t["y"], t["x"] + t["w"], t["y"] + t["h"]]) + [t["seats"]] for t in L["tables"]], "dish": [q(d["rect"]) for d in L["dish_up"]], "standby": q(L["robot_standby"]["rect"]), "ent": [L["entrance"]["side"], L["entrance"]["a0"], L["entrance"]["a1"]], } # 店の形の版がそろっているか(layouts/ の summary・layout・analysis の generation が全部同じ)と、 # 試算に使った卓までの距離が、いまの店の形の距離と同じか gens = {g["generation"] for g in json.load(open(os.path.join(h.LAYOUT_DIR, "summary.json"), encoding="utf-8"))} for f in os.listdir(h.LAYOUT_DIR): if f.endswith(".json") and f != "summary.json": gens.add(json.load(open(os.path.join(h.LAYOUT_DIR, f), encoding="utf-8"))["meta"]["generation"]) if len(gens) != 1: sys.exit(f"店の形の版がそろっていない: {gens}") for f in os.listdir(h.LAYOUT_DIR): if f.endswith("_analysis.json"): nm = "fam_" + f.split("_")[0] dists = [t["dist"] for t in json.load(open(os.path.join(h.LAYOUT_DIR, f), encoding="utf-8"))["tables"]] if json.load(open(res_path, encoding="utf-8"))["stores"][nm]["dist_list"] != dists: sys.exit(f"{nm}: 試算に使った距離が、いまの店の形と違う(試算を回し直す)") layout_gen = gens.pop() D = {"layout_generation": layout_gen, "k": keys + ["ng"], "r": rows, "stores": stores, "common": R["common"], "plans": plans, "robots": R["robots"], "robots_all": R["robots_all"], "ng_min": h.NG_MIN} html = open(tpl_path, encoding="utf-8").read().replace("__DATA__", json.dumps(D, ensure_ascii=False, separators=(",", ":"))) if "--into" in sys.argv: # 公開中のページの const D の1行と見出しの版だけを入れ替える(費用の欄などは残す) live = open(sys.argv[sys.argv.index("--into") + 1], encoding="utf-8").read() new_d = [l for l in html.split("\n") if l.startswith("const D = ")] old_d = [l for l in live.split("\n") if l.startswith("const D = ")] assert len(new_d) == 1 and len(old_d) == 1, "const D の行が1つでない" import re ver = re.search(r"試作 v2\.\d+", html).group(0) assert len(re.findall(r"試作 v2\.\d+", live)) == 1, "見出しの版が1つでない" html = re.sub(r"試作 v2\.\d+", ver, live.replace(old_d[0], new_d[0])) split_line = lambda t: [l for l in t.split("\n") if l.strip().startswith('["配膳の分担"')] new_l, old_l = split_line(open(tpl_path, encoding="utf-8").read()), split_line(html) assert len(new_l) == 1 and len(old_l) == 1, "配膳の分担の行が1つでない" html = html.replace(old_l[0], new_l[0]) open(out, "w", encoding="utf-8").write(html) print(len(html), "bytes /", len(rows), "conditions / 店が回らない", sum(r[-1] for r in rows), "/ 店の形の版", layout_gen)