refactor(db)!: adopt project-routed pooled databases
Reorganize WNDB by responsibility and remove legacy scheme endpoints.\n\nRoute analysis and time-series access through project pools, preserve transactional realtime replacement, and refresh GIS materialized views after writes.\n\nAdd database architecture documentation, live pooling coverage, API contract updates, and executable container verification.\n\nBREAKING CHANGE: legacy scheme APIs and flat app.native.wndb module imports are removed.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"""EPANET INP import, export, and section mapping."""
|
||||
@@ -0,0 +1,319 @@
|
||||
import os
|
||||
|
||||
from ..core.projects import close_project, have_project, is_project_open, open_project
|
||||
from ..core.database import ChangeSet
|
||||
from .sections import (
|
||||
BACKDROP,
|
||||
CONTROLS,
|
||||
COORDINATES,
|
||||
CURVES,
|
||||
DEMANDS,
|
||||
EMITTERS,
|
||||
END,
|
||||
ENERGY,
|
||||
JUNCTIONS,
|
||||
LABELS,
|
||||
MIXING,
|
||||
OPTIONS,
|
||||
PATTERNS,
|
||||
PIPES,
|
||||
PUMPS,
|
||||
QUALITY,
|
||||
REACTIONS,
|
||||
REPORT,
|
||||
RESERVOIRS,
|
||||
RULES,
|
||||
SOURCES,
|
||||
STATUS,
|
||||
TAGS,
|
||||
TANKS,
|
||||
TIMES,
|
||||
TITLE,
|
||||
VALVES,
|
||||
VERTICES,
|
||||
section_name,
|
||||
section_names_for_epanetv2,
|
||||
)
|
||||
from ..model.title import inp_out_title
|
||||
from ..model.junctions import inp_out_junction
|
||||
from ..model.reservoirs import inp_out_reservoir
|
||||
from ..model.tanks import inp_out_tank
|
||||
from ..model.pipes import inp_out_pipe
|
||||
from ..model.pumps import inp_out_pump
|
||||
from ..model.valves import inp_out_valve
|
||||
from ..model.tags import inp_out_tag
|
||||
from ..model.demands import inp_out_demand
|
||||
from ..model.status import inp_out_status
|
||||
from ..model.patterns import inp_out_pattern, inp_out_pattern_v3
|
||||
from ..model.curves import inp_out_curve, inp_out_curve_v3
|
||||
from ..model.controls import inp_out_control
|
||||
from ..model.rules import inp_out_rule
|
||||
from ..model.energy import inp_out_energy
|
||||
from ..model.emitters import inp_out_emitter
|
||||
from ..model.quality import inp_out_quality
|
||||
from ..model.sources import inp_out_source
|
||||
from ..model.reactions import inp_out_reaction
|
||||
from ..model.mixing import inp_out_mixing
|
||||
from ..model.times import inp_out_time
|
||||
from ..model.reports import inp_out_report
|
||||
from ..model.options_legacy import inp_out_option
|
||||
from ..model.options_v3 import inp_out_option_v3
|
||||
from ..gis.coordinates import inp_out_coord
|
||||
from ..gis.vertices import inp_out_vertex
|
||||
from ..gis.labels import inp_out_label
|
||||
from ..gis.backdrop import inp_out_backdrop
|
||||
#from .s28_end import *
|
||||
|
||||
|
||||
def dump_inp(project: str, inp: str, version: str = '3'):
|
||||
if version != '3' and version != '2':
|
||||
version = '2'
|
||||
|
||||
if not have_project(project):
|
||||
return
|
||||
|
||||
project_open = is_project_open(project)
|
||||
|
||||
if not project_open:
|
||||
open_project(project)
|
||||
|
||||
dir = os.getcwd()
|
||||
path = os.path.join(dir, inp)
|
||||
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
file = open(path, mode='w',encoding="UTF-8")
|
||||
|
||||
# REGION, BOUND, REGION_NODES 在 epanet v2 中没有,是我们自己定制的
|
||||
# v2 需要去掉我们自己定制的 section
|
||||
sections = section_names_for_epanetv2
|
||||
if version == '3':
|
||||
sections = section_name
|
||||
|
||||
for name in sections:
|
||||
if name == TITLE:
|
||||
file.write(f'[{name}]\n')
|
||||
else:
|
||||
file.write(f'\n[{name}]\n')
|
||||
|
||||
if name == TITLE:
|
||||
file.write('\n'.join(inp_out_title(project)))
|
||||
|
||||
elif name == JUNCTIONS: # + coords
|
||||
file.write('\n'.join(inp_out_junction(project)))
|
||||
|
||||
elif name == RESERVOIRS: # + coords
|
||||
file.write('\n'.join(inp_out_reservoir(project)))
|
||||
|
||||
elif name == TANKS: # + coords
|
||||
file.write('\n'.join(inp_out_tank(project)))
|
||||
|
||||
elif name == PIPES:
|
||||
file.write('\n'.join(inp_out_pipe(project)))
|
||||
|
||||
elif name == PUMPS:
|
||||
file.write('\n'.join(inp_out_pump(project)))
|
||||
|
||||
elif name == VALVES:
|
||||
file.write('\n'.join(inp_out_valve(project)))
|
||||
|
||||
elif name == TAGS:
|
||||
file.write('\n'.join(inp_out_tag(project)))
|
||||
|
||||
elif name == DEMANDS:
|
||||
file.write('\n'.join(inp_out_demand(project)))
|
||||
|
||||
elif name == STATUS:
|
||||
file.write('\n'.join(inp_out_status(project)))
|
||||
|
||||
elif name == PATTERNS:
|
||||
if version == '3':
|
||||
file.write('\n'.join(inp_out_pattern_v3(project)))
|
||||
else:
|
||||
file.write('\n'.join(inp_out_pattern(project)))
|
||||
|
||||
elif name == CURVES:
|
||||
if version == '3':
|
||||
file.write('\n'.join(inp_out_curve_v3(project)))
|
||||
else:
|
||||
file.write('\n'.join(inp_out_curve(project)))
|
||||
|
||||
elif name == CONTROLS:
|
||||
file.write('\n'.join(inp_out_control(project)))
|
||||
|
||||
elif name == RULES:
|
||||
file.write('\n'.join(inp_out_rule(project)))
|
||||
|
||||
elif name == ENERGY:
|
||||
file.write('\n'.join(inp_out_energy(project)))
|
||||
|
||||
elif name == EMITTERS:
|
||||
file.write('\n'.join(inp_out_emitter(project)))
|
||||
|
||||
elif name == QUALITY:
|
||||
file.write('\n'.join(inp_out_quality(project)))
|
||||
|
||||
elif name == SOURCES:
|
||||
file.write('\n'.join(inp_out_source(project)))
|
||||
|
||||
elif name == REACTIONS:
|
||||
file.write('\n'.join(inp_out_reaction(project)))
|
||||
|
||||
elif name == MIXING:
|
||||
file.write('\n'.join(inp_out_mixing(project)))
|
||||
|
||||
elif name == TIMES:
|
||||
file.write('\n'.join(inp_out_time(project)))
|
||||
|
||||
elif name == REPORT:
|
||||
file.write('\n'.join(inp_out_report(project)))
|
||||
|
||||
elif name == OPTIONS:
|
||||
if version == '3':
|
||||
file.write('\n'.join(inp_out_option_v3(project)))
|
||||
else:
|
||||
file.write('\n'.join(inp_out_option(project)))
|
||||
|
||||
elif name == COORDINATES:
|
||||
file.write('\n'.join(inp_out_coord(project)))
|
||||
|
||||
elif name == VERTICES:
|
||||
file.write('\n'.join(inp_out_vertex(project)))
|
||||
|
||||
elif name == LABELS:
|
||||
file.write('\n'.join(inp_out_label(project)))
|
||||
|
||||
elif name == BACKDROP:
|
||||
file.write('\n'.join(inp_out_backdrop(project)))
|
||||
|
||||
elif name == END:
|
||||
pass # :)
|
||||
|
||||
file.write('\n')
|
||||
|
||||
file.close()
|
||||
|
||||
if not project_open:
|
||||
close_project(project)
|
||||
|
||||
|
||||
def export_inp(project: str, version: str = '3') -> ChangeSet:
|
||||
if version != '3' and version != '2':
|
||||
version = '2'
|
||||
|
||||
if not have_project(project):
|
||||
return ChangeSet()
|
||||
|
||||
project_open = is_project_open(project)
|
||||
|
||||
if not project_open:
|
||||
open_project(project)
|
||||
|
||||
inp = ''
|
||||
|
||||
for name in section_name:
|
||||
if name == TITLE:
|
||||
inp += f'[{name}]\n'
|
||||
else:
|
||||
inp += f'\n[{name}]\n'
|
||||
|
||||
if name == TITLE:
|
||||
inp += '\n'.join(inp_out_title(project))
|
||||
|
||||
elif name == JUNCTIONS: # + coords
|
||||
inp += '\n'.join(inp_out_junction(project))
|
||||
|
||||
elif name == RESERVOIRS: # + coords
|
||||
inp += '\n'.join(inp_out_reservoir(project))
|
||||
|
||||
elif name == TANKS: # + coords
|
||||
inp += '\n'.join(inp_out_tank(project))
|
||||
|
||||
elif name == PIPES:
|
||||
inp += '\n'.join(inp_out_pipe(project))
|
||||
|
||||
elif name == PUMPS:
|
||||
inp += '\n'.join(inp_out_pump(project))
|
||||
|
||||
elif name == VALVES:
|
||||
inp += '\n'.join(inp_out_valve(project))
|
||||
|
||||
elif name == TAGS:
|
||||
inp += '\n'.join(inp_out_tag(project))
|
||||
|
||||
elif name == DEMANDS:
|
||||
inp += '\n'.join(inp_out_demand(project))
|
||||
|
||||
elif name == STATUS:
|
||||
inp += '\n'.join(inp_out_status(project))
|
||||
|
||||
elif name == PATTERNS:
|
||||
if version == '3':
|
||||
inp += '\n'.join(inp_out_pattern_v3(project))
|
||||
else:
|
||||
inp += '\n'.join(inp_out_pattern(project))
|
||||
|
||||
elif name == CURVES:
|
||||
if version == '3':
|
||||
inp += '\n'.join(inp_out_curve_v3(project))
|
||||
else:
|
||||
inp += '\n'.join(inp_out_curve(project))
|
||||
|
||||
elif name == CONTROLS:
|
||||
inp += '\n'.join(inp_out_control(project))
|
||||
|
||||
elif name == RULES:
|
||||
inp += '\n'.join(inp_out_rule(project))
|
||||
|
||||
elif name == ENERGY:
|
||||
inp += '\n'.join(inp_out_energy(project))
|
||||
|
||||
elif name == EMITTERS:
|
||||
inp += '\n'.join(inp_out_emitter(project))
|
||||
|
||||
elif name == QUALITY:
|
||||
inp += '\n'.join(inp_out_quality(project))
|
||||
|
||||
elif name == SOURCES:
|
||||
inp += '\n'.join(inp_out_source(project))
|
||||
|
||||
elif name == REACTIONS:
|
||||
inp += '\n'.join(inp_out_reaction(project))
|
||||
|
||||
elif name == MIXING:
|
||||
inp += '\n'.join(inp_out_mixing(project))
|
||||
|
||||
elif name == TIMES:
|
||||
inp += '\n'.join(inp_out_time(project))
|
||||
|
||||
elif name == REPORT:
|
||||
inp += '\n'.join(inp_out_report(project))
|
||||
|
||||
elif name == OPTIONS:
|
||||
if version == '3':
|
||||
inp += '\n'.join(inp_out_option_v3(project))
|
||||
else:
|
||||
inp += '\n'.join(inp_out_option(project))
|
||||
|
||||
elif name == COORDINATES:
|
||||
inp += '\n'.join(inp_out_coord(project))
|
||||
|
||||
elif name == VERTICES:
|
||||
inp += '\n'.join(inp_out_vertex(project))
|
||||
|
||||
elif name == LABELS:
|
||||
inp += '\n'.join(inp_out_label(project))
|
||||
|
||||
elif name == BACKDROP:
|
||||
inp += '\n'.join(inp_out_backdrop(project))
|
||||
|
||||
elif name == END:
|
||||
pass # :)
|
||||
|
||||
inp += '\n'
|
||||
|
||||
if not project_open:
|
||||
close_project(project)
|
||||
|
||||
return ChangeSet({'operation': 'export', 'inp': inp})
|
||||
@@ -0,0 +1,468 @@
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from psycopg import sql
|
||||
|
||||
from ..core.projects import (
|
||||
close_project,
|
||||
create_project,
|
||||
delete_project,
|
||||
have_project,
|
||||
is_project_open,
|
||||
open_project,
|
||||
)
|
||||
from ..core.connection import project_transaction
|
||||
from ..core.database import ChangeSet, refresh_materialized_views, sql_literal, write
|
||||
from .sections import (
|
||||
BACKDROP,
|
||||
BOUND,
|
||||
CONTROLS,
|
||||
COORDINATES,
|
||||
CURVES,
|
||||
DEMANDS,
|
||||
EMITTERS,
|
||||
ENERGY,
|
||||
JUNCTIONS,
|
||||
LABELS,
|
||||
MIXING,
|
||||
OPTIONS,
|
||||
PATTERNS,
|
||||
PIPES,
|
||||
PUMPS,
|
||||
QUALITY,
|
||||
REACTIONS,
|
||||
REGION,
|
||||
REGION_NODES,
|
||||
REPORT,
|
||||
RESERVOIRS,
|
||||
RULES,
|
||||
SOURCES,
|
||||
STATUS,
|
||||
TAGS,
|
||||
TANKS,
|
||||
TIMES,
|
||||
TITLE,
|
||||
VALVES,
|
||||
VERTICES,
|
||||
section_name,
|
||||
)
|
||||
from ..model.title import inp_in_title
|
||||
from ..model.junctions import inp_in_junction
|
||||
from ..model.reservoirs import inp_in_reservoir
|
||||
from ..model.tanks import inp_in_tank
|
||||
from ..model.pipes import inp_in_pipe
|
||||
from ..model.pumps import inp_in_pump
|
||||
from ..model.valves import inp_in_valve
|
||||
from ..model.tags import inp_in_tag
|
||||
from ..model.demands import inp_in_demand
|
||||
from ..model.status import inp_in_status
|
||||
from ..model.patterns import pattern_v3_types, inp_in_pattern
|
||||
from ..model.curves import curve_types, inp_in_curve
|
||||
from ..model.controls import inp_in_control
|
||||
from ..model.rules import inp_in_rule
|
||||
from ..model.energy import inp_in_energy
|
||||
from ..model.emitters import inp_in_emitter
|
||||
from ..model.quality import inp_in_quality
|
||||
from ..model.sources import inp_in_source
|
||||
from ..model.reactions import inp_in_reaction
|
||||
from ..model.mixing import inp_in_mixing
|
||||
from ..model.times import inp_in_time
|
||||
from ..model.reports import inp_in_report
|
||||
from ..model.options_legacy import inp_in_option
|
||||
from ..model.options_v3 import inp_in_option_v3
|
||||
from ..gis.coordinates import inp_in_coord
|
||||
from ..gis.vertices import inp_in_vertex
|
||||
from ..gis.labels import inp_in_label
|
||||
from ..gis.backdrop import inp_in_backdrop
|
||||
from ..gis.regions import inp_in_region, inp_in_bound, inp_in_regionnodes
|
||||
from ..gis.region_geometry import to_postgis_polygon
|
||||
|
||||
# DingZQ, 2024-12-28, export inp
|
||||
from .exporter import export_inp
|
||||
|
||||
_S = "S"
|
||||
_L = "L"
|
||||
|
||||
|
||||
def _inp_in_option(section: list[str], version: str = "3") -> str:
|
||||
return inp_in_option_v3(section) if version == "3" else inp_in_option(section)
|
||||
|
||||
|
||||
_handler = {
|
||||
TITLE: (_S, inp_in_title),
|
||||
JUNCTIONS: (_L, inp_in_junction), # line, demand_outside
|
||||
RESERVOIRS: (_L, inp_in_reservoir),
|
||||
TANKS: (_L, inp_in_tank),
|
||||
PIPES: (_L, inp_in_pipe),
|
||||
PUMPS: (_L, inp_in_pump),
|
||||
VALVES: (_L, inp_in_valve),
|
||||
TAGS: (_L, inp_in_tag),
|
||||
DEMANDS: (_L, inp_in_demand),
|
||||
STATUS: (_L, inp_in_status),
|
||||
PATTERNS: (_L, inp_in_pattern), # line, fixed
|
||||
CURVES: (_L, inp_in_curve),
|
||||
CONTROLS: (_L, inp_in_control),
|
||||
RULES: (_L, inp_in_rule),
|
||||
ENERGY: (_L, inp_in_energy),
|
||||
EMITTERS: (_L, inp_in_emitter),
|
||||
QUALITY: (_L, inp_in_quality),
|
||||
SOURCES: (_L, inp_in_source),
|
||||
REACTIONS: (_L, inp_in_reaction),
|
||||
MIXING: (_L, inp_in_mixing),
|
||||
TIMES: (_S, inp_in_time),
|
||||
REPORT: (_S, inp_in_report),
|
||||
OPTIONS: (_S, _inp_in_option), # line, version
|
||||
COORDINATES: (_L, inp_in_coord),
|
||||
VERTICES: (_L, inp_in_vertex),
|
||||
REGION: (_L, inp_in_region),
|
||||
BOUND: (_L, inp_in_bound),
|
||||
REGION_NODES: (_L, inp_in_regionnodes),
|
||||
LABELS: (_L, inp_in_label),
|
||||
BACKDROP: (_S, inp_in_backdrop),
|
||||
# END : 'END',
|
||||
}
|
||||
|
||||
_level_1 = [
|
||||
TITLE,
|
||||
PATTERNS,
|
||||
CURVES,
|
||||
CONTROLS,
|
||||
RULES,
|
||||
TIMES,
|
||||
REPORT,
|
||||
OPTIONS,
|
||||
BACKDROP,
|
||||
]
|
||||
|
||||
_level_2 = [
|
||||
JUNCTIONS,
|
||||
RESERVOIRS,
|
||||
TANKS,
|
||||
]
|
||||
|
||||
_level_3 = [
|
||||
PIPES,
|
||||
PUMPS,
|
||||
VALVES,
|
||||
DEMANDS,
|
||||
EMITTERS,
|
||||
QUALITY,
|
||||
SOURCES,
|
||||
MIXING,
|
||||
COORDINATES,
|
||||
LABELS,
|
||||
]
|
||||
|
||||
_level_4 = [
|
||||
TAGS,
|
||||
STATUS,
|
||||
ENERGY,
|
||||
REACTIONS,
|
||||
VERTICES,
|
||||
REGION,
|
||||
BOUND,
|
||||
REGION_NODES,
|
||||
]
|
||||
|
||||
map_regiontype = {
|
||||
# map the region types from desktop to server
|
||||
"DISTRIBUTION": "WDA",
|
||||
"DMA": "DMA",
|
||||
"PMA": "PMA",
|
||||
"VD": "VD",
|
||||
"SA": "SA",
|
||||
}
|
||||
|
||||
|
||||
class SQLBatch:
|
||||
def __init__(self, project: str, count: int = 100) -> None:
|
||||
self.batch: list[str] = []
|
||||
self.project = project
|
||||
self.count = count
|
||||
|
||||
def add(self, sql: str) -> None:
|
||||
self.batch.append(sql)
|
||||
if len(self.batch) == self.count:
|
||||
self.flush()
|
||||
|
||||
def flush(self) -> None:
|
||||
write(self.project, "".join(self.batch))
|
||||
self.batch.clear()
|
||||
|
||||
|
||||
def _print_time(desc: str) -> datetime.datetime:
|
||||
now = datetime.datetime.now()
|
||||
time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
print(f"{time}: {desc}")
|
||||
return now
|
||||
|
||||
|
||||
def _get_file_offset(inp: str) -> tuple[dict[str, list[int]], bool]:
|
||||
offset: dict[str, list[int]] = {}
|
||||
|
||||
current = ""
|
||||
demand_outside = False
|
||||
|
||||
with open(inp, encoding="utf-8") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if not line:
|
||||
break
|
||||
|
||||
line = line.strip()
|
||||
if line.startswith("["):
|
||||
for s in section_name:
|
||||
if line.startswith(f"[{s}"):
|
||||
if s not in offset:
|
||||
offset[s] = []
|
||||
offset[s].append(f.tell())
|
||||
current = s
|
||||
break
|
||||
elif line != "" and line.startswith(";") == False:
|
||||
if current == DEMANDS:
|
||||
demand_outside = True
|
||||
|
||||
return (offset, demand_outside)
|
||||
|
||||
|
||||
def parse_file(project: str, inp: str, version: str = "3") -> None:
|
||||
start = _print_time(f'Start reading file "{inp}"...')
|
||||
|
||||
_print_time("First scan...")
|
||||
offset, demand_outside = _get_file_offset(inp)
|
||||
|
||||
levels = _level_1 + _level_2 + _level_3 + _level_4
|
||||
|
||||
# parse the whole section rather than line
|
||||
sections: dict[str, list[str]] = {}
|
||||
for [s, t] in _handler.items():
|
||||
if t[0] == _S:
|
||||
sections[s] = []
|
||||
|
||||
variable_patterns = []
|
||||
current_pattern = None
|
||||
current_curve = None
|
||||
curve_type_desc_line = None
|
||||
current_region = None
|
||||
current_bound = []
|
||||
current_bound.clear()
|
||||
region_list = {}
|
||||
|
||||
sql_batch = SQLBatch(project)
|
||||
_print_time("Second scan...")
|
||||
with open(inp, encoding="utf-8") as f:
|
||||
for s in levels:
|
||||
if s not in offset:
|
||||
continue
|
||||
|
||||
if s == DEMANDS and demand_outside == False:
|
||||
continue
|
||||
|
||||
_print_time(f"[{s}]")
|
||||
|
||||
is_s = _handler[s][0] == _S
|
||||
handler = _handler[s][1]
|
||||
|
||||
for ptr in offset[s]:
|
||||
f.seek(ptr)
|
||||
|
||||
while True:
|
||||
line = f.readline()
|
||||
if not line:
|
||||
break
|
||||
|
||||
line = line.strip()
|
||||
if line.startswith("["):
|
||||
break
|
||||
elif line == "":
|
||||
continue
|
||||
|
||||
if is_s:
|
||||
sections[s].append(line)
|
||||
else:
|
||||
if line.startswith(";"):
|
||||
if version != "3": # v2
|
||||
line = line.removeprefix(";")
|
||||
if s == PATTERNS: # ;desc
|
||||
pass
|
||||
elif s == CURVES: # ;type: desc
|
||||
curve_type_desc_line = line
|
||||
continue
|
||||
|
||||
if s == PATTERNS:
|
||||
tokens = line.split()
|
||||
|
||||
if tokens[1].upper() in pattern_v3_types: # v3
|
||||
sql_batch.add(
|
||||
f"insert into network.patterns (id) values ({sql_literal(tokens[0])});"
|
||||
)
|
||||
current_pattern = tokens[0]
|
||||
if tokens[1].upper() == "VARIABLE":
|
||||
variable_patterns.append(tokens[0])
|
||||
continue
|
||||
|
||||
if current_pattern != tokens[0]:
|
||||
sql_batch.add(
|
||||
f"insert into network.patterns (id) values ({sql_literal(tokens[0])});"
|
||||
)
|
||||
current_pattern = tokens[0]
|
||||
|
||||
elif s == CURVES:
|
||||
tokens = line.split()
|
||||
|
||||
if tokens[1].upper() in curve_types: # v3
|
||||
sql_batch.add(
|
||||
f"insert into network.curves (id, curve_type) values ({sql_literal(tokens[0])}, {sql_literal(tokens[1].upper())});"
|
||||
)
|
||||
current_curve = tokens[0]
|
||||
continue
|
||||
|
||||
if current_curve != tokens[0]:
|
||||
type = curve_types[0]
|
||||
if curve_type_desc_line != None:
|
||||
type = curve_type_desc_line.split(":")[0].strip()
|
||||
sql_batch.add(
|
||||
f"insert into network.curves (id, curve_type) values ({sql_literal(tokens[0])}, {sql_literal(type)});"
|
||||
)
|
||||
current_curve = tokens[0]
|
||||
curve_type_desc_line = None
|
||||
elif s == REGION:
|
||||
tokens = line.split()
|
||||
region_list[tokens[0]] = tokens[1]
|
||||
continue
|
||||
elif s == BOUND:
|
||||
tokens = line.split()
|
||||
if tokens[0] != current_region and len(current_bound) > 0:
|
||||
current_bound.append(current_bound[0])
|
||||
current_geometry = to_postgis_polygon(current_bound)
|
||||
region_type = map_regiontype.get(
|
||||
region_list[current_region],
|
||||
region_list[current_region],
|
||||
)
|
||||
sql_batch.add(
|
||||
"insert into gis.regions(id, region_type, boundary) "
|
||||
f"values ({sql_literal(current_region)}, {sql_literal(region_type)}, "
|
||||
f"st_geomfromtext({sql_literal(current_geometry)}, 900914));"
|
||||
)
|
||||
current_bound.clear()
|
||||
vertex_point = (float(tokens[1]), float(tokens[2]))
|
||||
current_bound.append(vertex_point)
|
||||
current_region = tokens[0]
|
||||
if s == JUNCTIONS:
|
||||
sql_batch.add(handler(line, demand_outside))
|
||||
elif s == PATTERNS:
|
||||
sql_batch.add(
|
||||
handler(line, current_pattern not in variable_patterns)
|
||||
)
|
||||
elif s == BOUND:
|
||||
continue
|
||||
else:
|
||||
sql_batch.add(handler(line))
|
||||
|
||||
f.seek(0)
|
||||
|
||||
if is_s:
|
||||
if s == OPTIONS:
|
||||
sql_batch.add(handler(sections[s], version))
|
||||
else:
|
||||
sql_batch.add(handler(sections[s]))
|
||||
# need to insert the last region into database
|
||||
if len(current_bound) > 0:
|
||||
current_bound.append(current_bound[0])
|
||||
current_geometry = to_postgis_polygon(current_bound)
|
||||
region_type = map_regiontype.get(
|
||||
region_list[current_region],
|
||||
region_list[current_region],
|
||||
)
|
||||
sql_batch.add(
|
||||
"insert into gis.regions(id, region_type, boundary) "
|
||||
f"values ({sql_literal(current_region)}, {sql_literal(region_type)}, "
|
||||
f"st_geomfromtext({sql_literal(current_geometry)}, 900914));"
|
||||
)
|
||||
sql_batch.flush()
|
||||
|
||||
end = _print_time(f'End reading file "{inp}"')
|
||||
print(f"Total (in second): {(end-start).seconds}(s)")
|
||||
|
||||
|
||||
def read_inp(project: str, inp: str, version: str = "3") -> bool:
|
||||
if version != "3" and version != "2":
|
||||
version = "2"
|
||||
|
||||
if is_project_open(project):
|
||||
close_project(project)
|
||||
|
||||
if have_project(project):
|
||||
delete_project(project)
|
||||
|
||||
create_project(project)
|
||||
open_project(project)
|
||||
|
||||
with project_transaction(project):
|
||||
parse_file(project, inp, version)
|
||||
refresh_materialized_views(project)
|
||||
|
||||
"""try:
|
||||
parse_file(project, inp, version)
|
||||
except:
|
||||
close_project(project)
|
||||
delete_project(project)
|
||||
return False"""
|
||||
|
||||
close_project(project)
|
||||
return True
|
||||
|
||||
|
||||
# DingZQ, 2024-12-28, convert v3 to v2
|
||||
def convert_inp_v3_to_v2(inp: str) -> ChangeSet:
|
||||
project = "v3Tov2"
|
||||
|
||||
if is_project_open(project):
|
||||
close_project(project)
|
||||
|
||||
if have_project(project):
|
||||
delete_project(project)
|
||||
|
||||
create_project(project)
|
||||
open_project(project)
|
||||
|
||||
filename = f"inp/{project}_temp.inp"
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
f.write(inp)
|
||||
|
||||
parse_file(project, filename, "3")
|
||||
|
||||
"""try:
|
||||
parse_file(project, inp, version)
|
||||
except:
|
||||
close_project(project)
|
||||
delete_project(project)
|
||||
return False"""
|
||||
|
||||
return export_inp(project, "2")
|
||||
|
||||
|
||||
def import_inp(project: str, cs: ChangeSet, version: str = "3") -> bool:
|
||||
if version != "3" and version != "2":
|
||||
version = "2"
|
||||
|
||||
if "inp" not in cs.operations[0]:
|
||||
return False
|
||||
|
||||
filename = f"inp/{project}_temp.inp"
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
|
||||
_print_time(f'Start writing temp file "{filename}"...')
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
f.write(str(cs.operations[0]["inp"]))
|
||||
_print_time(f'End writing temp file "{filename}"...')
|
||||
|
||||
result = read_inp(project, filename, version)
|
||||
|
||||
# os.remove(filename)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,50 @@
|
||||
TITLE = 'TITLE'
|
||||
JUNCTIONS = 'JUNCTIONS'
|
||||
RESERVOIRS = 'RESERVOIRS'
|
||||
TANKS = 'TANKS'
|
||||
PIPES = 'PIPES'
|
||||
PUMPS = 'PUMPS'
|
||||
VALVES = 'VALVES'
|
||||
TAGS = 'TAGS'
|
||||
DEMANDS = 'DEMANDS'
|
||||
STATUS = 'STATUS'
|
||||
PATTERNS = 'PATTERNS'
|
||||
CURVES = 'CURVES'
|
||||
CONTROLS = 'CONTROLS'
|
||||
RULES = 'RULES'
|
||||
ENERGY = 'ENERGY'
|
||||
EMITTERS = 'EMITTERS'
|
||||
QUALITY = 'QUALITY'
|
||||
SOURCES = 'SOURCES'
|
||||
REACTIONS = 'REACTIONS'
|
||||
MIXING = 'MIXING'
|
||||
TIMES = 'TIMES'
|
||||
REPORT = 'REPORT'
|
||||
OPTIONS = 'OPTIONS'
|
||||
COORDINATES = 'COORDINATES'
|
||||
VERTICES = 'VERTICES'
|
||||
REGION='REGION'
|
||||
BOUND='BOUND'
|
||||
REGION_NODES='DATA_NODE_OF_REGION'
|
||||
LABELS = 'LABELS'
|
||||
BACKDROP = 'BACKDROP'
|
||||
END = 'END'
|
||||
|
||||
section_name = [TITLE, JUNCTIONS, RESERVOIRS, TANKS, PIPES,
|
||||
PUMPS, VALVES, TAGS, DEMANDS, STATUS,
|
||||
PATTERNS, CURVES, CONTROLS, RULES, ENERGY,
|
||||
EMITTERS, QUALITY, SOURCES, REACTIONS, MIXING,
|
||||
TIMES, REPORT, OPTIONS, COORDINATES, VERTICES,
|
||||
REGION, BOUND, REGION_NODES, LABELS, BACKDROP, END]
|
||||
|
||||
# DingZQ, 2025-02-04
|
||||
# 我们在从服务器调用run_project的时候
|
||||
# 会将 database的project内容dump成 epanet v2 的inp文件,然后调用 runepanet.exe 去计算结果
|
||||
# 其中上面的 SECTION : REGION, BOUND, REGION_NODES 在 epanet v2 中没有,是我们自己定制的
|
||||
# 所以需要将这些 section 从 section_name 中移除
|
||||
section_names_for_epanetv2 = [TITLE, JUNCTIONS, RESERVOIRS, TANKS, PIPES,
|
||||
PUMPS, VALVES, TAGS, DEMANDS, STATUS,
|
||||
PATTERNS, CURVES, CONTROLS, RULES, ENERGY,
|
||||
EMITTERS, QUALITY, SOURCES, REACTIONS, MIXING,
|
||||
TIMES, REPORT, OPTIONS, COORDINATES, VERTICES,
|
||||
LABELS, BACKDROP, END]
|
||||
Reference in New Issue
Block a user