using MathNet.Numerics; using MathNet.Numerics.LinearAlgebra.Double; // 引入 Math.NET Numerics 库进行多项式拟合 using Newtonsoft.Json; using System.ComponentModel; using System.Data; using System.IO; using System.Text; namespace IBox.WinFrmUI { public enum BuildingType { 住宅1, 住宅2, 住宅3, 商用1, 商用2, 商用3 } public enum 二供分区Type { 加压供水, 屋顶水箱, 市政直供 } [Serializable] public class BuildDataset { public BuildDataset(string name) { Name = name; } public BuildDataset() { } public void Clear() { if (this.Data != null) { Data.Clear(); } this.IsFitted = false; } public string Name { get; set; } private List _Data = new List(); public List Data { get { return _Data; } set { _Data = value; IsFitted = false; } } //= new List(); public int degree { get; set; } = 2; public List ForumParams = null; [JsonIgnore] [NonSerialized] public DenseVector coefficients = null; // 存储多项式系数 public double ErrNum { get { return -1; } } private int stepNum { get { return 100; } } private double step { get { return (range_X.Max - range_X.Min) / stepNum; } } /// /// 不需要读取 /// public string Legend { get { return "Legend1"; } } //public AxisType axisType //{ // get // { // return AxisType.Primary; // } //} public void CurveFit(Range rangeX = null) { if (!HasData) { IsFitted = false; return; } if (rangeX != null) range_X = rangeX; coefficients = Fit.Polynomial(Data.Select(p => (double)p.X).ToArray(), Data.Select(p => (double)p.Y).ToArray(), degree); if (range_X == null) { range_X = new Range(9999999, -9999999); Data.ForEach(p => { if (range_X.Min > p.X) range_X.Min = p.X; if (range_X.Max < p.X) range_X.Max = p.X; }); } IsFitted = true; } public double Evaluate(double x) { if (!IsFitted) return ErrNum; // 计算多项式在点 x 处的取值 double y = 0; for (int i = 0; i < coefficients.Count; i++) { y += coefficients[i] * Math.Pow(x, i); } return y; } public double Evaluate_Solve(double y) { if (!IsFitted) return ErrNum; if (degree == 2) // 多项式次数 { if (!is_Yvalue_validate(y)) return ErrNum; double a = coefficients[degree]; // 二次项系数 double b = coefficients[degree - 1]; // 一次项系数 double c = coefficients[0] - y; // 常数项系数减去给定的 y 值 // 求解二次方程 ax^2 + bx + c = 0 double delta = b * b - 4 * a * c; double x1 = (-b + Math.Sqrt(delta)) / (2 * a); double x2 = (-b - Math.Sqrt(delta)) / (2 * a); // 取两个解中符合实际情况的那个(可以根据具体应用场景来确定) double x = (x2 >= x1) ? x2 : x1; return x; } else { return ErrNum; } } public double YMax { get { if (!IsFitted) return ErrNum; double a = coefficients[degree]; double b = coefficients[degree - 1]; double c = coefficients[0]; double delta = b * b - 4 * a * c; double minExtremePoint = -b / (2 * a); // 计算导函数的零点 double maxValue = 0; if (a < 0) maxValue = -delta / (4 * a) + c; else maxValue = a * minExtremePoint * minExtremePoint + b * minExtremePoint + c; // 计算最小值 return maxValue; } } public double YdataMax { get { double max = -99999; foreach (var p in Data) { if (max < p.Y) max = p.Y; } return max; } } public bool is_Yvalue_validate(double y) { if (!IsFitted) return false; double a = coefficients[degree]; if (a < 0) return y <= YMax; else return y >= YMax; } /// /// X显示的范围 /// public Range range_X; /// /// Y显示的范围 /// public Range range_Y; public bool HasData { get { return Data.Count >= 3; } } [JsonIgnore] [NonSerialized] public bool IsFitted = false; [JsonIgnore] [NonSerialized] private List _FittedCurve = null; public List FittedCurve { get { if (_FittedCurve == null && range_X != null && coefficients != null) { /* var curvehelper = new CurveFitHelper(Data, degree); coefficients = curvehelper.coefficients; return curvehelper.GetFitCurve(stepNum);*/ List doubles = new List(); for (double x = range_X.Min; x <= range_X.Max; x += step) { double y = Evaluate(x); if (y == double.NaN) y = 0; //chart.Series[1].Points.AddXY(x, y); doubles.Add(new PointF((float)x, (float)y)); } _FittedCurve = doubles; return doubles; } return _FittedCurve; } set { _FittedCurve = value; } } public List FittedCurvebyRange(Range range) { List doubles = new List(); if (range == null) { range = new Range(); range.Min = range_X.Min; range.Max = range_X.Max; } var step = (range.Max - range.Min) / stepNum; for (double x = range.Min; x <= range.Max; x += step) { double y = Evaluate(x); //chart.Series[1].Points.AddXY(x, y); doubles.Add(new PointF((float)x, (float)y)); } return doubles; } } [Serializable] public class Range { public Range() { } public Range(double Min, double Max) { this.Min = Min; this.Max = Max; } public double Max; public double Min; public double Len { get { return Max - Min; } } public static Range Union(Range a, Range b) { if (a == null) return b; if (b == null) return a; return new Range(a.Min < b.Min ? a.Min : b.Min, a.Max > b.Max ? a.Max : b.Max); } public bool isValid { get { return Min < Max; } } } public class UDemand { public string Name { get; set; } public Range Range { get; set; } public string picUrl { get; set; } } [Serializable] public class Build { public Build() { Datasets = new Dictionary() { {"流量压降曲线",new BuildDataset("流量压降曲线") }, {"流量压降上限",new BuildDataset("流量压降上限") }, {"流量压降下限",new BuildDataset("流量压降下限") }, {"流量压降平均",new BuildDataset("流量压降平均") }, }; } public string ID { get; set; } [Category("1、基本信息")] [Description("名称")] [DisplayName("名称")] [Browsable(true)] public string Name { get; set; } [Category("1、基本信息")] [Description("二供供水的类型")] [DisplayName("二供类型")] [Browsable(true)] public 二供分区Type Type { get; set; } [Browsable(false)] public Dictionary Datasets { get; set; } [NonSerialized] [JsonIgnore] public Munity factory = null; public string factoryName = ""; [Category("2、计算参数")] [Description("楼层高度")] [DisplayName("峰值流量(L/s)")] [Browsable(false)] public double MaxDemand { get; set; } = 15; [Category("1、基本信息")] [Description("楼层高度")] [DisplayName("每层高度")] [Browsable(false)] public double 层高 { get; set; } = 3.5; [Category("2、计算参数")] [Description("最不利点")] [DisplayName("用户压力需求(m)")] [Browsable(true)] public int 用户压力需求 { get; set; } = 8; [Category("1、基本信息")] [Description("楼层数量")] [DisplayName("楼层数")] [Browsable(false)] public double 层数 { get; set; } = 5; [Category("1、基本信息")] [Description("每层户数")] [DisplayName("每层户数")] [Browsable(false)] public double 每层户数 { get; set; } = 4; [Category("1、基本信息")] [Description("用户总数")] [DisplayName("用户总数")] [Browsable(false)] public double 用户总数 { get { return 层数 * 每层户数 * 用户压力需求; } } [Category("2、计算参数")] [Description("用户总数")] [DisplayName("系统流量(m³/h)")] [Browsable(true)] public double 系统最大流量 { get; set; } = 5; [Browsable(false)] private string _templateID = null; public Build Copy() { Build p = new Build(); p.ID = ID; p.Name = Name; p.MaxDemand = MaxDemand; p.层高 = 层高; p.层数 = 层数; p.每层户数 = 每层户数; p.系统最大流量 = 系统最大流量; p.Type = Type; p.factory = factory; foreach (var pair in Datasets) { List list = new List(); pair.Value.Data.ForEach(m => list.Add(m)); p.Datasets[pair.Key].Data = list; Range rangeDefault = new Range(double.MaxValue, double.MinValue); p.Datasets[pair.Key].range_X = Range.Union(rangeDefault, pair.Value.range_X); p.Datasets[pair.Key].range_Y = Range.Union(rangeDefault, pair.Value.range_Y); } p.CurveFit(); return p; //factory.Pumps.Add(p); //_EditFlag = true; //DisplayFactoryListInTreeView(factoryList); } [Browsable(false)] //[Editor(typeof(TemplatePropertyEditor), typeof(UITypeEditor))] public string templateID { get { return _templateID; } set { _templateID = value; //template = TemplateList.GetTemplate(_templateID); } } //public Template template { get; private set; } = null; [Browsable(false)] public BuildDataset 流量压降曲线 { get { if (!Datasets.ContainsKey("流量压降曲线")) Datasets.Add("流量压降曲线", new BuildDataset("流量压降曲线")); return Datasets["流量压降曲线"]; } } [Browsable(false)] public BuildDataset 流量压降平均 { get { if (!Datasets.ContainsKey("流量压降平均")) Datasets.Add("流量压降平均", new BuildDataset("流量压降平均")); return Datasets["流量压降平均"]; } } [Browsable(false)] public BuildDataset 流量压降上限 { get { if (!Datasets.ContainsKey("流量压降上限")) Datasets.Add("流量压降上限", new BuildDataset("流量压降上限")); return Datasets["流量压降上限"]; } } [Browsable(false)] public BuildDataset 流量压降下限 { get { if (!Datasets.ContainsKey("流量压降下限")) Datasets.Add("流量压降下限", new BuildDataset("流量压降下限")); return Datasets["流量压降下限"]; } } [Browsable(false)] public BuildDataset 用水趋势曲线 { get { if (!Datasets.ContainsKey("用水趋势曲线")) Datasets.Add("用水趋势曲线", new BuildDataset("用水趋势曲线")); return Datasets["用水趋势曲线"]; } } [Browsable(false)] public BuildDataset 用水趋势下限 { get { if (!Datasets.ContainsKey("用水趋势下限")) Datasets.Add("用水趋势下限", new BuildDataset("用水趋势下限")); return Datasets["用水趋势下限"]; } } [Browsable(false)] public BuildDataset 用水趋势上限 { get { if (!Datasets.ContainsKey("用水趋势上限")) Datasets.Add("用水趋势上限", new BuildDataset("用水趋势上限")); return Datasets["用水趋势上限"]; } } public DataTable UDList { get; set; } public List UDNames { get; set; } public List UDPatterns { get; set; } public void CurveFit(bool is拟合 = true) { foreach (var data in Datasets) { //data.Value.Data.Sort((a, b) => a.X > b.X ? 1 : (a.X == b.X ? 0 : -1)); if (data.Value.HasData) data.Value.CurveFit(); } } public void ShowInGrid(DataGridView dataGridView) { //dataGridView.Rows.Clear(); DataTable dt = new DataTable(); dt.Columns.Add("流量"); dt.Columns.Add("压降"); dt.Columns.Add("上限"); dt.Columns.Add("下限"); for (int i = 0; i < 流量压降曲线.Data.Count; i++) { PointF p = 流量压降曲线.Data[i]; double y1 = 0, y2 = 0; //if (流量压降上限.IsFitted) y1 = 流量压降上限.Data[i].Y; //if (流量压降下限.IsFitted) y2 = 流量压降下限.Data[i].Y; dt.Rows.Add(Math.Round(p.X, 1), Math.Round(p.Y, 2), Math.Round(y1, 2), Math.Round(y2, 2)); //dataGridView.Rows.Add(Math.Round(p.X,1), Math.Round(p.Y,2), Math.Round(y1,2), Math.Round(y2, 2)); } dataGridView.DataSource = null; dataGridView.DataSource = dt; dataGridView.Refresh(); } //public void AddCurve(System.Windows.Forms.DataVisualization.Charting.Chart chart, string type = "流量压降曲线", bool onlyOne = false, Range Force_range_X = null, bool onlyCurve = true) //{ // Series series = new Series(""); // List Points; // List FittedCurve; // string baseType = "流量压降曲线"; // string baseType2 = "流量压降下限"; // if (onlyOne) // { // //if (Datasets[type].IsFitted) // { // FittedCurve = Datasets[type].FittedCurvebyRange(Force_range_X); // series = AddSeries("ChartArea1", SeriesChartType.Spline, Datasets[type].Legend, Datasets[type].axisType, $"{factoryName}_{Name}_{type}", FittedCurve, true); // chart.Series.Add(series); // } // } // else // { // if (!onlyCurve) // { // Points = Datasets[type].Data; // series = AddSeries("ChartArea1", SeriesChartType.Point, Datasets[type].Legend, Datasets[type].axisType, $"流量压降曲线", Points, false); // chart.Series.Add(series); // } // var series0 = series; // foreach (var data in Datasets) // { // var type0 = data.Key; // if (!Datasets[type0].IsFitted) continue; // FittedCurve = Datasets[type0].FittedCurve; // series = AddSeries("ChartArea1", SeriesChartType.Spline, Datasets[type0].Legend, Datasets[type0].axisType, $"{factoryName}_{Name}_{type0}", FittedCurve, true); // chart.Series.Add(series); // } // if (chart.Series.Count == 1 && series0.Points.Count == 0) // { // series0.Points.Add(-2, -2); // series0.Points.Add(-1, -1); // series0.IsVisibleInLegend = true; // series0.YAxisType = AxisType.Primary; // chart.ChartAreas[0].AxisX.Minimum = 0; // chart.ChartAreas[0].AxisX.Maximum = 3000; // chart.ChartAreas[0].AxisY.Minimum = 0; // chart.ChartAreas[0].AxisY.Maximum = 50; // chart.ChartAreas[0].AxisY2.Minimum = 0; // chart.ChartAreas[0].AxisY2.Maximum = 100; // return; // } // } // Range range_X = null; // if (Force_range_X != null && Force_range_X.isValid) // { // range_X = Force_range_X; // } // else if (Datasets[type].range_X != null && Datasets[type].range_X.isValid) // { // range_X = Datasets[type].range_X; // } // else if (Datasets[baseType].range_X != null && Datasets[baseType].range_X.isValid) // { // range_X = Datasets[baseType].range_X; // } // else // { // range_X = new Range(double.NaN, double.NaN); // } // if (!double.IsNaN(range_X.Min) && !double.IsNaN(range_X.Max)) // { // chart.ChartAreas[0].AxisX.Minimum = range_X.Min; // chart.ChartAreas[0].AxisX.Maximum = range_X.Max; // } // if (Datasets[baseType].range_Y != null && Datasets[baseType].range_Y.isValid) // { // // 设置 X 轴的最大最小值 // chart.ChartAreas[0].AxisY.Minimum = Datasets[baseType].range_Y.Min; // chart.ChartAreas[0].AxisY.Maximum = Datasets[baseType].range_Y.Max; // } // else // { // //chart.ChartAreas[0].AxisY.Minimum = double.NaN; // //chart.ChartAreas[0].AxisY.Maximum = double.NaN; // } // if (type != baseType && Datasets[type].range_Y != null && Datasets[type].range_Y.isValid) // { // // 设置 X 轴的最大最小值 // chart.ChartAreas[0].AxisY2.Minimum = Datasets[type].range_Y.Min; // chart.ChartAreas[0].AxisY2.Maximum = Datasets[type].range_Y.Max; // } // else // if (Datasets[baseType2].range_Y != null && Datasets[baseType2].range_Y.isValid) // { // // 设置 X 轴的最大最小值 // chart.ChartAreas[0].AxisY2.Minimum = Datasets[baseType2].range_Y.Min; // chart.ChartAreas[0].AxisY2.Maximum = Datasets[baseType2].range_Y.Max; // } // else // { // chart.ChartAreas[0].AxisY2.Minimum = 0; // chart.ChartAreas[0].AxisY2.Maximum = 100; // } //} //private Series AddSeries(string Area, SeriesChartType type, String Legend, AxisType axisType, String Name, List Points, bool showLegend = true) //{ // Series series = new Series(); // series.BorderWidth = 2; // series.YValuesPerPoint = 10; // series.ChartArea = Area; // series.ChartType = type; // series.Legend = Legend; // series.Name = Name; // series.IsVisibleInLegend = showLegend; // series.YAxisType = axisType; // if (Points != null) Points.ForEach(p => series.Points.AddXY(p.X, p.Y)); // return series; //} } public class Munity : Build { //public string Name { get; set; } public new BuildingType Type { get; set; } public List Pumps { get; set; } public void CurveFit() { foreach (var p in Pumps) { p.CurveFit(); } } } public class MunityList { public List Factories { get; set; } public MunityList() { Factories = new List(); } public Munity AddFactory(string name, BuildingType type) { Munity item = new Munity { Name = name, Type = type, Pumps = new List() }; Factories.Add(item); return item; } public Build AddPump(Munity factory, string name, 二供分区Type type) { Build pump = new Build { Name = name, Type = type, factoryName = factory.Name }; pump.factory = factory; factory.Pumps.Add(pump); return pump; } public void RemoveFactory(Munity factory) { Factories.Remove(factory); } //private string _filePath = "data.txt"; //private string _bakPath = "bak\\"; private string _filePath = null;// "data.txt"; private string _bakPath = Path.Combine(Directory.GetCurrentDirectory(), "bak\\"); private string _fileDirectory = Path.Combine(Directory.GetCurrentDirectory(), "sav\\"); //public FactoryListFileManager(string filePath) //{ // _filePath = filePath; //} public string Save(string filePath) { if (filePath == null) { using (SaveFileDialog ofd = new SaveFileDialog()) { ofd.InitialDirectory = _fileDirectory; ofd.Filter = "txt|*.txt|All|*.*"; var result = ofd.ShowDialog(); if (result == DialogResult.OK) _filePath = ofd.FileName; else return null; } } else { _filePath = filePath; } if (File.Exists(_filePath)) { FileInfo fi = new FileInfo(_filePath); if (!Directory.Exists(_bakPath)) Directory.CreateDirectory(_bakPath); string bakName = Path.Combine(_bakPath, $"{fi.Name}_bak_{fi.LastWriteTime.ToString("yyyy_MM_dd_HH_mm_ss")}.txt"); File.Copy(_filePath, bakName, true); } var sb = new StringBuilder(); //sb.Append( JsonConvert.SerializeObject(Factories)); //2023年7月1日 放弃文本化输出,采用json输出 foreach (var factory in Factories) { // 写入工厂信息 sb.AppendLine($"Factory,{factory.Type},{factory.Name},{factory.templateID}"); foreach (var build in factory.Pumps) { // 写入水泵信息 sb.AppendLine($"Pump,{build.Type},{build.Name},{build.层高},{build.每层户数},{build.系统最大流量},{build.层数},{build.templateID}"); foreach (var dataset in build.Datasets) { // 写入数据集信息 sb.AppendLine($"Dataset,{dataset.Value.Name}"); if (dataset.Value.range_X != null) sb.AppendLine($"Dataset_Range_X,{dataset.Value.range_X.Min},{dataset.Value.range_X.Max}"); if (dataset.Value.range_Y != null) sb.AppendLine($"Dataset_Range_Y,{dataset.Value.range_Y.Min},{dataset.Value.range_Y.Max}"); if (dataset.Value.Data != null) foreach (var point in dataset.Value.Data) { // 写入数据点信息 sb.AppendLine($"{point.X},{point.Y}"); } } } } // 将数据写入文件 File.WriteAllText(_filePath, sb.ToString()); return _filePath; } public string Load(string fileName) { //var factoryList = new FactoryList(); Factories.Clear(); if (!Directory.Exists(_fileDirectory)) Directory.CreateDirectory(_fileDirectory); if (fileName == null) { using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.InitialDirectory = _fileDirectory; ofd.Filter = "txt|*.txt|All|*.*"; var result = ofd.ShowDialog(); if (result == DialogResult.OK) _filePath = ofd.FileName; else return null; } } else { _filePath = fileName; } //var factoryList = new FactoryList(); if (!File.Exists(_filePath)) { return null; } Factories.Clear(); var currentFactory = default(Munity); var currentPump = default(Build); var currentDataset = default(BuildDataset); //var txt = File.ReadAllText(_filePath); //Factories = JsonConvert.DeserializeObject>(txt); //Factories.ToList().ForEach(factory => //{ // factory.Pumps.ForEach(pump => { pump.factory = factory; }); //}); foreach (var line in File.ReadAllLines(_filePath)) { var fields = line.Split(','); switch (fields[0]) { case "Factory": // 读取工厂信息 var factoryType = (BuildingType)Enum.Parse(typeof(BuildingType), fields[1]); var factoryName = fields[2]; currentFactory = new Munity { Name = factoryName, Type = factoryType, Pumps = new List(), templateID = fields.Length > 3 ? fields[3] : null }; this.Factories.Add(currentFactory); currentPump = null; currentDataset = null; break; case "Pump": // 读取水泵信息 var pumpType = (二供分区Type)Enum.Parse(typeof(二供分区Type), fields[1]); var pumpName = fields[2]; currentPump = new Build { Name = pumpName, Type = pumpType, factoryName = currentFactory.Name, factory = currentFactory, 层高 = double.Parse(fields[3]), 每层户数 = double.Parse(fields[4]), 系统最大流量 = double.Parse(fields[5]), 层数 = fields.Length >= 7 ? double.Parse(fields[6]) : 75, templateID = fields.Length >= 8 ? fields[7] : null }; currentFactory.Pumps.Add(currentPump); currentDataset = null; break; case "Dataset": // 读取数据集信息 var datasetName = fields[1]; currentDataset = new BuildDataset { Name = datasetName, Data = new List() }; currentPump.Datasets[currentDataset.Name] = currentDataset; break; case "Dataset_Range_X": { double d1 = 0; double d2 = 0; if (double.TryParse(fields[1], out d1) && double.TryParse(fields[2], out d2)) { currentDataset.range_X = new Range(d1, d2); } break; } case "Dataset_Range_Y": { double d1 = 0; double d2 = 0; if (double.TryParse(fields[1], out d1) && double.TryParse(fields[2], out d2)) { currentDataset.range_Y = new Range(d1, d2); } break; } default: // 读取数据点信息 var x = float.Parse(fields[0]); var y = float.Parse(fields[1]); currentDataset.Data.Add(new PointF(x, y)); break; } } CurveFit(); //return factoryList; return _filePath; } public void CurveFit() { foreach (var f in Factories) { f.CurveFit(); } } } }