using Hydro.Core.Model;
|
using Hydro.MapView.Common;
|
using System;
|
using System.Collections.Generic;
|
using System.Drawing;
|
using System.IO;
|
using System.Linq;
|
using System.Numerics;
|
using System.Text;
|
using System.Text.RegularExpressions;
|
using System.Threading.Tasks;
|
using System.Windows.Forms;
|
using static Hydro.Core.ObjectEnum;
|
using static Hydro.MapView.RepeaterViewModel;
|
using static System.Math;
|
namespace Hydro.MapView
|
{
|
partial class MapViewNetWork
|
{
|
|
public bool BuildFromInp(string filePath, bool use_old=false)
|
{
|
this.use_old = use_old;
|
if (this.use_old)
|
{
|
return loadInpFile_old(filePath);
|
}
|
if (filePath == null || !File.Exists(filePath)) return false;
|
List<Coor> points = new List<Coor>();
|
dict_dataset = new Dictionary<string, Dataset>();
|
StreamReader sr = new StreamReader(filePath);
|
//try
|
{
|
Nodes = new NodeViewModelList();
|
Links = new LinkViewModelList();
|
|
|
|
string line;
|
string section = "";
|
while ((line = sr.ReadLine()) != null)
|
{
|
if (line.Trim().StartsWith("["))
|
{
|
section = line.TrimStart('[').TrimEnd(']');
|
}
|
else
|
{
|
string s = line.Trim('\t').Trim(' ');
|
if (s.Length == 0 || s[0] == ';') continue;
|
//line = line.Replace("\t\t", "\t_\t").Replace("\t \t", "\t_\t");
|
Parts parts = new Parts(line);
|
section = section.ToUpper();
|
IBaseViewModel o = null;
|
switch (section)
|
{
|
case "JUNCTIONS":
|
{
|
NodeViewModel j = null;
|
if (parts[-2] == "Meter")
|
j = new MeterViewModel();
|
else if (parts[-2] == "Nozzle")
|
{
|
var n = new NozzleViewModel();
|
j = n;
|
n.FlowCoefficient = parts.ToFloat(-4, 0);
|
}
|
|
else
|
j = new JunctionViewModel();
|
|
o = j;
|
j.ID = parts.ToString(0, null);
|
j.Elev = parts.ToFloat(1, 0);
|
j.Demand = parts.ToFloat(2, 0);
|
j.PatternID = parts.ToString(3, null);
|
if (j.PatternID == "NONE") j.PatternID = null;
|
j.Level = parts.ToInt(-1, 0);
|
|
Nodes.Add(j);
|
}
|
break;
|
case "RESERVOIRS":
|
{
|
ReservoirViewModel r = new ReservoirViewModel();
|
o = r;
|
r.ID = parts.ToString(0, null);
|
r.Head = parts.ToFloat(1, 0);
|
r.PatternID = parts.ToString(2, null);
|
r.Level = parts.ToInt(-1, 0);
|
r.Elev = parts.ToFloat(-2, 0);
|
Nodes.Add(r);
|
}
|
break;
|
case "TANKS":
|
{
|
TankViewModel tank = new TankViewModel();
|
o = tank;
|
tank.ID = parts.ToString(0, null);
|
tank.Elev = parts.ToFloat(1, 0);
|
tank.InitLevel = parts.ToFloat(2, 0);
|
tank.MinLevel = parts.ToFloat(3, 0);
|
tank.MaxLevel = parts.ToFloat(4, 0);
|
tank.Diameter = parts.ToFloat(5, 0);
|
tank.MinVol = parts.ToFloat(6, 0);
|
tank.VolCurve = parts.ToString(7, null);
|
tank.Overflow = "";
|
tank.Level = parts.ToInt(-1, 0);
|
Nodes.Add(tank);
|
}
|
break;
|
case "PIPES":
|
{
|
|
if (parts[-2] == "Repeater")
|
{
|
RepeaterViewModel repeater = new RepeaterViewModel();
|
o = repeater;
|
repeater.ID = parts.ToString(0, null);
|
repeater.Node1 = parts.ToString(1, null);
|
repeater.Node2 = parts.ToString(2, null);
|
repeater.Length = parts.ToFloat(3, 0.1f);
|
repeater.Diameter = parts.ToFloat(4, 150f);
|
repeater.Roughness = parts.ToFloat(5, 100f);
|
repeater.MinorLoss = parts.ToFloat(6, 0);
|
//repeater.Status = parts.ToString(7, "OPEN");
|
// 取出Node1和Node2中的字母部分,例如“S201326593”被取出为“201326593”
|
repeater.Level = parts.ToInt(-1, 0);
|
repeater.TemplateID = parts.ToString(-3, null);
|
repeater.RepeatTimes = parts.ToInt(-4, 1);
|
RepeatStatus status;
|
if (Enum.TryParse(parts[-5], out status))
|
repeater.Status = status;
|
repeater.NetworkPreName = parts.ToString(-6, null);
|
repeater.NetworkPreStartNum = parts.ToInt(-7, 0);
|
repeater.NetworkIsAscNum = parts.ToBool(-8, false);
|
repeater.NetworkShowName = parts.ToString(-9, null);
|
|
Links.Add(repeater);
|
}
|
else
|
{
|
PipeViewModel p = new PipeViewModel();
|
o = p;
|
p.ID = parts.ToString(0, null);
|
p.Node1 = parts.ToString(1, null);
|
p.Node2 = parts.ToString(2, null);
|
p.Length = parts.ToFloat(3, 0.1f);
|
p.Diameter = parts.ToFloat(4, 150f);
|
p.Roughness = parts.ToFloat(5, 100f);
|
p.MinorLoss = parts.ToFloat(6, 0);
|
p.Status = StringToStatus(parts.ToString(7, "OPEN"));
|
|
|
|
p.Level = parts.ToInt(-1, 0);
|
Links.Add(p);
|
}
|
|
}
|
break;
|
|
case "VALVES":
|
{
|
ValveViewModel valve = new ValveViewModel();
|
o = valve;
|
valve.ID = parts.ToString(0, null);
|
|
// 取出Node1和Node2中的字母部分,例如“S201326593”被取出为“201326593”
|
valve.Node1 = parts.ToString(1, null); // Regex.Replace(parts[1], "[^0-9]", "");
|
valve.Node2 = parts.ToString(2, null); // Regex.Replace(parts[2], "[^0-9]", "");
|
valve.Diameter = parts.ToFloat(3, 0);
|
valve.Type = parts.ToString(4, null);
|
valve.Setting = parts.ToString(5, null);
|
valve.MinorLoss = parts.ToFloat(6, 0);
|
valve.Level = parts.ToInt(-1, 0);
|
Links.Add(valve);
|
}
|
break;
|
case "PUMPS":
|
{
|
PumpViewModel pump = new PumpViewModel();
|
o = pump;
|
pump.ID = parts.ToString(0, null);
|
|
// 取出Node1和Node2中的字母部分,例如“S201326593”被取出为“201326593”
|
pump.Node1 = parts.ToString(1, null); // Regex.Replace(parts[1], "[^0-9]", "");
|
pump.Node2 = parts.ToString(2, null); // Regex.Replace(parts[2], "[^0-9]", "");
|
int index = 3;
|
string label = null;
|
while ((label = parts.ToString(index, null)) != null)
|
{
|
label = label.ToUpper();
|
switch (label)
|
{
|
case "HEAD":
|
pump.HeadCurve = parts.ToString(index + 1, "PumpDefault");
|
break;
|
case "SPEED":
|
pump.当前转速 = parts.ToFloat(index + 1, 0);
|
break;
|
}
|
index += 2;
|
}
|
//pump.Diameter = parts.ToFloat(3, 0);
|
|
pump.Level = parts.ToInt(-1, 0);
|
Links.Add(pump);
|
}
|
break;
|
case "CURVES":
|
{
|
string ID = parts.ToString(0, "");
|
if (!dict_dataset.ContainsKey(ID))
|
{
|
var ds = new Dataset(ID, null);
|
dict_dataset.Add(ID, ds);
|
}
|
dict_dataset[ID]._data.Add(new PointF(parts.ToFloat(1, 0), parts.ToFloat(2, 0)));
|
}
|
break;
|
case "COORDINATES":
|
{
|
string id = parts[0];
|
float x;
|
float y;
|
if (float.TryParse(parts[1], out x) && float.TryParse(parts[2], out y))
|
{
|
points.Add(new Coor(id, new PointF(x, y)));
|
}
|
}
|
break;
|
case "STATUS":
|
{
|
LinkViewModel link = Links.Find(l => l.ID == parts.ToString(0, null));
|
if (link != null)
|
link.Status = StringToStatus(parts.ToString(1, "OPEN"));
|
}
|
break;
|
}
|
if (o!=null)
|
{
|
o.Tags =new TagList( parts.ToString(-3, null));
|
}
|
|
}
|
}
|
sr.Close();
|
|
|
if (!dict_dataset.ContainsKey("GPVDefault"))
|
{
|
var data = new Dataset("GPVDefault", null);
|
data.Data = new List<PointF>()
|
{
|
new PointF(0,0),
|
new PointF(100,0),
|
};
|
dict_dataset.Add("GPVDefault", data);
|
|
}
|
if (!dict_dataset.ContainsKey("PumpDefault"))
|
{
|
var data = new Dataset("PumpDefault", null);
|
data.Data = new List<PointF>()
|
{
|
new PointF(0f , 45.38f),
|
new PointF(83.33333333f ,45.25f),
|
new PointF(111.1111111f ,45.12f),
|
new PointF(138.8888889f ,44.96f),
|
new PointF(166.6666667f ,44.76f),
|
new PointF(194.4444444f ,44.52f),
|
new PointF(222.2222222f ,44.24f),
|
new PointF(250f ,43.92f),
|
new PointF(277.7777778f ,43.56f),
|
new PointF(305.5555556f ,43.17f),
|
new PointF(333.3333333f ,42.73f),
|
new PointF(361.1111111f ,42.25f),
|
new PointF(388.8888889f ,41.74f),
|
new PointF(416.6666667f ,41.18f),
|
new PointF(444.4444444f ,40.58f),
|
new PointF(472.2222222f ,39.95f),
|
new PointF(500f ,39.28f),
|
new PointF(527.7777778f ,38.56f),
|
new PointF(555.5555556f ,37.81f),
|
new PointF(583.3333333f ,37.02f),
|
new PointF(611.1111111f ,36.19f),
|
new PointF(638.8888889f ,35.32f),
|
new PointF(666.6666667f ,34.41f),
|
new PointF(694.4444444f ,33.46f),
|
new PointF(722.2222222f ,32.47f),
|
new PointF(750f ,31.44f),
|
new PointF(777.7777778f ,30.37f),
|
new PointF(805.5555556f ,29.27f),
|
|
|
|
};
|
dict_dataset.Add("PumpDefault", data);
|
}
|
|
//读取坐标
|
int k1 = 0;
|
int k2 = 0;
|
Nodes.Sort((a, b) => string.Compare(a.ID, b.ID));
|
points.Sort((a, b) => string.Compare(a.ID, b.ID));
|
k1 = 0;
|
k2 = 0;
|
while (k1 < Nodes.Count)
|
{
|
var J = Nodes[k1];
|
var coor = points[k2];
|
|
while (J.ID != coor.ID && k2 < points.Count)
|
{
|
k2++;
|
if (k2 < points.Count) coor = points[k2];
|
|
}
|
if (k2 == points.Count)
|
{
|
throw new Exception($"未找到Node[{J.ID}]的坐标");
|
}
|
J.X = coor.Position.X;
|
J.Y = coor.Position.Y;
|
|
k1++;
|
|
|
|
}
|
|
BuildRelation();
|
|
return true;
|
}
|
|
|
}
|
StatusType StringToStatus(string status)
|
{
|
|
switch (status)
|
{
|
case "CLOESD":
|
case "0":
|
return StatusType.CLOSED;
|
break;
|
case "OPEN":
|
case "1":
|
return StatusType.OPEN;
|
break;
|
case "ACTIVE":
|
return StatusType.ACTIVE;
|
break;
|
default:
|
return StatusType.DEFAULT;
|
break;
|
}
|
}
|
public void BuildRelation()
|
{
|
|
|
//读取坐标
|
int k1 = 0;//表示管线索引
|
int k2 = 0;//表示节点索引
|
Nodes.Sort((a, b) => string.Compare(a.ID, b.ID));
|
|
|
|
//建立点线关系链表StartNode,先将管线以Node1(节点1的ID)排序,再将Nodes按ID排序,建立两个游标k1、k2,正向一次循环,建立链表关系
|
//时间复杂度 O(n)
|
Links.Sort((a, b) => string.Compare(a.Node1, b.Node1));
|
k2 = 0;
|
k1 = 0;
|
while (k1 < Links.Count)
|
{
|
var p = Links[k1];
|
var J = Nodes[k2];
|
var k0 = k2;
|
while (J.ID != p.Node1 && k2 < Nodes.Count)
|
{
|
k2++;
|
if (k2 < Nodes.Count) J = Nodes[k2];
|
|
}
|
if (k2 == Nodes.Count)
|
{
|
k2 = k0;
|
k1++;
|
p.Visible = false;
|
continue;
|
//throw new Exception($"未找到Link[{p.ID}]的起始节点[{p.Node1}]");
|
}
|
p.StartNode = J;
|
p.Node1 = J.ID;
|
if (J.MaxDiameter < p.Diameter) J.MaxDiameter = p.Diameter;
|
J.Links.Add(p);
|
k1++;
|
}
|
|
//建立点线关系链表StartNode,先将管线以Node2(节点1的ID)排序,再将Nodes按ID排序,建立两个游标k1、k2,正向一次循环,建立链表关系
|
//时间复杂度 O(n)
|
Links.Sort((a, b) => string.Compare(a.Node2, b.Node2));
|
k2 = 0;
|
k1 = 0;
|
while (k1 < Links.Count)
|
{
|
var p = Links[k1];
|
var J = Nodes[k2];
|
var k0 = k2;
|
while (J.ID != p.Node2 && k2 < Nodes.Count)
|
{
|
k2++;
|
if (k2 < Nodes.Count) J = Nodes[k2];
|
}
|
if (k2 == Nodes.Count)
|
{
|
|
k2 = k0;
|
k1++;
|
p.Visible = false;
|
|
continue;
|
//throw new Exception($"未找到Link[{p.ID}]的终止节点[{p.Node2}]");
|
}
|
p.EndNode = J;
|
p.Node2 = J.ID;
|
if (J.MaxDiameter < p.Diameter) J.MaxDiameter = p.Diameter;
|
J.Links.Add(p);
|
k1++;
|
}
|
|
//while (valveNodes.Count>0)
|
//{
|
|
//}
|
|
|
|
foreach (ValveNodeViewModel vn in valveNodes.ToList())
|
{
|
if (vn.Links.Count != 2) continue;
|
|
var junc1 = AddJunction("S__" + vn.ID, vn.Position, vn.Elev);
|
if (vn.Links[0].StartNode.ID == vn.ID)
|
{
|
vn.Links[0].StartNode = junc1;
|
}
|
else
|
{
|
vn.Links[0].EndNode = junc1;
|
}
|
junc1.Links.Add(vn.Links[0]);
|
|
var junc2 = AddJunction("E__" + vn.ID, vn.Position, vn.Elev);
|
if (vn.Links[1].StartNode.ID == vn.ID)
|
{
|
vn.Links[1].StartNode = junc2;
|
}
|
else
|
{
|
vn.Links[1].EndNode = junc2;
|
}
|
junc2.Links.Add(vn.Links[1]);
|
ValveViewModel valve = AddValve(junc1, junc2);
|
|
valve.ID = vn.ID;
|
valve.Status = vn.Status;
|
junc1.Links.Add(valve);
|
junc2.Links.Add(valve);
|
Nodes.Remove(vn);
|
}
|
|
|
|
foreach (PumpNodeViewModel pn in pumpNodes.ToList())
|
{
|
if (pn.Links.Count != 2) continue;
|
if (pn.Links[0].ID != pn.Node1)
|
{
|
LinkViewModel temp0 = pn.Links[0] as LinkViewModel;
|
pn.Links.RemoveAt(0);
|
pn.Links.Add(temp0);
|
|
}
|
var junc1 = AddJunction("S__" + pn.ID, pn.Position, pn.Elev);
|
if (pn.Links[0].StartNode.ID == pn.ID)
|
{
|
pn.Links[0].StartNode = junc1;
|
}
|
else
|
{
|
pn.Links[0].EndNode = junc1;
|
}
|
junc1.Links.Add(pn.Links[0]);
|
|
var junc2 = AddJunction("E__" + pn.ID, pn.Position, pn.Elev);
|
if (pn.Links[1].StartNode.ID == pn.ID)
|
{
|
pn.Links[1].StartNode = junc2;
|
}
|
else
|
{
|
pn.Links[1].EndNode = junc2;
|
}
|
junc2.Links.Add(pn.Links[1]);
|
PumpViewModel pump = AddPump(junc1, junc2);
|
pump.Status = pn.Status;
|
pump.ID = pn.ID;
|
pump.额定功率 = pn.额定功率;
|
pump.额定扬程 = pn.额定扬程;
|
pump.额定流量 = pn.额定流量;
|
pump.HeadCurve = pn.HeadCurve;
|
pump.额定转速 = pn.额定转速;
|
pump.转速比 = pn.转速比;
|
junc1.Links.Add(pump);
|
junc2.Links.Add(pump);
|
Nodes.Remove(pn);
|
}
|
if (dict_dataset == null)
|
{
|
dict_dataset = new Dictionary<string, Dataset>();
|
|
}
|
pumps.ForEach(p =>
|
{
|
foreach (var kp in p.Datasets)
|
{
|
if (!dict_dataset.ContainsKey(kp.Value.Name))
|
{
|
dict_dataset.Add(kp.Value.Name, kp.Value);
|
}
|
}
|
});
|
pumps.ForEach((pump) =>
|
{
|
if (dict_dataset.ContainsKey(pump.HeadCurve))
|
{
|
Dataset ds = dict_dataset[pump.HeadCurve];
|
|
if (!pump.Datasets.ContainsKey("流量扬程曲线"))
|
{
|
pump.Datasets.Add("流量扬程曲线", ds);
|
}
|
else
|
{
|
pump.Datasets["流量扬程曲线"] = ds;
|
}
|
ds.pump = pump;
|
|
}
|
|
if (!pump.Datasets.ContainsKey("流量扬程曲线"))
|
{
|
pump.Datasets.Add("流量扬程曲线", new Dataset(pump.ID + "_Head", pump));
|
}
|
if (pump.Datasets["流量扬程曲线"].Data.Count <= 0)
|
{
|
pump.Datasets["流量扬程曲线"].Data.Add(new PointF((float)pump.额定流量, (float)pump.额定扬程));
|
}
|
pump.HeadCurve = pump.Datasets["流量扬程曲线"].Name;
|
});
|
|
//dict_dataset.Clear();
|
//valveNodes.ForEach(v => Nodes.Remove(v)); ;
|
Hash_ID = new HashSet<string>();
|
Nodes.ForEach(o => Hash_ID.Add(o.ID));
|
Links.ForEach(o => Hash_ID.Add(o.ID));
|
|
}
|
|
public bool loadInpFile_old(string filePath)
|
{
|
if (filePath == null || !File.Exists(filePath)) return false;
|
List<Coor> points = new List<Coor>();
|
StreamReader sr = new StreamReader(filePath);
|
//try
|
{
|
Nodes = new NodeViewModelList();
|
//reservoirs = new List<Reservoir>();
|
//tanks = new List<Tank>();
|
//meters = new List<Meter>();
|
|
Links =new LinkViewModelList();
|
//valves = new List<Valve>();
|
//repeaters = new List<Repeater>();
|
|
|
string line;
|
string section = "";
|
while ((line = sr.ReadLine()) != null)
|
{
|
if (line.Trim().StartsWith("["))
|
{
|
section = line.TrimStart('[').TrimEnd(']');
|
}
|
else
|
{
|
string s = line.Trim('\t').Trim(' ');
|
if (s.Length == 0 || s[0] == ';') continue;
|
line = line.Replace("\t\t", "\t_\t").Replace("\t \t", "\t_\t");
|
Parts parts = new Parts(line.Split(new char[] {
|
'\t', ' ', ';'
|
}, StringSplitOptions.RemoveEmptyEntries));
|
switch (section)
|
{
|
case "JUNCTIONS":
|
{
|
NodeViewModel j = null;
|
if (parts[4] == "Meter")
|
{
|
j = new MeterViewModel();
|
meters.Add((MeterViewModel)j);
|
}
|
else if (parts[4] == "Nozzle")
|
j = new NozzleViewModel();
|
else
|
j = new JunctionViewModel();
|
|
j.ID = parts[0];
|
float elev;
|
if (float.TryParse(parts[1], out elev))
|
j.Elev = elev;
|
float demand;
|
if (float.TryParse(parts[2], out demand))
|
j.Demand = demand;
|
j.PatternID = parts[3];
|
int level;
|
if (int.TryParse(parts[5], out level))
|
j.Level = level;
|
|
Nodes.Add(j);
|
|
}
|
break;
|
case "RESERVOIRS":
|
{
|
ReservoirViewModel r = new ReservoirViewModel();
|
r.ID = parts[0];
|
float head;
|
if (float.TryParse(parts[1], out head))
|
r.Head = head;
|
r.PatternID = parts.Length > 2 ? parts[2] : "";
|
int level;
|
if (int.TryParse(parts[3], out level))
|
r.Level = level;
|
Nodes.Add(r);
|
}
|
break;
|
case "TANKS":
|
{
|
TankViewModel tank = new TankViewModel(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]);
|
int level;
|
if (int.TryParse(parts[9], out level))
|
tank.Level = level;
|
Nodes.Add(tank);
|
}
|
break;
|
|
case "PIPES":
|
{
|
PipeViewModel p = new PipeViewModel();
|
p.ID = parts[0];
|
p.Node1 = parts[1];
|
p.Node2 = parts[2];
|
float length;
|
if (float.TryParse(parts[3], out length))
|
p.Length = length;
|
float diameter;
|
if (float.TryParse(parts[4], out diameter))
|
p.Diameter = diameter;
|
float roughness;
|
if (float.TryParse(parts[5], out roughness))
|
p.Roughness = roughness;
|
float minorLoss;
|
if (float.TryParse(parts[6], out minorLoss))
|
p.MinorLoss = minorLoss;
|
p.Status = parts.Length > 7 ? StringToStatus(parts[7]) : StatusType.DEFAULT;
|
int level;
|
if (int.TryParse(parts[8], out level))
|
p.Level = level;
|
Links.Add(p);
|
}
|
break;
|
|
case "VALVES":
|
{
|
ValveViewModel valve = new ValveViewModel();
|
valve.ID = parts[0];
|
|
// 取出Node1和Node2中的字母部分,例如“S201326593”被取出为“201326593”
|
valve.Node1 = parts[1]; // Regex.Replace(parts[1], "[^0-9]", "");
|
valve.Node2 = parts[2]; // Regex.Replace(parts[2], "[^0-9]", "");
|
|
float diameter;
|
if (float.TryParse(parts[3], out diameter))
|
valve.Diameter = diameter;
|
valve.Type = parts[4];
|
valve.Setting = parts[5];
|
float minorLoss;
|
if (float.TryParse(parts[6], out minorLoss))
|
valve.MinorLoss = minorLoss;
|
int level;
|
if (int.TryParse(parts[7], out level))
|
valve.Level = level;
|
Links.Add(valve);
|
}
|
break;
|
case "REPEATERS":
|
{
|
RepeaterViewModel repeater = new RepeaterViewModel();
|
repeater.ID = parts[0];
|
repeater.Node1 = parts[1];
|
repeater.Node2 = parts[2];
|
// 取出Node1和Node2中的字母部分,例如“S201326593”被取出为“201326593”
|
repeater.TemplateID = parts[3];
|
int repeatTimes;
|
if (int.TryParse(parts[4], out repeatTimes))
|
repeater.RepeatTimes = repeatTimes;
|
RepeatStatus status;
|
if (Enum.TryParse(parts[5], out status))
|
repeater.Status = status;
|
repeater.NetworkPreName = parts[6];
|
int networkPreStartNum;
|
if (int.TryParse(parts[7], out networkPreStartNum))
|
repeater.NetworkPreStartNum = networkPreStartNum;
|
bool networkIsAscNum;
|
if (bool.TryParse(parts[8], out networkIsAscNum))
|
repeater.NetworkIsAscNum = networkIsAscNum;
|
repeater.NetworkShowName = parts[9];
|
int level;
|
if (int.TryParse(parts[10], out level))
|
repeater.Level = level;
|
Links.Add(repeater);
|
}
|
break;
|
case "COORDINATES":
|
{
|
string id = parts[0];
|
float x;
|
float y;
|
if (float.TryParse(parts[1], out x) && float.TryParse(parts[2], out y))
|
{
|
points.Add(new Coor(id, new PointF(x, y)));
|
}
|
}
|
break;
|
}
|
}
|
}
|
sr.Close();
|
|
//读取坐标
|
int k1 = 0;
|
int k2 = 0;
|
Nodes.Sort((a, b) => string.Compare(a.ID, b.ID));
|
points.Sort((a, b) => string.Compare(a.ID, b.ID));
|
k1 = 0;
|
k2 = 0;
|
while (k1 < Nodes.Count)
|
{
|
var J = Nodes[k1];
|
var coor = points[k2];
|
|
while (J.ID != coor.ID && k2 < points.Count)
|
{
|
k2++;
|
if (k2 < points.Count) coor = points[k2];
|
|
}
|
if (k2 == points.Count)
|
{
|
throw new Exception($"未找到Node[{J.ID}]的坐标");
|
}
|
J.X = coor.Position.X;
|
J.Y = coor.Position.Y;
|
|
k1++;
|
|
|
|
}
|
|
|
|
//建立点线关系链表StartNode,先将管线以Node1(节点1的ID)排序,再将Nodes按ID排序,建立两个游标k1、k2,正向一次循环,建立链表关系
|
//时间复杂度 O(n)
|
Links.Sort((a, b) => string.Compare(a.Node1, b.Node1));
|
k1 = 0;
|
k2 = 0;
|
while (k2 < Links.Count)
|
{
|
var p = Links[k2];
|
var J = Nodes[k1];
|
while (J.ID != p.Node1 && k1 < Nodes.Count)
|
{
|
k1++;
|
if (k1 < Nodes.Count) J = Nodes[k1];
|
|
}
|
if (k1 == Nodes.Count)
|
{
|
throw new Exception($"未找到Link[{p.ID}]的起始节点[{p.Node1}]");
|
}
|
p.StartNode = J;
|
if (J.MaxDiameter < p.Diameter) J.MaxDiameter = p.Diameter;
|
J.Links.Add(p);
|
k2++;
|
}
|
|
//建立点线关系链表StartNode,先将管线以Node2(节点1的ID)排序,再将Nodes按ID排序,建立两个游标k1、k2,正向一次循环,建立链表关系
|
//时间复杂度 O(n)
|
Links.Sort((a, b) => string.Compare(a.Node2, b.Node2));
|
k1 = 0;
|
k2 = 0;
|
while (k2 < Links.Count)
|
{
|
var p = Links[k2];
|
var J = Nodes[k1];
|
while (J.ID != p.Node2 && k1 < Nodes.Count)
|
{
|
k1++;
|
if (k1 < Nodes.Count) J =Nodes[k1];
|
}
|
if (k1 == Nodes.Count)
|
{
|
throw new Exception($"未找到Link[{p.ID}]的终止节点[{p.Node2}]");
|
}
|
p.EndNode = J;
|
if (J.MaxDiameter < p.Diameter) J.MaxDiameter = p.Diameter;
|
J.Links.Add(p);
|
k2++;
|
|
}
|
|
|
return true;
|
}
|
|
|
}
|
public void ClearMinorLoss()
|
{
|
Links.ForEach(l => l.MinorLoss = 0);
|
}
|
public void CalcLinkMinorLoss()
|
{
|
Links.ForEach(l => l.MinorLoss = 0);
|
Nodes.ForEach(n =>
|
{
|
var links_Down= n.ViewLinks.FindAll(l=>l.StartNode==n ?l.EN_FLOW>0:l.EN_FLOW<0);
|
var links_Up = n.ViewLinks.FindAll(l => l.StartNode == n ? l.EN_FLOW < 0 : l.EN_FLOW > 0);
|
/*两侧均连接一个管线*/
|
if (links_Down.Count==1 && links_Up.Count==1)
|
{
|
var link_Down = links_Down[0];
|
var link_Up = links_Up[0];
|
double angle = GetAngle(link_Down, link_Up);
|
var minorLoss = 0f;
|
minorLoss += GetMinorLossByAngle(angle);
|
minorLoss += GetChangeDiameterMinorLoss(link_Down, link_Up);
|
link_Up.MinorLoss += minorLoss;
|
}
|
/*上游管线为1根,下游管线为多个*/
|
if (links_Up.Count == 1 && links_Down.Count>1)
|
{
|
|
var link_Up = links_Up[0];
|
|
|
//计算两个管线的夹角,根据夹角计算损失系数
|
foreach (var link_Down in links_Down)
|
{
|
var angle = GetAngle(link_Down, link_Up);
|
var minorLoss = 0f;
|
minorLoss += GetMinorLossByAngle(angle);
|
minorLoss += GetChangeDiameterMinorLoss(link_Down, link_Up);
|
link_Down.MinorLoss += minorLoss;
|
}
|
|
}
|
|
/*下游管线为1根,上游管线为多个*/
|
if (links_Up.Count > 1 && links_Down.Count == 1)
|
{
|
var link_Down = links_Down[0];
|
|
|
|
//计算两个管线的夹角,根据夹角计算损失系数
|
foreach (var link_Up in links_Up)
|
{
|
var angle = GetAngle(link_Down, link_Up);
|
var minorLoss = 0f;
|
minorLoss += GetMinorLossByAngle(angle);
|
minorLoss += GetChangeDiameterMinorLoss(link_Down, link_Up);
|
link_Up.MinorLoss += minorLoss;
|
}
|
|
}
|
|
/*下游管线为多个,上游管线为多个*/
|
if (links_Up.Count > 1 && links_Down.Count > 1)
|
{
|
|
|
var UpCount= links_Up.Count;
|
|
//计算两个管线的夹角,根据夹角计算损失系数
|
foreach (var link_Up in links_Up)
|
{
|
|
foreach (var link_Down in links_Down)
|
{
|
var angle = GetAngle(link_Down, link_Up);
|
var minorLoss = 0f;
|
minorLoss += GetMinorLossByAngle(angle);
|
minorLoss += GetChangeDiameterMinorLoss(link_Down, link_Up);
|
link_Up.MinorLoss += minorLoss / UpCount;
|
}
|
|
}
|
|
}
|
|
|
|
});
|
}
|
private static float GetChangeDiameterMinorLoss(LinkViewModel link_Down, LinkViewModel link_Up)
|
{
|
var Diameter_Down = link_Down.Diameter;
|
var Diameter_Up = link_Up.Diameter;
|
//计算两个管线变径的损失系数
|
var minorLoss = 0f;
|
if(Diameter_Down>=Diameter_Up)//突然变粗
|
{
|
minorLoss = (float)Pow(1 - Diameter_Up/ Diameter_Down,2);
|
}
|
else//突然变细
|
{
|
minorLoss = 0.5f * (1 - Diameter_Down / Diameter_Up);
|
}
|
|
return minorLoss;
|
}
|
private static double GetAngle(LinkViewModel link_Down, LinkViewModel link_Up)
|
{
|
|
//两个管线link_Down,link_Up的向量
|
Vector3D vector_Down = new Vector3D(link_Down.EndNode.X - link_Down.StartNode.X, link_Down.EndNode.Y - link_Down.StartNode.Y, link_Down.EndNode.Elev- link_Down.StartNode.Elev);
|
Vector3D vector_Up=new Vector3D(link_Up.EndNode.X - link_Up.StartNode.X, link_Up.EndNode.Y - link_Up.StartNode.Y, link_Up.EndNode.Elev - link_Up.StartNode.Elev);
|
//获取两个向量vector_Down,vector_Up的空间夹角
|
if (vector_Down.Length == 0 || vector_Up.Length == 0) return 0;
|
else
|
return Vector3D.CalculateAngle(vector_Down, vector_Up);
|
|
|
|
|
|
}
|
|
private static float GetMinorLossByAngle(double angle)
|
{
|
double minorLoss;
|
minorLoss=0.946 * Pow(Sin(Abs(angle) / 2),2)+2.05* Pow(Sin(Abs(angle) / 2),4);
|
|
return (float)minorLoss;
|
}
|
|
public void BuildToInp(string filePath, string userCoorString = null, string sourcePath = null, bool isReplace = false)
|
{
|
|
if (sourcePath == null) sourcePath = filePath;
|
|
string tempString = "";
|
if (!isReplace)
|
{
|
var tempPath = Path.Combine(Directory.GetCurrentDirectory(), @"template\inp\导出模板.inp");
|
if (!File.Exists(tempPath))
|
{
|
//MessageBox.Show($"模板文件不存在[{tempPath}]");
|
return;
|
}
|
tempString = File.ReadAllText(tempPath);
|
}
|
else
|
{
|
tempString = File.ReadAllText(sourcePath);
|
}
|
|
|
Dictionary<string, string> dictExchange = new Dictionary<string, string>() {
|
{"{junctions}","{0}" },
|
{"{reservoirs}","{1}" },
|
{"{tanks}","{2}" },
|
{"{pipes}","{3}" },
|
{"{valves}","{4}" },
|
{"{pumps}","{5}" },
|
{"{coor}","{6}" },
|
{"{curve}","{7}" },
|
};
|
dictExchange.ToList().ForEach(m => tempString = tempString.Replace(m.Key, m.Value));
|
|
StringBuilder junctionStringBuilder = new StringBuilder();
|
|
junctionStringBuilder.AppendLine(";ID Elev Demand Pattern Type");
|
|
Nodes.ForEach(o0 =>
|
{
|
var o = o0;
|
if (!o.Visible) return;
|
|
if (o is JunctionViewModel j)
|
junctionStringBuilder.AppendLine(j.ToString() + $"{j.Level}\tJunction\t{o.Tags}");
|
else if (o is MeterViewModel m)
|
junctionStringBuilder.AppendLine(m.ToString() + $"{o.Level}\tMeter\t{o.Tags}");
|
else if (o is NozzleViewModel no)
|
junctionStringBuilder.AppendLine(no.ToString() + $"{o.Level}\tNozzle\t{o.Tags}\t{no.FlowCoefficient}");
|
});
|
string junctionString = junctionStringBuilder.ToString();
|
|
StringBuilder reservoirStringBuilder = new StringBuilder();
|
|
reservoirStringBuilder.AppendLine(";ID Head Pattern ");
|
|
reservoirs.ForEach(o =>
|
{
|
if (!o.Visible) return;
|
reservoirStringBuilder.AppendLine(o.ToString() + $"{o.Level}\t{o.Elev}\t{o.Tags}");
|
});
|
string reserverString = reservoirStringBuilder.ToString();
|
|
StringBuilder tankStringBuilder = new StringBuilder();
|
|
tankStringBuilder.AppendLine(";ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve Overflow");
|
|
tanks.ForEach(o =>
|
{
|
if (!o.Visible) return;
|
tankStringBuilder.AppendLine(o.ToString() + $"{o.Level}\tTank\t{o.Tags}");
|
});
|
string tankString = tankStringBuilder.ToString();
|
|
StringBuilder pipeStringBuilder = new StringBuilder();
|
|
pipeStringBuilder.AppendLine(";ID Node1 Node2 Length Diameter Roughness MinorLoss Status");
|
|
Links.ForEach(o0 =>
|
{
|
var o = (LinkViewModel)o0;
|
if (!o.Visible) return;
|
if (o is PipeViewModel p)
|
pipeStringBuilder.AppendLine(p.ToString() + $"{p.Level}\tPipe\t{p.Tags}");
|
else if (o is RepeaterViewModel r)
|
pipeStringBuilder.AppendLine(r.ToString());
|
});
|
string pipeString = pipeStringBuilder.ToString();
|
|
StringBuilder valveStringBuilder = new StringBuilder();
|
|
valveStringBuilder.AppendLine(";ID Node1 Node2 Diameter Type Setting MinorLoss ");
|
|
valves.ForEach(o =>
|
{
|
if (!o.Visible) return;
|
valveStringBuilder.AppendLine(o.ToString() + $"{o.Level}\tValve\t{o.Tags}");
|
});
|
string valveString = valveStringBuilder.ToString();
|
|
StringBuilder pumpStringBuilder = new StringBuilder();
|
|
pumpStringBuilder.AppendLine(";ID Node1 Node2 Diameter Type Setting MinorLoss ");
|
|
pumps.ForEach(o =>
|
{
|
if (!o.Visible) return;
|
pumpStringBuilder.AppendLine(o.ToString() + $"{o.Level}\tPump\t{o.Tags}");
|
});
|
string pumpString = pumpStringBuilder.ToString();
|
|
|
StringBuilder curveStringBuilder = new StringBuilder();
|
|
|
|
//pumps.ForEach(o =>
|
//{
|
// if (!o.Visible || !o.Datasets.ContainsKey("流量扬程曲线")) return;
|
// curveStringBuilder.AppendLine(o.Datasets["流量扬程曲线"].ToString());
|
//});
|
curveStringBuilder.AppendLine(@";ID X-Value Y-Value
|
;HEADLOSS:
|
GPVDefault 0 0
|
GPVDefault 100 0 ");
|
if (dict_dataset != null)
|
curveStringBuilder.AppendLine(";PUMP: ");
|
foreach (var kp in dict_dataset)
|
{
|
curveStringBuilder.AppendLine(kp.Value.ToString());
|
}
|
string curveString = curveStringBuilder.ToString();
|
|
|
StringBuilder coorStringBuilder = new StringBuilder();
|
if (userCoorString == null)
|
{
|
|
coorStringBuilder.AppendLine(";Node X-Coord Y-Coord");
|
Nodes.ForEach(o => coorStringBuilder.AppendLine(o.ToCoorString()));
|
}
|
else
|
{
|
coorStringBuilder.Append(userCoorString);
|
}
|
|
|
|
string coorString = coorStringBuilder.ToString();
|
string output = "";
|
|
|
|
|
|
StringBuilder emitterStringBuilder = new StringBuilder();
|
emitterStringBuilder.AppendLine(";Junction \tCoefficient");
|
Nodes.ForEach(o => emitterStringBuilder.Append((o).ToEmitterString()));
|
|
|
|
string emitterString = emitterStringBuilder.ToString();
|
|
|
|
|
|
StringBuilder statusStringBuilder = new StringBuilder();
|
statusStringBuilder.AppendLine(";ID \tStatus/Setting\r\n");
|
Links.ForEach(o => statusStringBuilder.Append(o.ToStatusString()));
|
|
|
|
string statusString = statusStringBuilder.ToString();
|
|
|
|
output = tempString;
|
|
output = replaceContent(output, "JUNCTIONS", junctionString);
|
output = replaceContent(output, "RESERVOIRS", reserverString);
|
output = replaceContent(output, "TANKS", tankString);
|
output = replaceContent(output, "PIPES", pipeString);
|
output = replaceContent(output, "VALVES", valveString);
|
output = replaceContent(output, "PUMPS", pumpString);
|
output = replaceContent(output, "CURVES", curveString);
|
|
|
output = replaceContent(output, "COORDINATES", coorString);
|
output = replaceContent(output, "EMITTERS", emitterString);
|
output = replaceContent(output, "STATUS", statusString);
|
string backupFolderPath = Path.Combine(Path.GetDirectoryName(filePath), "bk");
|
if (!Directory.Exists(backupFolderPath))
|
{
|
Directory.CreateDirectory(backupFolderPath);
|
}
|
|
string backupFileName = $"{Path.GetFileNameWithoutExtension(filePath)}_{DateTime.Now:yyyyMMddHHmmss}{Path.GetExtension(filePath)}";
|
string backupFilePath = Path.Combine(backupFolderPath, backupFileName);
|
//if (File.Exists(filePath)) File.Copy(filePath, backupFilePath, true);
|
|
|
// 检查文件是否存在
|
try
|
{
|
Global.ClearFileReadOnly(filePath);
|
|
File.WriteAllText(filePath, output);
|
}
|
catch (Exception ex)
|
{
|
MessageBox.Show(ex.Message);
|
}
|
|
//MessageBox.Show($"保存成功!");
|
}
|
|
private string replaceContent(string text, string content, string replaceString)
|
{
|
|
|
string str = replaceString;
|
|
string replacedText = ReplaceCoordinatesSection(text, content, str);
|
|
return replacedText;
|
//Console.WriteLine(replacedText);
|
}
|
|
public static string ReplaceCoordinatesSection(string text, string content, string str)
|
{
|
string pattern = $@"(\[{content}\]).*?(\[|$)";
|
|
string replacedText = Regex.Replace(text, pattern, match =>
|
{
|
string section = match.Groups[2].Value.Trim();
|
|
if (!string.IsNullOrEmpty(section))
|
{
|
return $"{match.Groups[1].Value}\n{str}\n[";
|
}
|
else
|
{
|
return $"{match.Groups[1].Value}\n{str}\n[";
|
}
|
}, RegexOptions.Singleline);
|
|
return replacedText;
|
}
|
|
}
|
public class Vector3D
|
{
|
public double X { get; set; }
|
public double Y { get; set; }
|
public double Z { get; set; }
|
|
public Vector3D(double x, double y, double z)
|
{
|
X = x;
|
Y = y;
|
Z = z;
|
}
|
public static double CalculateAngle(Vector3D v1, Vector3D v2)
|
{
|
// 计算两个向量的点积
|
double dotProduct = v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;
|
|
// 计算两个向量的模长
|
double v1Magnitude = Math.Sqrt(v1.X * v1.X + v1.Y * v1.Y + v1.Z * v1.Z);
|
double v2Magnitude = Math.Sqrt(v2.X * v2.X + v2.Y * v2.Y + v2.Z * v2.Z);
|
|
// 计算夹角(弧度)
|
double angleInRadians = Math.Acos(dotProduct / (v1Magnitude * v2Magnitude));
|
|
// 将弧度转换为度数
|
//double angleInDegrees = angleInRadians * (180 / Math.PI);
|
|
return angleInRadians;
|
}
|
public double Length
|
{
|
get
|
{
|
return Math.Sqrt( X * X + Y * Y + Z * Z);
|
}
|
}
|
}
|
}
|