using FLY.Thick.BlowingScan.Common;
using FLY.Thick.BlowingScan.IService;
using FLY.Thick.BlowingScan.Server;
using MathNet.Numerics.LinearAlgebra.Double;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using ThickTcpUiInWindow;
using FObjBase;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace FLY.Thick.BlowingScan.UI.Client
{
    public class BlowingScanAnaylze : INotifyPropertyChanged
    {
        #region 缓存区

        /// <summary>
        /// 从BlowingScanService 接收过来的数据;
        /// 缓冲区第一个记录序号
        /// </summary>
        public int FirstBM { get; set; } = -1;

        /// <summary>
        /// 从BlowingScanService 接收过来的数据;
        /// 缓冲区最后一个记录序号
        /// </summary>
        public int LastBM { get; set; } = -1;


        
        /// <summary>
        /// 从BlowingScanService 接收过来的数据;
        /// 缓冲区
        /// </summary>
        public List<FlyData_BlowingScan> mBufList;
        public ObservableCollection<ScanInfoCell> ScanInfoList { get; } = new ObservableCollection<ScanInfoCell>();
        #endregion

        #region 试调整参数
        private int solvecnt = 4;
        /// <summary>
        /// 多少次扫描解一次方程
        /// </summary>
        public int SolveCnt
        {
            get { return solvecnt; }
            set
            {
                if (value < 4)
                    value = 4;

                if (solvecnt != value)
                {
                    solvecnt = value;
                }
            }
        }

        private int smooth = 1;
        /// <summary>
        /// 平滑
        /// </summary>
        public int Smooth
        {
            get { return smooth; }
            set
            {
                if (value < 0)
                    value = 0;
                if (smooth != value)
                {
                    smooth = value;
                }
            }
        }



        /// <summary>
        /// 旋转架转动角度
        /// </summary>
        public double RAngle { get; set; } = 350;


        /// <summary>
        /// 人字架到测厚仪膜的长度 单位m
        /// </summary>
        public double FilmLength { get; set; } = 23;

        #endregion

        #region 常量参数

        public int N2 { get; set; } = 400;

        public int Sensor { get; set; } = 240;


        public int OrgBoltNo { get; set; } = 1;

        public int NBolts { get; set; } = 88;

        #endregion

        #region 状态

        public bool IsStep2Ing { get; set; }

        #endregion

        #region 机架校正
        public GageData mGagedata = new GageData();


        #endregion

        BlowingAngleDetect mAngleDetect = new BlowingAngleDetect();


        public BlowingScanAnaylze()
        {
            MarkNo1.PropertyChanged += MarkNo_PropertyChanged;
            MarkNo2.PropertyChanged += MarkNo_PropertyChanged;
        }

        public int GetMarkNoFromRList(int idx)
        {
            if (idx >= ScanInfoList.Count())
                return LastBM;
            else if (idx < 0)
                return (LastBM - (ScanInfoList.Count() - 1));


            int markno = idx+ (LastBM - (ScanInfoList.Count() - 1));

            return markno;
        }
        int GetIdxFromRList(int markno)
        {
            int idx = markno - (LastBM - (ScanInfoList.Count() - 1));
            if (idx >= ScanInfoList.Count())
                return -1;
            else if (idx < 0)
                return -1;
            return idx;
        }

        private void MarkNo_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            MarkData markdata = sender as MarkData;
            if (e.PropertyName == "Number")
            {
                int idx = GetIdxFromRList(markdata.Number);
                if (idx >= 0)
                {
                    markdata.IsValid = true;
                    if (!ScanInfoList[idx].HasCast)//解方程
                        ToThicks_SolveEquation(idx);
                    
                    markdata.StartTime = ScanInfoList[idx].StartTime;
                    markdata.ThicksDT = ScanInfoList[idx].ThicksDT;
                    markdata.Thicks = ScanInfoList[idx].thicks;
                    
                    markdata.Direction = ScanInfoList[idx].Direction;
                    markdata.Border = mBufList[idx].FilmRange;
                    markdata.PosOfGrid = mBufList[idx].PosOfGrid;
                    markdata.Time = mBufList[idx].Time;
                    markdata.OrgThicks = mBufList[idx].Thicks;
                    markdata.Power = ScanInfoList[idx].power;
                    markdata.AvgThicks = ScanInfoList[idx].thicks_avg;
                }
                else
                {
                    markdata.IsValid = false;
                }
                UpdateCurrR();
                UpdateIsFocus();
            }
        }
        /// <summary>
        /// 能进入第2步解方程
        /// </summary>
        /// <returns></returns>
        public bool CanStep2()
        {
            if (mBufList == null)
                return false;
            else if (mBufList.Count() == 0)
                return false;
            else if (IsStep2Ing)
                return false;
            return true;
        }
        /// <summary>
        /// 更新 BufList
        /// </summary>
        /// <param name="firstbm"></param>
        /// <param name="lastbm"></param>
        /// <param name="datas"></param>
        public void Step1(int firstbm, int lastbm, List<FlyData_BlowingScan> datas)
        {
            FirstBM = firstbm;
            LastBM = lastbm;
            mBufList = datas;
            if (mBufList == null)
                return;    
            BufList2AngleInfo();

            BufList2ScanInfoList();

            BufList2GageData();


            //当MarkNo1.Number 不在范围内, 限制它们
            if (FirstBM < LastBM)
            {
                MarkNo1.Number = KeepInRange(MarkNo1.Number, FirstBM + SolveCnt - 1, LastBM);
                MarkNo2.Number = KeepInRange(MarkNo2.Number, FirstBM + SolveCnt - 1, LastBM);

                if (MarkNo1.Number == MarkNo2.Number)
                {
                    MarkNo2.Number = MarkNo1.Number + SolveCnt;
                }
            }
            else if (FirstBM > LastBM)
            {
                //转了一个圈,很异常,不处理
                MarkNo1.Number = FirstBM + SolveCnt - 1;
                MarkNo2.Number = MarkNo1.Number + SolveCnt;
            }
            else
            {
                //没有数据

            }
            
        }
        int KeepInRange(int number, int begin, int end)
        {
            if (number < begin)
                number = begin;
            else if (number > end)
                number = end;
            return number;

        }
        /// <summary>
        /// 解方程
        /// </summary>
        public void Step2()
        {
            IsStep2Ing = true;
            if (mBufList == null)
            {
                IsStep2Ing = false;
                return;
            }
            GetSolveEquation();
            ClearAllResult();

            MarkNo1.Refresh();
            MarkNo2.Refresh();
            //ToThicks_SolveEquation();
            IsStep2Ing = false;
        }
        /// <summary>
        /// 清除全部计算结果
        /// </summary>
        public void ClearAllResult()
        {
            for (int i = 0; i < mBufList.Count(); i++)
            {
                ScanInfoCell scaninfo = ScanInfoList[i];
                scaninfo.HasCast = false;
            }
        }

        /// <summary>
        /// 转为 旋转架信息
        /// </summary>
        void BufList2AngleInfo()
        {
            mAngleDetect.mLimitList.Clear();
            BlowingAngleDetect.LimitCell c_last = null;

            for (int i=0; i < mBufList.Count(); i++)
            {
                FlyData_BlowingScan f = mBufList[i];
                if (f.SignNo == -1)
                    continue;

                if (c_last == null)
                {
                    c_last = new BlowingAngleDetect.LimitCell();

                    c_last.no = f.SignNo;
                    c_last.dt_begin = f.Time;
                    c_last.dt_end = f.LeaveTime;

                    mAngleDetect.mLimitList.Add(c_last);
                }
                else if (c_last.no != f.SignNo)
                {
                    c_last = new BlowingAngleDetect.LimitCell();

                    c_last.no = f.SignNo;
                    c_last.dt_begin = f.Time;
                    c_last.dt_end = f.LeaveTime;

                    mAngleDetect.mLimitList.Add(c_last);
                }
                else
                {
                    c_last.no = f.SignNo;
                    c_last.dt_begin = f.Time;
                    c_last.dt_end = f.LeaveTime;
                }
            }
            mAngleDetect.RotationCnt = mBufList.Last().RotateCnt;
            mAngleDetect.LimitSignTime = mBufList.Last().LimitSignTime;
            mAngleDetect.RenZiJiaPeriod = mBufList.Last().RotatePeriod;

            //更新默认时间
            if (mAngleDetect.mLimitList.Count() > 0)
            {
                if(mAngleDetect.mLimitList[0].dt_begin != DateTime.MinValue)
                    mAngleDetect.mDefaultTime = mAngleDetect.mLimitList[0].dt_begin - mAngleDetect.RenZiJiaPeriod;
                else
                    mAngleDetect.mDefaultTime = mAngleDetect.mLimitList[0].dt_end - mAngleDetect.LimitSignTime - mAngleDetect.RenZiJiaPeriod;
            }
            
        }




        /// <summary>
        /// 转为 位置与厚度信息
        /// </summary>
        void BufList2ScanInfoList()
        {
            ScanInfoList.Clear();
            for (int i = 0; i < mBufList.Count(); i++)
            {
                FlyData_BlowingScan f = mBufList[i];
                int posOfGrid = f.PosOfGrid;
                Range boltrange = new Range() { Begin = f.FilmRange.Begin + Sensor / 2 + N2, End = f.FilmRange.End - (Sensor / 2 + N2) };
                Range border = f.FilmRange;

                ScanInfoCell scaninfocell = new ScanInfoCell(NBolts, OrgBoltNo);

                //对应的grid号
                int bp_idx = boltrange.Begin / posOfGrid;
                int ep_idx = boltrange.End / posOfGrid;

                if (bp_idx < 0)
                    bp_idx = 0;

                if (ep_idx > (f.Thicks.Length - 1))
                    ep_idx = f.Thicks.Length - 1;

                for (int j = bp_idx; j <= ep_idx; j++)
                {
                    int idx = j;
                    int pos = j * posOfGrid + posOfGrid / 2;//一个grid的中间脉冲,也就是大概而已
                    int thick = f.Thicks[idx];

                    pos -= border.Begin;//膜上面的位置
                    ScanPosCell scanpos = new ScanPosCell() { pos = pos, thick = thick, dt = f.ThicksDt[idx] };

                    //scaninfocell.StartTime 为整幅数据最早的时间
                    if (j == bp_idx)
                    {
                        scaninfocell.StartTime = scanpos.dt;
                    }
                    else
                    {
                        if (scaninfocell.StartTime > scanpos.dt)
                            scaninfocell.StartTime = scanpos.dt;
                    }
                    scaninfocell.frame.Add(scanpos);
                }
                ScanInfoList.Add(scaninfocell);
            }



        }
        /// <summary>
        /// 计算平均数据
        /// </summary>
        void BufList2GageData()
        {

            int[] gage_sum = null;
            int[] gage_cnt = null;
            Range border = new Range();
            int posOfGrid = 10;
            for (int i = 0; i < mBufList.Count(); i++)
            {
                FlyData_BlowingScan f = mBufList[i];
                posOfGrid = f.PosOfGrid;
                if (gage_sum == null)
                {
                    gage_sum = f.Thicks.Clone() as int[];
                    gage_cnt = new int[f.Thicks.Count()];
                    border.Copy(f.FilmRange);
                }
                    
                for (int j = 0; j < gage_sum.Count() && j < f.Thicks.Count(); j++)
                {
                    if (Misc.MyBase.ISVALIDATA(f.Thicks[j]))
                    {
                        if (gage_cnt[j] > 0)
                            gage_sum[j] += f.Thicks[j];
                        else
                            gage_sum[j] = f.Thicks[j];
                        gage_cnt[j]++;
                    }
                }
                if (border.Begin < f.FilmRange.Begin)
                    border.Begin = f.FilmRange.Begin;

                if (border.End > f.FilmRange.End)
                    border.End = f.FilmRange.End;

            }

            mGagedata.PosOfGrid = posOfGrid;
            mGagedata.Border = border;
            for (int i = 0; i < gage_sum.Count(); i++)
            {
                if (gage_cnt[i] > 0)
                {
                    gage_sum[i] = gage_sum[i] / gage_cnt[i];
                }
            }
            mGagedata.OrgThicks = gage_sum;
        }
        /// <summary>
        /// 第2步,修改 旋转角度,膜距离 执行, 耗时很短
        /// </summary>
        void GetSolveEquation()
        {
            mAngleDetect.RAngle = RAngle;
            //mAngleDetect.FilmLength = FilmLength;
            //获取角度信息
            for (int i = 0; i < mBufList.Count(); i++)
            {
                FlyData_BlowingScan f = mBufList[i];
                ScanInfoCell scaninfocell = ScanInfoList[i];
                scaninfocell.equationList.Clear();

                scaninfocell.State = ScanInfoCell.STATE.OK;

                for (int j = 0; j < scaninfocell.frame.Count(); j++)
                {
                    ScanPosCell scanpos = scaninfocell.frame[j];

                    BlowingAngleDetect.FilmInfo filminfo;

                    int ret = mAngleDetect.GetFilmInfo(
                        out filminfo,
                        scanpos.dt, f.FilmRange.Width, scanpos.pos, FilmLength, f.Velocity);

                    //探头直径转为角度范围
                    double sensor_angle = 180.0 * Sensor / f.FilmRange.Width;

                    if (ret == 0)
                    {
                        scanpos.angle1 = filminfo.angle1;
                        scanpos.angle2 = filminfo.angle2;
                        scanpos.direction = filminfo.direction;
                        //TODO
                        scanpos.rotateAngle = 180 * scanpos.pos / f.FilmRange.Width;
                    }
                    else
                    {
                        scaninfocell.State = ScanInfoCell.STATE.Err_GetAngle;
                        break;
                    }
                    if (j == 0)
                        scaninfocell.Direction = scanpos.direction;
                    else
                    {
                        if (scaninfocell.Direction != scanpos.direction)
                        {
                            scaninfocell.Direction = DIRECTION.FIX;
                            scaninfocell.State = ScanInfoCell.STATE.Err_Solve;
                            break;
                        }
                    }
                    scaninfocell.AddEquation(j,sensor_angle);
                }
            }            
        }
        /// <summary>
        /// 最后1步,解方程
        /// </summary>
        void ToThicks_SolveEquation()
        {

            for (int i = 0; i < mBufList.Count(); i++)
            {

                ToThicks_SolveEquation(i);
            }

            MarkNo1.Refresh();
            MarkNo2.Refresh();
        }
        bool ToThicks_SolveEquation(int scaninfo_idx)
        {
            ScanInfoCell scaninfo = ScanInfoList[scaninfo_idx];

            scaninfo.HasCast = true;

            if (scaninfo.State != ScanInfoCell.STATE.OK)
                return false;

            if (scaninfo.Direction == DIRECTION.FIX)//当前是Fix 不解方程
            {
                scaninfo.State = ScanInfoCell.STATE.Err_Solve;
                return false;
            }

            //全部方向都必须一致,当然也不能是Fix
            //for (int i = 0; i < (SolveCnt - 1); i++)
            //{
            //    if (scaninfo.Direction != ScanInfoList[scaninfo_idx - 1 - i].Direction)
            //    {
            //        scaninfo.State = ScanInfoCell.STATE.ERR_Solve_Maybe;
            //        break;
            //    }
            //}
            //平均值图
            {
                int[] _sum = new int[scaninfo.nbolts];
                int[] _cnt = new int[scaninfo.nbolts];

                foreach (EquationCell e in scaninfo.equationList)
                {
                    int avg = e.GetAvg();
                    foreach (KeyValuePair<int, int> kv in e.boltPower)
                    {
                        _sum[kv.Key] += avg;
                        _cnt[kv.Key] += 1;
                        scaninfo.thicks_avg[kv.Key] = _sum[kv.Key] / _cnt[kv.Key];
                    }
                }
            }
            List<EquationCell> equationlist = new List<EquationCell>();
            scaninfo.solveMember.Clear();
            int cnt = 0;
            for (int i = scaninfo_idx; i >= 0; i--)
            {
                if (ScanInfoList[i].Direction == DIRECTION.FIX)
                    continue;
                cnt++;

                equationlist.AddRange(ScanInfoList[i].equationList);
                //用到的扫描副
                scaninfo.solveMember.Add(i);

                if (cnt >= SolveCnt)
                    break;
            }
            //计算 每个分区的权
            for (int i = 0; i < scaninfo.power.Count(); i++)
            {
                scaninfo.power[i] = 0;
            }
            foreach (EquationCell e in equationlist)
            {
                foreach (KeyValuePair<int, int> kv in e.boltPower)
                {
                    //if(e.mainBolt == kv.Key)
                        scaninfo.power[kv.Key] += kv.Value;
                    //else
                    //    scaninfo.power[kv.Key+scaninfo.nbolts] += kv.Value;
                }
            }
            


            if (cnt < SolveCnt)//方程不够!!!!
            {
                scaninfo.State = ScanInfoCell.STATE.Err_Solve;
                return false;
            }

            if (equationlist.Count < 100)//方程数太少
            {
                scaninfo.State = ScanInfoCell.STATE.Err_Solve;
                return false;
            }

            SolveEquation(equationlist, ScanInfoList[scaninfo_idx].thicks);

           
            //2105 条方程,耗时 0.3s
            //SolveEquation(equationlist, ScanInfoList[scaninfo_idx].thicks);

            //不能有任何一个NULL_VALUE
            for (int i = 0; i < scaninfo.thicks.Count(); i++)
                if (!Misc.MyBase.ISVALIDATA(scaninfo.thicks[i]))
                    return false;

            if (Smooth > 0)
            {
                int[] data = new int[scaninfo.thicks.Count()];
                for (int i = 0; i < scaninfo.thicks.Count(); i++)
                {
                    int sum = 0;

                    for (int j = 0; j < (Smooth * 2 + 1); j++)
                    {
                        int index = i + j - Smooth;
                        if (index < 0)
                            index += scaninfo.thicks.Count();
                        else if (index >= scaninfo.thicks.Count())
                            index -= scaninfo.thicks.Count();
                        sum += scaninfo.thicks[index];
                    }
                    data[i] = (int)(sum / (Smooth * 2 + 1));
                }
                Array.Copy(data, scaninfo.thicks, data.Count());
            }


            scaninfo.ThicksDT = ScanInfoList[scaninfo_idx - (SolveCnt - 1)].StartTime;

            return true;
        }


        /// <summary>
        /// 解方程
        /// </summary>
        /// <param name="equationList">N条方程</param>
        /// <param name="frame_out">输出的结果</param>
        void SolveEquation(List<EquationCell> equationList, int[] frame_out)
        {

            int nrow = equationList.Count();
            //A~AX = A~B
            //转换为 DenseMatrix 与 DenseVector
            DenseMatrix matrixA = new DenseMatrix(nrow, NBolts);
            DenseMatrix matrixAT = new DenseMatrix(NBolts, nrow);
            DenseVector vectorB = new DenseVector(nrow);
            DenseVector vectorATB = new DenseVector(NBolts);


            int rowindex = 0;

            foreach (EquationCell ec in equationList)
            {

                double[] datas = ec.GetPower(NBolts);

                matrixA.SetRow(rowindex, datas);

                matrixAT.SetColumn(rowindex, datas);
                vectorB[rowindex] = ec.thick;
                rowindex++;
            }

            DenseMatrix ATA = (DenseMatrix)matrixA.TransposeThisAndMultiply(matrixA);
            DenseVector ATB = (DenseVector)matrixA.TransposeThisAndMultiply(vectorB);

            DenseVector X = (DenseVector)ATA.Solve(ATB);

            int j;
            for (int i = 0; i < NBolts; i++)
            {
                frame_out[i] = (int)X[i];
            }
        }

        #region INotifyPropertyChanged 接口
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        #endregion



        #region 两组数据

        private double currR = -1;
        public double CurrR
        {
            get { return currR; }
            set {
                if (currR != value)
                {
                    currR = value;
                    NotifyPropertyChanged("CurrR");
                }
            }
        }

        public MarkData MarkNo1 = new MarkData();
        public MarkData MarkNo2 = new MarkData();

        void UpdateCurrR()
        {
            if (MarkNo1.IsValid && MarkNo2.IsValid)
            {
                CurrR = Misc.MyMath.Correl(MarkNo1.Thicks, MarkNo2.Thicks);
            }
            else
            {
                CurrR = -1;
            }
        }
        void UpdateIsFocus()
        {
            int idx1 = GetIdxFromRList(MarkNo1.Number);
            int idx2 = GetIdxFromRList(MarkNo2.Number);

            for (int i = 0; i < ScanInfoList.Count(); i++)
            {
                ScanInfoList[i].IsFocus = false;
                ScanInfoList[i].IsFocus2 = false;
            }

            if (idx1 > 0)
            {
                ScanInfoList[idx1].IsFocus = true;
                foreach (int i in ScanInfoList[idx1].solveMember)
                {
                    ScanInfoList[i].IsFocus = true;
                }
            }

            if (idx2 > 0)
            {
                ScanInfoList[idx2].IsFocus2 = true;
                foreach (int i in ScanInfoList[idx2].solveMember)
                {
                    ScanInfoList[i].IsFocus2 = true;
                }
            }
        }
        

        #endregion

        #region 调试
        public bool Save(string path)
        {
            try
            {
                using (StreamWriter sw = new StreamWriter(path, false, Encoding.GetEncoding("GB2312")))
                {
                    bool hasHeader = false;
                    foreach (FlyData_BlowingScan f in mBufList)
                    {
                        if (!hasHeader)
                        {
                            hasHeader = true;
                            sw.WriteLine(f.GetHeader());
                        }
                        sw.WriteLine(f.ToString());
                    }
                    sw.Flush();
                    sw.Close();
                }
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }
        public bool Load(string path)
        {
            if (mBufList != null)
                mBufList.Clear();
            else
                mBufList = new List<FlyData_BlowingScan>();

            using (StreamReader sr = new StreamReader(path, Encoding.GetEncoding("GB2312")))
            {
                string header = sr.ReadLine();

                while (!sr.EndOfStream)
                {
                    string s = sr.ReadLine();
                    FlyData_BlowingScan f = new FlyData_BlowingScan();
                    if (f.TryParse(header, s))
                    {
                        mBufList.Add(f);
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        #endregion

    }

    public class GageData : INotifyPropertyChanged
    {
        private Range border = new Range();
        /// <summary>
        /// 膜边界,单位脉冲
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public Range Border { get; set; }


        /// <summary>
        /// pos / grid
        /// </summary>
        public int PosOfGrid { get; set; } = 10;



        /// <summary>
        /// 厚度数据,  大小 grid总数
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public int[] OrgThicks { get; set; }




        #region INotifyPropertyChanged 接口

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
    public class MarkData : INotifyPropertyChanged
    {
        public int Number { get; set; } = -1;

        /// <summary>
        /// 获取了焦点
        /// </summary>
        public bool IsFocus { get; set; }
        /// <summary>
        /// 有效
        /// </summary>
        public bool IsValid { get; set; }
        
        /// <summary>
        /// 扫描时间
        /// </summary>
        public DateTime StartTime { get; set; } = DateTime.MinValue;

        
        /// <summary>
        /// 旋转架旋转的方向,当同一次扫描中,有两个方向,Direction= Fix;
        /// </summary>
        public Misc.DIRECTION Direction { get; set; } = DIRECTION.FORWARD;

        
        /// <summary>
        /// 下面解方程得到的一幅数据对应的数据,与 StartTime 不一样
        /// </summary>
        public DateTime ThicksDT { get; set; } = DateTime.MinValue;


        /// <summary>
        /// 一幅数据
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public int[] Thicks { get; set; }

        
        /// <summary>
        /// 膜边界,单位脉冲
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public Range Border { get; set; }

        
        /// <summary>
        /// pos / grid
        /// </summary>
        public int PosOfGrid { get; set; } = 10;
        
        /// <summary>
        /// orgthick时间
        /// </summary>
        public DateTime Time { get; set; } = DateTime.MinValue;
        
        /// <summary>
        /// 厚度数据,  大小 grid总数
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public int[] OrgThicks { get; set; }

        
        /// <summary>
        /// 权重图
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public int[] Power { get; set; }

        /// <summary>
        /// 均值图
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public int[] AvgThicks { get; set; }

        public MarkData()
        {

        }

        public void Refresh()
        {
            NotifyPropertyChanged("Number");
        }

        #region INotifyPropertyChanged 接口
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        #endregion
    }

    public class BlowingAngleDetect : INotifyPropertyChanged
    {
        #region 自定义类

        public class LimitCell
        {
            public LimitCell Clone()
            {
                LimitCell l = new LimitCell();

                l.dt_begin = dt_begin;
                l.dt_end = dt_end;
                l.no = no;
                return l;
            }
            public DateTime dt_begin = DateTime.MinValue;
            public DateTime dt_end = DateTime.MinValue;
            public int no;

            public override string ToString()
            {
                return no.ToString() + " (" + dt_begin.ToString() + "-" + dt_end.ToString() + ")";
            }
        }
        #endregion

        #region 成员变量
        #region 参数
        /// <summary>
        /// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
        /// </summary>
        public double RAngle { get; set; } = 350;


        /// <summary>
        /// 离开限位 到 撞下一个限位  的 旋转架转动时间, 需要初始值,以后测量出来的
        /// </summary>
        public TimeSpan RenZiJiaPeriod { get; set; }


        /// <summary>
        /// 加速减速时间, 加速减速时间+正反转冷却时间 = 撞限位 到 离开限位 的时间
        /// </summary>
        public TimeSpan AccDecTime { get; set; }



        /// <summary>
        /// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的
        /// </summary>
        public TimeSpan LimitSignTime { get; set; }
        
        /// <summary>
        /// 人字架到测厚仪膜长 单位m
        /// </summary>
        public double FilmLength { get; set; }


        #endregion

        #region 状态
        
        /// <summary>
        /// 正反转冷却时间, 默认1s, 一定要小于撞限位 到 离开限位 的时间
        /// </summary>
        public TimeSpan SwapCoolTime { get; protected set; }


        #endregion


        public List<LimitCell> mLimitList = new List<LimitCell>();

        
        /// <summary>
        /// 累计旋转次数
        /// </summary>
        public int RotationCnt { get; set; }
  
        int LimitIndexToRotationCnt(int index)
        {
            if (RotationCnt == 0)
                return 0;
            else
                return RotationCnt - mLimitList.Count + index + 1;
        }
        #endregion

        public DateTime mDefaultTime;
        public BlowingAngleDetect()
        {
            RAngle = 337;//°
            RenZiJiaPeriod = TimeSpan.FromMinutes(8);
            AccDecTime = TimeSpan.FromSeconds(1);
            LimitSignTime = TimeSpan.FromSeconds(4);

            FilmLength = 25;//m

            SwapCoolTime = LimitSignTime - AccDecTime - AccDecTime;

            mDefaultTime = DateTime.Now;
        }

        public class FilmInfo
        {
            /// <summary>
            /// 上层 位于膜泡的角度 0~360°
            /// </summary>
            public double angle1;
            /// <summary>
            /// 下层 位于膜泡的角度 0~360°
            /// </summary>
            public double angle2;
            /// <summary>
            /// 膜泡旋转方向
            /// </summary>
            public Misc.DIRECTION direction;
            /// <summary>
            /// 线速度
            /// </summary>
            public double filmVelocity;
            /// <summary>
            /// 累计旋转次数
            /// </summary>
            public int rotationCnt;
            /// <summary>
            /// 在匀速阶段; 在限位信号 开始~结束 期间 inCV = false
            /// </summary>
            public bool inCV;
        }

        public enum GetLimitTimeResult
        {
            /// <summary>
            /// 获取成功
            /// </summary>
            OK,
            /// <summary>
            /// 不存在该点
            /// </summary>
            NOEXIST,
            /// <summary>
            /// 限位已经撞了,但膜还没走完,等吧
            /// </summary>
            ISFURTURE
        }


        /// <summary>
        /// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比LimitList.Last.dt+RenZiJiaPeriod还要后,更新RenZiJiaPeriod
        /// 返回 -1, 当前的时间点在列表以前;
        /// 返回 0, 当前的时间点在列表中;
        /// 返回 1, 当前的时间点在列表的未来;
        /// </summary>
        /// <param name="filminfo">输出资料</param>
        /// <param name="dt">测量的时间点</param>
        /// <param name="filmWidth">被压扁后的膜宽度</param>
        /// <param name="filmPosH">探头所在膜的横向位置</param>
        /// <param name="velocity">线速度</param>
        /// <returns></returns>
        public int GetFilmInfo(out FilmInfo filminfo, DateTime dt, double filmWidth, double filmPosH, double filmLen, double velocity)
        {
            filminfo = null;

            //与0°的偏移
            double a = filmPosH / filmWidth * 180;

            if ((a < 0) || (a > 180))
            {
                return -1;//不在膜的范围内
            }

            DateTime ago_dt;
            double angle0;
            double angle1;
            Misc.DIRECTION direction;
            int rotationcnt;
            bool inCV;
            if (dt == DateTime.MinValue)
            {
                //TODO, BUG
                return -1;
            }
            ago_dt = GetAgo(dt, filmLen, velocity);
            bool r = GetAngle(a, ago_dt, out angle0, out angle1, out direction, out rotationcnt, out inCV);
            if (r == false)//太久之前了
                return -1;

            filminfo = new FilmInfo()
            {
                angle1 = angle0,
                angle2 = angle1,
                filmVelocity = velocity,
                direction = direction,
                rotationCnt = rotationcnt,
                inCV = inCV
            };
            return 0;
        }


        #region 人字架时间点<-->24m后膜时间点

        /// <summary>
        /// 输入当前时间点,找到 刚出人字架时的时间点
        /// </summary>
        /// <param name="dt">当前时间</param>
        /// <param name="filmlength">24米真实长度</param>
        /// <param name="velocity">线速度</param>
        /// <returns>24米以前的时间</returns>
        DateTime GetAgo(DateTime dt, double filmlength, double velocity)
        {
            TimeSpan ts = TimeSpan.FromMinutes(filmlength / velocity);
            return dt - ts;
        }
        #endregion

        /// <summary>
        /// 计算出角度 -180~180 恒速阶段
        /// </summary>
        /// <param name="dtEnd">旋转结束时间点</param>
        /// <param name="dtStart">旋转开始时间点</param>
        /// <param name="noStart">开始时间点对应的限位 0 or 1</param>
        /// <param name="dt">用于转换为角度的时间</param>
        /// <param name="angle">输出: 角度</param>
        /// <param name="direction">输出: 方向</param>
        void LimitListCal_ConstantVelocity(DateTime dtEnd, DateTime dtStart, int noStart, DateTime dt, out double angle, out Misc.DIRECTION direction)
        {
            
            TimeSpan ts1 = dt - dtStart;
            TimeSpan ts2 = dtEnd - dtStart;

            angle = RAngle * ts1.Ticks / ts2.Ticks;

            if (noStart == 0)
            {
                angle = -RAngle / 2 + angle;
                direction = Misc.DIRECTION.FORWARD;
            }
            else
            {
                angle = RAngle / 2 - angle;
                direction = Misc.DIRECTION.BACKWARD;
            }
        }

        /// <summary>
        /// 计算出角度 -180~180 变速阶段
        /// </summary>
        /// <param name="dtEnd"></param>
        /// <param name="dtStart"></param>
        /// <param name="noStart">dtStart的信号</param>
        /// <param name="dt">dt 在 [dtStart , dtEnd]</param>
        ///  <param name="renZiJiaPeriod">上一次两个限位的间距时间</param>
        /// <param name="angle">角度</param>
        /// <param name="direction">方向, 只有正向 与  反向</param>
        void LimitListCal_SpeedChange(DateTime dtEnd, DateTime dtStart, int noStart, DateTime dt, TimeSpan renZiJiaPeriod, out double angle, out Misc.DIRECTION direction)
        {
            //默认 dt 在 dtStart <= dt <= dtEnd, 出错不处理!!!

            if ((dt < dtStart) || (dt > dtEnd))
            {
                throw new Exception("(dt<dtStart)|| (dt>dtEnd) dt=" + dt.ToString() + " dtStart=" + dtStart.ToString() + " dtEnd=" + dtEnd.ToString());
            }

            TimeSpan accDecTime = AccDecTime;
            if ((dtEnd - dtStart) < (accDecTime + accDecTime))
                accDecTime = TimeSpan.FromTicks((dtEnd - dtStart).Ticks / 2);

            DateTime dtMid = dtStart + TimeSpan.FromTicks((dtEnd - dtStart).Ticks / 2);

            if (noStart == 0)
            {
                if (dt < dtMid)
                    direction = Misc.DIRECTION.BACKWARD;
                else
                    direction = Misc.DIRECTION.FORWARD;
            }
            else
            {
                if (dt < dtMid)
                    direction = Misc.DIRECTION.FORWARD;
                else
                    direction = Misc.DIRECTION.BACKWARD;
            }



            double dAngle;
            if (dt < (dtStart + accDecTime))//减速
            {
                TimeSpan ts = dt - dtStart;//已经过去时间
                double vStart = RAngle / renZiJiaPeriod.TotalSeconds;//开始速度 °/s
                double vEnd = 0;//结束速度 °/s
                double a = (vEnd - vStart) / accDecTime.TotalSeconds;//加速度 °/s^2
                double vCurr = vStart + a * ts.TotalSeconds;//当前速度 //开始速度 °/s
                dAngle = (vStart + vCurr) / 2 * ts.TotalSeconds;//总角度 °

            }
            else if (dt < (dtEnd - accDecTime))//停止
            {
                double vStart = RAngle / renZiJiaPeriod.TotalSeconds;//开始速度 °/s
                double vEnd = 0;//结束速度 °/s
                double a = (vEnd - vStart) / accDecTime.TotalSeconds;//加速度 °/s^2
                dAngle = (vStart + vEnd) / 2 * accDecTime.TotalSeconds;//总角度 °
            }
            else //加速
            {
                TimeSpan ts = dtEnd - dt;
                double vStart = 0; //开始速度 °/s
                double vEnd = RAngle / renZiJiaPeriod.TotalSeconds;//结束速度 °/s
                double a = (vEnd - vStart) / accDecTime.TotalSeconds;//加速度 °/s^2
                double vCurr = vEnd - a * ts.TotalSeconds;//当前速度 //开始速度 °/s
                dAngle = (vEnd + vCurr) / 2 * ts.TotalSeconds;//总角度 °
            }

            if (noStart == 0)
                angle = -RAngle / 2 - dAngle;
            else
                angle = RAngle / 2 + dAngle;
        }

        /// <summary>
        /// 时间点查找旋转角度 -180~180
        /// </summary>
        /// <param name="dt">时间</param>
        /// <param name="modifyparam">当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod</param>
        /// <param name="angle"></param>
        /// <param name="direction"></param>
        /// <param name="rotationCnt"></param>
        /// <param name="inCV"></param>
        bool LimitListSearch(DateTime dt, bool modifyparam, out double angle, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
        {
            int index;
            ListFindIndexResult ret = LimitList_FindIndex(dt, out index);

            DateTime dt_start;
            DateTime dt_end;
            int no;
            //TODO,rotationCnt 还没处理!!!!!

            switch (ret)
            {
                case ListFindIndexResult.Future://发生在未来 
                    {
                        if (dt < mDefaultTime)
                            mDefaultTime = dt - TimeSpan.FromSeconds(10);

                        //检测是否需要修正 RenZiJiaPeriod
                        if (mLimitList.Count == 0)
                        {
                            dt_start = mDefaultTime;
                            no = 0;
                        }
                        else
                        {
                            dt_start = mLimitList[index].dt_end;
                            no = mLimitList[index].no;
                        }
                        rotationCnt = LimitIndexToRotationCnt(index);

                        if (dt > (dt_start + RenZiJiaPeriod))
                        {
                            if (modifyparam)
                            {
                                //修正!!!!
                                RenZiJiaPeriod = dt - dt_start + TimeSpan.FromSeconds(10);
                            }
                            else
                            {
                                dt = dt_start + RenZiJiaPeriod;//只是为了看,随便了。 时间停止下来
                            }
                        }

                        dt_end = dt_start + RenZiJiaPeriod;
                        inCV = true;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
                    }
                    break;
                case ListFindIndexResult.BetweenCells:
                    {
                        dt_start = mLimitList[index].dt_end;
                        dt_end = mLimitList[index + 1].dt_begin;
                        no = mLimitList[index].no;
                        rotationCnt = LimitIndexToRotationCnt(index);
                        inCV = true;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
                    }
                    break;
                case ListFindIndexResult.Past://过去 
                    {
                        if (mLimitList[0].no == 0)
                            no = 1;
                        else
                            no = 0;



                            dt_start = mDefaultTime;
                        dt_end = mLimitList[0].dt_begin;

                        if (dt < dt_start)
                        {
                            //数据发生在太遥远的过去,删除!!!!!
                            angle = 0;
                            direction = DIRECTION.FIX;
                            rotationCnt = -1;
                            inCV = false;

                            return false;
                        }

                        rotationCnt = LimitIndexToRotationCnt(-1);
                        inCV = true;
                        LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
                    }
                    break;
                case ListFindIndexResult.InCell://TODO
                    {
                        dt_start = mLimitList[index].dt_begin;
                        dt_end = mLimitList[index].dt_end;
                        no = mLimitList[index].no;
                        rotationCnt = LimitIndexToRotationCnt(index);

                        DateTime dtMid = dt_start + TimeSpan.FromTicks((dt_end - dt_start).Ticks / 2);

                        if (dt < dtMid)//以限位的中间时间为分界
                        {
                            rotationCnt--;
                        }

                        TimeSpan renZiJiaPeriod;
                        if (index >= 1)
                        {
                            renZiJiaPeriod = mLimitList[index].dt_begin - mLimitList[index - 1].dt_end;
                        }
                        else
                        {
                            renZiJiaPeriod = mLimitList[index].dt_begin - mDefaultTime;
                        }
                        inCV = false;
                        LimitListCal_SpeedChange(dt_end, dt_start, no, dt, renZiJiaPeriod, out angle, out direction);
                    }
                    break;
                default:
                    //case ListFindIndexResult.MaybeInCell:
                    {

                        dt_start = mLimitList[index].dt_begin;
                        dt_end = dt_start + LimitSignTime;

                        if (dt > dt_end)
                        {
                            if (modifyparam) //基本不会发生!!!
                            {
                                LimitSignTime = dt - dt_start + TimeSpan.FromSeconds(5);
                                dt_end = dt_start + LimitSignTime;
                            }
                            else
                            {
                                dt_end = dt;
                            }
                        }
                        no = mLimitList[index].no;
                        rotationCnt = LimitIndexToRotationCnt(index);

                        DateTime dtMid = dt_start + TimeSpan.FromTicks((dt_end - dt_start).Ticks / 2);

                        if (dt < dtMid)//以限位的中间时间为分界
                        {
                            rotationCnt--;
                        }

                        TimeSpan renZiJiaPeriod;
                        if (index >= 1)
                        {
                            renZiJiaPeriod = mLimitList[index].dt_begin - mLimitList[index - 1].dt_end;
                        }
                        else
                        {
                            renZiJiaPeriod = mLimitList[index].dt_begin - mDefaultTime;
                        }



                        inCV = false;
                        LimitListCal_SpeedChange(dt_end, dt_start, no, dt, renZiJiaPeriod, out angle, out direction);
                    }
                    break;
            }

            return true;


        }



        enum ListFindIndexResult
        {
            /// <summary>
            /// 发生在未来, index.dt_end ~ 未来, 可能 index都还没有发生
            /// </summary>
            Future,
            /// <summary>
            /// 发生在index.dt_begin ~ index.dt_end
            /// </summary>
            InCell,
            /// <summary>
            /// 应该发生在index.dt_begin ~ index.dt_end, 但 index.dt_end还没有数据
            /// </summary>
            MaybeInCell,
            /// <summary>
            /// 发生在 index.dt_end ~ (index+1).dt_begin
            /// </summary>
            BetweenCells,
            /// <summary>
            /// 发生在 过去
            /// </summary>
            Past
        }
        /// <summary>
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        ListFindIndexResult LimitList_FindIndex(DateTime dt, out int index)
        {
            index = 0;
            if (mLimitList.Count <= 0)//没数据,所以是发生在未来的
                return ListFindIndexResult.Future;

            for (int i = mLimitList.Count - 1; i >= 0; i--)
            {
                LimitCell lc = mLimitList[i];
                if (dt >= lc.dt_begin)
                {
                    //找到了

                    index = i;
                    if (lc.dt_end == DateTime.MinValue)
                    {
                        return ListFindIndexResult.MaybeInCell;
                    }
                    else if (dt <= mLimitList[i].dt_end)
                    {
                        return ListFindIndexResult.InCell;
                    }
                    else if (i == (mLimitList.Count - 1))//这个是未来的时间点,还没发生!!! 
                    {
                        return ListFindIndexResult.Future;
                    }
                    else
                    {
                        return ListFindIndexResult.BetweenCells;
                    }
                }
            }

            //这个在过去的时间
            return ListFindIndexResult.Past;
        }



        /// <summary>
        /// 获取膜角度, TODO
        /// </summary>
        /// <param name="a">与0°的偏移, 0~180</param>
        /// <param name="dt">时间点</param>
        /// <param name="angle0">上面的膜对应的膜角度</param>
        /// <param name="angle1">下面的膜对应的膜角度</param>
        /// <param name="direction">人字架正转?反转? FIX, 停止!</param>
        /// <param name="rotationCnt">扫描次数</param>
        /// <param name="inCV">在恒速阶段</param>
        bool GetAngle(double a, DateTime dt, out double angle0, out double angle1, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
        {
            angle0 = 0;
            angle1 = 0;
            direction = Misc.DIRECTION.FORWARD;
            rotationCnt = 0;
            inCV = true;
            if (a > 180 || a < 0)
                return false;

            //压扁对面的角度
            double a_other = 360 - a;
            double angle;


            bool r = LimitListSearch(dt, false, out angle, out direction, out rotationCnt, out inCV);

            if (r == false)
                return false;

            a += angle;
            if (a >= 360)
                a -= 360;
            else if (a < 0)
                a += 360;

            a_other += angle;
            if (a_other >= 360)
                a_other -= 360;
            else if (a_other < 0)
                a_other += 360;
            angle0 = a;
            angle1 = a_other;
            return true;
        }


        #region INotifyPropertyChanged 成员

        public event PropertyChangedEventHandler PropertyChanged;


        protected void NotifyPropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
            }
        }

        #endregion


    }
    public class ScanPosCell
    {
        public DateTime dt = DateTime.MinValue;
        public int pos;//位于膜的位置,不是机架
        public int thick;//两层的厚度
                         //由pos转换!!
        public double angle1;//上层 位于膜泡的角度 0~360°
        public double angle2;//下层 位于膜泡的角度 0~360°
        public Misc.DIRECTION direction;//只有一整副数据都是同一个方向,才能解方程

        public double rotateAngle;//0~180° 可以认为是夹扁后膜的变形
        public ScanPosCell Clone()
        {
            ScanPosCell scanpos = new ScanPosCell();
            scanpos.dt = dt;
            scanpos.pos = pos;
            scanpos.thick = thick;
            scanpos.angle1 = angle1;
            scanpos.angle2 = angle2;
            scanpos.direction = direction;
            scanpos.rotateAngle = rotateAngle;
            return scanpos;
        }
    }

    public class EquationCell
    {
        public Dictionary<int, int> boltPower = new Dictionary<int, int>();//key=boltindex, value=power
        public Dictionary<int, int> heatRotatePower = new Dictionary<int, int>();//key=boltindex, value=power

        public int thick = 0;
        public int mainBolt;//主分区号
        public void Add(int boltindex1, int boltindex2, int thick)
        {
            if (boltPower.ContainsKey(boltindex1))
            {
                boltPower[boltindex1]++;
            }
            else
            {
                boltPower.Add(boltindex1, 1);
            }

            if (boltPower.ContainsKey(boltindex2))
            {
                boltPower[boltindex2]++;
            }
            else
            {
                boltPower.Add(boltindex2, 1);
            }
            this.thick += thick;
        }

        public override string ToString()
        {
            string str = "";
            foreach (var b in boltPower)
            {
                str += "[" + b.Key.ToString() + "," + b.Value.ToString() + "] ";
            }
            return str;
        }
        public double[] GetPower(int nbolts)
        {
            double[] power = new double[nbolts];
            for (int i = 0; i < power.Count(); i++)
                power[i] = 0;

            foreach (var kv in boltPower)
            {
                power[kv.Key] = kv.Value;
            }
            return power;
        }

        public int GetAvg()
        {
            int cnt = 0;
            for (int i = 0; i < boltPower.Count(); i++)
            {
                cnt += boltPower.Values.ElementAt(i);
            }
            if (cnt > 0)
                return thick / cnt;
            else
                return Misc.MyBase.NULL_VALUE;
        }
    }
    public class ScanInfoCell : INotifyPropertyChanged
    {

        /// <summary>
        /// 已经解完方程
        /// </summary>
        public bool HasCast { get; set; }
        

        #region 数据
        public enum STATE
        {
            /// <summary>
            /// 获取角度出错
            /// </summary>
            Err_GetAngle,
            /// <summary>
            /// 解方程出错
            /// </summary>
            Err_Solve,
            /// <summary>
            /// 解方程可能出错
            /// </summary>
            ERR_Solve_Maybe,
            /// <summary>
            /// 没问题
            /// </summary>
            OK
        }
        public STATE State { get; set; } = STATE.OK;
        
        /// <summary>
        /// 扫描时间
        /// </summary>
        public DateTime StartTime { get; set; } = DateTime.MinValue;

        /// <summary>
        /// 旋转架旋转的方向,当同一次扫描中,有两个方向,Direction= Fix;
        /// </summary>
        public Misc.DIRECTION Direction { get; set; } = DIRECTION.FIX;


        /// <summary>
        /// 当前在观察
        /// </summary>
        public bool IsFocus { get; set; }



        /// <summary>
        /// 当前在观察
        /// </summary>
        public bool IsFocus2 { get; set; }

        /// <summary>
        /// 原始数据(时间,位置,总厚度)
        /// </summary>
        public List<ScanPosCell> frame = new List<ScanPosCell>();
        /// <summary>
        /// 方程列表
        /// </summary>
        public List<EquationCell> equationList = new List<EquationCell>();


        /// <summary>
        /// 下面解方程得到的一幅数据对应的数据,与 StartTime 不一样
        /// </summary>
        public DateTime ThicksDT { get; set; } = DateTime.MinValue;

        /// <summary>
        /// 一幅数据
        /// </summary>
        public int[] thicks;

        /// <summary>
        /// 分区数
        /// </summary>
        public int nbolts;

        /// <summary>
        /// 复位区号
        /// </summary>
        public int orgboltno;
        #endregion

        #region 分析观察
        /// <summary>
        /// 解方程 用的scaninfo
        /// </summary>
        public List<int> solveMember = new List<int>();
        /// <summary>
        /// 每个分区出现次数, 上下分区各不相同
        /// </summary>
        public int[] power;
        /// <summary>
        /// 只用当前 scaninfo 的方程。 算出 厚度。
        /// </summary>
        public int[] thicks_avg;
        #endregion

        public ScanInfoCell(int nbolts, int orgboltno)
        {
            this.nbolts = nbolts;
            this.orgboltno = orgboltno;
            thicks = new int[nbolts];
            power = new int[nbolts];
            thicks_avg = new int[nbolts];
            for (int i = 0; i < nbolts; i++)
            {
                thicks[i] = Misc.MyBase.NULL_VALUE;
                thicks_avg[i] = Misc.MyBase.NULL_VALUE;
            }
        }

        int GetBoltIndexFromAngle(double a)
        {
            int boltno1st = 1;
            int nbolts = thicks.Count();
            int boltindex = (int)(a / 360 * nbolts);

            boltindex += orgboltno - boltno1st;

            if (boltindex >= nbolts)
                boltindex -= nbolts;
            else if (boltindex < 0)
                boltindex += nbolts;
            return boltindex;
        }

        /// <summary>
        /// 完成了一些方程,返回true
        /// </summary>
        /// <param name="scanpos_idx"></param>
        /// <returns></returns>
        public bool AddEquation(int scanpos_idx,double sensor_angle)
        {

            ScanPosCell scanpos = frame[scanpos_idx];


            EquationCell equation = new EquationCell();
            equationList.Add(equation);


            //TOD0
            equation.Add(
                GetBoltIndexFromAngle(scanpos.angle1),
                GetBoltIndexFromAngle(scanpos.angle2),
                scanpos.thick);

            for (int i = 1; i < sensor_angle / 2; i++)
            {
                equation.Add(
                    GetBoltIndexFromAngle(scanpos.angle1 + i),
                    GetBoltIndexFromAngle(scanpos.angle2 + i),
                    scanpos.thick);

                equation.Add(
                    GetBoltIndexFromAngle(scanpos.angle1 - i),
                    GetBoltIndexFromAngle(scanpos.angle2 - i),
                    scanpos.thick);
            }
            return true;
        }

        #region INotifyPropertyChanged 接口
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
}