using System;
|
using System.Collections.Generic;
|
using System.ComponentModel;
|
using System.Data;
|
using System.Drawing;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using System.Windows.Forms;
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
|
using System.Windows.Forms.DataVisualization.Charting;
|
|
using System;
|
using System.Collections.Generic;
|
using System.ComponentModel;
|
using System.Data;
|
using System.Drawing;
|
using System.Linq;
|
using System.Windows.Forms;
|
using MathNet.Numerics.LinearAlgebra.Double; // 引入 Math.NET Numerics 库进行多项式拟合
|
using MathNet.Numerics;
|
using System.IO;
|
using System.Xml.Serialization;
|
using System.Xml.Linq;
|
using CloudWaterNetwork.建模;
|
using static System.Windows.Forms.LinkLabel;
|
using Series = System.Windows.Forms.DataVisualization.Charting.Series;
|
using System.Drawing.Imaging;
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Status;
|
using System.Reflection;
|
|
namespace CloudWaterNetwork.建模
|
{
|
|
public static class PointsExtansion
|
{
|
public static int FindNearest(this DataPointCollection dataPoints, double x)
|
{
|
var dataPoints_sort = dataPoints.ToList();
|
dataPoints_sort.Sort((a, b) => { return a.XValue > b.XValue ? 1 : -1; });
|
|
for (int i = 0; i < dataPoints_sort.Count; i++)
|
{
|
|
if (dataPoints_sort[i].XValue > x)
|
{
|
if (i == 0) return i;
|
var left = x - dataPoints_sort[i - 1].XValue;
|
var right = dataPoints_sort[i].XValue - x;
|
if (right < left)
|
return i;
|
else
|
return i - 1;
|
}
|
}
|
return dataPoints_sort.Count - 1;
|
}
|
}
|
public enum FactoryType
|
{
|
水厂,
|
泵站
|
}
|
public enum PumpType
|
{
|
调速泵,
|
定速泵
|
}
|
public class Dataset
|
{
|
public Dataset(string name)
|
{
|
Name = name;
|
}
|
public Dataset()
|
{
|
|
}
|
|
public string Name { get; set; }
|
public List<PointF> Data { get; set; } = new List<PointF>();
|
|
public int degree { get { return 2; } }
|
|
public List<double> ForumParams = null;
|
|
public DenseVector coefficients = null; // 存储多项式系数
|
|
public double ErrNum { get { return -1; } }
|
|
private int stepNum { get { return 50; } }
|
|
private double step { get { return (range_X.Max - range_X.Min) / stepNum; } }
|
/// <summary>
|
/// 不需要读取
|
/// </summary>
|
public string Legend
|
{
|
get
|
{
|
switch (Name)
|
{
|
case "流量扬程曲线":
|
return "Legend1";
|
case "流量功率曲线":
|
return "Legend1";
|
case "流量效率曲线":
|
return "Legend1";
|
|
}
|
return "Legend1";
|
}
|
}
|
|
public AxisType axisType
|
{
|
get {
|
switch(Name)
|
{
|
case "流量扬程曲线":
|
return AxisType.Primary;
|
default:
|
return AxisType.Secondary;
|
|
}
|
}
|
}
|
|
public void CurveFit()
|
{
|
if (!HasData)
|
{
|
IsFitted= false;
|
return;
|
}
|
|
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 string getForum
|
{
|
get
|
{
|
// 生成多项式的字符串表示形式
|
string polyStr = "y=";
|
for (int i = coefficients.Count - 1; i >= 0; i--)
|
{
|
if (coefficients[i] == 0) continue;
|
if (i == coefficients.Count - 1)
|
{
|
polyStr += coefficients[i].ToString() + "x^" + i;
|
}
|
else if (i == 1)
|
{
|
if (coefficients[i] > 0)
|
{
|
if (polyStr != "") polyStr += " + ";
|
polyStr += coefficients[i].ToString() + "x";
|
}
|
else
|
{
|
polyStr += " - " + (-coefficients[i]).ToString() + "x";
|
}
|
}
|
else if (i == 0)
|
{
|
if (coefficients[i] > 0)
|
{
|
if (polyStr != "") polyStr += " + ";
|
polyStr += coefficients[i].ToString();
|
}
|
else
|
{
|
polyStr += " - " + (-coefficients[i]).ToString();
|
}
|
}
|
else
|
{
|
if (coefficients[i] > 0)
|
{
|
if (polyStr != "") polyStr += " + ";
|
polyStr += coefficients[i].ToString() + "x^" + i;
|
}
|
else
|
{
|
polyStr += " - " + (-coefficients[i]).ToString() + "x^" + i;
|
}
|
}
|
}
|
if (polyStr == "") polyStr = "0";
|
return polyStr;
|
}
|
|
}
|
|
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;
|
}
|
|
/// <summary>
|
/// X显示的范围
|
/// </summary>
|
public Range range_X;
|
|
|
/// <summary>
|
/// Y显示的范围
|
/// </summary>
|
public Range range_Y;
|
|
public bool HasData { get { return Data.Count >= 3; } }
|
|
public bool IsFitted = false;
|
public List<PointF> FittedCurve
|
{
|
get
|
{
|
List<PointF> doubles = new List<PointF>();
|
for (double x = range_X.Min; x <= range_X.Max; x += step)
|
{
|
double y = Evaluate(x);
|
//chart.Series[1].Points.AddXY(x, y);
|
doubles.Add(new PointF ((float) x,(float) y ));
|
}
|
return doubles;
|
|
}
|
|
}
|
public List<PointF> FittedCurvebyRange(Range range)
|
{
|
List<PointF> doubles = new List<PointF>();
|
|
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;
|
}
|
|
|
}
|
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)
|
{
|
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 Pump
|
{
|
public Pump()
|
{
|
Datasets = new Dictionary<string, Dataset>() {
|
{"流量扬程曲线",new Dataset("流量扬程曲线") },
|
{"流量功率曲线",new Dataset("流量功率曲线") },
|
{"流量效率曲线",new Dataset("流量效率曲线") }
|
|
//,new Dataset("流量功率曲线"),new Dataset("流量效率曲线")
|
};
|
}
|
public string Name { get; set; }
|
public PumpType Type { get; set; }
|
public Dictionary<string, Dataset> Datasets { get; set; }
|
|
public string factoryName = "";
|
|
public double 额定转速 { get; set; } = 1500;
|
|
public double 额定流量 { get; set; } = 0;
|
|
public double 额定扬程 { get; set; } = 0;
|
|
public double 额定功率 { get; set; } = 75;
|
|
|
|
|
public Dataset 流量扬程曲线 { get { return Datasets["流量扬程曲线"]; } }
|
|
public Dataset 流量功率曲线 { get { return Datasets["流量功率曲线"]; } }
|
|
public Dataset 流量效率曲线 { get { return Datasets["流量效率曲线"]; } }
|
|
|
|
public void CurveFit()
|
{
|
|
|
foreach (var data in Datasets)
|
{
|
data.Value.Data.Sort((a, b) => a.X > b.X ? 1 : -1);
|
if (data.Value.HasData) data.Value.CurveFit();
|
}
|
string basetype = "流量扬程曲线";
|
string type = "";
|
|
|
type = "流量效率曲线";
|
var type1 = "流量功率曲线";
|
if (Datasets[type].IsFitted)
|
{
|
List<PointF> points = new List<PointF>();
|
List<PointF> points1 = new List<PointF>();
|
|
foreach (var yc in Datasets[basetype].Data)
|
{
|
float x= yc.X;
|
float y_扬程 = yc.Y;
|
float y_效率= (float)Datasets[type].Evaluate(x);
|
points.Add(new PointF(x, y_效率));
|
points1.Add(new PointF(x,x* y_扬程 / y_效率 /3.6f ));
|
|
|
}
|
Datasets[type].Data = points;
|
Datasets[type1].Data = points1;
|
Datasets[type1].CurveFit();
|
}
|
|
//type = "流量功率曲线";
|
//if (Datasets[type].IsFitted)
|
//{
|
// List<PointF> points = new List<PointF>();
|
// foreach (var x in Datasets[basetype].Data.Select(p => p.X))
|
// {
|
// points.Add(new PointF(x, (float)Datasets[type].Evaluate(x)));
|
// }
|
// Datasets[type].Data = points;
|
|
//}
|
}
|
|
public void ShowInGrid(DataGridView dataGridView)
|
{
|
dataGridView.Rows.Clear();
|
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;
|
dataGridView.Rows.Add(Math.Round(p.X,1), Math.Round(p.Y,2), Math.Round(y1,2), Math.Round(y2, 2));
|
}
|
}
|
|
public void AddCurve(Chart chart, string type = "流量扬程曲线",bool onlyOne=false,Range Force_range_X=null)
|
{
|
Series series;
|
List<PointF> Points;
|
List<PointF> 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
|
{
|
Points = Datasets[type].Data;
|
series = AddSeries("ChartArea1", SeriesChartType.Point, Datasets[type].Legend, Datasets[type].axisType, $"{type}点", 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);
|
|
}
|
|
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)
|
{
|
// 设置 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)
|
{
|
// 设置 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<PointF> Points, bool showLegend = true)
|
{
|
Series series = new Series();
|
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 Factory
|
{
|
public string Name { get; set; }
|
public FactoryType Type { get; set; }
|
public List<Pump> Pumps { get; set; }
|
|
public void CurveFit()
|
{
|
foreach(var p in Pumps)
|
{
|
p.CurveFit();
|
}
|
}
|
|
}
|
public class FactoryList
|
{
|
public List<Factory> Factories { get; set; }
|
|
public FactoryList()
|
{
|
Factories = new List<Factory>();
|
}
|
public void AddFactory(string name, FactoryType type)
|
{
|
Factories.Add(new Factory { Name = name, Type = type, Pumps = new List<Pump>() });
|
}
|
public Pump AddPump(Factory factory, string name, PumpType type)
|
{
|
Pump pump = new Pump { Name = name, Type = type, factoryName = factory.Name };
|
factory.Pumps.Add(pump);
|
return pump;
|
}
|
public void RemoveFactory(Factory factory)
|
{
|
Factories.Remove(factory);
|
}
|
private readonly string _filePath = "data.txt";
|
private readonly string _bakPath = "bak\\";
|
|
//public FactoryListFileManager(string filePath)
|
//{
|
// _filePath = filePath;
|
//}
|
|
public void Save()
|
{
|
if (File.Exists(_filePath))
|
{
|
FileInfo fi=new FileInfo(_filePath);
|
if (!Directory.Exists(_bakPath)) Directory.CreateDirectory(_bakPath);
|
string bakName = Path.Combine(_bakPath, $"data.txt_bak_{fi.LastWriteTime.ToString("yyyy_MM_dd_HH_mm_ss")}");
|
File.Copy(_filePath, bakName, true);
|
}
|
|
|
var sb = new StringBuilder();
|
foreach (var factory in Factories)
|
{
|
// 写入工厂信息
|
sb.AppendLine($"Factory,{factory.Type},{factory.Name}");
|
|
foreach (var pump in factory.Pumps)
|
{
|
// 写入水泵信息
|
sb.AppendLine($"Pump,{pump.Type},{pump.Name},{pump.额定转速},{pump.额定流量},{pump.额定扬程},{pump.额定功率}");
|
|
foreach (var dataset in pump.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());
|
}
|
|
public void Load()
|
{
|
//var factoryList = new FactoryList();
|
Factories.Clear();
|
if (!File.Exists(_filePath))
|
{
|
return;
|
}
|
|
var currentFactory = default(Factory);
|
var currentPump = default(Pump);
|
var currentDataset = default(Dataset);
|
|
foreach (var line in File.ReadAllLines(_filePath))
|
{
|
var fields = line.Split(',');
|
|
switch (fields[0])
|
{
|
case "Factory":
|
// 读取工厂信息
|
var factoryType = (FactoryType)Enum.Parse(typeof(FactoryType), fields[1]);
|
var factoryName = fields[2];
|
currentFactory = new Factory { Name = factoryName, Type = factoryType, Pumps = new List<Pump>() };
|
this.Factories.Add(currentFactory);
|
currentPump = null;
|
currentDataset = null;
|
break;
|
|
case "Pump":
|
// 读取水泵信息
|
var pumpType = (PumpType)Enum.Parse(typeof(PumpType), fields[1]);
|
var pumpName = fields[2];
|
currentPump = new Pump { Name = pumpName, Type = pumpType, factoryName = currentFactory.Name, 额定转速 = double.Parse(fields[3]), 额定流量 = double.Parse(fields[4]), 额定扬程 = double.Parse(fields[5]), 额定功率 = fields.Length >= 7 ? double.Parse(fields[6]) : 75 };
|
|
currentFactory.Pumps.Add(currentPump);
|
currentDataset = null;
|
break;
|
|
case "Dataset":
|
// 读取数据集信息
|
var datasetName = fields[1];
|
currentDataset = new Dataset { Name = datasetName, Data = new List<PointF>() };
|
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;
|
}
|
|
|
|
public void CurveFit()
|
{
|
foreach (var f in Factories)
|
{
|
f.CurveFit();
|
}
|
}
|
|
}
|
}
|