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;
        }

        

    }

}