using System; using System.Collections.Generic; using System.Linq; using FLY.FeedbackRenZiJia.Common; using FLY.Thick.RemoteHistory; using Misc; using System.IO; using System.Text; using System.ComponentModel; using FLY.FeedbackRenZiJia.IService; using FObjBase; using PropertyChanged; using FLY.FeedbackRenZiJia.Server.Model; namespace FLY.FeedbackRenZiJia.Server { /// <summary> /// 存放一堆厚度与加热数据。 /// 1.用于判断加热是否稳定了; /// 2.加热偏移; /// 3.加热/厚度 /// </summary> public class HeatBuf: IHeatBuf, Misc.ISaveToXml { #region 属性,对外接口 #region 参数 [DoNotCheckEquality] /// <summary> /// 加热生效曲线,允许为空,则不生效 /// </summary> public List<int> HeatEffectCurve { get; set; } = new List<int>(); /// <summary> /// 厚度稳定范围 %, 偏差在范围内,都是稳定的 /// </summary> public int StableRange { get; set; } = 2; /// <summary> /// 厚度稳定范围 %, 加热量变化为0时,偏差在范围内,都是稳定的 /// </summary> public int StableRange0 { get; set; } = 2; /// <summary> /// 对位模式, 加热与厚度相关性阀值, 相关性 >=0.7 /// </summary> public double ThresholdR { get; set; } = 0.7; /// <summary> /// 对位模式 厚度%差 的极差 >= +4%-4%=8%, 单位% /// </summary> public int ThresholdMaxMin { get; set; } = 8; /// <summary> /// % /// 最大sigma, 只有当前的sigma 在 最大sigma 与 最小 sigma 内,才能控制 /// </summary> public int ThresholdSigmaMax { get; set; } = 20; /// <summary> /// 使用这里独立的Kp, 与 HeatCell 不一样 /// </summary> public bool IsUsedLocalKp { get; set; } = false; /// <summary> /// 本地Kp, 用于判断稳定性 /// </summary> public double LocalKp { get; set; } = 3; #endregion #region 状态 /// <summary> /// 总延时 =Delay+mRenZiJiaService.FilmLength/mRenZiJiaService.Velocity /// </summary> public TimeSpan Delay { get; set; } = TimeSpan.FromSeconds(100); #region 对位 /// <summary> /// 进入了对位模式 /// </summary> public bool IsIntoAutoONo { get; set; } /// <summary> /// 对位模式结果 /// </summary> public EAutoONoResult AutoONoResult { get; set; } = EAutoONoResult.Idle; /// <summary> /// 计算的最佳复位区号 /// </summary> public int BestOrgBoltNo { get; set; } = -1; /// <summary> /// 计算的最佳复位区号 对应的 加热/厚度 /// </summary> public double BestKp { get; set; } = -1; /// <summary> /// 计算的最佳复位区号 对应的 加热与厚度相关性 /// </summary> public double MaxR { get; set; } = -1; /// <summary> /// 计算的最佳复位区号 对应的 厚度%变化极差 单位% /// </summary> public double MaxMin { get; set; } = -1; #endregion #region 稳定性 /// <summary> /// 当前检测出来的厚度数据 稳定状态 /// </summary> public STABILITY Stability { get; protected set; } = STABILITY.IDLE; /// <summary> /// 当前检测出来的厚度数据 的稳定性 /// </summary> public double CurrR { get; set; } = -1; /// <summary> /// 当前检测出来的厚度数据 2Sigma /// </summary> public double Curr2Sigma { get; set; } = -1; /// <summary> /// 当前打散程度。 /// 连续N点都在平均值同一侧为一个块; /// 这些块的长度平均值/总长度, 为打散度; /// 打散度越小越好, 最小为 1/NBolts /// </summary> public double CurrBreakUp { get; protected set; } = 50; /// <summary> /// 当前检测出来的厚度数据,时间 /// </summary> public DateTime CurrTime { get; set; } = DateTime.MinValue; /// <summary> /// 当前检测出来的厚度数据,方向 /// </summary> public Misc.DIRECTION CurrDirection { get; protected set; } = Misc.DIRECTION.FORWARD; #endregion #region 列表 /// <summary> /// 列表最后1个BM /// </summary> public int LastBM { get; protected set; } /// <summary> /// 列表第1个BM /// </summary> public int FirstBM { get; protected set; } #endregion #region 每个分区状态 [DoNotCheckEquality] public bool[] BoltIsStable { get; set; } #endregion #endregion #endregion /// <summary> /// HeatCell 的 Kp /// </summary> public double Kp { get; set; } = 3; /// <summary> /// 厚度数量 /// </summary> int BoltCnt = 96; /// <summary> /// 加热数量 /// </summary> int ChannelCnt = 96; /// <summary> /// 缓存区大小 /// </summary> int BufSize = 60; /// <summary> /// 最后一幅数据的复位区号 /// </summary> int orgBoltNo = 1; /// <summary> /// 最后一幅数据的旋转角度 /// </summary> double rAngle = 355; public RList<FlyData_FeedbackHeat> mData; /// <summary> /// 当复位区号改变,它应该被清空 /// </summary> public BoltHeat[] mBoltData; static HeatBuf() { } public HeatBuf() { mData = new RList<FlyData_FeedbackHeat>(BufSize); Load(); checkParamErr(); PropertyChanged += HeatBuf_PropertyChanged; } void checkParamErr() { if (StableRange < 1 || StableRange > 10) { StableRange = 2; } if (StableRange0 < 1 || StableRange0 > 10) { StableRange0 = 2; } if (ThresholdR < 0.1 || ThresholdR > 0.9) { ThresholdR = 0.7; } if (ThresholdMaxMin < 2 || ThresholdMaxMin > 20) { ThresholdMaxMin = 8; } if (LocalKp < 1 || LocalKp > 20) { LocalKp = 3; } } private void HeatBuf_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsIntoAutoONo") { if (IsIntoAutoONo) { AutoONoResult = EAutoONoResult.Idle; } else { if (AutoONoResult == EAutoONoResult.Idle) { //被强制关闭 AutoONoResult = EAutoONoResult.Err_Break; } } } } public void Init(int boltCnt, int channelCnt) { BoltCnt = boltCnt; ChannelCnt = channelCnt; mBoltData = new BoltHeat[BoltCnt]; for (int i = 0; i < BoltCnt; i++) { mBoltData[i] = new BoltHeat(); } mData.Clear(); //TODO 测试 //LoadData(); } public bool IsStable() { if ((int)Stability > 0) return true; else return false; } public bool IsUnstable() { if ((int)Stability < 0) return true; else return false; } public int[] GetThicks() { if (mData.Count() == 0) return null; return mData.Last().Thicks; } /// <summary> /// 1.添加数据到缓冲; /// 2.判断是否稳定 /// 3.根据稳定变化,加热变化,计算偏移 /// </summary> /// <param name="time">测厚的开始时间</param> /// <param name="endtime">测厚的结束时间</param> /// <param name="direction">方向</param> /// <param name="period">旋转时间</param> /// <param name="rotateCnt">旋转次数</param> /// <param name="orgBoltNo">复位区号</param> /// <param name="rAngle">旋转角度</param> /// <param name="filmLength">膜距离</param> /// <param name="frames">厚度数据</param> /// <param name="boltMaps">分区表</param> /// <param name="htime">加热的时间</param> /// <param name="heats">加热量</param> /// <returns></returns> public bool Add( DateTime time, DateTime endtime, Misc.DIRECTION direction, TimeSpan period, int rotateCnt, int orgBoltNo, double rAngle, double filmLength, int[] frames, List<Thick.Blowing.IService.BoltMapCell> boltMaps, DateTime htime, int[] heats) { //thicks 的数据量一定要是 BoltCnt //heats 的数据量一定要是 ChannelCnt //不是返回 false if (frames.Count() != BoltCnt)//异常 return false; if (heats.Count() != ChannelCnt)//异常 return false; FlyData_FeedbackHeat d = new FlyData_FeedbackHeat() { Time = time, EndTime = endtime, Direction = direction, RotatePeriod = period, RotateCnt = rotateCnt, OrgBoltNo = orgBoltNo, RAngle = rAngle, FilmLength = filmLength, Thicks = frames, Boltmap = boltMaps, HTime = htime, Heats = heats }; UpdateStable1(d);//判断是否异常数据 //检查,它可能只是因为 复位区号,或旋转角度,膜距离改变了,再重发一次而已 if (mData.Count() > 0) { for (int i = 0; i < mData.Count(); i++) { int idx = mData.Count() - 1 - i; FlyData_FeedbackHeat f = mData[idx]; if (time < f.Time) { //要继续找向前找 if (idx == 0) { //已经到头了, 在idx前面插入 mData.Insert(0, d); return false; } } else if (f.Time == time) { //只是以前的数据而已,替换掉 mData[idx] = d; return false; } else { //找过了,在idx后面插入 if (idx == mData.Count() - 1) { //在最后插入数据 //mData.RAdd(d); //idx_new = idx; } else { mData.Insert(idx, d); return false; } break; } } } if ((d.RAngle != this.rAngle) || (d.OrgBoltNo != this.orgBoltNo)) { ClearBoltHeat(); this.rAngle = d.RAngle; this.orgBoltNo = d.OrgBoltNo; } if (d.Stability != STABILITY.IDLE) { //数据异常 CurrR = -1; } else { //自动对位 if (IsIntoAutoONo) { UpdateAutoONo(d); IsIntoAutoONo = false; } else { //更新每个分区的稳定状态 UpdateStable2(d); } } Stability = d.Stability; CurrDirection = d.Direction; CurrTime = d.Time; mData.RAdd(d); LastBM = mData.GetLastNo(); FirstBM = LastBM + 1 - mData.Count(); //TODO 测试 //SaveData(); return true; } /// <summary> /// 获取上一次没有交集的数据序号 /// </summary> /// <param name="index">在列表中的待比较数据序号</param> /// <returns></returns> int GetJustBeforeIdx(FlyData_FeedbackHeat d_now, int index) { for (int i = 0; i <= index; i++) { int idx = index - i; FlyData_FeedbackHeat d = mData[idx]; if (d.EndTime <= d_now.Time) return idx; } return -1; } bool CanCompared(STABILITY stability) { switch (stability) { case STABILITY.ERROR_NO_ARRIVE: case STABILITY.ERROR_POSITION_NOTSURE: case STABILITY.ERROR_SIGMA_OVERSIZE: return false; default: return true; //case STABILITY.IDLE: //case STABILITY.ERROR_ROTATE_CHANGED: //case STABILITY.ERROR_THICK_CHANGED: //case STABILITY.ERROR_CORREL: //case STABILITY.OK_SIGMA_SMALL_ENOUGH: //case STABILITY.OK_CORREL: //case STABILITY.OK_HEAT_AND_THICK_CORREL: // return true; } } /// <summary> /// 更新稳定状态, 一个就能判断的状态 /// </summary> /// <param name="d"></param> void UpdateStable1(FlyData_FeedbackHeat d) { if (d.RotateCnt <= 0)//旋转次数太少,位置不能确定 { d.Stability = STABILITY.ERROR_POSITION_NOTSURE; return; } { DateTime dt_takeeffect = d.HTime + Delay;//起效时间 if (d.Time >= dt_takeeffect)//最早的时间都比起效时间晚,不用删除 { } else { DateTime dt = d.Time + TimeSpan.FromTicks((long)((d.EndTime - d.Time).Ticks * 0.3)); if (dt < dt_takeeffect) { //有30% 数据都是以前的,无效, 加热还没起效 d.Stability = STABILITY.ERROR_NO_ARRIVE; return; } } } int avg = Misc.MyMath.Avg(d.Thicks); CurrBreakUp = Common.MyMath.BreakUp(d.Thicks) * 100.0; int sigma = Misc.MyMath.Sigma(d.Thicks); double sigma2_percents = 100.0 * 2 * sigma / avg; Curr2Sigma = sigma2_percents; if (sigma2_percents >= ThresholdSigmaMax) { //一副数据,Sigma > 15% : 不稳定,变化太大,超出控制范围 , (当为自动时,停止所有加热) d.Stability = STABILITY.ERROR_SIGMA_OVERSIZE; return; } } /// <summary> /// 更新稳定状态,需要2幅数据才能比较的 /// </summary> /// <param name="index">在列表的数据</param> void UpdateStable2(FlyData_FeedbackHeat d) { if (mData.Count() == 0)//没有以前的数据 return; //找到之前没有交集的一幅数据, 并且它不是异常的 int idx_last = mData.Count() - 1; FlyData_FeedbackHeat d_last; while (true) { idx_last = GetJustBeforeIdx(d, idx_last); if (idx_last < 0)//没有上一幅数据,暂时待定 { return; } d_last = mData[idx_last]; if (CanCompared(d_last.Stability)) { break; } idx_last--; if (idx_last < 0)//没有上一幅数据,暂时待定 return; } if (Math.Abs((d_last.RotatePeriod - d.RotatePeriod).TotalSeconds / d.RotatePeriod.TotalSeconds) > 0.03) { //连续两幅图旋转时间变化 > 3%:不稳定,旋转速度波动大,测量不准确 d.Stability = STABILITY.ERROR_ROTATE_CHANGED; return; } int avg = Misc.MyMath.Avg(d.Thicks); int avg_last = Misc.MyMath.Avg(d_last.Thicks); if (Math.Abs( ((double)(avg_last - avg)) / avg) > 0.05) { //厚度变化5%, 不稳定 d.Stability = STABILITY.ERROR_THICK_CHANGED; return; } int[] thickpercents_diff, thickpercents_mid, heatpercents_diff, heatpercents_mid; GetPercentDatas(d, d_last, out thickpercents_diff, out thickpercents_mid, out heatpercents_diff, out heatpercents_mid); UpdateR(thickpercents_diff, thickpercents_mid, heatpercents_diff, heatpercents_mid); if (CurrR >= ThresholdR) { d.Stability = STABILITY.OK_HEAT_AND_THICK_CORREL; if (d.Heats.All((h) => { return h == 0; })) d.Stability = STABILITY.OK_CORREL; } } void GetPercentDatas(FlyData_FeedbackHeat d, FlyData_FeedbackHeat d_last, out int[] thickpercents_diff,out int[] thickpercents_mid, out int[] heatpercents_diff,out int[] heatpercents_mid ) { //厚度%偏差 int[] thickpercents_now = Common.MyMath.GetPercent(d.Thicks); //当两幅图复位区号不一致时,移动以前的数据,使之一致 int[] thickpercents_last = Common.MyMath.Move(Common.MyMath.GetPercent(d_last.Thicks), d.OrgBoltNo - d_last.OrgBoltNo); thickpercents_diff = Common.MyMath.GetDiff(thickpercents_now, thickpercents_last); thickpercents_mid = Common.MyMath.GetMid(thickpercents_now, thickpercents_last); //加热%偏差 int[] heatpercents_now = Common.MyMath.OffsetAvgBe0(d.Heats);//平移到平均值为0 heatpercents_now = Common.MyMath.ZoomIn(heatpercents_now, BoltCnt / ChannelCnt);//放大数量为分区量 heatpercents_now = Common.MyMath.Filter(heatpercents_now, HeatEffectCurve.ToArray());//滤波变为加热对厚度的效果,还没 / Kp int[] heatpercents_last = Common.MyMath.OffsetAvgBe0(d_last.Heats);//平移到平均值为0 heatpercents_last = Common.MyMath.ZoomIn(heatpercents_last, BoltCnt / ChannelCnt);//放大数量为分区量 heatpercents_last = Common.MyMath.Filter(heatpercents_last, HeatEffectCurve.ToArray());//滤波变为加热对厚度的效果,还没 / Kp heatpercents_diff = Common.MyMath.GetDiff(heatpercents_last, heatpercents_now);//-加热增量, 它是反转的!!!! heatpercents_mid = Common.MyMath.GetMid(heatpercents_now, heatpercents_last);//加热均值 } void UpdateR( int[] thickpercents_diff, int[] thickpercents_mid, int[] heatpercents_diff, int[] heatpercents_mid) { //降低要求 thickpercents_diff = Common.MyMath.Filter(thickpercents_diff, 3); thickpercents_mid = Common.MyMath.Filter(thickpercents_mid, 3); heatpercents_diff = Common.MyMath.Filter(heatpercents_diff, 3); heatpercents_mid = Common.MyMath.Filter(heatpercents_mid, 3); double kp = (IsUsedLocalKp) ? LocalKp : Kp; if (kp < 1) kp = 1; //当加热全为0,使用StableRange0 int stableRange = (heatpercents_diff.All((h) => h == 0)) ? StableRange0 : StableRange; if (stableRange < 1) stableRange = 1; for (int i = 0; i < BoltCnt; i++) { BoltHeatRecord record = new BoltHeatRecord() { ThickDiff = thickpercents_diff[i], ThickMid = thickpercents_mid[i], HeatDiff = -heatpercents_diff[i], HeatMid = heatpercents_mid[i] }; mBoltData[i].ThickRange = StableRange0; mBoltData[i].HeatRange = 5; if ((Misc.MyBase.ISVALIDATA(thickpercents_diff[i])) && (Math.Abs(thickpercents_diff[i] + heatpercents_diff[i] / kp) <= stableRange)) { //稳定!!!!! mBoltData[i].IsStable = true; mBoltData[i].StableAdd(record); } else { mBoltData[i].IsStable = false; mBoltData[i].UnstableAdd(record); } } CurrR = 1.0 * mBoltData.Count((_b) => { return _b.IsStable; }) / mBoltData.Count(); var IsStables = from r in mBoltData select r.IsStable; BoltIsStable = IsStables.ToArray(); } /// <summary> /// 执行自动对位 /// </summary> /// <param name="d"></param> /// <returns>处理完</returns> void UpdateAutoONo(FlyData_FeedbackHeat d) { //d 这幅图,是刚加热生效的, 它必须是稳定的, 只有找到以前空加热的数据,对比就好了。 if (mData.Count() == 0)//没有以前的数据 { //异常 AutoONoResult = EAutoONoResult.Err_Break; return; } //找到之前没有交集的一幅数据, 并且它不是异常的 //找出空的那幅数据!!!!! int idx_last = mData.Count() - 1; FlyData_FeedbackHeat d_last; while (true) { idx_last = GetJustBeforeIdx(d, idx_last); if (idx_last < 0)//没有上一幅数据,暂时待定 { //异常 AutoONoResult = EAutoONoResult.Err_Break; return; } d_last = mData[idx_last]; if (CanCompared(d_last.Stability)) { break; } idx_last--; if (idx_last < 0)//没有上一幅数据,暂时待定 { //异常 AutoONoResult = EAutoONoResult.Err_Break; return; } } if (Math.Abs((d_last.RotatePeriod - d.RotatePeriod).TotalSeconds / d.RotatePeriod.TotalSeconds) > 0.03) { //连续两幅图旋转时间变化 > 3%:不稳定,旋转速度波动大,测量不准确 d.Stability = STABILITY.ERROR_ROTATE_CHANGED; //异常 AutoONoResult = EAutoONoResult.Err_Break; return; } int avg = Misc.MyMath.Avg(d.Thicks); int avg_last = Misc.MyMath.Avg(d_last.Thicks); if (Math.Abs(((double)(avg_last - avg)) / avg) > 0.05) { //厚度变化5%, 不稳定 d.Stability = STABILITY.ERROR_THICK_CHANGED; //异常 AutoONoResult = EAutoONoResult.Err_Break; return; } int[] thickpercents_diff, thickpercents_mid, heatpercents_diff, heatpercents_mid; GetPercentDatas(d, d_last, out thickpercents_diff, out thickpercents_mid, out heatpercents_diff, out heatpercents_mid); //计算极差 MaxMin = Common.MyMath.CalMaxMin(Common.MyMath.Filter(thickpercents_diff, 3)); //计算平移 int offset; double max_r; Common.MyMath.CalBestMove(heatpercents_diff, thickpercents_diff, 0.5, out offset, out max_r); MaxR = max_r; int orgboltno = d.OrgBoltNo + offset; if (orgboltno > BoltCnt) orgboltno -= BoltCnt; else if (orgboltno < 1) orgboltno += BoltCnt; BestOrgBoltNo = orgboltno; //计算Kp int[] thickpercents_diff_mov = Common.MyMath.Move(thickpercents_diff, offset); double b; double a; Misc.MyMath.Linest(heatpercents_diff, thickpercents_diff_mov, out a, out b); BestKp = a; if (MaxMin >= ThresholdMaxMin) { if (MaxR >= ThresholdR) { //计算结果可靠性很高 AutoONoResult = EAutoONoResult.OK; } else { AutoONoResult = EAutoONoResult.Err_MaxR; } } else { AutoONoResult = EAutoONoResult.Err_MaxMin; } if (AutoONoResult == EAutoONoResult.OK) { Kp = BestKp; if (offset != 0) { d.Thicks = Common.MyMath.Move(d.Thicks, offset); d.OrgBoltNo = BestOrgBoltNo; thickpercents_diff = Common.MyMath.Move(thickpercents_diff, offset); thickpercents_mid = Common.MyMath.Move(thickpercents_mid, offset); ClearBoltHeat(); } } UpdateR(thickpercents_diff, thickpercents_mid, heatpercents_diff, heatpercents_mid); if (CurrR >= ThresholdR) { d.Stability = STABILITY.OK_HEAT_AND_THICK_CORREL; if (d.Heats.All((h) => { return h == 0; })) d.Stability = STABILITY.OK_CORREL; } } #region 列表 /// <summary> /// 获取记录历史 返回类型为 FlyData_FeedbackHeat /// </summary> /// <param name="bookmark">记录点</param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> public void GetHeatsData(int bookmark, AsyncCBHandler AsyncDelegate, object AsyncState) { int idx = mData.No2Index(bookmark); if (idx < 0 || idx >= mData.Count()) { //返回空数据 AsyncDelegate(AsyncState, null); } else { AsyncDelegate(AsyncState, mData[idx]); } } #endregion /// <summary> /// 清空每个分区的加热记录 /// </summary> public void ClearBoltHeat() { foreach (BoltHeat h in mBoltData) { h.StableList.Clear(); h.UnstableList.Clear(); } } /// <summary> /// 获取每个分区的加热历史 返回 FlyData_BoltHeatRecord /// </summary> /// <param name="no">分区号</param> /// <param name="AsyncDelegate"></param> /// <param name="AsyncState"></param> public void GetBoltHeatRecord(int no, AsyncCBHandler AsyncDelegate, object AsyncState) { if (no < 1 || no > mBoltData.Count()) { AsyncDelegate(AsyncState, null); } else { FlyData_BoltHeatRecord f = new FlyData_BoltHeatRecord(); f.StableList = mBoltData[no - 1].StableList.ToArray(); f.UnstableList = mBoltData[no - 1].UnstableList.ToArray(); AsyncDelegate(AsyncState, f); } } public void Apply() { Save(); } bool Load() { return Misc.SaveToXmlHepler.Load("heatbuf.xml", this); } void Save() { Misc.SaveToXmlHepler.Save("heatbuf.xml", this); } #region INotifyPropertyChanged 成员 public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string propertyname) { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname)); } } #endregion public string[] GetSavePropertyNames() { return new string[]{ "HeatEffectCurve", "StableRange", "StableRange0", "ThresholdR", "ThresholdMaxMin", "ThresholdSigmaMax", "IsUsedLocalKp", "LocalKp" }; } } /// <summary> /// 单个分区判断 /// </summary> public class BoltHeat { /// <summary> /// 当前是否稳定 /// </summary> public bool IsStable = false; /// <summary> /// 稳定列表 /// </summary> public RList<BoltHeatRecord> StableList; /// <summary> /// 不稳定列表 /// </summary> public RList<BoltHeatRecord> UnstableList; /// <summary> /// 厚度%相同的定义 /// </summary> public int ThickRange=5; /// <summary> /// 加热%相同的定义 /// </summary> public int HeatRange=5; public BoltHeat() { StableList = new RList<BoltHeatRecord>(60); UnstableList = new RList<BoltHeatRecord>(60); } public void StableAdd(BoltHeatRecord r) { var ss = from s in StableList where (Math.Abs(s.HeatDiff - r.HeatDiff) <= HeatRange) && (Math.Abs(s.HeatMid - r.HeatMid) <= HeatRange) && (Math.Abs(s.ThickDiff - r.ThickDiff) <= ThickRange) && (Math.Abs(s.ThickMid - r.ThickMid) <= ThickRange) select s; if (ss.Count() > 0) { ss.First().Cnt++; } else { StableList.Add(r); } } public void UnstableAdd(BoltHeatRecord r) { var ss = from s in UnstableList where (Math.Abs(s.HeatDiff - r.HeatDiff) <= HeatRange) && (Math.Abs(s.HeatMid - r.HeatMid) <= HeatRange) && (Math.Abs(s.ThickDiff - r.ThickDiff) <= ThickRange) && (Math.Abs(s.ThickMid - r.ThickMid) <= ThickRange) select s; if (ss.Count() > 0) { ss.First().Cnt++; } else { UnstableList.Add(r); } } /// <summary> /// 根据历史,判断最佳输出 /// </summary> /// <param name="thickpercent">当前厚度%</param> /// <param name="heat">当前加热%</param> /// <param name="offset">准备要改变的加热量</param> /// <returns></returns> public int Suggest(int thickpercent, int heat, int offset) { if (offset == 0)//没有加热,直接返回 return offset; if (StableList.Count() == 0 && UnstableList.Count() == 0)//没有历史,直接返回 return offset; //从稳定的历史找相同的加热 var lr_s = from r in StableList where r.IsSameThick(thickpercent, ThickRange) && (Math.Abs(Math.Abs(offset) - Math.Abs(r.HeatDiff)) <= HeatRange) select r; int cnt = lr_s.Sum((r) => { return r.Cnt; }); if (cnt > 3)//数量要足够多,才可信 { //看看以前这样加热 厚度变化了多少 int thickdiff; if (offset > 0) { thickdiff = (int)lr_s.Average((r) => { if (r.HeatDiff > 0) return r.ThickDiff; else return -r.ThickDiff; }); } else { thickdiff = (int)lr_s.Average((r) => { if (r.HeatDiff > 0) return -r.ThickDiff; else return r.ThickDiff; }); } if (Math.Abs(thickdiff) <= ThickRange) { //变化很小,不如别调 //TODO, 加热以前最小的加热量 //int min_heat = lr_s.Min((r) => { return Math.Min(Math.Abs(r.Heat0), Math.Abs(r.Heat1)); }); //offset = min_heat - heat; //return offset; return 0; } } else { //这样加热没有稳定的历史 //从不稳定的历史找相同的加热 var lr_u = from r in UnstableList where r.IsSameThick(thickpercent, ThickRange) && (Math.Abs(Math.Abs(offset) - Math.Abs(r.HeatDiff)) <= HeatRange) select r; cnt = lr_u.Sum((r) => { return r.Cnt; }); if (cnt > 3)//数量要足够多,才可信 { //这样加热肯定不稳定,别调了 return 0; } } return offset; } } }