using Newtonsoft.Json; using System.Numerics; using System.Text; namespace HydroUI { [Serializable] public class MapViewNetWork : NetWork { bool use_old = false; public string Name; private LinkViewModelList _links = new LinkViewModelList(); public LinkViewModelList Links { get { return _links; } set { base.Links = _links = value; } } private NodeViewModelList _nodes = new NodeViewModelList(); public NodeViewModelList Nodes { get { return _nodes; } set { base.Nodes = _nodes = value; } } private List _areas = new List(); public List Areas { get { return _areas; } set { _areas = value; } } public NodeViewModel StartPoint { get; set; } public NodeViewModel EndPoint { get; set; } public double[] getMinMax(ColourType colourType) { //根据当前选中的ColourType,判断是否有对应的网络数据 double max = 0; double min = 0; switch (colourType) { case ColourType.节点自由压力: max = Nodes.Where(node => !(node is ReservoirViewModel || node is TankViewModel)).Max(node => node.EN_PRESSURE); min = Nodes.Where(node => !(node is ReservoirViewModel || node is TankViewModel)).Min(node => node.EN_PRESSURE); break; case ColourType.节点绝对压力: max = Nodes.Where(node => !(node is ReservoirViewModel || node is TankViewModel)).Max(node => node.EN_PRESSURE); min = Nodes.Where(node => !(node is ReservoirViewModel || node is TankViewModel)).Min(node => node.EN_PRESSURE); break; case ColourType.节点需水量: max = Nodes.Where(node => !(node is ReservoirViewModel || node is TankViewModel)).Max(node => node.EN_DEMAND); min = Nodes.Where(node => !(node is ReservoirViewModel || node is TankViewModel)).Min(node => node.EN_DEMAND); break; case ColourType.管线流量: max = Links.Max(link => link.EN_FLOW); min = Links.Min(link => link.EN_FLOW); break; case ColourType.管线流速: max = Links.Max(link => link.EN_VELOCITY); min = Links.Min(link => link.EN_VELOCITY); break; default: break; } return new double[] { min, max }; } public HashSet Hash_ID; public List junctions { get { return Nodes.FindAll(n => n is JunctionViewModel).Select(n => n as JunctionViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is JunctionViewModel); Nodes.AddRange(value); } } public List reservoirs { get { return Nodes.FindAll(n => n is ReservoirViewModel).Select(n => n as ReservoirViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is ReservoirViewModel); Nodes.AddRange(value); } } public List tanks { get { return Nodes.FindAll(n => n is TankViewModel).Select(n => n as TankViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is TankViewModel); Nodes.AddRange(value); } } public List meters { get { return Nodes.FindAll(n => n is MeterViewModel).Select(n => n as MeterViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is MeterViewModel); Nodes.AddRange(value); } } public List nozzles { get { return Nodes.FindAll(n => n is NozzleViewModel).Select(n => n as NozzleViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is NozzleViewModel); Nodes.AddRange(value); } } public List pipes { get { return Links.FindAll(n => n is PipeViewModel).Select(n => n as PipeViewModel)?.ToList(); } set { if (value == null) return; Links.RemoveAll(n => n is PipeViewModel); Links.AddRange(value); } } public List valves { get { return Links.FindAll(n => n is ValveViewModel).Select(n => n as ValveViewModel)?.ToList(); } set { if (value == null) return; Links.RemoveAll(n => n is ValveViewModel); Links.AddRange(value); } } public List valveNodes { get { return Nodes.FindAll(n => n is ValveNodeViewModel).Select(n => n as ValveNodeViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is ValveNodeViewModel); Nodes.AddRange(value); } } public List repeaters { get { return Links.FindAll(n => n is RepeaterViewModel).Select(n => n as RepeaterViewModel)?.ToList(); } set { if (value == null) return; Links.RemoveAll(n => n is RepeaterViewModel); Links.AddRange(value); } } public List pumps { get { return Links.FindAll(n => n is PumpViewModel).Select(n => n as PumpViewModel)?.ToList(); } set { if (value == null) return; Links.RemoveAll(n => n is PumpViewModel); Links.AddRange(value); } } public List pumpNodes { get { return Nodes.FindAll(n => n is PumpNodeViewModel).Select(n => n as PumpNodeViewModel)?.ToList(); } set { if (value == null) return; Nodes.RemoveAll(n => n is PumpNodeViewModel); Nodes.AddRange(value); } } public List Deserialized_junctions { get; set; } public List Deserialized_tanks { get; set; } public List Deserialized_reservoirs { get; set; } public List Deserialized_meters { get; set; } public List Deserialized_nozzles { get; set; } public List Deserialized_pipes { get; set; } public List Deserialized_valves { get; set; } public List Deserialized_valveNodes { get; set; } public List Deserialized_repeaters { get; set; } public List Deserialized_pumps { get; set; } public List Deserialized_pumpNodes { get; set; } public List areas = new List(); public bool ReadFromJson(string json) { try { var net = JsonConvert.DeserializeObject(json); junctions = net.Deserialized_junctions; tanks = net.Deserialized_tanks; reservoirs = net.Deserialized_reservoirs; meters = net.Deserialized_meters; nozzles = net.Deserialized_nozzles; pipes = net.Deserialized_pipes; valves = net.Deserialized_valves; valveNodes = net.Deserialized_valveNodes; repeaters = net.Deserialized_repeaters; pumps = net.Deserialized_pumps; pumpNodes = net.Deserialized_pumpNodes; StartPoint = net.StartPoint; EndPoint = net.EndPoint; return true; } catch { return false; } } public string WriteToJson() { var net = new MapViewNetWork(); net.Deserialized_junctions = junctions; net.Deserialized_tanks = tanks; net.Deserialized_reservoirs = reservoirs; net.Deserialized_meters = meters; net.Deserialized_nozzles = nozzles; net.Deserialized_pipes = pipes; net.Deserialized_valves = valves; net.Deserialized_repeaters = repeaters; net.Deserialized_pumps = pumps; net.StartPoint = StartPoint; net.EndPoint = EndPoint; string json = JsonConvert.SerializeObject(net); return json; } public JunctionViewModel AddJunction(string ID, PointF position, float z = 0) { JunctionViewModel j = new JunctionViewModel(); j.ID = ID; j.Demand = 0; j.PatternID = ""; j.Elev = z; j.X = position.X; j.Y = position.Y; Nodes.Add(j); return j; } public JunctionViewModel AddJunction(PointF position, float z = 0) { JunctionViewModel j = new JunctionViewModel(); int i = 0; string ID = $"{Default.GetPreString(j)}{i}"; while (Nodes.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(j)}{i}"; } j.ID = ID; j.Elev = 0; j.Demand = 0; j.PatternID = ""; j.Elev = z; j.X = position.X; j.Y = position.Y; Nodes.Add(j); return j; } public MeterViewModel AddMeter(PointF position) { MeterViewModel j = new MeterViewModel(); int i = 0; string ID = $"{Default.GetPreString(j)}{i}"; while (Nodes.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(j)}{i}"; } j.ID = ID; j.Elev = 0; j.Demand = 0; j.PatternID = ""; j.X = position.X; j.Y = position.Y; Nodes.Add(j); return j; } public ReservoirViewModel AddReservoir(PointF position) { ReservoirViewModel j = new ReservoirViewModel(); int i = 0; string ID = $"{Default.GetPreString(j)}{i}"; while (Nodes.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(j)}{i}"; } j.ID = ID; j.Elev = 0; j.Demand = 0; j.PatternID = ""; j.Head = 100; j.X = position.X; j.Y = position.Y; Nodes.Add(j); return j; } public TankViewModel AddTank(PointF position) { TankViewModel j = new TankViewModel(); int i = 0; string ID = $"{Default.GetPreString(j)}{i}"; while (Nodes.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(j)}{i}"; } j.ID = ID; j.Elev = 0; j.Demand = 0; j.PatternID = ""; j.X = position.X; j.Y = position.Y; Nodes.Add(j); return j; } public PipeViewModel AddPipe(NodeViewModel node1, NodeViewModel node2) { PipeViewModel pipe = new PipeViewModel(); int i = 0; string ID = $"{Default.GetPreString(pipe)}{i}"; while (Links.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(pipe)}{i}"; } pipe.ID = ID; pipe.Diameter = 100; pipe.MinorLoss = 0; pipe.Roughness = 100; pipe.Status = StatusType.DEFAULT; pipe.StartNode = node1; pipe.EndNode = node2; pipe.Length = -1; Links.Add(pipe); return pipe; } public ValveViewModel AddValve(NodeViewModel node1, NodeViewModel node2) { ValveViewModel valve = new ValveViewModel(); int i = 0; string ID = $"{Default.GetPreString(valve)}{i}"; while (Links.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(valve)}{i}"; } valve.ID = ID; valve.Diameter = 100; valve.Status = StatusType.DEFAULT; valve.StartNode = node1; valve.EndNode = node2; Links.Add(valve); return valve; } public RepeaterViewModel AddRepeater(NodeViewModel node1, NodeViewModel node2) { RepeaterViewModel repeater = new RepeaterViewModel(); int i = 0; string ID = $"{Default.GetPreString(repeater)}{i}"; while (Links.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(repeater)}{i}"; } repeater.ID = ID; repeater.StartNode = node1; repeater.EndNode = node2; Links.Add(repeater); return repeater; } public PumpViewModel AddPump(NodeViewModel node1, NodeViewModel node2) { PumpViewModel pump = new PumpViewModel(); int i = 0; string ID = $"{Default.GetPreString(pump)}{i}"; while (Links.Find(p0 => p0.ID == ID) != null) { i++; ID = $"{Default.GetPreString(pump)}{i}"; } pump.ID = ID; pump.StartNode = node1; pump.EndNode = node2; Links.Add(pump); return pump; } public List Remove(List list) { List objs = new List(); list.ForEach(o => { if (o == null) return; o.Selected = false; if (o is NodeViewModel n) { n.Links.ForEach(l => { if (this.Links.Remove(l)) objs.Add((LinkViewModel)l); }); if (this.Nodes.Remove(n)) objs.Add(n); } else if (o is LinkViewModel l) { if (this.Links.Remove(l)) objs.Add(l); } }); return objs; } public void ChangePoint(NodeViewModel source, NodeViewModel newPoint) { Links.ForEach(l => { if (l.StartNode == source) { l.StartNode = newPoint; } else if (l.EndNode == source) { l.EndNode = newPoint; } }); source.Visible = false; } public void ChangeNodeID(NodeViewModel node, string ID) { node.ID = ID; node.Links.ForEach(l => { if (l.StartNode == node) { l.Node1 = node.ID; } else { l.Node2 = node.ID; } }); } public string GetValidID(IBaseViewModel obj) { string DefaultString = Default.GetPreString(obj); int i = 0; string ID = obj.ID; ID = $"{DefaultString}{i}"; while (Hash_ID.Contains(ID)) { ID = $"{DefaultString}{i}"; i++; } return ID; } public MapViewNetWork CreateNew(PointF3D basepoint) { MapViewNetWork net = this.DeepCopy(); var BasePos = StartPoint.Position3D; net.Nodes.ForEach(n => { n.X += basepoint.X - BasePos.X; n.Y += basepoint.Y - BasePos.Y; n.Elev += basepoint.Z - BasePos.Z; }); return net; } public void AddJoinNet(MapViewNetWork network) { if (Nodes.Count == 0) { Nodes.AddRange(network.Nodes); Links.AddRange(network.Links); StartPoint = network.StartPoint; EndPoint = network.EndPoint; } else { ChangePoint(EndPoint, network.StartPoint); Nodes.AddRange(network.Nodes); Links.AddRange(network.Links); EndPoint = network.EndPoint; } } public List Add(MapViewNetWork net0, PointF3D offset = null, bool isCopy = false, NodeViewModel ConnectNode = null) { if (offset == null) { offset = new PointF3D(0, 0, 0); } List list = new List(); if (Hash_ID == null) Hash_ID = new HashSet(); MapViewNetWork net = isCopy ? net0.DeepCopy() : net0; net.Nodes.ForEach(n0 => { NodeViewModel n = (NodeViewModel)n0; if (Hash_ID.Contains(n.ID)) { string ID = GetValidID(n); net.ChangeNodeID(n, ID); } n.X += offset.X; n.Y += offset.Y; n.Elev += offset.Z; Nodes.Add(n); Hash_ID.Add(n.ID); list.Add(n); if (n0 == net.EndPoint && list[0] != n) { int index = list.IndexOf(n); var o = list[0]; list[0] = n; list[index] = o; } }); net.Links.ForEach(l0 => { LinkViewModel l = (LinkViewModel)l0; if (Hash_ID.Contains(l.ID)) { string ID = GetValidID(l); l.ID = ID; } Links.Add(l); Hash_ID.Add(l.ID); list.Add(l); }); if (ConnectNode != null) { var l = AddPipe(ConnectNode, net.StartPoint); list.Add(l); Hash_ID.Add(l.ID); } return list; } public void Add(List objects) { Nodes.AddRange(objects.FindAll(o => o is NodeViewModel).Select(o => (NodeViewModel)o)); Links.AddRange(objects.FindAll(o => o is LinkViewModel).Select(o => (LinkViewModel)o)); } public void Clear() { Nodes.Clear(); Links.Clear(); } public List MapObjects { get { List objects = new List(); objects.AddRange(Nodes); objects.AddRange(Links); return objects.Select(o => (IBaseViewModel)o)?.ToList(); } } public void Rename() { MapObjects.ForEach(o => o.ID = $"{Name}_{o.ID}"); } public void Add(RepeaterViewModel repeater) { ChangePoint(repeater.StartNode, repeater.StartNode_inner); MoveDownNetwork(repeater); ChangePoint(repeater.EndNode, repeater.EndNode_inner); Nodes.AddRange(repeater.netList.Nodes); Links.AddRange(repeater.netList.Links); } public void LoadRepeaters(int MaxLevel, dict param, Dictionary viewModel, bool ViewMode = true) { repeaters.ForEach(r => { if (!r.Load(MaxLevel, param, viewModel, ViewMode)) { r.Status = RepeatStatus.收起; r.Visible = true; } else { Add(r); r.Visible = false; } }); } public void MoveDownNetwork(RepeaterViewModel repeater) { visited = new Dictionary(); visited.Add(repeater.StartNode, true); var p1 = repeater.EndNode_inner; var p2 = repeater.EndNode; PointF3D p = new PointF3D(p1.X - p2.X, p1.Y - p2.Y, p1.Elev - p2.Elev); BFS(this, repeater.EndNode, new Vector3(p.X, p.Y, p.Z)); } // 定义 visited 字典记录已访问过的节点和待访问的节点队列 Dictionary visited; private bool _isCalculated = false; public void BFS(MapViewNetWork net, NodeViewModel startNode, Vector3 vector) { Queue queue = new Queue(); // 标记起始节点为已访问,并加入队列 visited[startNode] = true; queue.Enqueue(startNode); // 不断从队列中取出节点进行访问,直到队列为空 while (queue.Count > 0) { // 取出队首节点并输出 NodeViewModel node = queue.Dequeue(); node.Move(vector); // 遍历当前节点的所有未访问邻居节点,并标记为已访问 foreach (LinkViewModel link in net.Links) { if (link.StartNode == node && !visited.ContainsKey(link.EndNode)) { visited[link.EndNode] = true; queue.Enqueue(link.EndNode); } else if (link.EndNode == node && !visited.ContainsKey(link.StartNode)) { visited[link.StartNode] = true; queue.Enqueue(link.StartNode); } } } } public List GetNode(string node) { return Nodes.FindAll(n => n.ID == node).Select(n => (NodeViewModel)n)?.ToList(); } public Dictionary> CheckValidate() { BuildRelation(); string result = null; Dictionary> result_dict = new Dictionary>(); StringBuilder result_sb = new StringBuilder(); //to-do var objs = Links.Select(o => o as LinkViewModel)?.ToList();//new List() { Links[0] }; //Links.FindAll(o => o is LinkViewModel).Select(o => o as LinkViewModel)?.ToList(); //objs去掉重复的元素 objs = objs.Distinct()?.ToList(); var visitedNodes = new HashSet(); FindObjs = new HashSet(); objs.ForEach(o => TraversePipeNetworkALL(o, visitedNodes)); List list_孤立点 = new List(); for (int i = 0; i < Nodes.Count; i++) { if (!FindObjs.Contains(Nodes[i])) { result_sb.AppendLine($"节点{Nodes[i].ID}是孤立点"); list_孤立点.Add(Nodes[i].ID); } } if (list_孤立点.Count > 0) result_dict.Add("孤立点", list_孤立点); //属性判断 var list_管道连接关系异常 = new List(); var list_管道长度小于等于0 = new List(); var list_粗糙系数设置错误 = new List(); var list_管径设置错误 = new List(); pipes.ForEach(p => { if (p.StartNode == null || p.EndNode == null) { result_sb.AppendLine($"{p.ID}管道连接关系异常"); list_管道连接关系异常.Add(p.ID); } if (p.Length <= 0) { result_sb.AppendLine($"{p.ID}管道长度小于等于0"); list_管道长度小于等于0.Add(p.ID); } if (p.Roughness <= 0.1 || p.Roughness > 10000) { result_sb.AppendLine($"{p.ID}粗糙系数设置错误"); list_粗糙系数设置错误.Add(p.ID); } if (p.Diameter <= 0.1 || p.Diameter > 10000) { result_sb.AppendLine($"{p.ID}管径设置错误"); list_管径设置错误.Add(p.ID); } }); if (list_管道连接关系异常.Count > 0) result_dict.Add("管道连接关系异常", list_管道连接关系异常); if (list_管道长度小于等于0.Count > 0) result_dict.Add("管道长度小于等于0", list_管道长度小于等于0); if (list_粗糙系数设置错误.Count > 0) result_dict.Add("粗糙系数设置错误", list_粗糙系数设置错误); if (list_管径设置错误.Count > 0) result_dict.Add("管径设置错误", list_管径设置错误); return result_dict; } HashSet FindObjs; private void TraversePipeNetworkALL(LinkViewModel startLink, HashSet visitedNodes = null, int direction = 0) { Queue queue = new Queue(); queue.Enqueue(startLink); if (visitedNodes == null) visitedNodes = new HashSet(); while (queue.Count > 0) { LinkViewModel currentLink = queue.Dequeue(); foreach (var node in new NodeViewModel[] { currentLink.StartNode, currentLink.EndNode }) { if (direction == 1 && currentLink.EN_FLOW >= 0 && node == currentLink.StartNode) continue; if (direction == -1 && currentLink.EN_FLOW <= 0 && node == currentLink.EndNode) continue; if (node != null && !visitedNodes.Contains(node)) { visitedNodes.Add(node); if (!FindObjs.Contains(node)) FindObjs.Add(node); foreach (var link in node.ViewLinks) { if (!visitedNodes.Contains(link.StartNode) || !visitedNodes.Contains(link.EndNode)) { if (!FindObjs.Contains(link)) FindObjs.Add(link); queue.Enqueue(link); } } } } } } public void SetNodeDemand() { Nodes.ForEach(n => { if (n is NodeViewModel node && node.Demand == 0) { node.Demand = 0.01f; } else if (n is MeterViewModel meter && meter.Demand == 0) { meter.Demand = 0.01f; } else if (n is NozzleViewModel nozzle && nozzle.Demand == 0) { nozzle.Demand = 0.01f; } }); } 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 points = new List(); StreamReader sr = new StreamReader(filePath); { 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; Parts parts = new Parts(line); section = section.ToUpper(); IBaseViewModel o = null; switch (section) { case "JUNCTIONS": { NodeViewModel j = null; if (parts[-2] == "Meter") { var m = new MeterViewModel(); j = m; } 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.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); valve.Node1 = parts.ToString(1, null); valve.Node2 = parts.ToString(2, null); 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); pump.Node1 = parts.ToString(1, null); pump.Node2 = parts.ToString(2, null); 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.Level = parts.ToInt(-1, 0); Links.Add(pump); } break; case "CURVES": { } 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(); //读取坐标 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; } } private StatusType StringToStatus(string status) { switch (status) { case "CLOESD": case "0": return StatusType.CLOSED; case "OPEN": case "1": return StatusType.OPEN; case "ACTIVE": return StatusType.ACTIVE; default: return StatusType.DEFAULT; } } public void BuildRelation() { //读取坐标 int k1 = 0;//表示管线索引 int k2 = 0;//表示节点索引 Nodes.Sort((a, b) => string.Compare(a.ID, b.ID)); 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++; } 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; } p.EndNode = J; p.Node2 = J.ID; if (J.MaxDiameter < p.Diameter) J.MaxDiameter = p.Diameter; J.Links.Add(p); k1++; } 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); }   Hash_ID = new HashSet(); Nodes.ForEach(o => Hash_ID.Add(o.ID)); Links.ForEach(o => Hash_ID.Add(o.ID)); var err = Links.Where(x => x.StartNode.ID == x.EndNode.ID).ToList(); Links.RemoveAll(x => x.StartNode.ID == x.EndNode.ID); Links.RemoveAll(x => x.Length == 0); var a = err; } public bool loadInpFile_old(string filePath) { if (filePath == null || !File.Exists(filePath)) return false; List points = new List(); StreamReader sr = new StreamReader(filePath); { 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.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]; valve.Node1 = parts[1]; valve.Node2 = parts[2]; 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]; 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++; } 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++; } 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)Math.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 * Math.Pow(Math.Sin(Math.Abs(angle) / 2), 2) + 2.05 * Math.Pow(Math.Sin(Math.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)) { throw new Exception($"模板文件不存在[{tempPath}]"); } tempString = File.ReadAllText(tempPath); } else { tempString = File.ReadAllText(sourcePath); } Dictionary dictExchange = new Dictionary() { {"{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}\t{m.DlTemplateID}"); 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(); curveStringBuilder.AppendLine(@";ID X-Value Y-Value ;HEADLOSS: GPVDefault 0 0 GPVDefault 100 0 "); 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); HelperC.Copy(filePath, backupFilePath, true); // 检查文件是否存在 try { HelperC.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); } } }