实现多进程 epanet 模拟,不保留临时文件

This commit is contained in:
2026-03-18 16:56:44 +08:00
parent c5d3075ae2
commit 7c44654195
6 changed files with 114 additions and 127 deletions
+65 -34
View File
@@ -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)