lixiaojun
2025-01-22 46f64905a3c309a50c0f245b3350cdeb8dd699c6
Hydro/Yw.EPAnet.Calcu.Core/04-Inp/InpInteropHelper.cs
@@ -1,4 +1,7 @@
namespace Yw.EPAnet
using System.Text;
using System.Text.RegularExpressions;
namespace Yw.EPAnet
{
    /// <summary>
    /// Inp交互辅助类
@@ -8,20 +11,693 @@
        /// <summary>
        /// 转换至Inp字符串
        /// </summary>
        /// <returns></returns>
        public static string ToInpString(this Network rhs)
        public static string ToInpString(this Network network, CalcuResult minorResult = null)
        {
            return default;
            //模板文件
            var templateFilePath = TemplateHelper.GetPath();
            if (!File.Exists(templateFilePath))
            {
                throw new Exception("模板文件不存在," + templateFilePath);
            }
            var templateString = File.ReadAllText(templateFilePath);
            //状态
            var statusSb = new StringBuilder();
            statusSb.AppendLine(";ID              \tStatus/Setting\r\n");
            //扩散器
            var emitterSb = new StringBuilder();
            emitterSb.AppendLine(";Junction        \tCoefficient");
            //坐标
            var coorStringBuilder = new StringBuilder();
            coorStringBuilder.AppendLine(";Node               X-Coord              Y-Coord");
            Dictionary<string, string> dictExchange = new Dictionary<string, string>() {
                {"{junctions}","{0}" },
                {"{reservoirs}","{1}" },
                {"{tanks}","{2}" },
                {"{pipes}","{3}" },
                {"{valves}","{4}" },
                {"{pumps}","{5}" },
                {"{coor}","{6}" },
                {"{curve}","{7}" },
                {"{emitters}","{8}" },
            };
            dictExchange.ToList().ForEach(m => templateString = templateString.Replace(m.Key, m.Value));
            //连接节点
            var junctionStringBuilder = new StringBuilder();
            junctionStringBuilder.AppendLine(";ID                 Elev           Demand         Pattern         Type");
            //连接节点处理
            network.Junctions?.ForEach(x =>
            {
                var demandPattern = x.DemandPattern;
                if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
                {
                    demandPattern = string.Empty;
                }
                junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tJunction");
                coorStringBuilder.AppendLine(x.Id + "    " + x.Position.X + "    " + x.Position.Y);
            });
            network.GetAllCouplings()?.ForEach(x =>
            {
                var demandPattern = x.DemandPattern;
                if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
                {
                    demandPattern = string.Empty;
                }
                junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tCoupling");
                coorStringBuilder.AppendLine(x.Id + "    " + x.Position.X + "    " + x.Position.Y);
            });
            network.GetAllInstruments()?.ForEach(x =>
            {
                var demandPattern = x.DemandPattern;
                if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
                {
                    demandPattern = string.Empty;
                }
                junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tInstrument");
                coorStringBuilder.AppendLine(x.Id + "    " + x.Position.X + "    " + x.Position.Y);
            });
            //水表处理
            network.Meters?.ForEach(x =>
            {
                var demandPattern = x.DemandPattern;
                if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
                {
                    demandPattern = "";
                }
                junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tMeters");
                coorStringBuilder.AppendLine(x.Id + "    " + x.Position.X + "    " + x.Position.Y);
            });
            //喷头处理
            network.Nozzles?.ForEach(x =>
            {
                var demandPattern = x.DemandPattern;
                if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
                {
                    demandPattern = "";
                }
                junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tNozzle\t{x.Coefficient}");
                double coefficient = x.Coefficient * Math.Pow(10 / 101.972, 0.5) / 1000 * 60;
                emitterSb.AppendLine(x.Id + "    " + coefficient);
                coorStringBuilder.AppendLine(x.Id + "    " + x.Position.X + "    " + x.Position.Y);
            });
            var junctionString = junctionStringBuilder.ToString();
            //水库处理
            var reservoirSb = new StringBuilder();
            reservoirSb.AppendLine(";ID                 Head           Pattern ");
            network.Reservoirs?.ForEach(x =>
            {
                reservoirSb.AppendLine($"{x.Id}\t{x.Head}\t{x.HeadPattern}\t;\t" + $"0\t{x.PoolElev}");
                coorStringBuilder.AppendLine(x.Id + "    " + x.Position.X + "    " + x.Position.Y);
            });
            string reserverString = reservoirSb.ToString();
            //水池处理
            var tankSb = new StringBuilder();
            tankSb.AppendLine(";ID                 Elevation      InitLevel      MinLevel       MaxLevel       Diameter       MinVol         VolCurve           Overflow");
            network.GetTanks()?.ForEach(o =>
            {
                // tankStringBuilder.AppendLine($"{o.Id}\t{o.PoolElev}\t{o.InitLevel}\t{o.MinLevel}\t{o.MaxLevel}\t{o.Diameter}\t{o.MinVol}\t{o.VolCurve}\t{o.Overflow}\t;\t");// + $"0");
                tankSb.AppendLine($"{o.Id}\t{o.PoolElev}\t{o.InitLevel}\t{o.MinLevel}\t{o.MaxLevel}\t{o.Diameter}\t{o.MinVol}\t{o.VolCurve}\t;\t");// + $"0");
                coorStringBuilder.AppendLine(o.Id + "    " + o.Position.X + "    " + o.Position.Y);
            });
            string tankString = tankSb.ToString();
            //管道处理
            var pipeSb = new StringBuilder();
            pipeSb.AppendLine(";ID                 Node1              Node2              Length         Diameter       Roughness      MinorLoss      Status");
            network.GetAllPipes()?.ForEach(x =>
            {
                if (x.Roughness == 0)
                {
                    x.Roughness = 110;
                }
                string statusString = x.LinkStatus == PipeStatus.Closed ? "CLOSED" : "";
                double minorLoss = x.MinorLossCoeff + x.EndMinorLossCoeff + x.StartMinorLossCoeff;
                pipeSb.AppendLine($"{x.Id}\t{x.StartNode.Id}\t{x.EndNode.Id}\t{x.Length}\t{x.Diameter}\t{x.Roughness}\t{minorLoss}\t{statusString}\t;\t");// + $"{p.Level}");
                if (x.LinkStatus != PipeStatus.Open)
                {
                    statusSb.AppendLine(x.Id + "\t" + statusString);
                }
            });
            string pipeString = pipeSb.ToString();
            //阀门处理
            var valveSb = new StringBuilder();
            valveSb.AppendLine(";ID                 Node1              Node2              Diameter       Type   Setting        MinorLoss  ");
            network.Valves?.ForEach(x =>
            {
                if (x.Id == "694311")
                {
                }
                string settings;
                if (x.LinkStatus == ValveStatus.None)
                {
                    settings = x.ValveSetting;
                }
                else
                {
                    settings = "Undefined";
                }
                if (string.IsNullOrEmpty(settings)) settings = "Undefined";
                valveSb.AppendLine($"{x.Id}\t{x.StartNode.Id}\t{x.EndNode.Id}\t{x.Diameter:F4}\t{x.ValveType}\t{settings}\t{x.MinorLoss:F4}\t;\t");// + $"0");
                if (!string.IsNullOrEmpty(x.LinkStatus) && x.LinkStatus != ValveStatus.None)
                {
                    statusSb.AppendLine(x.Id + "\t" + x.LinkStatus);
                }
            });
            network.GetAllResistances()?.ForEach(x =>
            {
                string type = null;
                if (x is Exchanger)
                {
                    type = "Exchanger";
                }
                else if (x is Compressor)
                {
                    type = "Compressor";
                }
                string settings;
                if (x.LinkStatus == ValveStatus.None)
                {
                    settings = x.CurveQL;
                }
                else
                {
                    settings = "Undefined";
                }
                if (string.IsNullOrEmpty(settings)) settings = "Undefined";
                valveSb.AppendLine($"{x.Id}\t{x.StartNode.Id}\t{x.EndNode.Id}\t{x.Diameter:F4}\tGPV\t{settings}\t{x.MinorLoss:F4}\t;\t{type}");// + $"0");
                if (!string.IsNullOrEmpty(x.LinkStatus) && x.LinkStatus != ValveStatus.None)
                {
                    statusSb.AppendLine(x.Id + "\t" + x.LinkStatus);
                }
            });
            string valveString = valveSb.ToString();
            //水泵处理
            var pumpSb = new StringBuilder();
            pumpSb.AppendLine(";ID                 Node1              Node2              Parameters  ");
            network.Pumps?.ForEach(o =>
            {
                pumpSb.AppendLine($"{o.Id}\t{o.StartNode.Id}\t{o.EndNode.Id}\tHead\t{o.CurveQH}\t;\t");// + $"0");
                string statusString = null;
                if (o.LinkStatus == PipeStatus.Closed)
                {
                    statusString = "\tCLOSED";
                }
                else
                {
                    if (o.SpeedRatio == null)
                        statusString = "\tOPEN";
                    else
                        statusString = $"\t{o.SpeedRatio}";
                }
                if (!string.IsNullOrEmpty(o.LinkStatus))
                {
                    statusSb.AppendLine(o.Id + statusString);
                }
            });
            string pumpString = pumpSb.ToString();
            //曲线处理
            var curveSb = new StringBuilder();
            curveSb.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    ");
            network.Curves?.ForEach(o =>
            {
                var curvePtList = o.CurveData?.OrderBy(x => x.X).ToList();
                foreach (var curvePt in curvePtList)
                {
                    curveSb.AppendLine($"{o.Id}  {curvePt.X} {curvePt.Y}");
                }
                //curveStringBuilder.AppendLine(o.ToString());
            });
            string curveString = curveSb.ToString();
            string coorString = coorStringBuilder.ToString();
            string output = "";
            string emitterString = emitterSb.ToString();
            string statusString = statusSb.ToString();
            output = templateString;
            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);
            return output;
        }
        private static 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;
        }
        /// <summary>
        /// 从Inp字符串中解析
        /// </summary>
        /// <param name="inpString">inp字符串</param>
        /// <returns></returns>
        public static Network FromInpString(string inpString)
        {
            return default;
            string InpPath = inpString;
            var net = new Network();
            if (InpPath == null || !File.Exists(InpPath)) return null;
            List<InpCoor> points = new List<InpCoor>();
            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");
                        InpParts parts = new InpParts(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[-2] == "Meter")
                                    {
                                        net.Meters.Add(new Meter()
                                        {
                                            Id = ID,
                                            Elev = Elev,
                                            Demand = Demand,
                                            DemandPattern = PatternID,
                                        });
                                    }
                                    else if (parts[-2] == "Nozzle")
                                    {
                                        net.Nozzles.Add(new Nozzle()
                                        {
                                            Id = ID,
                                            Elev = Elev,
                                            Demand = Demand,
                                            DemandPattern = PatternID,
                                        });
                                    }
                                    else
                                    {
                                        net.Junctions.Add(new Junction()
                                        {
                                            Id = ID,
                                            Elev = Elev,
                                            Demand = Demand,
                                            DemandPattern = PatternID,
                                        });
                                    }
                                }
                                break;
                            case "RESERVOIRS":
                                {
                                    var r = new Reservoir();
                                    r.Id = parts[0];
                                    float head;
                                    if (float.TryParse(parts[1], out head))
                                        r.Head = head;
                                    r.HeadPattern = parts.Length > 2 ? parts[2] : "";
                                    int level;
                                    //if (int.TryParse(parts[3], out level))
                                    //    r.Level = level;
                                    net.Reservoirs.Add(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 Tank()
                                    {
                                        Id = parts[0],
                                        PoolElev = float.Parse(parts[1]),
                                        InitLevel = initLevel,
                                        MinLevel = minLevel,
                                        MaxLevel = maxLevel,
                                        Diameter = diamter,
                                        MinVol = minVol,
                                        VolCurve = "",
                                    };
                                    //int level;
                                    //if (int.TryParse(parts[9], out level))
                                    //    tank.Level = level;
                                    net.Tanks.Add(tank);
                                }
                                break;
                            case "PIPES":
                                {
                                    InpPipe p = new InpPipe();
                                    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.MinorLossCoeff = minorLoss;
                                    p.LinkStatus = parts.Length > 7 ? (parts[7].ToUpper().Contains("CLOSE") ? PipeStatus.Closed : PipeStatus.Open) : PipeStatus.Open;
                                    net.Pipes.Add(p);
                                }
                                break;
                            case "VALVES":
                                {
                                    if (parts[-2] == "Exchanger")
                                    {
                                        InpHeatExchanger valve = new InpHeatExchanger();
                                        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.CurveQL = parts[5];
                                        float minorLoss;
                                        if (float.TryParse(parts[6], out minorLoss))
                                            valve.MinorLoss = minorLoss;
                                        net.Exchangers.Add(valve);
                                    }
                                    else if (parts[-2] == "Compressor")
                                    {
                                        InpAirCompressor valve = new InpAirCompressor();
                                        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.CurveQL = parts[5];
                                        float minorLoss;
                                        if (float.TryParse(parts[6], out minorLoss))
                                            valve.MinorLoss = minorLoss;
                                        net.Compressors.Add(valve);
                                    }
                                    else
                                    {
                                        InpValve valve = new InpValve();
                                        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.ValveType = parts[4];
                                        valve.ValveSetting = parts[5];
                                        float minorLoss;
                                        if (float.TryParse(parts[6], out minorLoss))
                                            valve.MinorLoss = minorLoss;
                                        net.Valves.Add(valve);
                                    }
                                }
                                break;
                            case "PUMPS":
                                {
                                    InpPump pump = new InpPump();
                                    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);// 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.CurveQH = parts.ToString(index + 1, "PumpDefault");
                                                break;
                                            case "SPEED":
                                                pump.SpeedRatio = parts.ToFloat(index + 1, 0);
                                                break;
                                        }
                                        index += 2;
                                    }
                                    //pump.Diameter = parts.ToFloat(3, 0);
                                    pump.LinkStatus = "OPEN";
                                    net.Pumps.Add(pump);
                                }
                                break;
                            case "STATUS":
                                {
                                    Link link = net.GetAllLinks().Find(l => l.Id == parts.ToString(0, null));
                                    if (link != null)
                                        link.LinkStatus = parts[1];
                                }
                                break;
                            case "COORDINATES":
                                {
                                    string id = parts[0];
                                    double x;
                                    double y;
                                    if (double.TryParse(parts[1], out x) && double.TryParse(parts[2], out y))
                                    {
                                        points.Add(new InpCoor(id, new Position2d(x, y)));
                                    }
                                }
                                break;
                            case "EMITTERS":
                                {
                                    string id = parts[0];
                                    if (double.TryParse(parts[1], out double x))
                                    {
                                        double Coefficient = x / Math.Pow(10 / 101.972, 0.5) * 1000 / 60;
                                        net.Nozzles.Find(o => o.Id == id).Coefficient = Coefficient;
                                    }
                                }
                                break;
                        }
                    }
                }
                sr.Close();
                #region 优化方案
                int k1 = 0;
                int k2 = 0;
                var Nodes = net.GetAllNodes();
                var Links = net.GetAllLinks();
                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)
                    {
                        break;
                        //throw new Exception($"未找到Node[{J.Id}]的坐标");
                    }
                    J.Position = new Position2d();
                    J.Position.X = coor.Position.X;
                    J.Position.Y = coor.Position.Y;
                    k1++;
                }
                #endregion
                //建立点线关系链表StartNode,先将管线以Node1(节点1的ID)排序,再将Nodes按ID排序,建立两个游标k1、k2,正向一次循环,建立链表关系
                //时间复杂度 O(n)
                Links.Sort((a, b) => string.Compare(((IInpParser)a).Node1, ((IInpParser)b).Node1));
                k1 = 0;
                k2 = 0;
                while (k2 < Links.Count)
                {
                    var p = Links[k2];
                    var J = Nodes[k1];
                    while (J.Id != ((IInpParser)p).Node1 && k1 < Nodes.Count)
                    {
                        k1++;
                        if (k1 < Nodes.Count) J = Nodes[k1];
                    }
                    if (k1 == Nodes.Count)
                    {
                        break;
                        //throw new Exception($"未找到Link[{p.Id}]的起始节点[{((IInpParser)p).Node1}]");
                    }
                    p.StartNode = J;
                    if (J.Links == null) J.Links = new List<Link>();
                    J.Links.Add(p);
                    k2++;
                }
                //建立点线关系链表StartNode,先将管线以Node2(节点1的ID)排序,再将Nodes按ID排序,建立两个游标k1、k2,正向一次循环,建立链表关系
                //时间复杂度 O(n)
                Links.Sort((a, b) => string.Compare(((IInpParser)a).Node2, ((IInpParser)b).Node2));
                k1 = 0;
                k2 = 0;
                while (k2 < Links.Count)
                {
                    var p = Links[k2];
                    var J = Nodes[k1];
                    while (J.Id != ((IInpParser)p).Node2 && k1 < Nodes.Count)
                    {
                        k1++;
                        if (k1 < Nodes.Count) J = Nodes[k1];
                    }
                    if (k1 == Nodes.Count)
                    {
                        break;
                        //throw new Exception($"未找到Link[{p.Id}]的终止节点[{((IInpParser)p).Node2}]");
                    }
                    p.EndNode = J;
                    if (J.Links == null) J.Links = new List<Link>();
                    J.Links.Add(p);
                    k2++;
                }
                return net;
            }
        }
    }