using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.IO;

using MathNet.Numerics.LinearAlgebra.Double;


using Misc;
using FLY.Thick.BulkDataModule;
using FlyADBase;
using FLY.Thick.Base.Common;

using FLY.Thick.BlowingScan.IService;
using FLY.Thick.Base.Server;
using FObjBase;
using FLY.Thick.BlowingScan.Common;
using FLY.Thick.Blowing.IService;
using FLY.Thick.Blowing.Server;
using FLY.Thick.Blowing.Server.Model;

using HistoryDb = FLY.Thick.BlowingScan.Server.Model.HistoryDb;

namespace FLY.Thick.BlowingScan.Server
{
    public class GM_BlowingScan:GM_ScanMotion, IBlowingScanService,ISaveToXml
    {
        #region 延时操作,markno
        const int MARKNO_SAVE = 1;
        const int MARKNO_PROFILE_ADD = 87;
        const int MARKNO_PROFILE_CHANGED = 88;
        #endregion


        /// <summary>
        ///边界查找模块 
        /// </summary>
        private BorderSearch mBorderSearch;


        /// <summary>
        ///人字架位置检测器 
        /// </summary>
        public BlowingDetect mPDetect;
        
        /// <summary>
        /// AD值转厚度值 
        /// </summary>
        private AD2ThickHandler AD2Thick;

        /// <summary>
        /// 扫描数据库
        /// </summary>
        private IBulkDataServiceAdd mBulkData;

        BlowingProfileParam mProfileParam;

        HistoryDb mHistoryDb;

        BulkDb mBulkDb;

        /// <summary>
        /// 上幅数据解方程失败,bookmark被释放出来
        /// </summary>
        private int freebm=-1;
        private int GetFreeBm() 
        {

            int r = freebm;
            freebm = -1;
            return r;
        }
        private void SetFreeBm(int bm) 
        {
            freebm = bm;
        }

        /// <summary>
        /// 动态数据组合
        /// </summary>
        private DynArea mDynArea;
        /// <summary>
        /// 扫描脉冲检测 
        /// </summary>
        private ScanPosDetect mScanPosDetect;
        /// <summary>
        /// 当前扫描号
        /// </summary>
        public int ScanNo =0;
        class ScanPosCell 
        {
            public DateTime dt = DateTime.MinValue;
            public int pos;//位于膜的位置,不是机架
            public int thick;//两层的厚度
            //由pos转换!!
            public double angle1;//上层 位于膜泡的角度
            public double angle2;//下层 位于膜泡的角度
            public Misc.DIRECTION direction;//只有一整副数据都是同一个方向,才能解方程
            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;
                return scanpos;
            }
        }
        class EquationCell
        {
            public Dictionary<int,int> boltPower=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;
            }
        }
        class ScanInfoCell 
        {
            public ScanInfoCell Clone() 
            {
                ScanInfoCell scaninfo = new ScanInfoCell(ScanNo, thicks.Count());
                scaninfo.Direction = Direction;
                scaninfo.ScanNo = ScanNo;
                scaninfo.IsFull = IsFull;
                scaninfo.IndexOfToGetTime = IndexOfToGetTime;
                foreach(ScanPosCell scanpos in frame)
                {
                    scaninfo.frame.Add(scanpos.Clone());
                }
                scaninfo.width = width;
                scaninfo.beginPos = beginPos;
                scaninfo.endPos = endPos;
                scaninfo.lockbm = lockbm;
                return scaninfo;
            }

            public enum GetAngleResult
            {
                /// <summary>
                /// 全部grid 数据已经获取,而且都成功转为方程
                /// </summary>
                AllGet,
                /// <summary>
                /// 部分grid 数据已经获取,这次转化了一些方程
                /// </summary>
                PartGet,
                /// <summary>
                /// 什么都没干,没有转化为任何方程
                /// </summary>
                NothingToDo

            }
            public GetAngleResult GetAngle(BlowingDetect rdetect, int orgboltno, int sensorWidth)
            {
                bool bHasNewData = false;
                for (; IndexOfToGetAngle < IndexOfToGetTime; IndexOfToGetAngle++)
                {
                    int i = IndexOfToGetAngle;
                    ScanPosCell scanpos = frame[i];

                    BlowingDetect.FilmInfo filminfo;
                    if (scanpos.dt == DateTime.MinValue)
                    { 
                    
                    }
                    int ret = rdetect.GetFilmInfo(
                        out filminfo,
                        scanpos.dt,
                        width,
                        scanpos.pos
                        );

                    if (ret == 1)
                    {
                        //发生在未来,等下一次Poll

                        if (IsFull)
                            return GetAngleResult.NothingToDo;
                        else
                        {
                            if (bHasNewData)
                                return GetAngleResult.PartGet;
                            else
                                return GetAngleResult.NothingToDo;
                        }
                    }
                    //成功获取
                    scanpos.angle1 = filminfo.angle1;
                    scanpos.angle2 = filminfo.angle2;
                    scanpos.direction = filminfo.direction;
                    if (IndexOfToGetAngle == 0)
                    {
                        Direction = scanpos.direction;
                    }
                    else
                    {
                        if (Direction != scanpos.direction)
                        {
                            Direction = DIRECTION.FIX;
                        }
                    }
                    //探头直径转为角度范围
                    double sensor_angle = 180.0 * sensorWidth / width;

                    if (AddEquation(i, orgboltno, sensor_angle))
                        bHasNewData = true;
                }

                if (IsFull)
                    return GetAngleResult.AllGet;
                else
                {
                    if (bHasNewData)
                        return GetAngleResult.PartGet;
                    else
                        return GetAngleResult.NothingToDo;
                }
            }


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

                boltindex += orgboltno - boltno1st;

                if (boltindex >= nbolts)
                    boltindex -= nbolts;

                return boltindex;
            }

            int boltindex1_last = -1;
            /// <summary>
            /// 完成了一些方程,返回true
            /// </summary>
            /// <param name="scanpos_idx"></param>
            /// <param name="orgboltno">复位区号</param>
            /// <param name="sensor_angle">探头直径对应的角度范围</param>
            /// <returns></returns>
            bool AddEquation(int scanpos_idx, int orgboltno, double sensor_angle)
            {
                ScanPosCell scanpos = frame[scanpos_idx];

                if (equationList.Count() == 0)//复位记录
                    boltindex1_last = -1;

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

                int boltindex1 = GetBoltIndexFromAngle(scanpos.angle1, orgboltno);
                equation.mainBolt = boltindex1;
                equation.Add(
                    GetBoltIndexFromAngle(scanpos.angle1, orgboltno),
                    GetBoltIndexFromAngle(scanpos.angle2, orgboltno),
                    scanpos.thick);

                //添加直径范围内的方程
                for (int i = 1; i < sensor_angle / 2; i++)
                {
                    equation.Add(
                        GetBoltIndexFromAngle(scanpos.angle1 + i, orgboltno),
                        GetBoltIndexFromAngle(scanpos.angle2 + i, orgboltno),
                        scanpos.thick);

                    equation.Add(
                        GetBoltIndexFromAngle(scanpos.angle1 - i, orgboltno),
                        GetBoltIndexFromAngle(scanpos.angle2 - i, orgboltno),
                        scanpos.thick);
                }
                
                if (boltindex1_last != boltindex1)//记录不同,通知外部是时候解方程了
                {
                    boltindex1_last = boltindex1;
                    return true;
                }
                return false;
            }
            public int GetAvg() 
            {
                if (equationList.Count() < 0)
                    return Misc.MyBase.NULL_VALUE;
                int sum = 0;
                int cnt = 0;
                for (int i = 0; i < equationList.Count(); i++)
                {
                    EquationCell equation = equationList[i];
                    int a = equation.GetAvg();
                    if (Misc.MyBase.ISVALIDATA(a)) 
                    {
                        sum += a;
                        cnt++;
                    }
                }
                if (cnt > 0)
                    return sum / cnt;
                else
                    return Misc.MyBase.NULL_VALUE;
            }
            //public void SaveEquation(StreamWriter sw) 
            //{
                
            //    for (int i = 0; i < equationList.Count(); i++) 
            //    {
            //        EquationCell equation = equationList[i];
            //        string s = ScanNo.ToString();
            //        s +=","+ Direction.ToString();
            //        s += "," + StartTime.ToString();
            //        s+=","+equation.thick.ToString();
            //        for (int j = 0; j <  equation.boltPower.Count(); j++) 
            //        {
            //            int boltindex = equation.boltPower.Keys.ElementAt(j);
            //            s += "," + boltindex.ToString();
            //            s += "," + equation.boltPower[boltindex].ToString();
            //        }
            //        sw.WriteLine(s);
            //    }    
            //}
            //public static void SaveEquation_Header(StreamWriter sw)
            //{
            //    string s = "序号,方向,时间,总厚度,序号1,数量1,序号2,数量2";
            //    sw.WriteLine(s);
            //}
            public ScanInfoCell(int scanNo, int nbolts) 
            {
                Reset(scanNo);
                thicks = new int[nbolts];
                for (int i = 0; i < nbolts; i++) 
                {
                    thicks[i] = Misc.MyBase.NULL_VALUE;
                }
            }
            /// <summary>
            /// 开始测量时间
            /// </summary>
            public DateTime StartTime;
            /// <summary>
            /// 结束测量时间
            /// </summary>
            public DateTime EndTime;
            /// <summary>
            /// 旋转架旋转的方向,当同一次扫描中,有两个方向,Direction= Fix;
            /// </summary>
            public Misc.DIRECTION Direction;
            /// <summary>
            /// 扫描号,必须唯一!!!!!
            /// </summary>
            public int ScanNo;
            /// <summary>
            /// grid数据,已经全部放入!!!!!
            /// </summary>
            public bool IsFull;
            /// <summary>
            /// 准备要去获取时间的ScanPosCell序号, -1, 整个scaninfo 都发生在过去,作废
            /// </summary>
            public int IndexOfToGetTime;
            /// <summary>
            /// 准备要去转角度的ScanPosCell序号
            /// </summary>
            public int IndexOfToGetAngle;
            /// <summary>
            /// 快速解方法,equationList序号
            /// </summary>
            public int IndexOfToSolveEquationQuick;
            public void Reset(int scanNo)
            {
                ScanNo = scanNo;
                frame.Clear();
                equationList.Clear();
                IsFull = false;
                IndexOfToGetTime = 0;
                IndexOfToGetAngle = 0;
                Direction = DIRECTION.FORWARD;
            }
            /// <summary>
            /// 复位数据;一幅grid成功生成,重新获取pos,thick,time,angle,equation
            /// </summary>
            /// <param name="beginPos"></param>
            /// <param name="endPos"></param>
            public void Reset_IsFull(int beginPos, int endPos)
            {
                this.beginPos = beginPos;
                this.endPos = endPos;
                this.width = endPos - beginPos + 1;//膜宽
                this.IsFull = true;

                equationList.Clear();
                frame.Clear();
                IndexOfToGetTime = 0;
                IndexOfToGetAngle = 0;
            }



            void ClearFrame() 
            {
                equationList.Clear();
                frame.Clear();
                IndexOfToGetTime = 0;
                IndexOfToGetAngle = 0;
            }
            public void Reset2GetAngle()
            {
                IndexOfToGetAngle = 0;
                equationList.Clear();
                for (int i = 0; i < thicks.Count(); i++)
                    thicks[i] = Misc.MyBase.NULL_VALUE;
            }
            /// <summary>
            /// 原始数据(时间,位置,总厚度
            /// </summary>
            public List<ScanPosCell> frame=new List<ScanPosCell>();
            /// <summary>
            /// 膜的宽度
            /// </summary>
            public int width=-1;
            /// <summary>
            /// 膜边界左
            /// </summary>
            public int beginPos;
            /// <summary>
            /// 膜边界右
            /// </summary>
            public int endPos;

            /// <summary>
            /// 方程列表
            /// </summary>
            public List<EquationCell> equationList=new List<EquationCell>();
            /// <summary>
            /// 下面解方程得到的一幅数据对应的数据,与 StartTime 不一样
            /// </summary>
            public DateTime thicks_dt;
            /// <summary>
            /// 一幅数据
            /// </summary>
            public int[] thicks;
            /// <summary>
            /// 经过分区表后的结果
            /// </summary>
            public int[] thicks_map;
            /// <summary>
            /// 对应bulkdata 的数据, -1, 没有对应的bookmark,因为从来都没数据
            /// </summary>
            public int bookmark=-1;
            /// <summary>
            /// 锁定 bookmark,只要解过一次方程,bookmark 就会被锁定,不能再修改
            /// </summary>
            public bool lockbm = false;
            public class SolveQuickResult 
            {
                /// <summary>
                /// 新得到数据开始序号
                /// </summary>
                public int start_index;
                /// <summary>
                /// 新得到数据长度,这是一个环形队列
                /// </summary>
                public int len=0;
            }
            /// <summary>
            /// 快速解方程
            /// </summary>
            /// <param name="org_thicks">上一幅完成的数据</param>
            /// <param name="power">上一幅图所占的权重,当前只占1</param>
            /// <returns></returns>
            public SolveQuickResult SolveQuick(int[] org_thicks, int power) 
            {
                List<int> updateboltindex = new List<int>();
                for (int i = IndexOfToSolveEquationQuick; i < equationList.Count; i++, IndexOfToSolveEquationQuick++)
                {
                    EquationCell equation = equationList[i];

                    
                    int mainpower = equation.boltPower[equation.mainBolt];
                    double sum = 0;
                    for (int j = 0; j < equation.boltPower.Count; j++)
                    {
                        int boltindex = equation.boltPower.Keys.ElementAt(j);

                        if (boltindex != equation.mainBolt)
                        {
                            if (org_thicks == null)
                            {
                                mainpower += equation.boltPower[boltindex];
                            }
                            else if (!Misc.MyBase.ISVALIDATA(org_thicks[boltindex]))
                            {
                                mainpower += equation.boltPower[boltindex];
                            }
                            else
                            {
                                sum += equation.boltPower[boltindex] * org_thicks[boltindex];
                            }
                        }
                    }
                    int mainBolt_value = (org_thicks != null)? org_thicks[equation.mainBolt]:Misc.MyBase.NULL_VALUE;

                    if (Misc.MyBase.ISVALIDATA(mainBolt_value))
                    {
                        thicks[equation.mainBolt] = (mainBolt_value * power + (int)((equation.thick - sum) / mainpower)) / (power + 1);
                    }
                    else
                    {
                        thicks[equation.mainBolt] = (int)((equation.thick - sum) / mainpower);
                    }

                    updateboltindex.Add(equation.mainBolt);
                }
                SolveQuickResult result = new SolveQuickResult();
                if (updateboltindex.Count > 0)
                {
                    result.start_index = updateboltindex.Min();
                    result.len = updateboltindex.Max() - updateboltindex.Min() + 1;
                }
                return result;



            }
        }
        /// <summary>
        /// 每次扫描的数据, 
        /// 不是每一幅数据都能解出方程,
        /// 因为要连续3幅才能解出方程
        /// </summary>
        private RList<ScanInfoCell> mScanInfoList;
        /// <summary>
        /// 与 mScanInfoList 基本一样
        /// </summary>
        private RList<FlyData_BlowingScan> mBufList;
        #region ISaveToXml
        public string[] GetSavePropertyNames()
        {
            return new string[]{
                "ChannelCnt",
                "BPC",
                "OrgBoltNo",
                "SolveCnt",
                "Smooth",
                "SensorWidth",
                "IsUsedMap",
                "Map"
            };
        }
        #endregion

        #region IRenZiJiaService




        #region 分区设置


        /// <summary>
        /// 加热通道数
        /// </summary>
        public int ChannelCnt { get; set; } = 88;


        /// <summary>
        /// 分区数/加热通道数
        /// </summary>
        public int BPC { get; set; } = 1;

        /// <summary>
        /// 风环螺丝数
        /// </summary>
        public int NBolts => ChannelCnt * BPC;

        /// <summary>
        /// 人字架复位时,对应的螺丝号,0° 对应的 螺丝号
        /// </summary>
        public int OrgBoltNo { get; set; } = 1;

        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 bool IsUsedMap { get; set; }
        bool IsNeedMap()
        {
            if (IsUsedMap && Map.Count() > 0)
                return true;
            else
                return false;
        }

        private List<BoltMapCell> map = new List<BoltMapCell>();
        /// <summary>
        /// 分区表,必须保证不能为null
        /// </summary>
        public List<BoltMapCell> Map
        {
            get { return map; }
            set
            {
                //检测是否不一样
                if (value == null)
                    return;
                if (checkNoSame_map(map, value))
                {
                    if (!checkParamErr_map(value))
                    {
                        IsUsedMap = false;
                        map = new List<BoltMapCell>();
                    }
                    else
                    {
                        map = value;
                    }
                }
            }
        }
        bool checkNoSame_map(List<BoltMapCell> mapCells1, List<BoltMapCell> mapCells2)
        {
            if (mapCells1.Count() != mapCells2.Count())
                return true;
            for (int i = 0; i < mapCells1.Count(); i++)
            {
                var mapCell1 = mapCells1[i];
                var mapCell2 = mapCells2[i];

                if (mapCell1.NewNo != mapCell2.NewNo)
                    return true;
                if (mapCell1.OldNo != mapCell2.OldNo)
                    return true;
            }
            return false;
        }
        /// <summary>
        /// 检测通过,返回true
        /// </summary>
        /// <returns></returns>
        bool checkParamErr_map(List<BoltMapCell> mapCells)
        {
            bool map_err = false;
            bool g_o = false;
            bool g_n = false;
            //Map 检测
            //OldNo,NewNo不能 <1 >NBolts
            //Map OldNo 必须从小到大环形排列
            //Map NewNo 必须从小到大环形排列
            for (int i = 0; i < mapCells.Count(); i++)
            {
                var mapCell = mapCells[i];
                if (mapCell.NewNo < 1 || mapCell.NewNo > NBolts)
                {
                    map_err = true;
                    break;
                }
                if (mapCell.OldNo < 1 || mapCell.OldNo > NBolts)
                {
                    map_err = true;
                    break;
                }

                int i_next = i + 1;
                if (i_next >= mapCells.Count())
                    i_next = 0;

                var mapCell_next = mapCells[i_next];
                if (mapCell.OldNo >= mapCell_next.OldNo)
                {
                    if (!g_o)
                        g_o = true;
                    else
                    {
                        map_err = true;
                        break;
                    }

                }

                if (mapCell.NewNo >= mapCell_next.NewNo)
                {
                    if (!g_n)
                        g_n = true;
                    else
                    {
                        map_err = true;
                        break;
                    }

                }
            }

            if (map_err)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        #endregion


        /// <summary>
        /// 应用
        /// </summary>
        public void Apply()
        {
            Save();
        }
        /// <summary>
        /// 扫描一次推送事件
        /// </summary>
        public event RenZiJiaDataEventHandler DataEvent;
        #endregion

        /// <summary>
        /// 缓冲区第一个记录序号
        /// </summary>
        public int FirstBM { get; set; } = -1;


        /// <summary>
        /// 缓冲区最后一个记录序号
        /// </summary>
        public int LastBM { get; protected set; } = -1;

        private int sensorwidth = 250;
        /// <summary>
        /// 探头宽度,用于解方程,可以与实际不符合 单位脉冲
        /// </summary>
        public int SensorWidth
        {
            get { return sensorwidth; }
            set
            {
                if (value < 1)
                    value = 1;

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

        private int solvecnt = 4;
        /// <summary>
        /// 多少次扫描解一次方程
        /// </summary>
        public int SolveCnt
        {
            get { return solvecnt; }
            set
            {
                if (value < 3)
                    value = 3;

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


            if (ChannelCnt < 20 || ChannelCnt > 160)
            {
                ChannelCnt = 44;
            }

            if (BPC < 1 || BPC > 4)
            {
                BPC = 2;
            }
            
            if (OrgBoltNo < 1 || OrgBoltNo > NBolts)
            {
                OrgBoltNo = 1;
            }

            if (SolveCnt > 20)
            {
                SolveCnt = 5;
            }

            if (!checkParamErr_map(Map))
            {
                IsUsedMap = false;
                Map = new List<BoltMapCell>();
            }
        }



        static GM_BlowingScan()
        {
            Misc.SaveToXmlHepler.Regist(typeof(BoltMapCell));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="flyad"></param>
        /// <param name="gsample"></param>
        public GM_BlowingScan(FlyAD7 flyad, GSample gsample)
            : base(flyad, gsample)
        {

            mPDetect = new BlowingDetect();
            mScanInfoList = new RList<ScanInfoCell>(30);
            mBufList = new RList<FlyData_BlowingScan>(100);
            mScanPosDetect = new ScanPosDetect();
            
            ChannelCnt = 44;//加热棒数量
            BPC = 2;//分区数量/加热棒数量

            OrgBoltNo = 10;//复位时,测厚仪对应的螺丝号

            SolveCnt = 5;
            mPDetect.Load();
            Load();

            //检测参数
            mPDetect.CheckParamErr();
            checkParamErr();

            if (SolveCnt < 3)
                SolveCnt = 3;

            PropertyChanged+=new PropertyChangedEventHandler(RenZiJiaScan_PropertyChanged);
            
            mPDetect.PropertyChanged += (s, e) =>
            {
                NotifyPropertyChanged(e.PropertyName);
            };

            mPDetect.FilmInfoChangedEvent += new BlowingDetect.FilmInfoChangedEventHandler(mPDetect_FilmInfoChangedEvent);
            mPDetect.ClearEvent += new BlowingDetect.ClearEventHandler(mPDetect_ClearEvent);

            mPDetect.Init(mFlyAD);

            mScanPosDetect.Init(mFlyAD);

            ScanCnt = -1;

            
        }


        bool Load()
        {
            return Misc.SaveToXmlHepler.Load("renzijiascan.xml", this);
            
        }
        void Save()
        {
            Misc.SaveToXmlHepler.Save("renzijiascan.xml", this);
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="bulkdata"></param>
        /// <param name="borderSearch"></param>
        /// <param name="dynArea"></param>
        /// <param name="func_ad2thick"></param>
        /// <param name="profileParam"></param>
        /// <param name="historyDb"></param>
        /// <param name="bulkDb"></param>
        public void Init(
            IBulkDataServiceAdd bulkdata,
            BorderSearch borderSearch,
            DynArea dynArea, 
            AD2ThickHandler func_ad2thick,
            BlowingProfileParam profileParam,
            HistoryDb historyDb, BulkDb bulkDb
            )
        {
            AD2Thick = func_ad2thick;
            mBulkData = bulkdata;
            mBorderSearch = borderSearch;
            mDynArea = dynArea;
            mProfileParam = profileParam;
            mHistoryDb = historyDb;
            mBulkDb = bulkDb;

            //接收PollScan的推送
            DataPosEvent += new EndEventHandler(pollScan_DataPosEvent);
            EndEvent += new EndEventHandler(pollScan_EndEvent);

            //记录调试数据
            MarkDebugInit();
        }
        #region 记录原始数据
        DateTime markdebug_dt = DateTime.MinValue;
        int markdebug_lastBm = -1;
        void MarkDebugInit()
        {
            if (markdebug_dt == DateTime.MinValue)
                markdebug_dt = DateTime.Now;

            //启动1分钟定时器
            FObjBase.PollModule.Current.Poll_Config(
                FObjBase.PollModule.POLL_CONFIG.ADD,
                () =>
                {
                    mPDetect.GetSignList(markdebug_dt, (asyncState, retData) =>
                    {
                        var reponse = retData as GetSignListReponse;
                        var cells = reponse.datas;
                        if (cells.Count > 0)
                        {
                            mHistoryDb.AddSignData(
                                new Lc_Sign()
                                {
                                    Time = cells.First().Time,
                                    Signs = cells.ToArray()
                                });
                        }
                    }, null);
                    mPDetect.GetRollList(markdebug_dt, (asyncState, retData) =>
                    {
                        var reponse = retData as GetRollListReponse;
                        var cells = reponse.datas;
                        if (cells.Count > 0)
                        {
                            mHistoryDb.AddRollData(
                                new Lc_Roll()
                                {
                                    Time = cells.First().dt,
                                    Signs = cells.ToArray()
                                });
                        }
                    }, null);
                    this.GetBufList((asyncState, retData) =>
                    {
                        var reponse = retData as GetBufListReponse;
                        var cells = reponse.datas;
                        if (cells.Count() == 0)
                        {
                            //没有数据
                            markdebug_lastBm = reponse.lastbm;
                            return;
                        }
                        int new_cnt = reponse.lastbm - markdebug_lastBm;
                        if (new_cnt > cells.Count())
                            new_cnt = cells.Count();
                        markdebug_lastBm = reponse.lastbm;

                        if (new_cnt <= 0)
                            return;
                        List<Model.Lc_OrgScanData> lc_OrgScanDatas = new List<Model.Lc_OrgScanData>();
                        for (int i = 0; i < new_cnt; i++)
                        {
                            int index = reponse.datas.Count() - new_cnt + i;
                            var data = reponse.datas[index];

                            var times = data.ThicksDt.Where(dt => dt != DateTime.MinValue);
                            if (times.Count() == 0)
                                continue;
                            Model.Lc_OrgScanData lcOrgScanData = new Model.Lc_OrgScanData();
                            lcOrgScanData.Time = times.Min();
                            lcOrgScanData.FilmBegin = data.FilmRange.Begin;
                            lcOrgScanData.FilmEnd = data.FilmRange.End;
                            lcOrgScanData.PosOfGrid = data.PosOfGrid;
                            lcOrgScanData.K = mProfileParam.K;
                            lcOrgScanData.Thicks = ToRealThicks(data.Thicks);
                            lcOrgScanData.ThicksDt = data.ThicksDt;
                            lc_OrgScanDatas.Add(lcOrgScanData);
                        }
                        mHistoryDb.AddOrgScanDatas(lc_OrgScanDatas);
                        
                    }, null);
                    markdebug_dt = DateTime.Now;
                },
                TimeSpan.FromMinutes(1));
        }

        #endregion
        double[] ToRealThicks(IEnumerable<int> frame)
        {
            return frame.Select(t =>
            {
                if (Misc.MyBase.ISVALIDATA(t))
                    return t / 100.0;
                else
                    return double.NaN;
            }).ToArray();
        }
        void RenZiJiaScan_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "NBolts") 
            {
                //所有数据删除
                mScanInfoList.Clear();
                //TODO!!!!!
                //停下来
            }
        }
        void mPDetect_FilmInfoChangedEvent(object sender, BlowingDetect.FilmInfoChangedEventArgs e)
        {
            // 找到 e.Time 对应的 scaninfo

            for (int i = 0; i < mScanInfoList.Count; i++) 
            {
                ScanInfoCell scaninfo = mScanInfoList[i];
                if (scaninfo.IndexOfToGetTime > 0) 
                {
                    DateTime dt_start = scaninfo.frame[0].dt;
                    DateTime dt_end = scaninfo.frame[scaninfo.IndexOfToGetTime-1].dt;
                    if (e.Time < dt_end)//找到了 
                    {
                        for (int j = i; j < mScanInfoList.Count; j++) 
                        {
                            scaninfo = mScanInfoList[j];
                            scaninfo.IndexOfToGetAngle = 0;
                        }
                        return;
                    }
                }
            }
        }
        void mPDetect_ClearEvent(object sender)
        {
            //已经很久没数据,清空全部!!!!!!!

            //scanposdetect
            mScanPosDetect.ScanNo = ScanNo;

            if (mScanInfoList.Count > 0)
            {
                mScanInfoList.Clear();

                //scaninfo
                ScanInfoCell scaninfo = new ScanInfoCell(ScanNo, NBolts);
                //scaninfo.bookmark = bookmark;
                mScanInfoList.Add(scaninfo);
            }
        }


        #region 1.grid 数据 转 ScanPosCell.(pos,thick)  放入 mScanInfoList.Last()
        /// <summary>
        /// 动态获取!!!! 
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        void pollScan_DataPosEvent(object obj, FlyADBase.MiniGridEventArgs e)
        {
            DIRECTION direction = e.direction;
            int posOfGrid = e.posOfGrid;
            int start_idx = e.grid_start;
            int start_pos = e.grid_start * e.posOfGrid;
            int[] dat = e.buf;
            //-------------------------------------
            //DIR
            mDynArea.Direction = direction;
            mDynArea.AD = dat[0];
            mDynArea.Thick = AD2Thick(dat[0]);


            if (mScanInfoList.Count == 0)
                return;

            ScanInfoCell scaninfocell = mScanInfoList.Last();
            if (scaninfocell.width <= 0)
            {
                //刚开始,连膜边都还没找到
                return;
            }

            //找边界前,要对数据进行校正

            //尽可能不要两个边的数据,N2是边界向内减, 只是不要更多的数据而已
            int bp = scaninfocell.beginPos + mBorderSearch.N2;
            int ep = scaninfocell.endPos - mBorderSearch.N2;
            //对应的grid号
            int bp_idx = bp / posOfGrid;
            int ep_idx = ep / posOfGrid;

            if(bp_idx < start_idx)
                bp_idx = start_idx;

            if(ep_idx > (start_idx+dat.Length-1))
                ep_idx = start_idx+dat.Length-1;

            for (int i = bp_idx; i <= ep_idx; i++)
            {
                int idx = i - start_idx;

                int pos = i * posOfGrid + posOfGrid / 2;//一个grid的中间脉冲,也就是大概而已
                int thick = Misc.MyBase.NULL_VALUE;
                if (Misc.MyBase.ISVALIDATA(dat[idx]))
                    thick = AD2Thick(dat[idx])*2;
               
                pos -= scaninfocell.beginPos;//膜上面的位置
                scaninfocell.frame.Add(new ScanPosCell() { pos = pos, thick = thick });
            }
            OnPoll_GetTime2();
        }
        void pollScan_EndEvent(object obj, FlyADBase.MiniGridEventArgs e)
        {
            DIRECTION direction = e.direction;
            int posOfGrid = e.posOfGrid;
            int start_idx = e.grid_start;
            int start_pos = e.grid_start * e.posOfGrid;
            int[] dat = e.buf;

            //int scaninfo_idx = mScanInfoList.FindIndex(sil => sil.IndexOfToGetTime < sil.frame.Count);
            //if ((scaninfo_idx>0) && (mScanInfoList.Count - scaninfo_idx > 1) )
            //{
            //    //有问题
            //}


            ScanNo++;
            mScanPosDetect.ScanNo = ScanNo;

            if(gsample.Samples[0].AD <= 0)
            {
                //无温修AD
                if (mScanInfoList.Count == 0)
                    return;

                ScanInfoCell scaninfo = mScanInfoList.Last();
                if (scaninfo.bookmark != -1)
                    mBulkData.ChangeEmply(scaninfo.bookmark);
                scaninfo.Reset(ScanNo);
                return;
            }

            //找边界
            bool ret = mBorderSearch.FindFilm(direction, posOfGrid, start_idx, dat);
            if (!ret)//不能找到边界!!!没膜!!!!
            {
                mBorderSearch.Init();

            }
            //设置扫描范围
            //POLLSCAN----------------------------------------	
            Range scanrange = mBorderSearch.GetScanRange();

            ScanStart = scanrange.Begin;// beginPos;
            ScanEnd = scanrange.End;// endPos;

            if (!ret)
            {
                if (mScanInfoList.Count == 0)
                    return;

                ScanInfoCell scaninfo = mScanInfoList.Last();
                if (scaninfo.bookmark != -1)
                    mBulkData.ChangeEmply(scaninfo.bookmark);
                scaninfo.Reset(ScanNo);
                return;
            }

            //必须设置好 mBorderSearch.SensorWidth,它就是探头直径
            Range border = mBorderSearch.GetBorder(direction);

            if (mScanInfoList.Count == 0)//安全检查,不可能发生的!!!!!
                return;

            ScanInfoCell scaninfocell = mScanInfoList.Last();
            //膜宽,和位置一起计算旋转架上的角度
            scaninfocell.Reset_IsFull(border.Begin, border.End);

            //尽可能不要两个边的数据,N2是边界向内减, 只是不要更多的数据而已
            Range boltrange = mBorderSearch.GetBoltRange(direction);

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

            if (bp_idx < start_idx)
                bp_idx = start_idx;

            if (ep_idx > (start_idx + dat.Length - 1))
                ep_idx = start_idx + dat.Length - 1;

            for (int i = bp_idx; i <= ep_idx; i++)
            {
                int idx = i - start_idx;
                int pos = i * posOfGrid + posOfGrid / 2;//一个grid的中间脉冲,也就是大概而已
                int thick = Misc.MyBase.NULL_VALUE;
                if (Misc.MyBase.ISVALIDATA(dat[idx]))
                    thick = AD2Thick(dat[idx]) * 2;

                pos -= border.Begin;//膜上面的位置
                scaninfocell.frame.Add(new ScanPosCell() { pos = pos, thick = thick });
            }



            //或取每个数据对应的时间点
            OnPoll_GetTime2();

            //把扫描数据放多一份到 mBufList
            PushToBufList(e, border, scaninfocell.ScanNo);

            //插入纵向数据
            #region sqlite3 历史数据保存
            {
                List<double> boltValues = new List<double>();
                double boltwidth = scaninfocell.width * 1.0 / (NBolts / 2);
                int boltIdx = 0;
                double boltSum = 0;
                int boltCnt = 0;

                for (int i = 0; i < scaninfocell.frame.Count(); i++)
                {
                    int idx = (int)(scaninfocell.frame[i].pos / boltwidth);
                    if (boltIdx != idx)
                    {
                        if (boltCnt > 0)
                        {
                            boltValues.Add(boltSum / boltCnt);
                            boltSum = 0;
                            boltCnt = 0;
                        }
                    }
                    boltIdx = idx;
                    if (Misc.MyBase.ISVALIDATA(scaninfocell.frame[i].thick))
                    {
                        boltSum += scaninfocell.frame[i].thick / 2 / 100.0;
                        boltCnt++;
                    }

                }
                if (boltCnt > 0)
                {
                    boltValues.Add(boltSum / boltCnt);
                    boltSum = 0;
                    boltCnt = 0;
                }
                
                mHistoryDb.AddTrendData(new Lc_TrendData()
                {
                    Time = DateTime.Now,
                    K = mProfileParam.K,
                    Thicks = boltValues.ToArray()
                });
            }

            #endregion


            //创建一个新的 scaninfocell 
            //为下一趟即时扫描作准备
            scaninfocell = new ScanInfoCell(ScanNo,NBolts);

            direction = (direction == DIRECTION.FORWARD) ? DIRECTION.BACKWARD : DIRECTION.FORWARD;
            border = mBorderSearch.GetBorder(direction);
            scaninfocell.beginPos = border.Begin;
            scaninfocell.endPos = border.End;
            scaninfocell.width = border.Width;//膜宽
            scaninfocell.IsFull = false;
            mScanInfoList.RAdd(scaninfocell);
        }
        void PushToBufList(MiniGridEventArgs e, Range border, int scanNo)
        {
            DIRECTION direction = e.direction;
            int posOfGrid = e.posOfGrid;
            int start_idx = e.grid_start;
            int start_pos = e.grid_start * e.posOfGrid;
            int[] dat = e.buf;
            

            FlyData_BlowingScan f = new FlyData_BlowingScan();
            f.FilmRange = border;
            f.PosOfGrid = posOfGrid;
            f.Thicks = new int[mFlyAD.PosLen/ posOfGrid];
            f.ThicksDt = new DateTime[f.Thicks.Count()];
            //对应的grid号
            for (int i = 0; i < f.Thicks.Count(); i++)
            {
                int idx = i - start_idx;
                int pos = i * posOfGrid + posOfGrid / 2;//一个grid的中间脉冲,也就是大概而已

                int thick = Misc.MyBase.NULL_VALUE;
                if (idx >= 0 && idx < dat.Count())
                {
                    if (Misc.MyBase.ISVALIDATA(dat[idx]))
                        thick = AD2Thick(dat[idx]) * 2;
                }
                f.Thicks[i] = thick;

                DateTime dt = DateTime.MinValue;
                if (Misc.MyBase.ISVALIDATA(thick))
                {
                    int ret = mScanPosDetect.GetTime(pos, scanNo, out dt);
                }
                f.ThicksDt[i] = dt;
            }

            f.RotateCnt = mPDetect.RotationCnt;
            if (mPDetect.mLimitList.Count() > 0)
            {
                LimitCell lc = mPDetect.mLimitList.Last();
                f.SignNo = lc.no;
                f.Time = lc.dt_begin;
                f.LeaveTime = lc.dt_end;
            }
            else
            {
                f.SignNo = -1;
                f.Time = DateTime.MinValue;
                f.LeaveTime = DateTime.MinValue;
            }

            f.LimitSignTime = mPDetect.LimitSignTime;
            f.RotatePeriod = mPDetect.RenZiJiaPeriod;

            f.Velocity = mPDetect.FilmVelocity;
            

            mBufList.RAdd(f);
            FirstBM = mBufList.Index2No(0);
            LastBM = mBufList.GetLastNo();



        }
        #endregion
        #region 2.填入 ScanPosCell.(dt)  
        /// <summary>
        /// 找到一幅数据,每个grid 对应的时间点
        /// </summary>
        void OnPoll_GetTime2()
        {
            ScanInfoCell scaninfo = mScanInfoList.Last();

            for (; scaninfo.IndexOfToGetTime < scaninfo.frame.Count; scaninfo.IndexOfToGetTime++)
            {
                int i = scaninfo.IndexOfToGetTime;
                ScanPosCell scanpos = scaninfo.frame[i];
                //还原为机架位置
                int pos = scaninfo.beginPos + scanpos.pos;

                DateTime dt;
                int ret = mScanPosDetect.GetTime(pos, scaninfo.ScanNo, out dt);
                if (ret == -2)
                {
                    //发生在过去,不正常

                }
                else if (ret == 1)//这个是未来的,等下一次Poll
                {
                    return;
                }
                else if (dt == DateTime.MinValue)
                {
                    //出错, 把scaninfo 删除掉
                    //mScanInfoList.RemoveAt(scaninfo_idx);
                    //scaninfo_idx--;
                    break;
                }
                //成功获取

                scanpos.dt = dt;
                if (i == 0)
                {
                    scaninfo.StartTime = dt;
                    scaninfo.thicks_dt = dt;
                }
                scaninfo.EndTime = dt;
            }
        }
        #endregion
        #region 3.填入 ScanPosCell.(angle1,angle2), 且转为 Equation 
        void OnPoll_GetAngle() 
        {
            int scaninfo_idx = mScanInfoList.FindIndex(
                sil => 
                    (sil.IndexOfToGetAngle < sil.IndexOfToGetTime));

            if (scaninfo_idx == -1)
            {
                return;//全部都转换完成!!
            }

            while (true)
            {
                ScanInfoCell scaninfo = mScanInfoList[scaninfo_idx];

                ScanInfoCell.GetAngleResult result = scaninfo.GetAngle(mPDetect, OrgBoltNo, SensorWidth);
                if (result == ScanInfoCell.GetAngleResult.NothingToDo)
                {
                    //getangle 失败,发生在未来,等下一次Poll
                    break;
                }

                //解方程,画数据!!
                ToThicks(scaninfo_idx);
                    
                
                //全部GetAngle 成功,下一个scaninfo
                scaninfo_idx++;
                if (scaninfo_idx >= mScanInfoList.Count)
                    break;//已经没有了
            }
        }
        

        class CalSolveCnt 
        {
            List<int> list = new List<int>();
            const int listcap = 5;
            void Add(int cnt) 
            {
                list.Add(cnt);
                if (list.Count > listcap)
                    list.RemoveAt(0);
            }
            public int Max() 
            {
                if (list.Count > 0)
                    return list.Max();
                else
                    return 4;
            }
            Misc.DIRECTION direction = DIRECTION.FIX;
            int cnt = 0;
            public bool Check(Misc.DIRECTION direction) 
            {
                if (this.direction != direction)
                {
                    if((this.direction!=Misc.DIRECTION.FIX) && (cnt>3))
                        Add(cnt);
                    cnt = 0;
                }
                cnt++;
                this.direction = direction;
                return OK();
            }
            public bool OK() 
            {
                if (list.Count >= listcap) 
                {
                    return true;
                }
                return false;
            }
        }
        CalSolveCnt mCalSolveCnt = new CalSolveCnt();

        bool ToThicks_SolveEquation(int scaninfo_idx) 
        {
            ScanInfoCell scaninfo = mScanInfoList[scaninfo_idx];
            if (scaninfo.Direction == DIRECTION.FIX)//当前是Fix 不解方程
                return false;

            //解方程数量检测,免得一直数量不足,解不了方程
            //if (mCalSolveCnt.Check(scaninfo.Direction)) 
            //{
            //    if (SolveCnt > (mCalSolveCnt.Max()-1)) 
            //    {
            //        SolveCnt = (mCalSolveCnt.Max() - 1);
            //    }
            //}
            
            if (scaninfo_idx < (SolveCnt - 1))
                return false;

            //全部方向都必须一致,当然也不能是Fix
            //for (int i = 0; i < (SolveCnt - 1); i++)
            //{
            //    if (scaninfo.Direction != mScanInfoList[scaninfo_idx - 1 - i].Direction)
            //        return false;
            //}

            int avg = scaninfo.GetAvg();
            if (!Misc.MyBase.ISVALIDATA(avg))//平均值不合法!!!!
                return false;

            if (avg < 500)//值太小!!!
                return false;

            List<EquationCell> equationlist = new List<EquationCell>();
            ////全部平均值相差不能超过 5%
            //for (int i = 0; i < SolveCnt; i++)
            //{
            //    int a = mScanInfoList[scaninfo_idx - 1 - i].GetAvg();
            //    if (Math.Abs((a - avg) / avg) > 0.05)
            //        return false;
            //    equationlist.AddRange(mScanInfoList[scaninfo_idx - i].equationList);
            //}


            int cnt = 0;
            for (int i = scaninfo_idx; i >=0; i--) 
            {
                if (mScanInfoList[i].Direction == DIRECTION.FIX)
                    continue;
                    cnt++;
                double a = mScanInfoList[i].GetAvg();
                double p = (a - avg) / avg;
                if (Math.Abs(p) > 0.05)
                    return false;
                equationlist.AddRange(mScanInfoList[i].equationList);
                
                if (cnt >= SolveCnt)
                    break;
            }
            if (cnt < SolveCnt)//方程不够!!!!
                return false;
            
            if (equationlist.Count < 100)//方程数太少
                return false;

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

            //不能有任何一个NULL_VALUE
            for (int i = 0; i < scaninfo.thicks.Count(); i++)
            {
                if (scaninfo.thicks[i] > 1000000)
                    return false;
                if (scaninfo.thicks[i] < -10000)
                    return false;
            }

            //检测
            {
                int sigma = Misc.MyMath.Sigma(scaninfo.thicks);
                int avg2 = Misc.MyMath.Avg(scaninfo.thicks);


                double p = 1.0 * (avg2 - avg) / avg;
                if (Math.Abs(p) > 0.05)//变化太大,不可能!!!
                    return false;
                p = 1.0 * sigma / avg2;
                if (Math.Abs(p) > 0.5) //波动太大,异常
                    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.thicks_dt = mScanInfoList[scaninfo_idx - (SolveCnt-1)].StartTime;

            return true;
        }


        enum BindingMotion
        {
            Add,
            Remove
        }
        void BookmarkRestartToBinding(int scaninfo_idx, BindingMotion motion) 
        {
            if (motion == BindingMotion.Add)
            {
                if (mScanInfoList[scaninfo_idx].bookmark == -1)
                {
                    int bookmark = mScanInfoList[scaninfo_idx].bookmark;
                    mBulkData.ChangeEmply(bookmark);

                    //删除与 bulkdata 关系
                    mScanInfoList[scaninfo_idx].bookmark = -1;
                    for (int i = mScanInfoList.Count()-1;i>(scaninfo_idx + 1); i--)
                    {
                        if (mScanInfoList[i].bookmark != -1)
                        {
                            int tmp = bookmark;
                            bookmark = mScanInfoList[i].bookmark;
                            mScanInfoList[i].bookmark = tmp;
                        }
                    }
                }
            }
            else 
            {
                if (mScanInfoList[scaninfo_idx].bookmark != -1)
                {
                    int bookmark = mScanInfoList[scaninfo_idx].bookmark;
                    mBulkData.ChangeEmply(bookmark);

                    //删除与 bulkdata 关系
                    mScanInfoList[scaninfo_idx].bookmark = -1;
                    for (int i = scaninfo_idx + 1; i < mScanInfoList.Count(); i++)
                    {
                        if (mScanInfoList[i].bookmark != -1)
                        {
                            int tmp = bookmark;
                            bookmark = mScanInfoList[i].bookmark;
                            mScanInfoList[i].bookmark = tmp;
                        }
                    }
                }
            }
        }

        FR_DATA_TYPE Direction2FR_DATA_TYPE(Misc.DIRECTION direction)
        {
            if (direction == DIRECTION.FORWARD)
                return FR_DATA_TYPE.INVALID;
            else
                return FR_DATA_TYPE.VALID;
        }

        bool ToThicks(int scaninfo_idx) 
        {
            ScanInfoCell scaninfo = mScanInfoList[scaninfo_idx];

            if (scaninfo.IsFull)//所有grid数据都获取完,解方程
            {
                if (scaninfo.lockbm)
                { 
                    //不需要再解方程,算thicks 的值!!!
                    return false;
                }



                if (ToThicks_SolveEquation(scaninfo_idx))
                {
                    //解方程成功
                    if (scaninfo.bookmark == -1)
                        scaninfo.bookmark = GetFreeBm();

                    //重新分区
                    if (IsNeedMap())
                    {
                        Dictionary<int, int> map = new Dictionary<int, int>();
                        for (int i = 0; i < Map.Count(); i++)
                            map.Add(Map[i].OldNo - 1, Map[i].NewNo - 1);
                        scaninfo.thicks_map = Misc.MyMath.Map(scaninfo.thicks, map);
                    }
                    
                    //更新数据库内容
                    {
                        int[] frame;
                        List<BoltMapCell> map;
                        if (IsNeedMap())
                        {
                            frame = scaninfo.thicks_map;
                            map = new List<BoltMapCell>();
                            foreach (var m in Map)
                                map.Add(m.Clone());
                        }
                        else
                        {
                            frame = scaninfo.thicks;
                            map = null;
                        }
                        var realthicks = ToRealThicks(frame);
                        var renZiJiaDataEventArgs = new RenZiJiaDataEventArgs()
                        {
                            Time = scaninfo.thicks_dt,
                            EndTime = scaninfo.EndTime,
                            IsBackw = scaninfo.Direction == DIRECTION.BACKWARD,
                            RPeriod = mPDetect.RenZiJiaPeriod,
                            RCnt = mPDetect.RotationCnt,
                            OrgBoltNo = OrgBoltNo,
                            RAngle = mPDetect.RAngle,
                            FilmLength = mPDetect.FilmLength,
                            FilmVelocity = mPDetect.FilmVelocity,
                            K = mProfileParam.K,
                            Thicks = realthicks,
                            Boltmap = map
                        };

                        DataEvent?.Invoke(this, renZiJiaDataEventArgs);

                        //TODO 需要检查是否重复写入了!!!!!
                        #region sqlite3 历史数据保存
                        var lc_scandata = Lc_AutoMapperProfile.Mapper.Map<Lc_ScanData>(renZiJiaDataEventArgs);
                        mHistoryDb.AddScanData(lc_scandata);
                        #endregion

                    }


                    int[] thicks = IsNeedMap()? scaninfo.thicks_map:scaninfo.thicks;

                    if (scaninfo.bookmark == -1)
                    {
                        scaninfo.bookmark = mBulkData.Add(thicks, Direction2FR_DATA_TYPE(scaninfo.Direction), scaninfo.thicks_dt);
                    }
                    else
                    {
                        mBulkData.Change(scaninfo.bookmark, 0, thicks, Direction2FR_DATA_TYPE(scaninfo.Direction), scaninfo.thicks_dt);
                    }
                    
                    mBulkData.Save();
                }
                else 
                {
                    //失败,删除!!!
                    for (int i = 0; i < scaninfo.thicks.Count(); i++) 
                        scaninfo.thicks[i] = Misc.MyBase.NULL_VALUE;
                    
                    if (scaninfo.bookmark != -1)
                    {
                        mBulkData.ChangeEmply(scaninfo.bookmark);
                        SetFreeBm(scaninfo.bookmark);
                        scaninfo.bookmark = -1;
                    }
                }
                scaninfo.lockbm = true;
                return true;
            }
            else
            {
                //还没获取完所有grid, 简易解方程,只认为 mainBolt 是未知的,其它都是已知的,用上一幅数据的值
                //这样的数据不需要放在数据库中!!!!!!

                int[] org_thick = null;
                if (scaninfo_idx != 0)
                    org_thick = mScanInfoList[scaninfo_idx - 1].thicks;

                ScanInfoCell.SolveQuickResult result = scaninfo.SolveQuick(org_thick,SolveCnt-1);
                if (result.len > 0) 
                {
                    //如果scaninfo.bookmark ==-1 ,也就是没有对应的 bulkdata 数据,创建一个
                    //先从freebm 获取,不行再创建新的
                    if (scaninfo.bookmark == -1)
                        scaninfo.bookmark = GetFreeBm();

                    if (scaninfo.bookmark == -1)
                        scaninfo.bookmark = mBulkData.AddEmply();

                    int[] thicks = scaninfo.thicks.Where((t, i) => (i >= result.start_index && i < (result.start_index + result.len))).ToArray();

                    mBulkData.Change(
                        scaninfo.bookmark,
                        result.start_index, thicks, Direction2FR_DATA_TYPE(scaninfo.Direction), scaninfo.thicks_dt);

                    
                    mBulkDb.SetTempFrame(0, ToRealThicks(thicks));
                }
            }
            return false;
        }
        /// <summary>
        /// 解方程
        /// </summary>
        /// <param name="equationList">N条方程</param>
        /// <param name="frame_out">输出的结果</param>
        void SolveEquation(int[] frame_out, List<EquationCell> equationList)
        {
            
            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);


            for (int i = 0; i < NBolts; i++)
            {
                frame_out[i] = (int)X[i];
            }
        }
        #endregion
        
        /// <summary>
        /// 开始扫描
        /// </summary>
        /// <returns></returns>
        public override bool Start()
        {
            bool b = base.Start();
            if (!b)
                return false;

            //设置扫描范围
            int beginPos, endPos;
            
            mBorderSearch.Init();
            mBorderSearch.GetScanRange(out beginPos, out endPos);



            ScanNo++;
            //scanposdetect
            mScanPosDetect.ScanNo = ScanNo;

            //scaninfo
            ScanInfoCell scaninfo = new ScanInfoCell(ScanNo, NBolts);
            mScanInfoList.Add(scaninfo);


            mBulkData.InitPush();
            //清空临时数据
            mBulkDb.SetTempFrame(0, null);

            #region sqlite3 历史数据保存
            CheckProfile();
            mProfileParam.PropertyChanged += MProfileParam_PropertyChanged;
            #endregion


            return base.Start2(beginPos, endPos);
        }
        #region sqlite3 历史数据保存
        string[] profile_propertynames_add = new string[]{
                "PName",
                "OrderNo",
                "Number"
            };
        string[] profile_propertynames_update = new string[]{
                "Target",
                "TolerancePercent"
            };
        /// <summary>
        /// 检查产品信息更新情况,是否需要新加单,或者更新单信息
        /// </summary>
        void CheckProfile()
        {
            if (mHistoryDb.localDb.IsProfileFinished)
            {
                //上一单结束了,添加新单
                AddProfile();
            }
            else
            {
                if (mHistoryDb.localDb.CurrProfile != null)
                {
                    Db_Profile db_Profile = mHistoryDb.localDb.CurrProfile;

                    if ((db_Profile.PName != mProfileParam.PName) ||
                    (db_Profile.OrderNo != mProfileParam.OrderNo) ||
                    (db_Profile.Number != mProfileParam.Number))
                    {
                        //产品名称/订单号/卷号改变了,需要新加单
                        AddProfile();
                    }
                    else
                    {
                        //继续上一次生产!!!!更新订单信息
                        UpdateProfile();
                    }
                }
                else
                {
                    //异常,居然没有订单信息
                    AddProfile();
                }

            }
        }
        void AddProfile()
        {
            mHistoryDb.AddProfile(new Db_Profile()
            {
                PName = mProfileParam.PName,
                OrderNo = mProfileParam.OrderNo,
                Number = mProfileParam.Number,
                Target = mProfileParam.Target,
                TolerancePercent = mProfileParam.TolerancePercent,
                StartTime = DateTime.Now,
                EndTime = DateTime.Now
            });
        }
        void UpdateProfile()
        {
            mHistoryDb.UpdateProfile(new Db_Profile()
            {
                PName = mProfileParam.PName,
                OrderNo = mProfileParam.OrderNo,
                Number = mProfileParam.Number,
                Target = mProfileParam.Target,
                TolerancePercent = mProfileParam.TolerancePercent,
            });
        }
        private void MProfileParam_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (profile_propertynames_add.Contains(e.PropertyName))
            {
                FObjBase.PollModule.Current.Poll_JustOnce(
                    () =>
                    {
                        if (!mHistoryDb.localDb.IsProfileFinished)
                        {
                            mHistoryDb.FinishProfile();
                        }
                        AddProfile();//新
                    }, this, MARKNO_PROFILE_ADD);
            }
            else if (profile_propertynames_update.Contains(e.PropertyName))
            {
                FObjBase.PollModule.Current.Poll_JustOnce(
                    () =>
                    {
                        UpdateProfile();//修改
                    }, this, MARKNO_PROFILE_CHANGED);
            }
        }
        #endregion

        /// <summary>
        /// 停止扫描
        /// </summary>
        public override void Stop()
        {
            #region sqlite3 历史数据保存
            mProfileParam.PropertyChanged -= MProfileParam_PropertyChanged;
            #endregion

            base.Stop();
            //已经停止
            //删除全部
            mScanInfoList.Clear();
        }

        /// <summary>
        /// 周期动作处理
        /// </summary>
        protected override void OnPoll()
        {
            base.OnPoll();
            OnPoll_GetAngle();
        }

        /// <summary>
        /// 获取 缓冲区列表
        /// </summary>
        /// <param name="AsyncDelegate">retData 为 </param>
        /// <param name="AsyncState"></param>
        public void GetBufList(AsyncCBHandler AsyncDelegate, object AsyncState)
        {
            GetBufListReponse p = new GetBufListReponse();

            p.lastbm = LastBM;
            p.firstbm = FirstBM;
            p.datas = mBufList;

            AsyncDelegate(AsyncState, p);
        }
    }
    /// <summary>
    /// 扫描位置检测
    /// 里面的列表会缓存一些旧数据,如1次扫描前的位置信息。
    /// 什么时候删除,由ScanPosDetect 自行决定,使用者不用关心
    /// </summary>
    public class ScanPosDetect 
    {
        /// <summary>
        /// IOStatus 推送出来扫描位置与时间
        /// </summary>
        class PosCell
        {
            public DateTime dt;
            public int position;
            public override string ToString()
            {
                return dt.ToString() + " pos=" + position.ToString();
            }
        }
        class PosCellList
        {
            public int scanNo;
            private List<PosCell> mPosList = new List<PosCell>();
            public void Add(int position, DateTime dt) 
            {
                if (mPosList.Count() > 0) 
                {
                    if (mPosList.Last().position == position) 
                    {
                        mPosList.Last().dt = dt;
                        return;
                    }
                }
                mPosList.Add(new PosCell() { position = position, dt = dt });
            }
            private DateTime GetTime(int position, int idx)
            {
                
                double d_pos = mPosList[idx].position - mPosList[idx + 1].position;
                
                TimeSpan ts = mPosList[idx].dt - mPosList[idx + 1].dt;
                long ticks = (long)((position - mPosList[idx + 1].position) / d_pos * ts.Ticks);
                return mPosList[idx + 1].dt + new TimeSpan(ticks);
            }
            /// <summary>
            /// 获取位置对应的数据点.
            /// 返回
            /// -1:position在列表的前面;
            /// 0:position在列表的里面
            /// 1:position在列表的后面
            /// </summary>
            /// <param name="position"></param>
            /// <param name="dt"></param>
            /// <returns></returns>
            public int GetTime(int position, out DateTime dt)
            {
                dt = DateTime.MinValue;
                //TODO
                if (mPosList.Count < 2)
                    return -1;
                //通过列表最前面的数据,与最后面的数据,确认方向
                DIRECTION direction;
                if (mPosList.First().position < mPosList.Last().position)
                    direction = DIRECTION.FORWARD;
                else
                    direction = DIRECTION.BACKWARD;
                int idx;
                if (direction == DIRECTION.FORWARD)
                {
                    idx = mPosList.FindIndex(p => position <= p.position);
                }
                else
                {
                    idx = mPosList.FindIndex(p => position >= p.position);
                }
                
                if (idx == -1) 
                {
                    //在列表后面
                    dt = GetTime(position, mPosList.Count - 2);
                    return 1;
                }
                else if (idx == 0)
                {
                    //在列表前面
                    dt = GetTime(position, 0);
                    return -1;
                }
                else 
                {
                    //在列表里面
                    dt = GetTime(position, idx - 1);
                    return 0;
                }
            }
        }
        //TODO
        private List<PosCellList> mPosLists = new List<PosCellList>();
        private int lastGetTimeScanNo=-1;
        private int scanNo;//当前扫描号。每扫描一次,scanNo++
        public int ScanNo 
        {
            get {
                return scanNo;
            }
            set {
                if (scanNo != value) 
                {

                    //Clear(ScanNo, -2);

                    scanNo = value;
                }
            }
        }

        /// <summary>
        /// 删除scanNo 偏移 offset 之前的数据
        /// </summary>
        /// <param name="scanNo"></param>
        /// <param name="offset"></param>
        public void Clear(int scanNo,int offset) 
        {
            if (mPosLists.Count == 0)//没数据
                return;

            int index = mPosLists.FindIndex(pl => pl.scanNo == scanNo);
            if (index == -1)
                return;//可能还没出现
            index += offset;
            if (index < 0)
                return;//没有之前的数据
            mPosLists.RemoveRange(0, index + 1);
        }
        public void Clear() 
        {
            mPosLists.Clear();
        }
        public ScanPosDetect()
        {
        }
        
        public void Init(IFlyAD flyad) 
        {
            flyad.IStatusChangedEvent += new IStatusChangedEventHandler(flyad_IStatusChangedEvent);
        }

        void flyad_IStatusChangedEvent(object sender, IStatusChangedEventArgs e)
        {
            PosCellList poslist;
            if (mPosLists.Count == 0)
                mPosLists.Add(new PosCellList() { scanNo = ScanNo });
            else
            {
                poslist = mPosLists.Last();
                if (poslist.scanNo != ScanNo)
                {
                    mPosLists.Add(new PosCellList() { scanNo = ScanNo });
                }
            }
            poslist = mPosLists.Last();

            poslist.Add(e.Position,e.Time);
            
            
            //保持20个

            //if (mPosLists.Count() > 20) 
            //{
            //    mPosLists.RemoveAt(0);
            //}
        }
        
        
        /// <summary>
        /// 获取pos 对应的时间;
        /// 返回 -1 数据在 列表前;
        /// 返回 0 数据在列表内;
        /// 返回 1 数据在列表外;
        /// </summary>
        /// <param name="position">位置</param>
        /// <param name="scanNo">扫描号</param>
        /// <returns></returns>
        public int GetTime(int position, int scanNo, out DateTime dt)
        {
            if (lastGetTimeScanNo != scanNo)
            {
                //TODO
                Clear(lastGetTimeScanNo, -8);
                lastGetTimeScanNo = scanNo;
            }
            dt = DateTime.MinValue;
            if (mPosLists.Count == 0)//没数据
                return 1;

            int d = scanNo - mPosLists[0].scanNo;
            if (d < 0)
            { 
                //发生在过去
                //不正常
                return -2;
            }
            
            int index = mPosLists.FindIndex(pl => pl.scanNo == scanNo);
            if (index == -1)
                return 1;//可能还没出现


            PosCellList poslist = mPosLists[index];
            
            int ret = poslist.GetTime(position, out dt);
            if( (ret == 1) && (index != (mPosLists.Count - 1)))
                return 0;
            else if( (ret == -1) && (index != 0))
                return 0;
            else
                return ret;
        }
    }
}