实现多进程 epanet 模拟,不保留临时文件
This commit is contained in:
+65
-34
@@ -8,6 +8,7 @@ from datetime import datetime
|
||||
import subprocess
|
||||
import logging
|
||||
from typing import Any
|
||||
import uuid
|
||||
|
||||
sys.path.append("..")
|
||||
from app.native.wndb import project
|
||||
@@ -281,8 +282,6 @@ class Output:
|
||||
def _dump_output(path: str) -> dict[str, Any]:
|
||||
opt = Output(path)
|
||||
data = opt.dump()
|
||||
with open(path + ".json", "w") as f:
|
||||
json.dump(data, f)
|
||||
return data
|
||||
|
||||
|
||||
@@ -302,25 +301,37 @@ def dump_output_binary(path: str) -> str:
|
||||
return str(bast64_data, "utf-8")
|
||||
|
||||
|
||||
def _safe_remove(path: str) -> None:
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
except Exception:
|
||||
logging.warning("failed to remove temp file: %s", path)
|
||||
|
||||
|
||||
def _make_isolated_run_paths(base_name: str, cwd: str) -> tuple[str, str, str]:
|
||||
# 进程号 + UUID 生成唯一后缀,避免并发进程互相覆盖临时文件。
|
||||
token = f"{os.getpid()}_{uuid.uuid4().hex}"
|
||||
inp = os.path.join(cwd, "db_inp", f"{base_name}.db.{token}.inp")
|
||||
rpt = os.path.join(cwd, "temp", f"{base_name}.db.{token}.rpt")
|
||||
opt = os.path.join(cwd, "temp", f"{base_name}.db.{token}.opt")
|
||||
return inp, rpt, opt
|
||||
|
||||
|
||||
# DingZQ, 2025-02-04, 返回dict[str, Any]
|
||||
def run_project_return_dict(name: str, readable_output: bool = False) -> dict[str, Any]:
|
||||
def run_project_return_dict(name: str, readable_output: bool = True) -> dict[str, Any]:
|
||||
if not project.have_project(name):
|
||||
raise Exception(f"Not found project [{name}]")
|
||||
|
||||
dir = os.path.abspath(os.getcwd())
|
||||
cwd = os.path.abspath(os.getcwd())
|
||||
|
||||
db_inp = os.path.join(os.path.join(dir, "db_inp"), name + ".db.inp")
|
||||
inp_out.dump_inp(name, db_inp, "2")
|
||||
inp, rpt, opt = _make_isolated_run_paths(name, cwd)
|
||||
inp_out.dump_inp(name, inp, "2")
|
||||
|
||||
input = name + ".db"
|
||||
if platform.system() == "Windows":
|
||||
exe = os.path.join(os.path.dirname(__file__), "windows", "runepanet.exe")
|
||||
else:
|
||||
exe = os.path.join(os.path.dirname(__file__), "linux", "runepanet")
|
||||
inp = os.path.join(os.path.join(dir, "db_inp"), input + ".inp")
|
||||
rpt = os.path.join(os.path.join(dir, "temp"), input + ".rpt")
|
||||
opt = os.path.join(os.path.join(dir, "temp"), input + ".opt")
|
||||
command = f"{exe} {inp} {rpt} {opt}"
|
||||
|
||||
if platform.system() != "Windows":
|
||||
if not os.access(exe, os.X_OK):
|
||||
@@ -334,8 +345,7 @@ def run_project_return_dict(name: str, readable_output: bool = False) -> dict[st
|
||||
lib_dir = os.path.dirname(exe)
|
||||
env["LD_LIBRARY_PATH"] = f"{lib_dir}:{env.get('LD_LIBRARY_PATH', '')}"
|
||||
|
||||
# 使用 subprocess 替代 os.system 以传递 env
|
||||
process = subprocess.run(command, shell=True, env=env)
|
||||
process = subprocess.run([exe, inp, rpt, opt], env=env)
|
||||
result = process.returncode
|
||||
|
||||
if result != 0:
|
||||
@@ -347,33 +357,36 @@ def run_project_return_dict(name: str, readable_output: bool = False) -> dict[st
|
||||
else:
|
||||
data["output"] = dump_output_binary(opt)
|
||||
|
||||
data["input_file"] = inp
|
||||
data["report_file"] = rpt
|
||||
data["output_file"] = opt
|
||||
data["report"] = dump_report(rpt)
|
||||
|
||||
# 返回内容后删除仿真临时文件,避免临时文件堆积。
|
||||
_safe_remove(inp)
|
||||
_safe_remove(rpt)
|
||||
_safe_remove(opt)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# original code
|
||||
def run_project(name: str, readable_output: bool = False) -> str:
|
||||
def run_project(name: str, readable_output: bool = True) -> str:
|
||||
if not project.have_project(name):
|
||||
raise Exception(f"Not found project [{name}]")
|
||||
|
||||
dir = os.path.abspath(os.getcwd())
|
||||
cwd = os.path.abspath(os.getcwd())
|
||||
|
||||
db_inp = os.path.join(os.path.join(dir, "db_inp"), name + ".db.inp")
|
||||
inp_out.dump_inp(name, db_inp, "2")
|
||||
inp, rpt, opt = _make_isolated_run_paths(name, cwd)
|
||||
inp_out.dump_inp(name, inp, "2")
|
||||
|
||||
input = name + ".db"
|
||||
if platform.system() == "Windows":
|
||||
exe = os.path.join(os.path.dirname(__file__), "windows", "runepanet.exe")
|
||||
else:
|
||||
exe = os.path.join(os.path.dirname(__file__), "linux", "runepanet")
|
||||
inp = os.path.join(os.path.join(dir, "db_inp"), input + ".inp")
|
||||
rpt = os.path.join(os.path.join(dir, "temp"), input + ".rpt")
|
||||
opt = os.path.join(os.path.join(dir, "temp"), input + ".opt")
|
||||
|
||||
command = f"{exe} {inp} {rpt} {opt}"
|
||||
logging.info(f"Run simulation at {datetime.now()}")
|
||||
logging.info(command)
|
||||
logging.info("%s %s %s %s", exe, inp, rpt, opt)
|
||||
|
||||
if platform.system() != "Windows":
|
||||
if not os.access(exe, os.X_OK):
|
||||
@@ -388,7 +401,7 @@ def run_project(name: str, readable_output: bool = False) -> str:
|
||||
env["LD_LIBRARY_PATH"] = f"{lib_dir}:{env.get('LD_LIBRARY_PATH', '')}"
|
||||
|
||||
# DingZQ, 2025-06-02, 使用subprocess替代os.system
|
||||
process = subprocess.run(command, shell=True, env=env)
|
||||
process = subprocess.run([exe, inp, rpt, opt], env=env)
|
||||
result = process.returncode
|
||||
# logging.info(f"Simulation result: {result}")
|
||||
|
||||
@@ -402,27 +415,35 @@ def run_project(name: str, readable_output: bool = False) -> str:
|
||||
logging.info("simulation successful")
|
||||
|
||||
if readable_output:
|
||||
data |= _dump_output(opt)
|
||||
data["output"] = _dump_output(opt)
|
||||
else:
|
||||
data["output"] = dump_output_binary(opt)
|
||||
|
||||
data["input_file"] = inp
|
||||
data["report_file"] = rpt
|
||||
data["output_file"] = opt
|
||||
data["report"] = dump_report(rpt)
|
||||
|
||||
# 返回内容后删除仿真临时文件,避免临时文件堆积。
|
||||
_safe_remove(inp)
|
||||
_safe_remove(rpt)
|
||||
_safe_remove(opt)
|
||||
# logging.info(f"Report: {data['report']}")
|
||||
|
||||
return json.dumps(data)
|
||||
|
||||
|
||||
def run_inp(name: str) -> str:
|
||||
dir = os.path.abspath(os.getcwd())
|
||||
def run_inp(name: str, readable_output: bool = True) -> str:
|
||||
cwd = os.path.abspath(os.getcwd())
|
||||
|
||||
if platform.system() == "Windows":
|
||||
exe = os.path.join(os.path.dirname(__file__), "windows", "runepanet.exe")
|
||||
else:
|
||||
exe = os.path.join(os.path.dirname(__file__), "linux", "runepanet")
|
||||
inp = os.path.join(os.path.join(dir, "inp"), name + ".inp")
|
||||
rpt = os.path.join(os.path.join(dir, "temp"), name + ".rpt")
|
||||
opt = os.path.join(os.path.join(dir, "temp"), name + ".opt")
|
||||
command = f"{exe} {inp} {rpt} {opt}"
|
||||
source_inp = os.path.join(cwd, "inp", name + ".inp")
|
||||
token = f"{os.getpid()}_{uuid.uuid4().hex}"
|
||||
rpt = os.path.join(cwd, "temp", f"{name}.{token}.rpt")
|
||||
opt = os.path.join(cwd, "temp", f"{name}.{token}.opt")
|
||||
|
||||
if platform.system() != "Windows":
|
||||
if not os.access(exe, os.X_OK):
|
||||
@@ -436,16 +457,26 @@ def run_inp(name: str) -> str:
|
||||
lib_dir = os.path.dirname(exe)
|
||||
env["LD_LIBRARY_PATH"] = f"{lib_dir}:{env.get('LD_LIBRARY_PATH', '')}"
|
||||
|
||||
process = subprocess.run(command, shell=True, env=env)
|
||||
process = subprocess.run([exe, source_inp, rpt, opt], env=env)
|
||||
result = process.returncode
|
||||
|
||||
if result != 0:
|
||||
data["simulation_result"] = "failed"
|
||||
else:
|
||||
data["simulation_result"] = "successful"
|
||||
# data |= _dump_output(opt)
|
||||
data["output"] = dump_output_binary(opt)
|
||||
if readable_output:
|
||||
data["output"] = _dump_output(opt)
|
||||
else:
|
||||
data["output"] = dump_output_binary(opt)
|
||||
|
||||
data["input_file"] = source_inp
|
||||
data["report_file"] = rpt
|
||||
data["output_file"] = opt
|
||||
data["report"] = dump_report(rpt)
|
||||
|
||||
# 返回内容后删除仿真临时文件,避免临时文件堆积。
|
||||
_safe_remove(source_inp)
|
||||
_safe_remove(rpt)
|
||||
_safe_remove(opt)
|
||||
|
||||
return json.dumps(data)
|
||||
|
||||
Reference in New Issue
Block a user