using Hydro.Core.Model; //using Hydro.HydraulicModel; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.IO.Ports; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using static Hydro.Core.EpanetEnum; using static Hydro.Core.ObjectEnum; namespace Hydro.Inp { public class NetWork //: INetWork { public NetWork() { } public List Junctions { get { var js = Nodes.Where(d => d is JunctionModel); return js?.Select(d => d as JunctionModel).ToList(); } } public List Tanks { get { var js = Nodes.Where(d => d is TankModel); return js?.Select(d => d as TankModel).ToList(); } } public List Pipes { get { var js = Links.Where(d => d is PipeModel); return js?.Select(d => d as PipeModel).ToList(); } } public List Pumps { get { var js = Links.Where(d => d is PumpModel); return js?.Select(d => d as PumpModel).ToList(); } } public List Valves { get { var js = Links.Where(d => d is ValveModel); return js?.Select(d => d as ValveModel).ToList(); } } public List Reservos { get { var js = Nodes.Where(d => d is ReservoisModel); return js?.Select(d => d as ReservoisModel).ToList(); } } public List Meters { get { var js = Nodes.Where(d => d is MeterModel); return js?.Select(d => d as MeterModel).ToList(); } } public List Nozzles { get { var js = Nodes.Where(d => d is NozzleModel); return js?.Select(d => d as NozzleModel).ToList(); } } public virtual void AddJunction(JunctionModel junction) { CheckNodesExist(junction); Nodes.Add(junction); } public virtual void AddJunctions(List junctions) { junctions.ForEach(junction => { AddJunction(junction); }); } public virtual void AddTank(TankModel tank) { CheckNodesExist(tank); Nodes.Add(tank); } public virtual void AddTanks(List tanks) { tanks.ForEach(tank => { AddTank(tank); }); } public virtual void AddPipe(PipeModel pipe) { CheckLinksExist(pipe); Links.Add(pipe); } public virtual void AddPipes(List pipes) { pipes.ForEach(pipe => { AddPipe(pipe); }); } public virtual void AddPump(PumpModel pump) { CheckLinksExist(pump); Links.Add(pump); } public virtual void AddPumps(List pumps) { pumps.ForEach(pump => { AddPump(pump); }); } public virtual void AddValve(ValveModel valve) { CheckLinksExist(valve); Links.Add(valve); } public virtual void AddValves(List valves) { valves.ForEach(valve => { AddValve(valve); }); } public virtual void AddReservos(ReservoisModel reservois) { CheckNodesExist(reservois); Nodes.Add(reservois); } public virtual void AddReservoss(List reservoiss) { reservoiss.ForEach(reservois => { AddReservos(reservois); }); } public virtual void AddMeter(MeterModel meter) { CheckNodesExist(meter); Nodes.Add(meter); } public virtual void AddMeters(List meters) { meters.ForEach(meter => { AddMeter(meter); }); } public virtual void AddNozzle(NozzleModel nozzle) { CheckNodesExist(nozzle); Nodes.Add(nozzle); } public virtual void AddNozzles(List nozzles) { nozzles.ForEach(nozzle => { AddNozzle(nozzle); }); } private void CheckNodesExist(NodeModel node) { if (Nodes.Any(d => d.ID == node.ID)) throw new Exception("已存在重复的对象"); } private void CheckLinksExist(LinkModel link) { if (Links.Any(d => d.ID == link.ID)) throw new Exception("已存在重复的对象"); } public List Nodes { get; set; } = new List(); public List Links { get; set; } = new List(); /// /// 根据INP文件生成 /// /// /// public virtual bool BuildFromInp(string InpPath) { if (InpPath == null || !File.Exists(InpPath)) return false; List points = new List(); StreamReader sr = new StreamReader(InpPath); //try { 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": { var ID = parts[0]; float elev; float.TryParse(parts[1], out elev); var Elev = elev; float demand; float.TryParse(parts[2], out demand); var Demand = demand; var PatternID = parts[3]; int level; int.TryParse(parts[6], out level); var Level = level; if (parts[5] == "Meter") { AddMeter(new MeterModel() { ID = ID, Elev = Elev, Demand = Demand, PatternID = PatternID, }); } else if (parts[5] == "Nozzle") { AddNozzle(new NozzleModel() { ID = ID, Elev = Elev, Demand = Demand, PatternID = PatternID, }); } else { AddJunction(new JunctionModel() { ID = ID, Elev = Elev, Demand = Demand, PatternID = PatternID, }); } } break; case "RESERVOIRS": { ReservoisModel r = new ReservoisModel(); 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; AddReservos(r); } break; case "TANKS": { //TankModel tank = new TankModel(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]); float initLevel = 0; float.TryParse(parts[2], out initLevel); float minLevel = 0; float.TryParse(parts[3], out minLevel); float maxLevel = 0; float.TryParse(parts[4], out maxLevel); float diamter = 0; float.TryParse(parts[5], out diamter); float minVol = 0; float.TryParse(parts[6], out minVol); var tank = new TankModel() { ID = parts[0], Elev = float.Parse(parts[1]), InitLevel = initLevel, MinLevel = minLevel, MaxLevel = maxLevel, Diameter = diamter, MinVol = minVol, VolCurve = "", IsOverFlow = true }; //int level; //if (int.TryParse(parts[9], out level)) // tank.Level = level; AddTank(tank); } break; case "PIPES": { PipeModel p = new PipeModel(); 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 ? StatusType.CLOSED : StatusType.DEFAULT; //int level; //if (int.TryParse(parts[8], out level)) // p.Level = level; AddPipe(p); } break; case "VALVES": { ValveModel valve = new ValveModel(); 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.CurvSetting = 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; AddValve(valve); } break; case "REPEATERS": { //Repeater repeater = new Repeater(); //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(); //读取坐标 Junctions.ForEach(o => { var p = points.Where(d => d.ID == o.ID).FirstOrDefault(); if (p != null) { o.X = p.Position.X; o.Y = p.Position.Y; } }); Reservos.ForEach(o => { var p = points.Where(d => d.ID == o.ID).FirstOrDefault(); if (p != null) { o.X = p.Position.X; o.Y = p.Position.Y; } }); Tanks.ForEach(o => { var p = points.Where(d => d.ID == o.ID).FirstOrDefault(); if (p != null) { o.X = p.Position.X; o.Y = p.Position.Y; } }); Meters.ForEach(o => { var p = points.Where(d => d.ID == o.ID).FirstOrDefault(); if (p != null) { o.X = p.Position.X; o.Y = p.Position.Y; } }); Nozzles.ForEach(o => { var p = points.Where(d => d.ID == o.ID).FirstOrDefault(); if (p != null) { o.X = p.Position.X; o.Y = p.Position.Y; } }); int k1 = 0; int k2 = 0; #region delete //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++; //} #endregion //建立点线关系链表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; } } /// /// 生成INP文件 /// /// Inp文件路径 /// Inp模板文件绝对路径,如果为空就用系统默认模板文件 /// public virtual void BuildToInp(string InpPath, string TemplateInpFullPath = "") { string tempString = ""; var tempPath = Path.Combine(Directory.GetCurrentDirectory(), @"template\inp\导出模板.inp"); if (!string.IsNullOrEmpty(TemplateInpFullPath)) { tempPath = TemplateInpFullPath; } if (!File.Exists(tempPath)) { throw new Exception("模板文件不存在," + tempPath); } if (!File.Exists(InpPath)) File.Create(InpPath).Close(); tempString = File.ReadAllText(tempPath); StringBuilder statusStringBuilder = new StringBuilder(); statusStringBuilder.AppendLine(";ID \tStatus/Setting\r\n"); StringBuilder emitterStringBuilder = new StringBuilder(); emitterStringBuilder.AppendLine(";Junction \tCoefficient"); StringBuilder coorStringBuilder = new StringBuilder(); coorStringBuilder.AppendLine(";Node X-Coord Y-Coord"); 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"); Junctions.ForEach(j => { junctionStringBuilder.AppendLine(j.ToString() + $"0\tJunction"); coorStringBuilder.AppendLine(j.ToCoorString()); }); Meters.ForEach(m => { junctionStringBuilder.AppendLine(m.ToString() + $"0\tJunction"); coorStringBuilder.AppendLine(m.ToCoorString()); }); Nozzles.ForEach(no => { junctionStringBuilder.AppendLine(no.ToString() + $"0\tNozzle\t{no.FlowCoefficient}"); emitterStringBuilder.Append(no.ToEmitterString()); coorStringBuilder.AppendLine(no.ToCoorString()); }); string junctionString = junctionStringBuilder.ToString(); StringBuilder reservoirStringBuilder = new StringBuilder(); reservoirStringBuilder.AppendLine(";ID Head Pattern "); Reservos.ForEach(o => { reservoirStringBuilder.AppendLine(o.ToString() + $"0\t{o.Elev}"); coorStringBuilder.AppendLine(o.ToCoorString()); }); string reserverString = reservoirStringBuilder.ToString(); StringBuilder tankStringBuilder = new StringBuilder(); tankStringBuilder.AppendLine(";ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve Overflow"); Tanks.ForEach(o => { tankStringBuilder.AppendLine(o.ToString());// + $"{o.Level}"); coorStringBuilder.AppendLine(o.ToCoorString()); }); string tankString = tankStringBuilder.ToString(); StringBuilder pipeStringBuilder = new StringBuilder(); pipeStringBuilder.AppendLine(";ID Node1 Node2 Length Diameter Roughness MinorLoss Status"); Pipes.ForEach(p => { pipeStringBuilder.AppendLine(p.ToString());// + $"{p.Level}"); statusStringBuilder.Append(p.ToStatusString()); }); string pipeString = pipeStringBuilder.ToString(); StringBuilder valveStringBuilder = new StringBuilder(); valveStringBuilder.AppendLine(";ID Node1 Node2 Diameter Type Setting MinorLoss "); Valves.ForEach(o => { valveStringBuilder.AppendLine(o.ToString());// + $"{o.Level}"); statusStringBuilder.Append(o.ToStatusString()); }); string valveString = valveStringBuilder.ToString(); StringBuilder pumpStringBuilder = new StringBuilder(); pumpStringBuilder.AppendLine(";ID Node1 Node2 Diameter Type Setting MinorLoss "); Pumps.ForEach(o => { pumpStringBuilder.AppendLine(o.ToString());// + $"{o.Level}"); statusStringBuilder.Append(o.ToStatusString()); }); string pumpString = pumpStringBuilder.ToString(); StringBuilder curveStringBuilder = new StringBuilder(); curveStringBuilder.AppendLine(@";ID X-Value Y-Value ;HEADLOSS: GPVDefault 0 0 GPVDefault 100 0 ;PUMP: PumpDefault 0 45.38 PumpDefault 83.33333333 45.25 PumpDefault 111.1111111 45.12 PumpDefault 138.8888889 44.96 PumpDefault 166.6666667 44.76 PumpDefault 194.4444444 44.52 PumpDefault 222.2222222 44.24 PumpDefault 250 43.92 PumpDefault 277.7777778 43.56 PumpDefault 305.5555556 43.17 PumpDefault 333.3333333 42.73 PumpDefault 361.1111111 42.25 PumpDefault 388.8888889 41.74 PumpDefault 416.6666667 41.18 PumpDefault 444.4444444 40.58 PumpDefault 472.2222222 39.95 PumpDefault 500 39.28 PumpDefault 527.7777778 38.56 PumpDefault 555.5555556 37.81 PumpDefault 583.3333333 37.02 PumpDefault 611.1111111 36.19 PumpDefault 638.8888889 35.32 PumpDefault 666.6666667 34.41 PumpDefault 694.4444444 33.46 PumpDefault 722.2222222 32.47 PumpDefault 750 31.44 PumpDefault 777.7777778 30.37 PumpDefault 805.5555556 29.27 "); Pumps.ForEach(o => { curveStringBuilder.AppendLine(o.CreateFlowCurve()); }); string curveString = curveStringBuilder.ToString(); string coorString = coorStringBuilder.ToString(); string output = ""; string emitterString = emitterStringBuilder.ToString(); 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(InpPath), "bk"); if (!Directory.Exists(backupFolderPath)) Directory.CreateDirectory(backupFolderPath); string backupFileName = $"{Path.GetFileNameWithoutExtension(InpPath)}_{DateTime.Now:yyyyMMddHHmmss}{Path.GetExtension(InpPath)}"; string backupFilePath = Path.Combine(backupFolderPath, backupFileName); if (File.Exists(InpPath)) File.Copy(InpPath, backupFilePath, true); //Global.ClearFileReadOnly(InpPath); File.WriteAllText(InpPath, output); } /// /// 根据Inp文件计算 /// /// public virtual List Calc(string InpPath) { HydraulicModel.Epanet epanet = new HydraulicModel.Epanet(); var result = new List(); var err = epanet.open(InpPath, "d:\\5.log", ""); if (err != 0) { throw new Exception($"计算失败:{err}"); } int nodeCount = 0, linkCount = 0; epanet.getcount((int)CountType.Node, ref nodeCount); epanet.getcount((int)CountType.Link, ref linkCount); const int MAXID = 31; var sb = new StringBuilder(MAXID); for (int i = 1; i < nodeCount; i++) { epanet.getnodeid(i, sb); var arr = System.Enum.GetValues(typeof(HydraulicModel.NodeValueType)); for (var j = 0; j < arr.Length; j++) { float v = 0; var t = (HydraulicModel.NodeValueType)j; epanet.getnodevalue(i, (int)t, ref v); result.Add(new TimePoint() { Key = sb.ToString() + "_" + t.ToString(), Value = v, }); } } for (int i = 1; i < linkCount; i++) { epanet.getlinkid(i, sb); var arr = System.Enum.GetValues(typeof(HydraulicModel.LinkValueType)); for (var j = 0; j < arr.Length; j++) { float v = 0; var t = (HydraulicModel.LinkValueType)j; epanet.getlinkvalue(i, (int)t, ref v); result.Add(new TimePoint() { Key = sb.ToString() + "_" + t.ToString(), Value = v, }); } } return result; } /// /// 先生成Inp文件,再计算 /// /// public virtual List Calc() { var inpPath = Path.Combine(Directory.GetCurrentDirectory(), @"data\inp\calc.inp"); BuildToInp(inpPath); return Calc(inpPath); } /// /// 数据有效性校验 /// /// public virtual CheckModel Check() { var result = new CheckModel(); return result; } private string ReplaceContent(string text, string content, string replaceString) { string str = replaceString; string replacedText = ReplaceCoordinatesSection(text, content, str); return replacedText; } private 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; } } }