using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Runtime.Serialization;
|
using System.Security.Permissions;
|
using System.ComponentModel;
|
|
namespace IStation.Model
|
{
|
|
/// <summary>
|
/// 曲线表达式
|
/// </summary>
|
[DataContract]
|
public class CurveExpress : ICloneable
|
{
|
#region 构造函数
|
|
public CurveExpress() { }
|
public CurveExpress(CurveExpress rhs)
|
{
|
this.FitPow = rhs.FitPow;
|
this.FitType = rhs.FitType;
|
this.Min = rhs.Min;
|
this.Max = rhs.Max;
|
this.Index0 = rhs.Index0;
|
this.Index1 = rhs.Index1;
|
this.Index2 = rhs.Index2;
|
this.Index3 = rhs.Index3;
|
this.DefinePoints = rhs.DefinePoints?.Select(x => new CurvePoint(x)).ToList();
|
this.IsNull = rhs.IsNull;
|
}
|
public CurveExpress(CurveExpress rhs, double ratio_y)
|
{
|
this.FitPow = rhs.FitPow;
|
this.FitType = rhs.FitType;
|
this.Min = rhs.Min;
|
this.Max = rhs.Max;
|
this.Index0 = rhs.Index0 * ratio_y;
|
this.Index1 = rhs.Index1 * ratio_y;
|
this.Index2 = rhs.Index2 * ratio_y;
|
this.Index3 = rhs.Index3 * ratio_y;
|
this.DefinePoints = rhs.DefinePoints?.Select(x => new CurvePoint(x.X, x.Y * ratio_y)).ToList();
|
this.IsNull = rhs.IsNull;
|
}
|
public CurveExpress(string strParas)
|
{
|
if (string.IsNullOrEmpty(strParas))
|
{
|
this.IsNull = true;
|
return;
|
}
|
var str_list = strParas.Split(new string[] { Separator }, StringSplitOptions.RemoveEmptyEntries);
|
var count = str_list.Count();
|
if (count < 8)
|
{
|
this.IsNull = true;
|
return;
|
}
|
this.IsNull = false ;
|
this.FitPow = Convert.ToInt32(str_list[0]);
|
this.FitType = (eCurveFitType)Convert.ToInt32(str_list[1]);
|
this.Min = Convert.ToDouble(str_list[2]);
|
this.Max = Convert.ToDouble(str_list[3]);
|
this.Index0 = Convert.ToDouble(str_list[4]);
|
this.Index1 = Convert.ToDouble(str_list[5]);
|
this.Index2 = Convert.ToDouble(str_list[6]);
|
this.Index3 = Convert.ToDouble(str_list[7]);
|
if (count > 8)
|
{
|
this.DefinePoints = CurvePoint.ToList(str_list[8]);
|
}
|
}
|
public CurveExpress(List<CurvePoint> points)
|
{
|
ResetCurve(points, eCurveFitType.CubicCurve, false);
|
}
|
public CurveExpress(List<CurvePoint> points, bool savePoints)
|
{
|
ResetCurve(points, eCurveFitType.CubicCurve, savePoints);
|
}
|
public CurveExpress(List<CurvePoint> points, eCurveFitType fitType)
|
{
|
ResetCurve(points, fitType,false);
|
}
|
public CurveExpress(List<CurvePoint> points, eCurveFitType fitType, bool savePoints)
|
{
|
ResetCurve(points, fitType, savePoints);
|
}
|
public CurveExpress(List<CurvePoint> points, int fitPow)
|
{
|
ResetCurve(points, fitPow,false);
|
}
|
public CurveExpress(List<CurvePoint> points, int fitPow, bool savePoints)
|
{
|
ResetCurve(points, fitPow, savePoints);
|
}
|
|
/// <summary>
|
/// 重置曲线
|
/// </summary>
|
public virtual void ResetCurve(CurveExpress rhs)
|
{
|
this.FitPow = rhs.FitPow;
|
this.FitType = rhs.FitType;
|
this.Min = rhs.Min;
|
this.Max = rhs.Max;
|
this.Index0 = rhs.Index0;
|
this.Index1 = rhs.Index1;
|
this.Index2 = rhs.Index2;
|
this.Index3 = rhs.Index3;
|
this.DefinePoints = rhs.DefinePoints?.Select(x => new CurvePoint(x)).ToList();
|
this.IsNull = rhs.IsNull;
|
}
|
|
/// <summary>
|
/// 重置曲线
|
/// </summary>
|
public virtual void ResetCurve(List<CurvePoint> points, eCurveFitType fitType, bool savePoints = false )
|
{
|
var express = FitCurveHelper.BuildCurveExpress(points, fitType);
|
if (express == null)
|
{
|
this.IsNull = true;
|
return;
|
}
|
if (express.IsNull)
|
{
|
this.IsNull = true;
|
return;
|
}
|
this.FitType = express.FitType;
|
this.FitPow = express.FitPow;
|
this.Min = express.Min;
|
this.Max = express.Max;
|
this.Index0 = express.Index0;
|
this.Index1 = express.Index1;
|
this.Index2 = express.Index2;
|
this.Index3 = express.Index3;
|
this.IsNull = express.IsNull;
|
if (savePoints)
|
{
|
this.DefinePoints = express.DefinePoints;
|
}
|
else if (fitType == eCurveFitType.ThroughPoint)
|
{
|
this.DefinePoints = express.DefinePoints;
|
}
|
}
|
|
/// <summary>
|
/// 重置曲线
|
/// </summary>
|
public virtual void ResetCurve(List<CurvePoint> points, int fitPow, bool savePoints = false )
|
{
|
var fitType = GetFitType(fitPow);
|
this.ResetCurve(points, fitType, savePoints);
|
}
|
|
|
#endregion
|
|
#region 静态值
|
|
public const string Separator = "#";
|
|
#endregion
|
|
#region 属性
|
|
/// <summary>
|
/// 拟合次数
|
/// </summary>
|
[DataMember]
|
public int FitPow
|
{
|
get { return fitPow; }
|
set { fitPow = value; }
|
}
|
protected int fitPow = 3;
|
|
/// <summary>
|
/// 拟合类型
|
/// </summary>
|
[DataMember]
|
public eCurveFitType FitType
|
{
|
get { return fitType; }
|
set { fitType = value; }
|
}
|
protected eCurveFitType fitType = eCurveFitType.CubicCurve;
|
|
/// <summary>
|
/// 最大值
|
/// </summary>
|
[DataMember]
|
public double Max
|
{
|
get { return max; }
|
set { max = value; }
|
}
|
protected double max;
|
|
/// <summary>
|
/// 最小值
|
/// </summary>
|
[DataMember]
|
public double Min
|
{
|
get { return min; }
|
set { min = value; }
|
}
|
protected double min;
|
|
/// <summary>
|
/// 0次系数
|
/// </summary>
|
[DataMember]
|
public double Index0
|
{
|
get { return index0; }
|
set { index0 = value; }
|
}
|
protected double index0;
|
|
/// <summary>
|
/// 1次系数
|
/// </summary>
|
[DataMember]
|
public double Index1
|
{
|
get { return index1; }
|
set { index1 = value; }
|
}
|
protected double index1;
|
|
/// <summary>
|
/// 2次系数
|
/// </summary>
|
[DataMember]
|
public double Index2
|
{
|
get { return index2; }
|
set { index2 = value; }
|
}
|
protected double index2;
|
|
/// <summary>
|
/// 3次系数
|
/// </summary>
|
[DataMember]
|
public double Index3
|
{
|
get { return index3; }
|
set { index3 = value; }
|
}
|
protected double index3;
|
|
/// <summary>
|
/// 定义点
|
/// </summary>
|
[DataMember]
|
public List<CurvePoint> DefinePoints
|
{
|
get { return definePoints; }
|
set { definePoints = value; }
|
}
|
protected List<CurvePoint> definePoints = null;
|
|
/// <summary>
|
/// 是否为空
|
/// </summary>
|
[DataMember]
|
public bool IsNull
|
{
|
get { return isNull; }
|
set { isNull = value; }
|
}
|
protected bool isNull;
|
|
|
#endregion
|
|
|
/// <summary>
|
/// 转化为保存字符串
|
/// </summary>
|
public string ToDsString()
|
{
|
var str_list = new List<string>();
|
str_list.Add(this.FitPow.ToString());
|
str_list.Add(((int)this.FitType).ToString());
|
str_list.Add(this.Min.ToString());
|
str_list.Add(this.Max.ToString());
|
//str_list.Add(this.Index0.ToString());
|
//str_list.Add(this.Index1.ToString("0.00000"));
|
//str_list.Add(this.Index2.ToString("0.0000000000"));
|
//str_list.Add(this.Index3.ToString("0.00000000000000"));
|
|
//nsx
|
var dc = new DoubleConverter();
|
str_list.Add(dc.ConvertToString(this.Index0));
|
str_list.Add(dc.ConvertToString(this.Index1));
|
str_list.Add(dc.ConvertToString(this.Index2));
|
str_list.Add(dc.ConvertToString(this.Index3));
|
|
if (this.DefinePoints != null && this.DefinePoints.Count > 0)
|
{
|
str_list.Add(this.DefinePoints.ToDsString());
|
}
|
return string.Join(Separator, str_list);
|
}
|
|
object ICloneable.Clone()
|
{
|
return Clone();
|
}
|
|
public CurveExpress Clone()
|
{
|
return new CurveExpress(this);
|
}
|
|
/// <summary>
|
/// 是否在内部
|
/// </summary>
|
public bool IsInterior(double x)
|
{
|
if (x < Min)
|
return false;
|
if (x > Max)
|
return false;
|
return true;
|
}
|
|
/// <summary>
|
/// 获取拟合次方
|
/// </summary>
|
public static int GetFitPow(eCurveFitType fitType)
|
{
|
switch (fitType)
|
{
|
case eCurveFitType.CubicCurve: return 3;
|
case eCurveFitType.ConicCurve: return 2;
|
case eCurveFitType.ConicCurveB0: return 2;
|
case eCurveFitType.LinearCurve: return 1;
|
case eCurveFitType.ThroughPoint: return 0;
|
default: return 3;
|
}
|
}
|
|
/// <summary>
|
/// 获取拟合类型
|
/// </summary>
|
public static eCurveFitType GetFitType(int fitPow)
|
{
|
switch (fitPow)
|
{
|
case 3: return eCurveFitType.CubicCurve;
|
case 2: return eCurveFitType.ConicCurve;
|
case 1: return eCurveFitType.LinearCurve;
|
case 0: return eCurveFitType.ThroughPoint;
|
default: return eCurveFitType.CubicCurve;
|
}
|
}
|
|
/// <summary>
|
/// 获取拟合点
|
/// </summary>
|
public double GetFitPointY(double x)
|
{
|
return FitCurveHelper.GetFitPointY(this, x);
|
}
|
|
/// <summary>
|
/// 获取拟合点
|
/// </summary>
|
public List<CurvePoint> GetFitPoints(int pointNumber = 20)
|
{
|
if (this.IsNull)
|
return default;
|
return FitCurveHelper.GetFitPoints(this, pointNumber);
|
}
|
|
/// <summary>
|
/// 通过区间内拟合点
|
/// </summary>
|
public List<CurvePoint> GetFitPointsByRange(double minX, double maxX, int pointNumber = 20)
|
{
|
if (this.IsNull)
|
return default;
|
return FitCurveHelper.GetFitPointsByRange(this, minX, maxX, pointNumber);
|
}
|
|
/// <summary>
|
/// 获取拟合点
|
/// </summary>
|
public List<CurvePoint> GetFitPointsByExtend(double ratioExtend, int pointNumber = 20)
|
{
|
if (this.IsNull)
|
return default;
|
return GetFitPointsByRange(this.min, this.max * ratioExtend, pointNumber);
|
}
|
|
/// <summary>
|
/// 获取第一个拟合点
|
/// </summary>
|
public CurvePoint GetFirstFitPoint()
|
{
|
return new CurvePoint(this.Min, GetFitPointY(this.Min));
|
}
|
|
/// <summary>
|
/// 获取最后一个拟合点
|
/// </summary>
|
public CurvePoint GetLastFitPoint()
|
{
|
return new CurvePoint(this.Max, GetFitPointY(this.Max));
|
}
|
|
/// <summary>
|
/// 获取零点的Y值
|
/// </summary>
|
public double GetZeroFitPointY()
|
{
|
return this.Index0;
|
}
|
|
/// <summary>
|
/// 转换为Model
|
/// </summary>
|
public static CurveExpress ToModel(string strParas)
|
{
|
if (string.IsNullOrEmpty(strParas))
|
return default;
|
var express = new CurveExpress(strParas);
|
if (express == null)
|
return default;
|
return express;
|
}
|
|
/// <summary>
|
/// 是否相等
|
/// </summary>
|
public bool IsEquals(CurveExpress rhs)
|
{
|
if (rhs == null)
|
return false;
|
if (rhs.ToDsString().Equals(this.ToDsString()))
|
return true;
|
else
|
return false;
|
}
|
|
/// <summary>
|
/// 是否为Zero表达式
|
/// </summary>
|
public bool IsZeroExpress()
|
{
|
if (this.Index0 != 0)
|
return false;
|
if (this.Index1 != 0)
|
return false;
|
if (this.Index2 != 0)
|
return false;
|
if (this.Index3 != 0)
|
return false;
|
return true;
|
}
|
|
/// <summary>
|
/// 通过 X转换
|
/// </summary>
|
public CurveExpress TransferX(double ratioX)
|
{
|
var fitPoints = this.GetFitPoints();
|
fitPoints = fitPoints?.Select(x => new CurvePoint(x.X * ratioX, x.Y)).ToList();
|
if (fitPoints == null || fitPoints.Count < 1)
|
return default;
|
var exp = new CurveExpress(fitPoints, this.FitType);
|
var definePoints = this.DefinePoints?.Select(x => new CurvePoint(x.X * ratioX, x.Y)).ToList();
|
if (definePoints != null && definePoints.Count > 0)
|
exp.DefinePoints = definePoints;
|
return exp;
|
}
|
|
/// <summary>
|
/// 相似换算(speed_ratio = 新的转速 除以 老的转速)
|
/// </summary>
|
public CurveExpress SimuBySpeed(eFeatCurveType featType, double speed_ratio, int pointNumber = 20)
|
{
|
double space = (this.Max - this.Min) / (pointNumber - 1);
|
switch (featType)
|
{
|
case eFeatCurveType.QH:
|
{
|
List<CurvePoint> pointInfo60 = new List<CurvePoint>();
|
|
for (int i = 0; i < pointNumber; i++)
|
{
|
double q50 = space * i + this.Min;
|
double q60 = q50 * speed_ratio;
|
double Y50 = GetFitPointY(q50);
|
pointInfo60.Add(new CurvePoint() { X = q60, Y = Y50 * speed_ratio * speed_ratio });
|
}
|
|
var new_curve = new CurveExpress(pointInfo60, this.FitType);
|
new_curve.DefinePoints = this.DefinePoints?.Select(x => new CurvePoint() { X = x.X * speed_ratio, Y = x.Y * speed_ratio * speed_ratio }).ToList();
|
return new_curve;
|
}
|
case eFeatCurveType.QE:
|
{
|
List<CurvePoint> pointInfo60 = new List<CurvePoint>();
|
|
for (int i = 0; i < pointNumber; i++)
|
{
|
double q50 = space * i + this.Min;
|
double q60 = q50 * speed_ratio;
|
double Y50 = GetFitPointY(q50);
|
pointInfo60.Add(new CurvePoint() { X = q60, Y = Y50 });
|
}
|
|
var new_curve = new CurveExpress(pointInfo60, this.FitType);
|
return new_curve;
|
}
|
case eFeatCurveType.QP:
|
{
|
List<CurvePoint> pointInfo60 = new List<CurvePoint>();
|
|
for (int i = 0; i < pointNumber; i++)
|
{
|
double q50 = space * i + this.Min;
|
double q60 = q50 * speed_ratio;
|
double Y50 = GetFitPointY(q50);
|
pointInfo60.Add(new CurvePoint() { X = q60, Y = Y50 * speed_ratio * speed_ratio * speed_ratio });
|
}
|
|
var new_curve = new CurveExpress(pointInfo60);
|
new_curve.DefinePoints = this.DefinePoints?.Select(x => new CurvePoint() { X = x.X * speed_ratio, Y = x.Y * speed_ratio * speed_ratio * speed_ratio }).ToList();
|
return new_curve;
|
}
|
default:
|
{
|
List<CurvePoint> pointInfo60 = new List<CurvePoint>();
|
|
for (int i = 0; i < pointNumber; i++)
|
{
|
double q50 = space * i + this.Min;
|
double q60 = q50 * speed_ratio;
|
double Y50 = GetFitPointY(q50);
|
pointInfo60.Add(new CurvePoint() { X = q60, Y = Y50 });
|
}
|
|
var new_curve = new CurveExpress(pointInfo60);
|
new_curve.DefinePoints = this.DefinePoints?.Select(x => new CurvePoint() { X = x.X * speed_ratio, Y = x.Y }).ToList();
|
return new_curve;
|
}
|
}
|
}
|
|
/// <summary>
|
/// 50 到 60
|
/// </summary>
|
public CurveExpress SimuFrom50To60(eFeatCurveType featType, int pointNumber = 20)
|
{
|
return SimuBySpeed(featType, 1.2, pointNumber);
|
}
|
|
|
/// <summary>
|
/// 曲线方程:用于显示
|
/// </summary>
|
public string GetCurveFunction(string xName = "Q")
|
{
|
//double signValue = 0;
|
//int value = 0;
|
|
StringBuilder sb = new StringBuilder();
|
|
//sb.Append(this.index0.ToString("G"));
|
//sb.Append(this.index0.ToString("0.00E+000"));//ToString("G"));
|
sb.Append(Math.Abs(this.Index0).ToString("0.00"));//.ToString("G"));
|
|
if (this.Index1 > 0)
|
sb.Append(" + ");
|
else
|
sb.Append(" - ");
|
//GetFuncScientificValue(1,Math.Abs(index1),ref signValue,ref value);
|
//sb.AppendFormat("{0}E{1}",signValue,value);
|
sb.Append(Math.Abs(this.Index1).ToString("0.0000"));//.ToString("G"));
|
sb.Append(" * ");
|
sb.Append(xName);
|
|
|
if (this.Index2 > 0)
|
sb.Append(" + ");
|
else
|
sb.Append(" - ");
|
//GetFuncScientificValue(2,Math.Abs(index2),ref signValue,ref value);
|
//sb.AppendFormat("{0}E{1}",signValue,value);
|
//sb.Append(Math.Round(Math.Abs(this.index2),4).ToString("G"));//.ToString("G"));
|
sb.Append(Math.Abs(this.Index2).ToString("0.000000000"));//.ToString("G"));
|
sb.Append(" * ");
|
sb.Append(xName);
|
sb.Append("^2 ");
|
|
|
if (this.Index3 > 0)
|
sb.Append(" + ");
|
else
|
sb.Append(" - ");
|
//GetFuncScientificValue(3,Math.Abs(index3),ref signValue,ref value);
|
//sb.AppendFormat("{0}E{1}",signValue,value);
|
//sb.Append(Math.Abs(this.index3).ToString("G"));//.ToString("G"));
|
sb.Append(Math.Abs(this.Index3).ToString("0.000000000000"));//.ToString("G"));
|
sb.Append(" * ");
|
sb.Append(xName);
|
sb.Append("^3 ");
|
|
return sb.ToString();
|
}
|
|
|
private static void GetFuncScientificValue(int FunctionIndex,
|
double FunctionCoeff, ref double signValue, ref int indexValue)
|
{
|
string strList = "";
|
if (FunctionIndex == 0)
|
{
|
strList = FunctionCoeff.ToString("0.000e+000");
|
}
|
else if (FunctionIndex == 1)
|
{
|
strList = FunctionCoeff.ToString("0.000000e+000");
|
}
|
else if (FunctionIndex == 2)
|
{
|
strList = FunctionCoeff.ToString("#.#########e+000");
|
}
|
else if (FunctionIndex == 3)
|
{
|
strList = FunctionCoeff.ToString("#.############e+000");
|
}
|
string[] st1 = null;
|
st1 = strList.Split('e');
|
signValue = Convert.ToDouble(st1[0]);
|
indexValue = Convert.ToInt32(st1[1]);
|
}
|
|
|
|
|
|
|
|
|
|
|
}
|
}
|