Accept Merge Request #184: (parse -> master)

Merge Request: Optimize read inp

Created By: @王琼钰
Accepted By: @王琼钰
URL: https://tjwater.coding.net/p/tjwatercloud/d/TJWaterServer/git/merge/184?initial=true
This commit is contained in:
王琼钰
2023-03-22 18:53:57 +08:00
35 changed files with 798 additions and 896 deletions

View File

@@ -1,7 +1,8 @@
import datetime
import os
from .project import *
from .database import ChangeSet, get_current_operation, set_restore_operation
from .sections import section_name
from .batch_cmds import execute_batch_commands
from .database import ChangeSet, write
from .sections import *
from .s1_title import inp_in_title
from .s2_junctions import inp_in_junction
from .s3_reservoirs import inp_in_reservoir
@@ -12,8 +13,8 @@ from .s7_valves import inp_in_valve
from .s8_tags import inp_in_tag
from .s9_demands import inp_in_demand
from .s10_status import inp_in_status
from .s11_patterns import inp_in_pattern
from .s12_curves import inp_in_curve
from .s11_patterns import pattern_v3_types, inp_in_pattern
from .s12_curves import curve_types, inp_in_curve
from .s13_controls import inp_in_control
from .s14_rules import inp_in_rule
from .s15_energy import inp_in_energy
@@ -25,220 +26,255 @@ from .s20_mixing import inp_in_mixing
from .s21_times import inp_in_time
from .s22_report import inp_in_report
from .s23_options import inp_in_option
from .s23_options_v3 import inp_in_option_v3
from .s24_coordinates import inp_in_coord
from .s25_vertices import inp_in_vertex
from .s26_labels import inp_in_label
from .s27_backdrop import inp_in_backdrop
#from .s28_end import *
_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),
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,
]
def _parse_inp(inp: str) -> dict[str, list[str]]:
file: dict[str, list[str]] = {}
for s in section_name:
file[s] = []
class SQLBatch:
def __init__(self, project: str, count: int = 100) -> None:
self.batch: list[str] = []
self.project = project
self.count = count
section = ''
def add(self, sql: str) -> None:
self.batch.append(sql)
if len(self.batch) == self.count:
self.flush()
for line in open(inp):
line = line.strip()
if line == '':
# skip empty line for control and rule
if section == 'CONTROLS' or section == 'RULES':
pass
else:
section = ''
continue
def flush(self) -> None:
write(self.project, ''.join(self.batch))
self.batch.clear()
if line.startswith('['):
is_section = False
for s in section_name:
if line.startswith(f'[{s}'):
section = s
is_section = True
break
if is_section:
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) 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
sql_batch = SQLBatch(project)
_print_time("Second scan...")
with open(inp) as f:
for s in levels:
if s not in offset:
continue
if section != '':
file[section].append(line)
return file
def _parse_cs(cs: ChangeSet) -> dict[str, list[str]]:
file: dict[str, list[str]] = {}
for s in section_name:
file[s] = []
section = ''
for line in str(cs.operations[0]['inp']).split('\n'):
line = line.strip()
if line == '':
# skip empty line for control and rule
if section == 'CONTROLS' or section == 'RULES':
pass
else:
section = ''
continue
if line.startswith('['):
is_section = False
for s in section_name:
if line.startswith(f'[{s}'):
section = s
is_section = True
break
if is_section:
if s == DEMANDS and demand_outside == False:
continue
if section != '':
file[section].append(line)
_print_time(f"[{s}]")
return file
is_s = _handler[s][0] == _S
handler = _handler[s][1]
for ptr in offset[s]:
f.seek(ptr)
def _read_inp(file: dict[str, list[str]]) -> ChangeSet:
file_cs: dict[str, ChangeSet] = {}
for s in section_name:
file_cs[s] = ChangeSet()
while True:
line = f.readline()
if not line:
break
for name, section in file.items():
if name == 'TITLE':
file_cs[name].merge(inp_in_title(section))
elif name == 'JUNCTIONS': # + coords
file_cs[name].merge(inp_in_junction(section))
elif name == 'RESERVOIRS': # + coords
file_cs[name].merge(inp_in_reservoir(section))
elif name == 'TANKS': # + coords
file_cs[name].merge(inp_in_tank(section))
elif name == 'PIPES':
file_cs[name].merge(inp_in_pipe(section))
elif name == 'PUMPS':
file_cs[name].merge(inp_in_pump(section))
elif name == 'VALVES':
file_cs[name].merge(inp_in_valve(section))
elif name == 'TAGS':
file_cs[name].merge(inp_in_tag(section))
elif name == 'DEMANDS':
file_cs[name].merge(inp_in_demand(section))
elif name == 'STATUS':
file_cs[name].merge(inp_in_status(section))
elif name == 'PATTERNS':
file_cs[name].merge(inp_in_pattern(section))
elif name == 'CURVES':
file_cs[name].merge(inp_in_curve(section))
elif name == 'CONTROLS':
file_cs[name].merge(inp_in_control(section))
elif name == 'RULES':
file_cs[name].merge(inp_in_rule(section))
elif name == 'ENERGY':
file_cs[name].merge(inp_in_energy(section))
elif name == 'EMITTERS':
file_cs[name].merge(inp_in_emitter(section))
elif name == 'QUALITY':
file_cs[name].merge(inp_in_quality(section))
elif name == 'SOURCES':
file_cs[name].merge(inp_in_source(section))
elif name == 'REACTIONS':
file_cs[name].merge(inp_in_reaction(section))
elif name == 'MIXING':
file_cs[name].merge(inp_in_mixing(section))
elif name == 'TIMES':
file_cs[name].merge(inp_in_time(section))
elif name == 'REPORT':
file_cs[name].merge(inp_in_report(section))
elif name == 'OPTIONS':
file_cs[name].merge(inp_in_option(section))
elif name == 'COORDINATES':
coords = inp_in_coord(section)
for s in ['JUNCTIONS', 'RESERVOIRS', 'TANKS']:
for node in file_cs[s].operations:
if node['type'] == 'demand':
line = line.strip()
if line.startswith('['):
break
elif line == '':
continue
if node['id'] in coords:
coord = coords[node['id']]
node |= { 'x' : coord['x'], 'y' : coord['y'] }
if is_s:
sections[s].append(line)
else:
print(f"WARNING: [{s}] {node['id']} has no coordinate, set it at origin!")
node |= { 'x' : 0.0, 'y' : 0.0 }
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
elif name == 'VERTICES':
file_cs[name].merge(inp_in_vertex(section))
if s == PATTERNS:
tokens = line.split()
elif name == 'LABELS':
file_cs[name].merge(inp_in_label(section))
if tokens[1].upper() in pattern_v3_types: #v3
sql_batch.add(f"insert into _pattern (id) values ('{tokens[0]}');")
current_pattern = tokens[0]
if tokens[1].upper() == 'VARIABLE':
variable_patterns.append(tokens[0])
continue
elif name == 'BACKDROP':
file_cs[name].merge(inp_in_backdrop(section))
if current_pattern != tokens[0]:
sql_batch.add(f"insert into _pattern (id) values ('{tokens[0]}');")
current_pattern = tokens[0]
elif name == 'END':
pass # :)
elif s == CURVES:
tokens = line.split()
# release file
file = {}
if tokens[1].upper() in curve_types: #v3
sql_batch.add(f"insert into _curve (id, type) values ('{tokens[0]}', '{tokens[1].upper()}');")
current_curve = tokens[0]
continue
cs = ChangeSet()
priorities = [
'PATTERNS',
'CURVES',
'JUNCTIONS',
'RESERVOIRS',
'TANKS',
'COORDINATES',
'PIPES',
'PUMPS',
'VALVES',
'DEMANDS',
'STATUS',
'OPTIONS',
'TIMES',
'EMITTERS',
'QUALITY',
'SOURCES',
'REACTIONS',
'MIXING',
'ENERGY',
'REPORT',
'VERTICES',
'CONTROLS',
'RULES',
'TITLE',
'TAGS',
'LABELS',
'BACKDROP',
'END',
]
for s in priorities:
cs.merge(file_cs[s])
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 _curve (id, type) values ('{tokens[0]}', '{type}');")
current_curve = tokens[0]
curve_type_desc_line = None
return cs
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 == OPTIONS:
sql_batch.add(handler(line, version))
else:
sql_batch.add(handler(line))
f.seek(0)
if is_s:
sql_batch.add(handler(sections[s]))
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):
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)
@@ -248,34 +284,37 @@ def read_inp(project: str, inp: str):
create_project(project)
open_project(project)
file = _parse_inp(inp)
cs = _read_inp(file)
parse_file(project, inp, version)
execute_batch_commands(project, cs)
op = get_current_operation(project)
set_restore_operation(project, op)
'''try:
parse_file(project, inp, version)
except:
close_project(project)
delete_project(project)
return False'''
close_project(project)
return True
def import_inp(project: str, cs: ChangeSet) -> ChangeSet:
if is_project_open(project):
close_project(project)
def import_inp(project: str, cs: ChangeSet, version: str = '3') -> bool:
if version != '3' and version != '2':
version = '2'
if have_project(project):
delete_project(project)
if 'inp' not in cs.operations[0]:
return False
create_project(project)
open_project(project)
filename = f'inp/{project}_temp.inp'
if os.path.exists(filename):
os.remove(filename)
file = _parse_cs(cs)
new_cs = _read_inp(file)
_print_time(f'Start writing temp file "{filename}"...')
with open(filename, 'w') as f:
f.write(str(cs.operations[0]['inp']))
_print_time(f'End writing temp file "{filename}"...')
success_cs = execute_batch_commands(project, new_cs)
op = get_current_operation(project)
set_restore_operation(project, op)
result = read_inp(project, filename, version)
close_project(project)
os.remove(filename)
# return ?
return success_cs
return result

View File

@@ -1,7 +1,7 @@
import os
from .project import *
from .database import ChangeSet
from .sections import section_name
from .sections import *
from .s1_title import inp_out_title
from .s2_junctions import inp_out_junction
from .s3_reservoirs import inp_out_reservoir
@@ -12,8 +12,8 @@ from .s7_valves import inp_out_valve
from .s8_tags import inp_out_tag
from .s9_demands import inp_out_demand
from .s10_status import inp_out_status
from .s11_patterns import inp_out_pattern
from .s12_curves import inp_out_curve
from .s11_patterns import inp_out_pattern, inp_out_pattern_v3
from .s12_curves import inp_out_curve, inp_out_curve_v3
from .s13_controls import inp_out_control
from .s14_rules import inp_out_rule
from .s15_energy import inp_out_energy
@@ -25,6 +25,7 @@ from .s20_mixing import inp_out_mixing
from .s21_times import inp_out_time
from .s22_report import inp_out_report
from .s23_options import inp_out_option
from .s23_options_v3 import inp_out_option_v3
from .s24_coordinates import inp_out_coord
from .s25_vertices import inp_out_vertex
from .s26_labels import inp_out_label
@@ -32,7 +33,10 @@ from .s27_backdrop import inp_out_backdrop
#from .s28_end import *
def dump_inp(project: str, inp: str):
def dump_inp(project: str, inp: str, version: str = '3'):
if version != '3' and version != '2':
version = '2'
if not have_project(project):
return
@@ -48,93 +52,102 @@ def dump_inp(project: str, inp: str):
file = open(path, mode='w')
for name in section_name:
if name == 'TITLE':
if name == TITLE:
file.write(f'[{name}]\n')
else:
file.write(f'\n[{name}]\n')
if name == 'TITLE':
if name == TITLE:
file.write('\n'.join(inp_out_title(project)))
elif name == 'JUNCTIONS': # + coords
elif name == JUNCTIONS: # + coords
file.write('\n'.join(inp_out_junction(project)))
elif name == 'RESERVOIRS': # + coords
elif name == RESERVOIRS: # + coords
file.write('\n'.join(inp_out_reservoir(project)))
elif name == 'TANKS': # + coords
elif name == TANKS: # + coords
file.write('\n'.join(inp_out_tank(project)))
elif name == 'PIPES':
elif name == PIPES:
file.write('\n'.join(inp_out_pipe(project)))
elif name == 'PUMPS':
elif name == PUMPS:
file.write('\n'.join(inp_out_pump(project)))
elif name == 'VALVES':
elif name == VALVES:
file.write('\n'.join(inp_out_valve(project)))
elif name == 'TAGS':
elif name == TAGS:
file.write('\n'.join(inp_out_tag(project)))
elif name == 'DEMANDS':
elif name == DEMANDS:
file.write('\n'.join(inp_out_demand(project)))
elif name == 'STATUS':
elif name == STATUS:
file.write('\n'.join(inp_out_status(project)))
elif name == 'PATTERNS':
file.write('\n'.join(inp_out_pattern(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':
file.write('\n'.join(inp_out_curve(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':
elif name == CONTROLS:
file.write('\n'.join(inp_out_control(project)))
elif name == 'RULES':
elif name == RULES:
file.write('\n'.join(inp_out_rule(project)))
elif name == 'ENERGY':
elif name == ENERGY:
file.write('\n'.join(inp_out_energy(project)))
elif name == 'EMITTERS':
elif name == EMITTERS:
file.write('\n'.join(inp_out_emitter(project)))
elif name == 'QUALITY':
elif name == QUALITY:
file.write('\n'.join(inp_out_quality(project)))
elif name == 'SOURCES':
elif name == SOURCES:
file.write('\n'.join(inp_out_source(project)))
elif name == 'REACTIONS':
elif name == REACTIONS:
file.write('\n'.join(inp_out_reaction(project)))
elif name == 'MIXING':
elif name == MIXING:
file.write('\n'.join(inp_out_mixing(project)))
elif name == 'TIMES':
elif name == TIMES:
file.write('\n'.join(inp_out_time(project)))
elif name == 'REPORT':
elif name == REPORT:
file.write('\n'.join(inp_out_report(project)))
elif name == 'OPTIONS':
file.write('\n'.join(inp_out_option(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':
elif name == COORDINATES:
file.write('\n'.join(inp_out_coord(project)))
elif name == 'VERTICES':
elif name == VERTICES:
file.write('\n'.join(inp_out_vertex(project)))
elif name == 'LABELS':
elif name == LABELS:
file.write('\n'.join(inp_out_label(project)))
elif name == 'BACKDROP':
elif name == BACKDROP:
file.write('\n'.join(inp_out_backdrop(project)))
elif name == 'END':
elif name == END:
pass # :)
file.write('\n')
@@ -144,7 +157,10 @@ def dump_inp(project: str, inp: str):
close_project(project)
def export_inp(project: str) -> ChangeSet:
def export_inp(project: str, version: str = '3') -> ChangeSet:
if version != '3' and version != '2':
version = '2'
if not have_project(project):
return ChangeSet()
@@ -156,93 +172,102 @@ def export_inp(project: str) -> ChangeSet:
inp = ''
for name in section_name:
if name == 'TITLE':
if name == TITLE:
inp += f'[{name}]\n'
else:
inp += f'\n[{name}]\n'
if name == 'TITLE':
if name == TITLE:
inp += '\n'.join(inp_out_title(project))
elif name == 'JUNCTIONS': # + coords
elif name == JUNCTIONS: # + coords
inp += '\n'.join(inp_out_junction(project))
elif name == 'RESERVOIRS': # + coords
elif name == RESERVOIRS: # + coords
inp += '\n'.join(inp_out_reservoir(project))
elif name == 'TANKS': # + coords
elif name == TANKS: # + coords
inp += '\n'.join(inp_out_tank(project))
elif name == 'PIPES':
elif name == PIPES:
inp += '\n'.join(inp_out_pipe(project))
elif name == 'PUMPS':
elif name == PUMPS:
inp += '\n'.join(inp_out_pump(project))
elif name == 'VALVES':
elif name == VALVES:
inp += '\n'.join(inp_out_valve(project))
elif name == 'TAGS':
elif name == TAGS:
inp += '\n'.join(inp_out_tag(project))
elif name == 'DEMANDS':
elif name == DEMANDS:
inp += '\n'.join(inp_out_demand(project))
elif name == 'STATUS':
elif name == STATUS:
inp += '\n'.join(inp_out_status(project))
elif name == 'PATTERNS':
inp += '\n'.join(inp_out_pattern(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':
inp += '\n'.join(inp_out_curve(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':
elif name == CONTROLS:
inp += '\n'.join(inp_out_control(project))
elif name == 'RULES':
elif name == RULES:
inp += '\n'.join(inp_out_rule(project))
elif name == 'ENERGY':
elif name == ENERGY:
inp += '\n'.join(inp_out_energy(project))
elif name == 'EMITTERS':
elif name == EMITTERS:
inp += '\n'.join(inp_out_emitter(project))
elif name == 'QUALITY':
elif name == QUALITY:
inp += '\n'.join(inp_out_quality(project))
elif name == 'SOURCES':
elif name == SOURCES:
inp += '\n'.join(inp_out_source(project))
elif name == 'REACTIONS':
elif name == REACTIONS:
inp += '\n'.join(inp_out_reaction(project))
elif name == 'MIXING':
elif name == MIXING:
inp += '\n'.join(inp_out_mixing(project))
elif name == 'TIMES':
elif name == TIMES:
inp += '\n'.join(inp_out_time(project))
elif name == 'REPORT':
elif name == REPORT:
inp += '\n'.join(inp_out_report(project))
elif name == 'OPTIONS':
inp += '\n'.join(inp_out_option(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':
elif name == COORDINATES:
inp += '\n'.join(inp_out_coord(project))
elif name == 'VERTICES':
elif name == VERTICES:
inp += '\n'.join(inp_out_vertex(project))
elif name == 'LABELS':
elif name == LABELS:
inp += '\n'.join(inp_out_label(project))
elif name == 'BACKDROP':
elif name == BACKDROP:
inp += '\n'.join(inp_out_backdrop(project))
elif name == 'END':
elif name == END:
pass # :)
inp += '\n'

View File

@@ -72,45 +72,21 @@ def set_status(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA2][EPA3][IN][OUT]
# link value
#--------------------------------------------------------------
class InpStatus:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.link = str(tokens[0])
self.value = tokens[1].upper()
self.is_status = True
if self.value == LINK_STATUS_OPEN or self.value == LINK_STATUS_CLOSED or self.value == LINK_STATUS_ACTIVE:
self.status = str(self.value)
else:
self.setting = float(self.value)
self.is_status = False
def inp_in_status(section: list[str]) -> ChangeSet:
objs: dict[str, list[InpStatus]] = {}
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpStatus(s)
if obj.link not in objs:
objs[obj.link] = []
objs[obj.link].append(obj)
def inp_in_status(line: str) -> str:
tokens = line.split()
cs = ChangeSet()
for link, values in objs.items():
obj_cs : dict[str, Any] = g_update_prefix | {'type': 'status', 'link' : link, 'status': None, 'setting': None}
for obj in values:
if obj.is_status:
obj_cs['status'] = obj.status
else:
obj_cs['setting'] = obj.setting
cs.append(obj_cs)
return cs
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
link = str(tokens[0])
value = tokens[1].upper()
if value == LINK_STATUS_OPEN or value == LINK_STATUS_CLOSED or value == LINK_STATUS_ACTIVE:
return f"insert into status (link, status, setting) values ('{link}', '{value}', null);"
else:
return f"insert into status (link, status, setting) values ('{link}', null, {float(value)});"
def inp_out_status(name: str) -> list[str]:

View File

@@ -1,5 +1,9 @@
from .database import *
PATTERN_V3_TYPE_FIXED = 'FIXED'
PATTERN_V3_TYPE_VARIABLE = 'VARIABLE'
pattern_v3_types = [PATTERN_V3_TYPE_FIXED, PATTERN_V3_TYPE_VARIABLE]
def get_pattern_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
@@ -98,32 +102,25 @@ def delete_pattern(name: str, cs: ChangeSet) -> ChangeSet:
# ;desc
# id mult1 mult2 .....
#--------------------------------------------------------------
def inp_in_pattern(section: list[str]) -> ChangeSet:
descs = {}
patterns: dict[str, list[float]] = {}
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# id FIXED (interval)
# id factor1 factor2 ...
# id VARIABLE
# id time1 factor1 time2 factor2 ...
#--------------------------------------------------------------
count = len(section)
for i in range(0, count):
if section[i].startswith(';'):
# this is description
next = i + 1
if next < count and section[next].startswith(';') == False:
next_tokens = section[next].split()
descs[next_tokens[0]] = section[i].removeprefix(';')
continue
tokens = section[i].split()
if tokens[0] not in patterns:
patterns[tokens[0]] = []
def inp_in_pattern(line: str, fixed: bool = True) -> str:
tokens = line.split()
sql = ''
if fixed:
for token in tokens[1:]:
patterns[tokens[0]].append(float(token))
cs = ChangeSet()
for id, factors in patterns.items():
cs.append(g_add_prefix | {'type': 'pattern', 'id' : id, 'factors' : factors})
#print(descs)
return cs
sql += f"insert into patterns (id, factor) values ('{tokens[0]}', {float(token)});"
else:
for token in tokens[1::2]:
sql += f"insert into patterns (id, factor) values ('{tokens[0]}', {float(token)});"
return sql
def inp_out_pattern(name: str) -> list[str]:
@@ -136,49 +133,6 @@ def inp_out_pattern(name: str) -> list[str]:
return lines
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# id FIXED (interval)
# id factor1 factor2 ...
# id VARIABLE
# id time1 factor1 time2 factor2 ...
#--------------------------------------------------------------
def inp_in_pattern_v3(section: list[str]) -> ChangeSet:
patterns: dict[str, list[float]] = {}
variable_patterns: list[str] = []
count = len(section)
for i in range(0, count):
if section[i].startswith(';'):
continue
tokens = section[i].split()
# for EPA3, ignore time of variable pattern...
if tokens[1] == 'VARIABLE':
variable_patterns.append(tokens[0])
continue
elif tokens[1] == 'FIXED':
continue
if tokens[0] not in patterns:
patterns[tokens[0]] = []
if tokens[0] not in variable_patterns:
for token in tokens[1:]:
patterns[tokens[0]].append(float(token))
else:
for token in tokens[1::2]:
patterns[tokens[0]].append(float(token))
cs = ChangeSet()
for id, factors in patterns.items():
cs.append(g_add_prefix | {'type': 'pattern', 'id' : id, 'factors' : factors})
#print(descs)
return cs
def inp_out_pattern_v3(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from patterns order by _order")

View File

@@ -5,6 +5,8 @@ CURVE_TYPE_EFFICIENCY = 'EFFICIENCY'
CURVE_TYPE_VOLUME = 'VOLUME'
CURVE_TYPE_HEADLOSS = 'HEADLOSS'
curve_types = [CURVE_TYPE_PUMP, CURVE_TYPE_EFFICIENCY, CURVE_TYPE_VOLUME, CURVE_TYPE_HEADLOSS]
def get_curve_schema(name: str) -> dict[str, dict[str, Any]]:
return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True },
'c_type' : {'type': 'str' , 'optional': False , 'readonly': False},
@@ -126,37 +128,16 @@ def delete_curve(name: str, cs: ChangeSet) -> ChangeSet:
# ;type: desc
# id x y
#--------------------------------------------------------------
def inp_in_curve(section: list[str]) -> ChangeSet:
types = {}
descs = {}
curves: dict[str, list[dict[str, float]]] = {}
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# id type
# id x y
#--------------------------------------------------------------
count = len(section)
for i in range(0, count):
if section[i].startswith(';'):
# ;type: desc
type_plus_desc = section[i].removeprefix(';')
type_plus_desc_tokens = type_plus_desc.split(':')
next = i + 1
if next < count and section[next].startswith(';') == False:
next_tokens = section[next].split()
types[next_tokens[0]] = type_plus_desc_tokens[0].strip().upper()
if len(type_plus_desc_tokens) > 1:
descs[next_tokens[0]] = type_plus_desc_tokens[1].strip()
continue
tokens = section[i].split()
if tokens[0] not in curves:
curves[tokens[0]] = []
curves[tokens[0]].append({'x': float(tokens[1]), 'y': float(tokens[2])})
cs = ChangeSet()
for id, coords in curves.items():
c_type = types[id] if id in types else CURVE_TYPE_PUMP
cs.append(g_add_prefix | {'type': 'curve', 'id' : id, 'c_type': c_type, 'coords' : coords})
#print(descs)
return cs
def inp_in_curve(line: str) -> str:
tokens = line.split()
return f"insert into curves (id, x, y) values ('{tokens[0]}', {float(tokens[1])}, {float(tokens[2])});"
def inp_out_curve(name: str) -> list[str]:
@@ -175,40 +156,6 @@ def inp_out_curve(name: str) -> list[str]:
return lines
#--------------------------------------------------------------
# [EPA3][IN][OUT]
# id type
# id x y
#--------------------------------------------------------------
def inp_in_curve_v3(section: list[str]) -> ChangeSet:
types = {}
curves: dict[str, list[dict[str, float]]] = {}
count = len(section)
for i in range(0, count):
if section[i].startswith(';'):
continue
tokens = section[i].split()
# for EPA3
if tokens[1] == 'PUMP' or tokens[1] == 'EFFICIENCY' or tokens[1] == 'VOLUME' or tokens[1] == 'HEADLOSS':
types[tokens[0]] = tokens[1]
continue
if tokens[0] not in curves:
curves[tokens[0]] = []
curves[tokens[0]].append({'x': float(tokens[1]), 'y': float(tokens[2])})
cs = ChangeSet()
for id, coords in curves.items():
c_type = types[id] if id in types else CURVE_TYPE_PUMP
cs.append(g_add_prefix | {'type': 'curve', 'id' : id, 'c_type': c_type, 'coords' : coords})
#print(descs)
return cs
def inp_out_curve_v3(name: str) -> list[str]:
lines = []
types = read_all(name, f"select * from _curve")

View File

@@ -42,10 +42,10 @@ def set_control(name: str, cs: ChangeSet) -> ChangeSet:
# (0) (1) (2) (3) (4) (5) (6) (7)
# todo...
#--------------------------------------------------------------
def inp_in_control(section: list[str]) -> ChangeSet:
if len(section) > 0:
return ChangeSet(g_update_prefix | {'type': 'control', 'controls' : section})
return ChangeSet()
def inp_in_control(line: str) -> str:
return f"insert into controls (line) values ('{line}');"
def inp_out_control(name: str) -> list[str]:

View File

@@ -38,10 +38,10 @@ def set_rule(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA2][EPA3]
# TODO...
#--------------------------------------------------------------
def inp_in_rule(section: list[str]) -> ChangeSet:
if len(section) > 0:
return ChangeSet(g_update_prefix | {'type': 'rule', 'rules' : section})
return ChangeSet()
def inp_in_rule(line: str) -> str:
return f"insert into rules (line) values ('{line}');"
def inp_out_rule(name: str) -> list[str]:

View File

@@ -137,36 +137,37 @@ def set_pump_energy(name: str, cs: ChangeSet) -> ChangeSet:
# PUMP id {PRICE/PATTERN/EFFIC} value
# DEMAND CHARGE value
#--------------------------------------------------------------
def inp_in_energy(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
if s.startswith(';'):
continue
tokens = s.strip().split()
if tokens[0].upper() == 'PUMP':
pump = tokens[1]
key = tokens[2].lower()
value = tokens[3]
if key == 'price':
value = float(value)
cs.append(g_update_prefix | { 'type' : 'pump_energy', 'pump' : pump, key: value })
def inp_in_energy(line: str) -> str:
tokens = line.split()
if tokens[0].upper() == 'PUMP':
pump = tokens[1]
key = tokens[2].lower()
value = tokens[3]
if key == 'price':
value = float(value)
else:
line = s.upper().strip()
for key in get_energy_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
value = f"'{value}'"
if key == 'efficiency':
key = 'effic'
# exception here
if line.startswith('GLOBAL EFFICIENCY'):
value = line.removeprefix('GLOBAL EFFICIENCY').strip()
cs.append(g_update_prefix | { 'type' : 'energy', key : value })
return f"insert into energy_pump_{key} (pump, {key}) values ('{pump}', {value});"
return cs
else:
line = line.upper().strip()
for key in get_energy_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
# exception here
if line.startswith('GLOBAL EFFICIENCY'):
value = line.removeprefix('GLOBAL EFFICIENCY').strip()
return f"update energy set value = '{value}' where key = '{key}';"
return ''
def inp_out_energy(name: str) -> list[str]:

View File

@@ -66,27 +66,19 @@ def set_emitter(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA3][IN][OUT]
# node Ke (exponent pattern)
#--------------------------------------------------------------
class InpEmitter:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.junction = str(tokens[0])
self.coefficient = float(tokens[1])
def inp_in_emitter(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpEmitter(s)
cs.append(g_update_prefix | {'type': 'emitter', 'junction': obj.junction, 'coefficient': obj.coefficient})
return cs
def inp_in_emitter(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
junction = str(tokens[0])
coefficient = float(tokens[1])
return f"insert into emitters (junction, coefficient) values ('{junction}', {coefficient});"
def inp_out_emitter(name: str) -> list[str]:

View File

@@ -63,27 +63,19 @@ def set_quality(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA2][EPA3][IN][OUT]
# node initqual
#--------------------------------------------------------------
class InpQuality:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.node = str(tokens[0])
self.quality = float(tokens[1])
def inp_in_quality(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpQuality(s)
cs.append(g_update_prefix | {'type': 'quality', 'node': obj.node, 'quality': obj.quality})
return cs
def inp_in_quality(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
node = str(tokens[0])
quality = float(tokens[1])
return f"insert into quality (node, quality) values ('{node}', {quality});"
def inp_out_quality(name: str) -> list[str]:

View File

@@ -106,29 +106,23 @@ def delete_source(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA2][EPA3][IN][OUT]
# node sourcetype quality (pattern)
#--------------------------------------------------------------
class InpSource:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.node = str(tokens[0])
self.s_type = str(tokens[1].upper())
self.strength = float(tokens[2])
self.pattern = str(tokens[3]) if num_without_desc >= 4 else None
def inp_in_source(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpSource(s)
cs.append(g_add_prefix | {'type': 'source', 'node': obj.node, 's_type': obj.s_type, 'strength': obj.strength, 'pattern': obj.pattern})
return cs
def inp_in_source(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
node = str(tokens[0])
s_type = str(tokens[1].upper())
strength = float(tokens[2])
pattern = str(tokens[3]) if num_without_desc >= 4 else None
pattern = f"'{pattern}'" if pattern != None else 'null'
return f"insert into sources (node, type, strength, pattern) values ('{node}', '{s_type}', {strength}, {pattern});"
def inp_out_source(name: str) -> list[str]:

View File

@@ -192,34 +192,30 @@ def set_tank_reaction(name: str, cs: ChangeSet) -> ChangeSet:
# LIMITING POTENTIAL value
# ROUGHNESS CORRELATION value
#--------------------------------------------------------------
def inp_in_reaction(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
if s.startswith(';'):
continue
tokens = s.strip().split()
token0 = tokens[0].upper()
if token0 == 'BULK' or token0 == 'WALL':
pipe = tokens[1]
key = token0.lower()
value = tokens[2]
cs.append(g_update_prefix | { 'type' : 'pipe_reaction', 'pipe' : pipe, key: value })
def inp_in_reaction(line: str) -> str:
tokens = line.split()
token0 = tokens[0].upper()
if token0 == 'BULK' or token0 == 'WALL':
pipe = tokens[1]
key = token0.lower()
value = tokens[2]
return f"insert into reactions_pipe_{key} (pipe, value) values ('{pipe}', {value});"
elif token0 == 'TANK':
tank = tokens[1]
value = tokens[2]
cs.append(g_update_prefix | { 'type' : 'tank_reaction', 'tank' : tank, 'value': value })
elif token0 == 'TANK':
tank = tokens[1]
value = tokens[2]
return f"insert into reactions_tank (tank, value) values ('{tank}', {value});"
else:
line = s.upper().strip()
for key in get_reaction_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
cs.append(g_update_prefix | { 'type' : 'reaction', key : value })
else:
line = line.upper().strip()
for key in get_reaction_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
return f"update reactions set value = '{value}' where key = '{key}';"
return cs
return ''
def inp_out_reaction(name: str) -> list[str]:

View File

@@ -27,17 +27,12 @@ def set_title(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, set_title_cmd(name ,cs))
class InpTitle:
def __init__(self, section) -> None:
self.value = '\n'.join(section)
def inp_in_title(section: list[str]) -> ChangeSet:
def inp_in_title(section: list[str]) -> str:
if section == []:
return ChangeSet()
return ''
obj = InpTitle(section)
return ChangeSet(g_update_prefix | {'type': 'title', 'value' : obj.value})
title = '\n'.join(section)
return f"update title set value = '{title}';"
def inp_out_title(name: str) -> list[str]:

View File

@@ -101,28 +101,21 @@ def delete_mixing(name: str, cs: ChangeSet) -> ChangeSet:
# TankID MixModel FractVolume
# FractVolume if type == MIX2
#--------------------------------------------------------------
class InpMixing:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.tank = str(tokens[0])
self.model = str(tokens[1].upper())
self.value = float(tokens[3]) if num_without_desc >= 4 else None
def inp_in_mixing(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpMixing(s)
cs.append(g_add_prefix | {'type': 'mixing', 'tank': obj.tank, 'model': obj.model, 'value': obj.value})
return cs
def inp_in_mixing(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
tank = str(tokens[0])
model = str(tokens[1].upper())
value = float(tokens[3]) if num_without_desc >= 4 else None
value = value if value != None else 'null'
return f"insert into mixing (tank, model, value) values ('{tank}', '{model}', {value});"
def inp_out_mixing(name: str) -> list[str]:

View File

@@ -81,20 +81,20 @@ def set_time(name: str, cs: ChangeSet) -> ChangeSet:
# START CLOCKTIME value (AM PM)
# [EPA3] supports [EPA2] keyword
#--------------------------------------------------------------
def inp_in_time(section: list[str]) -> ChangeSet:
if len(section) > 0:
cs = g_update_prefix | { 'type' : 'time' }
for s in section:
if s.startswith(';'):
continue
line = s.upper().strip()
for key in get_time_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
cs |= { key : value }
return ChangeSet(cs)
return ChangeSet()
def inp_in_time(section: list[str]) -> str:
sql = ''
for s in section:
if s.startswith(';'):
continue
line = s.upper().strip()
for key in get_time_schema('').keys():
if line.startswith(key):
value = line.removeprefix(key).strip()
sql += f"update times set value = '{value}' where key = '{key}';"
return sql
def inp_out_time(name: str) -> list[str]:

View File

@@ -18,8 +18,10 @@ from .database import *
# [EPA3][NOT SUPPORT]
# TRIALS {YES/NO}
#--------------------------------------------------------------
def inp_in_report(section: list[str]) -> ChangeSet:
return ChangeSet()
def inp_in_report(section: list[str]) -> str:
return ''
def inp_out_report(name: str) -> list[str]:

View File

@@ -11,7 +11,7 @@ def set_option(name: str, cs: ChangeSet) -> ChangeSet:
return execute_batch_command(name, new_cs)
def inp_in_option(section: list[str]) -> ChangeSet:
def _inp_in_option(section: list[str]) -> ChangeSet:
if len(section) <= 0:
return ChangeSet()
@@ -22,9 +22,10 @@ def inp_in_option(section: list[str]) -> ChangeSet:
tokens = s.strip().split()
if tokens[0].upper() == 'PATTERN': # can not upper id
cs |= { 'PATTERN' : tokens[1] }
value = tokens[1] if len(tokens) > 1 else ''
cs |= { 'PATTERN' : value }
elif tokens[0].upper() == 'QUALITY': # can not upper trace node
value = tokens[1]
value = tokens[1] if len(tokens) > 1 else ''
if len(tokens) > 2:
value += f' {tokens[2]}'
cs |= { 'QUALITY' : value }
@@ -40,6 +41,20 @@ def inp_in_option(section: list[str]) -> ChangeSet:
return result
def inp_in_option(section: list[str]) -> str:
sql = ''
result = _inp_in_option(section)
for op in result.operations:
for key in op.keys():
if key == 'operation' or key == 'type':
continue
if op['type'] == 'option':
sql += f"update options set value = '{op[key]}' where key = '{key}';"
else:
sql += f"update options_v3 set value = '{op[key]}' where key = '{key}';"
return sql
def inp_out_option(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from options")

View File

@@ -16,7 +16,8 @@ def _parse_v2(v2_lines: list[str]) -> dict[str, str]:
for s in v2_lines:
tokens = s.split()
if tokens[0].upper() == 'PATTERN': # can not upper id
cs_v2 |= { 'PATTERN' : tokens[1] }
value = tokens[1] if len(tokens) > 1 else ''
cs_v2 |= { 'PATTERN' : value }
elif tokens[0].upper() == 'QUALITY': # can not upper trace node
value = tokens[1]
if len(tokens) > 2:
@@ -31,7 +32,7 @@ def _parse_v2(v2_lines: list[str]) -> dict[str, str]:
return cs_v2
def inp_in_option_v3(section: list[str]) -> ChangeSet:
def _inp_in_option_v3(section: list[str]) -> ChangeSet:
if len(section) <= 0:
return ChangeSet()
@@ -44,7 +45,11 @@ def inp_in_option_v3(section: list[str]) -> ChangeSet:
tokens = s.strip().split()
key = tokens[0]
if key in get_option_v3_schema('').keys():
value = tokens[1] if len(tokens) >= 2 else ''
value = ''
if len(tokens) == 2:
value = tokens[1]
elif len(tokens) > 2:
value = ' '.join(tokens[1:])
cs_v3 |= { key : value }
else:
v2_lines.append(s.strip())
@@ -58,6 +63,20 @@ def inp_in_option_v3(section: list[str]) -> ChangeSet:
return result
def inp_in_option_v3(section: list[str]) -> str:
sql = ''
result = _inp_in_option_v3(section)
for op in result.operations:
for key in op.keys():
if key == 'operation' or key == 'type':
continue
if op['type'] == 'option_v3':
sql += f"update options_v3 set value = '{op[key]}' where key = '{key}';"
else:
sql += f"update options set value = '{op[key]}' where key = '{key}';"
return sql
def inp_out_option_v3(name: str) -> list[str]:
lines = []
objs = read_all(name, f"select * from options_v3")

View File

@@ -16,15 +16,13 @@ def get_node_coord(name: str, id: str) -> dict[str, float]:
# id x y
#--------------------------------------------------------------
# exception ! need merge to node change set !
def inp_in_coord(section: list[str]) -> dict[str, dict[str, float]]:
coords = {}
for s in section:
# skip comment
if s.startswith(';'):
continue
tokens = s.split()
coords[tokens[0]] = { 'x': tokens[1], 'y': tokens[2] }
return coords
def inp_in_coord(line: str) -> str:
tokens = line.split()
node = tokens[0]
coord = f"'({tokens[1]}, {tokens[2]})'"
return f"insert into coordinates (node, coord) values ('{node}', {coord});"
def inp_out_coord(name: str) -> list[str]:

View File

@@ -80,22 +80,14 @@ def delete_vertex(name: str, cs: ChangeSet) -> ChangeSet:
# id x y
# [EPA3][NOT SUPPORT]
#--------------------------------------------------------------
def inp_in_vertex(section: list[str]) -> ChangeSet:
vertices: dict[str, list[dict[str, float]]] = {}
for s in section:
if s.startswith(';'):
continue
tokens = s.split()
if tokens[0] not in vertices:
vertices[tokens[0]] = []
vertices[tokens[0]].append({'x': float(tokens[1]), 'y': float(tokens[2])})
cs = ChangeSet()
for link, coords in vertices.items():
cs.append(g_add_prefix | {'type': 'vertex', 'link' : link, 'coords' : coords})
return cs
def inp_in_vertex(line: str) -> str:
tokens = line.split()
link = tokens[0]
x = float(tokens[1])
y = float(tokens[2])
return f"insert into vertices (link, x, y) values ('{link}', {x}, {y});"
def inp_out_vertex(name: str) -> list[str]:

View File

@@ -99,29 +99,20 @@ def delete_label(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, delete_label_cmd(name, cs))
class InpLabel:
def __init__(self, line: str) -> None:
tokens = line.split()
def inp_in_label(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.x = float(tokens[0])
self.y = float(tokens[1])
self.label = str(tokens[2])
self.node = str(tokens[3]) if num >= 4 else None
x = float(tokens[0])
y = float(tokens[1])
label = str(tokens[2])
node = str(tokens[3]) if num >= 4 else None
node = f"'{node}'" if node != None else 'null'
def inp_in_label(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpLabel(s)
cs.append(g_add_prefix | {'type': 'label', 'x': obj.x, 'y': obj.y, 'label': obj.label, 'node': obj.node})
return cs
return f"insert into labels (x, y, label, node) values ({x}, {y}, '{label}', {node});"
def inp_out_label(name: str) -> list[str]:

View File

@@ -26,16 +26,12 @@ def set_backdrop(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, set_backdrop_cmd(name, cs))
class InpBackdrop:
def __init__(self, section) -> None:
self.value = '\n'.join(section)
def inp_in_backdrop(section: list[str]) -> str:
if section == []:
return ''
def inp_in_backdrop(section: list[str]) -> ChangeSet:
if len(section) > 0:
obj = InpBackdrop(section)
return ChangeSet(g_update_prefix | {'type': 'backdrop', 'content' : obj.value})
return ChangeSet()
content = '\n'.join(section)
return f"update backdrop set content = '{content}';"
def inp_out_backdrop(name: str) -> list[str]:

View File

@@ -125,31 +125,27 @@ def delete_junction(name: str, cs: ChangeSet) -> ChangeSet:
# [OUT]
# id elev. * * minpressure fullpressure
#--------------------------------------------------------------
class InpJunction:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.id = str(tokens[0])
self.elevation = float(tokens[1])
self.demand = float(tokens[2]) if num_without_desc >= 3 else None
self.pattern = str(tokens[3]) if num_without_desc >= 4 else None
self.desc = str(tokens[-1]) if has_desc else None
def inp_in_junction(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpJunction(s)
cs.append(g_add_prefix | {'type': 'junction', 'id': obj.id, 'elevation': obj.elevation, 'demand': obj.demand, 'pattern': obj.pattern})
cs.append(g_update_prefix | { 'type': 'demand', 'junction': obj.id, 'demands': [{'demand': obj.demand, 'pattern': obj.pattern, 'category': None}] })
return cs
def inp_in_junction(line: str, demand_outside: bool) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
elevation = float(tokens[1])
demand = float(tokens[2]) if num_without_desc >= 3 else None
pattern = str(tokens[3]) if num_without_desc >= 4 else None
pattern = f"'{pattern}'" if pattern != None else 'null'
desc = str(tokens[-1]) if has_desc else None
sql = f"insert into _node (id, type) values ('{id}', 'junction');insert into junctions (id, elevation) values ('{id}', {elevation});"
if demand != None and demand_outside == False:
sql += f"insert into demands (junction, demand, pattern) values ('{id}', {demand}, {pattern});"
return sql
def inp_out_junction(name: str) -> list[str]:

View File

@@ -120,29 +120,22 @@ def delete_reservoir(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA2][EPA3][IN][OUT]
# id elev (pattern) ;desc
#--------------------------------------------------------------
class InpReservoir:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.id = str(tokens[0])
self.head = float(tokens[1])
self.pattern = str(tokens[2]) if num_without_desc >= 3 else None
self.desc = str(tokens[-1]) if has_desc else None
def inp_in_reservoir(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpReservoir(s)
cs.append(g_add_prefix | {'type': 'reservoir', 'id': obj.id, 'head': obj.head, 'pattern': obj.pattern})
return cs
def inp_in_reservoir(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
head = float(tokens[1])
pattern = str(tokens[2]) if num_without_desc >= 3 else None
pattern = f"'{pattern}'" if pattern != None else 'null'
desc = str(tokens[-1]) if has_desc else None
return f"insert into _node (id, type) values ('{id}', 'reservoir');insert into reservoirs (id, head, pattern) values ('{id}', {head}, {pattern});"
def inp_out_reservoir(name: str) -> list[str]:

View File

@@ -156,35 +156,29 @@ def delete_tank(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA3]
# id elev initlevel minlevel maxlevel diam minvol (vcurve)
#--------------------------------------------------------------
class InpTank:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.id = str(tokens[0])
self.elevation = float(tokens[1])
self.init_level = float(tokens[2])
self.min_level = float(tokens[3])
self.max_level = float(tokens[4])
self.diameter = float(tokens[5])
self.min_vol = float(tokens[6]) if num_without_desc >= 7 else 0.0
self.vol_curve = str(tokens[7]) if num_without_desc >= 8 and tokens[7] != '*' else None
self.overflow = str(tokens[8].upper()) if num_without_desc >= 9 else None
self.desc = str(tokens[-1]) if has_desc else None
def inp_in_tank(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpTank(s)
cs.append(g_add_prefix | {'type': 'tank', 'id': obj.id, 'elevation': obj.elevation, 'init_level': obj.init_level, 'min_level': obj.min_level, 'max_level': obj.max_level, 'diameter': obj.diameter, 'min_vol': obj.min_vol, 'vol_curve': obj.vol_curve, 'overflow': obj.overflow})
return cs
def inp_in_tank(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
elevation = float(tokens[1])
init_level = float(tokens[2])
min_level = float(tokens[3])
max_level = float(tokens[4])
diameter = float(tokens[5])
min_vol = float(tokens[6]) if num_without_desc >= 7 else 0.0
vol_curve = str(tokens[7]) if num_without_desc >= 8 and tokens[7] != '*' else None
vol_curve = f"'{vol_curve}'" if vol_curve != None else 'null'
overflow = str(tokens[8].upper()) if num_without_desc >= 9 else None
overflow = f"'{overflow}'" if overflow != None else 'null'
desc = str(tokens[-1]) if has_desc else None
return f"insert into _node (id, type) values ('{id}', 'tank');insert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow) values ('{id}', {elevation}, {init_level}, {min_level}, {max_level}, {diameter}, {min_vol}, {vol_curve}, {overflow});"
def inp_out_tank(name: str) -> list[str]:

View File

@@ -130,35 +130,27 @@ def delete_pipe(name: str, cs: ChangeSet) -> ChangeSet:
# [OUT]
# id node1 node2 length diam rcoeff lcoeff (status) ;desc
#--------------------------------------------------------------
class InpPipe:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.id = str(tokens[0])
self.node1 = str(tokens[1])
self.node2 = str(tokens[2])
self.length = float(tokens[3])
self.diameter = float(tokens[4])
self.roughness = float(tokens[5])
self.minor_loss = float(tokens[6])
# status is must-have, here fix input
self.status = str(tokens[7].upper()) if num_without_desc >= 8 else PIPE_STATUS_OPEN
self.desc = str(tokens[-1]) if has_desc else None
def inp_in_pipe(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpPipe(s)
cs.append(g_add_prefix | {'type': 'pipe', 'id': obj.id, 'node1': obj.node1, 'node2': obj.node2, 'length': obj.length, 'diameter': obj.diameter, 'roughness': obj.roughness, 'minor_loss': obj.minor_loss, 'status': obj.status})
return cs
def inp_in_pipe(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
node1 = str(tokens[1])
node2 = str(tokens[2])
length = float(tokens[3])
diameter = float(tokens[4])
roughness = float(tokens[5])
minor_loss = float(tokens[6])
# status is must-have, here fix input
status = str(tokens[7].upper()) if num_without_desc >= 8 else PIPE_STATUS_OPEN
desc = str(tokens[-1]) if has_desc else None
return f"insert into _link (id, type) values ('{id}', 'pipe');insert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ('{id}', '{node1}', '{node2}', {length}, {diameter}, {roughness}, {minor_loss}, '{status}');"
def inp_out_pipe(name: str) -> list[str]:

View File

@@ -119,36 +119,32 @@ def delete_pump(name: str, cs: ChangeSet) -> ChangeSet:
# id node1 node2 KEYWORD value {KEYWORD value ...} ;desc
# where KEYWORD = [POWER,HEAD,PATTERN,SPEED]
#--------------------------------------------------------------
class InpPump:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.id = str(tokens[0])
self.node1 = str(tokens[1])
self.node2 = str(tokens[2])
self.props = {}
for i in range(3, num_without_desc, 2):
self.props |= { tokens[i].lower(): tokens[i + 1] }
self.power = float(self.props['power']) if 'power' in self.props else None
self.head = str(self.props['head']) if 'head' in self.props else None
self.speed = float(self.props['speed']) if 'speed' in self.props else None
self.pattern = str(self.props['pattern']) if 'pattern' in self.props else None
self.desc = str(tokens[-1]) if has_desc else None
def inp_in_pump(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpPump(s)
cs.append(g_add_prefix | {'type': 'pump', 'id': obj.id, 'node1': obj.node1, 'node2': obj.node2, 'power': obj.power, 'head': obj.head, 'speed': obj.speed, 'pattern': obj.pattern})
return cs
def inp_in_pump(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
node1 = str(tokens[1])
node2 = str(tokens[2])
props = {}
for i in range(3, num_without_desc, 2):
props |= { tokens[i].lower(): tokens[i + 1] }
power = float(props['power']) if 'power' in props else None
power = power if power != None else 'null'
head = str(props['head']) if 'head' in props else None
head = f"'{head}'" if head != None else 'null'
speed = float(props['speed']) if 'speed' in props else None
speed = speed if speed != None else 'null'
pattern = str(props['pattern']) if 'pattern' in props else None
pattern = f"'{pattern}'" if pattern != None else 'null'
desc = str(tokens[-1]) if has_desc else None
return f"insert into _link (id, type) values ('{id}', 'pump');insert into pumps (id, node1, node2, power, head, speed, pattern) values ('{id}', '{node1}', '{node2}', {power}, {head}, {speed}, {pattern});"
def inp_out_pump(name: str) -> list[str]:

View File

@@ -128,33 +128,25 @@ def delete_valve(name: str, cs: ChangeSet) -> ChangeSet:
# for GPV, setting is string = head curve id
# [NOT SUPPORT] for PCV, add loss curve if present
#--------------------------------------------------------------
class InpValve:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.id = str(tokens[0])
self.node1 = str(tokens[1])
self.node2 = str(tokens[2])
self.diameter = float(tokens[3])
self.v_type = str(tokens[4].upper())
self.setting = str(tokens[5])
self.minor_loss = float(tokens[6])
self.desc = str(tokens[-1]) if has_desc else None
def inp_in_valve(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpValve(s)
cs.append(g_add_prefix | {'type': 'valve', 'id': obj.id, 'node1': obj.node1, 'node2': obj.node2, 'diameter': obj.diameter, 'v_type': obj.v_type, 'setting': obj.setting, 'minor_loss': obj.minor_loss})
return cs
def inp_in_valve(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
id = str(tokens[0])
node1 = str(tokens[1])
node2 = str(tokens[2])
diameter = float(tokens[3])
v_type = str(tokens[4].upper())
setting = str(tokens[5])
minor_loss = float(tokens[6])
desc = str(tokens[-1]) if has_desc else None
return f"insert into _link (id, type) values ('{id}', 'valve');insert into valves (id, node1, node2, diameter, type, setting, minor_loss) values ('{id}', '{node1}', '{node2}', {diameter}, '{v_type}', '{setting}', {minor_loss});"
def inp_out_valve(name: str) -> list[str]:

View File

@@ -79,28 +79,22 @@ def set_tag(name: str, cs: ChangeSet) -> ChangeSet:
return execute_command(name, set_tag_cmd(name, cs))
class InpTag:
def __init__(self, line: str) -> None:
tokens = line.split()
def inp_in_tag(line: str) -> str:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.t_type = str(tokens[0].upper())
self.id = str(tokens[1])
self.tag = str(tokens[2])
t_type = str(tokens[0].upper())
id = str(tokens[1])
tag = str(tokens[2])
def inp_in_tag(section: list[str]) -> ChangeSet:
cs = ChangeSet()
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpTag(s)
cs.append(g_update_prefix | {'type': 'tag', 't_type': obj.t_type, 'id': obj.id, 'tag': obj.tag})
return cs
if t_type == TAG_TYPE_NODE:
return f"insert into tags_node (id, tag) values ('{id}', '{tag}');"
elif t_type == TAG_TYPE_LINK:
return f"insert into tags_link (id, tag) values ('{id}', '{tag}');"
return ''
def inp_out_tag(name: str) -> list[str]:

View File

@@ -63,38 +63,23 @@ def set_demand(name: str, cs: ChangeSet) -> ChangeSet:
# [EPA2][EPA3][IN][OUT]
# node base_demand (pattern) ;category
#--------------------------------------------------------------
class InpDemand:
def __init__(self, line: str) -> None:
tokens = line.split()
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
self.junction = str(tokens[0])
self.demand = float(tokens[1])
self.pattern = str(tokens[2]) if num_without_desc >= 3 else None
self.category = str(tokens[3]) if num_without_desc >= 4 else None
def inp_in_demand(section: list[str]) -> ChangeSet:
objs: dict[str, list[InpDemand]] = {}
for s in section:
# skip comment
if s.startswith(';'):
continue
obj = InpDemand(s)
if obj.junction not in objs:
objs[obj.junction] = []
objs[obj.junction].append(obj)
def inp_in_demand(line: str) -> str:
tokens = line.split()
cs = ChangeSet()
for junction, demands in objs.items():
obj_cs : dict[str, Any] = g_update_prefix | {'type': 'demand', 'junction' : junction, 'demands' : []}
for obj in demands:
obj_cs['demands'].append({'demand': obj.demand, 'pattern' : obj.pattern, 'category': obj.category})
cs.append(obj_cs)
return cs
num = len(tokens)
has_desc = tokens[-1].startswith(';')
num_without_desc = (num - 1) if has_desc else num
junction = str(tokens[0])
demand = float(tokens[1])
pattern = str(tokens[2]) if num_without_desc >= 3 else None
pattern = f"'{pattern}'" if pattern != None else 'null'
category = str(tokens[3]) if num_without_desc >= 4 else None
category = f"'{category}'" if category != None else 'null'
return f"insert into demands (junction, demand, pattern, category) values ('{junction}', {demand}, {pattern}, {category});"
def inp_out_demand(name: str) -> list[str]:

View File

@@ -34,9 +34,38 @@ s29_scada_device = 'scada_device'
s30_scada_device_data = 'scada_device_data'
s31_scada_element = 'scada_element'
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',
'LABELS', 'BACKDROP', 'END']
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'
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,
LABELS, BACKDROP, END]

View File

@@ -1,14 +1,14 @@
from tjnetwork import *
files = [
#'bwsn',
#'exnet',
#'fengxian',
#'jbh',
#'nanjing',
#'net3',
#'zj',
#'suzhouhe',
'bwsn',
'exnet',
'fengxian',
'jbh',
'nanjing',
'net3',
'zj',
'suzhouhe',
]
def inp2db():
@@ -20,7 +20,7 @@ def db2inp():
dump_inp(file, f'./db_inp/{file}.db.inp')
if __name__ == '__main__':
#inp2db()
inp2db()
#db2inp()
#print(run_inp('net3'))
pass

View File

@@ -1 +1 @@
from .epanet2 import run_project, run_inp, dump_output
from .epanet import run_project, run_inp, dump_output

View File

@@ -227,17 +227,21 @@ def dump_output(path: str) -> str:
return json.dumps(data)
def run_project(name: str) -> str:
def run_project(name: str, version: str = '2') -> str:
if version != '3' and version != '2':
version = '2'
if not project.have_project(name):
raise Exception(f'Not found project [{name}]')
dir = 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)
inp_out.dump_inp(name, db_inp, version)
input = name + '.db'
exe = os.path.join(os.path.join(dir, 'epanet'), 'runepanet.exe')
program = 'runepanet.exe' if version == '2' else 'run-epanet3.exe'
exe = os.path.join(os.path.join(dir, 'epanet'), program)
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')
@@ -250,17 +254,24 @@ def run_project(name: str) -> str:
data['simulation_result'] = 'failed'
else:
data['simulation_result'] = 'successful'
data |= _dump_output(opt)
if version == '2':
data |= _dump_output(opt)
else:
pass # TODO: epanet3 output format?
data['report'] = dump_report(rpt)
return json.dumps(data)
def run_inp(name: str) -> str:
def run_inp(name: str, version: str = '2') -> str:
if version != '3' and version != '2':
version = '2'
dir = os.path.abspath(os.getcwd())
exe = os.path.join(os.path.join(dir, 'epanet'), 'runepanet.exe')
program = 'runepanet.exe' if version == '2' else 'run-epanet3.exe'
exe = os.path.join(os.path.join(dir, 'epanet'), program)
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')
@@ -273,7 +284,10 @@ def run_inp(name: str) -> str:
data['simulation_result'] = 'failed'
else:
data['simulation_result'] = 'successful'
data |= _dump_output(opt)
if version == '2':
data |= _dump_output(opt)
else:
pass # TODO: epanet3 output format?
data['report'] = dump_report(rpt)

View File

@@ -185,24 +185,24 @@ def close_project(name: str) -> None:
def copy_project(source: str, new: str) -> None:
return api.copy_project(source, new)
def read_inp(name: str, inp: str) -> None:
return api.read_inp(name, inp)
def read_inp(name: str, inp: str, version: str = '3') -> bool:
return api.read_inp(name, inp, version)
def dump_inp(name: str, inp: str) -> None:
return api.dump_inp(name, inp)
def dump_inp(name: str, inp: str, version: str = '3') -> None:
return api.dump_inp(name, inp, version)
def import_inp(name: str, cs: ChangeSet) -> ChangeSet:
return api.import_inp(name, cs)
def import_inp(name: str, cs: ChangeSet, version: str = '3') -> bool:
return api.import_inp(name, cs, version)
def export_inp(name: str) -> ChangeSet:
return api.export_inp(name)
def export_inp(name: str, version: str = '3') -> ChangeSet:
return api.export_inp(name, version)
def run_project(name: str) -> str:
return epanet.run_project(name)
def run_project(name: str, version: str = '2') -> str:
return epanet.run_project(name, version)
# put in inp folder, name without extension
def run_inp(name: str) -> str:
return epanet.run_inp(name)
def run_inp(name: str, version: str = '2') -> str:
return epanet.run_inp(name, version)
# path is absolute path
def dump_output(path: str) -> str: