using AutoMapper;
using FlyAd2021.Inc;
using FlyADBase;
using FObjBase;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace FlyAd2021
{
    public partial class FlyAd2021 : IFlyADClient
    {
        #region MARKNO
        const int MARKNO_SET_SAVE = 4;
        #endregion

        #region INotifyPropertyChanged 成员
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IFlyADClient property
        /// <summary>
        /// 连接成功
        /// </summary>
        public bool IsConnected { get; private set; }

        /// <summary>
        /// 重连次数
        /// </summary>
        public int ConnectCnt { get; private set; }

        /// <summary>
        /// CRC 校正, “没有用,不实现这个功能”
        /// </summary>
        public bool HasCRC { get; set; }

        /// <summary>
        /// 已经从AD盒获取全部当前数据
        /// </summary>
        public bool IsReady { get; private set; }

        /// <summary>
        ///  通过判断systick,判断AD盒是否发生重启;
        ///  BeResetTime为上一次被复位时间, 此为 电脑的时间
        /// </summary>
        public DateTime BeResetTime { get; private set; }

        /// <summary>
        /// 当前在同步状态
        /// </summary>
        public bool IsSync{ get; private set; }


        /// <summary>
        /// 只要connect成功,获取systick被复位,都会从AD盒设备读取参数;
        /// 否则, 设置参数 到 AD盒
        /// </summary>
        public bool IsReadParamFromDev { get; set; }

        /// <summary>
        /// 同步列表,完成后,会删除
        /// </summary>
        public ObservableCollection<SyncOrder> SyncOrders { get; } = new ObservableCollection<SyncOrder>();
        #endregion

        #region IFlyAD property
        /// <summary>
        /// AD盒地址, 当为 TCP通讯时,为 192.168.251.200:20020;
        /// 当为 串口通讯时, 为 COM6
        /// </summary>
        public string Addr { get; set; }

        /// <summary>
        /// 从flyad7 获取 的systick 转换的时间
        /// </summary>
        public DateTime Now { get; private set; }

        /// <summary>
        /// AD盒版本号 版本1 没有systick;版本2 有systick;
        /// 通过通讯读取
        /// </summary>
        public int Version { get; } = 2021;

        /// <summary>
        /// 硬件版本,设置的!!!
        /// </summary>
        public int HardwareVersion { get; set; } = 2021;

        /// <summary>
        /// 脉冲
        /// </summary>
        public int Position { get; private set; }

        /// <summary>
        /// 速度 pps
        /// </summary>
        public int Speed { get; private set; }

        /// <summary>
        /// 纵向脉冲,也叫主轴脉冲
        /// </summary>
        public int Position2 { get; private set; }
        /// <summary>
        /// 纵向速度
        /// </summary>
        public int Speed2 { get; private set; }

        /// <summary>
        /// 1000ms从AD卡获取(或推送)1个AD值。此AD值只用于调试,定点
        /// </summary>
        public int AD { get; private set; }

        /// <summary>
        /// AD值最大值 ,恒定返回65535
        /// </summary>
        public int ADMax => 0xFFFF;

        /// <summary>
        /// 输入口状态,只有12位有效
        /// </summary>
        public UInt16 IStatus { get; private set; } = 0xffff;

        /// <summary>
        /// 输出口状态,只有4位有效
        /// </summary>
        public UInt16 OStatus { get; private set; } = 0xffff;


        /// <summary>
        /// 电机类型
        /// </summary>
        public MOTORTYPE MotorType { get; set; } = MOTORTYPE.SERVO;
        /// <summary>
        /// 脉冲/grid, 最大1000个grid
        /// </summary>
        private UInt16 posofgrid = 10;
        /// <summary>
        /// 脉冲/grid, 最大1000个grid
        /// </summary>
        public UInt16 PosOfGrid
        {
            get { return posofgrid; }
            set
            {
                if (value < 1)
                    value = 1;

                if (posofgrid != value)
                    posofgrid = value;

            }
        }
        /// <summary>
        /// 脉冲比例 分母(电机脉冲)
        /// </summary>
        public UInt16 Ratio01 { get; set; } = 4;
        /// <summary>
        /// 脉冲比例 分子(编码器脉冲)
        /// </summary>
        public UInt16 Ratio02 { get; set; } = 1;


        /// <summary>
        /// 脉冲平移(0位处脉冲值)
        /// </summary>
        public Int16 PosOffset { set; get; }
        /// <summary>
        /// 手动速度 pps
        /// </summary>
        public UInt32 JogVelocity { get; set; } = 5000;

        /// <summary>
        /// runto 速度 pps
        /// </summary>
        public UInt32 Velocity { get; private set; } = 8000;

        /// <summary>
        /// 开始速度 pps
        /// </summary>
        public UInt32 SVelocity { get; private set; } = 200;

        /// <summary>
        /// 加速时间 ms
        /// </summary>
        public UInt32 ATime { get; private set; } = 300;

        /// <summary>
        /// 减速时间 ms
        /// </summary>
        public UInt32 DTime { get; private set; } = 200;

        /// <summary>
        /// 归0 第1段速 pps
        /// </summary>
        public UInt32 HVelocity1 { get; private set; } = 4000;

        /// <summary>
        /// 归0 第2段速 pps
        /// </summary>
        public UInt32 HVelocity2 { get; private set; } = 500;

        /// <summary>
        /// 使用码区状态
        /// </summary>
        public AREA_STATUS AreaStatus { get; private set; }
        /// <summary>
        /// 使用码区出错码
        /// </summary>
        public AREA_ERR AreaRet { get; private set; }

        /// <summary>
        /// 序列码byte[6],  AD盒只需要 6个byte,但flysecute 软件需要 7byte, code[6]=0x06
        /// </summary>
        public byte[] Code { get; private set; } = new byte[7];
        /// <summary>
        /// 系统剩余时间
        /// </summary>
        public int Surplus { get; private set; }
        /// <summary>
        /// 系统授权码access[8]
        /// </summary>
        public byte[] Access { get; private set; } = new byte[8];


        /// <summary>
        /// 当前运行指令
        /// </summary>
        public DRIVE_MAN_ORDER DriveOrder { get; set; } = DRIVE_MAN_ORDER.IDLE;

        /// <summary>
        /// 之前运行动作的状态
        /// </summary>
        public DRIVE_MAN_STATUS DriveStatus { get; set; } = DRIVE_MAN_STATUS.STOP;

        /// <summary>
        /// 同步用标示
        /// </summary>
        public Int32 Marker { get; private set; }

        /// <summary>
        /// 逻辑横向脉冲偏移  Pos1 + Pos1LCShift = Pos1LC
        /// </summary>
        public int Pos1LCShift { get; set; }
        /// <summary>
        /// 逻辑纵向脉冲偏移  Pos2 + Pos2LCShift = Pos2LC
        /// </summary>
        public int Pos2LCShift { get; set; }

        /// <summary>
        /// timegrid 事件; 版本1: 1.28s 一次; 版本2: 0.2s一次, 
        /// ad值不会被机架修正
        /// </summary>
        public event TimeGridEventHandler TimeGridEvent;

        /// <summary>
        /// runto 中,每经过1个grid 推送数据
        /// </summary>
        public event MiniGridEventHandler MiniGridEvent;

        /// <summary>
        /// runto 的 grid事件
        /// </summary>
        public event MiniGridEventHandler GridEvent;

        /// <summary>
        /// 输入状态改变事件
        /// </summary>
        public event IStatusChangedEventHandler IStatusChangedEvent;

        /// <summary>
        /// 指令状态改变事件
        /// </summary>
        public event DriveStatusChangedEventHandler DriveStatusChangedEvent;

        /// <summary>
        /// 脉冲1改变事件
        /// </summary>
        public event PositionChangedEventHandler PositionChangedEvent;

        /// <summary>
        /// 脉冲2改变事件
        /// </summary>
        public event PositionChangedEventHandler Position2ChangedEvent;
        #endregion


        IsReadyContext isReadyContext = new IsReadyContext();
        SysTickContext sysTickContext = new SysTickContext();
        CalSpeed calSpeed = new CalSpeed();

        IFlyAd2021Comm comm;
        FlyAd2021Core core;
        /// <summary>
        /// 不设置服务器
        /// </summary>
        bool bShieldSetValueEx;


        /// <summary>
        /// 用于检测当 为IsRunning时,脉冲是否停了很久。
        /// </summary>
        int last_position = int.MinValue;
        /// <summary>
        /// 最后一次动作时间
        /// </summary>
        DateTime drive_time;

        static string[] propertyName_save;

        string defaultPath = "flyad.json";
        string jsonDbPath;

        static FlyAd2021()
        {
            propertyName_save = FlyAd2021JsonDb.GetMemberNames();
        }

        public FlyAd2021()
        {
            constructor();
            advConstructor();
        }
        void constructor() 
        {
            Now = DateTime.Now;
            sysTickContext.BeReseted += () => BeResetTime = DateTime.Now;




            core = new FlyAd2021Core();

            core.SendMsgEvent += (s) =>
            {
                byte[] msg = core.GetSendMsg();
                if (msg == null)
                    return;
                comm?.SendMsg(msg);
            };

            core.PushDataEvent += Core_PushDataEvent;
            core.PushRunResultEvent += Core_PushRunResultEvent;
            core.StartMeasure();
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, core.OnPoll_TimeOut, TimeSpan.FromSeconds(0.5));


            core.PropertyChanged += Core_PropertyChanged;



            Misc.BindingOperations.SetBinding(core, nameof(core.IsConnected), this, nameof(IsConnected));
            Misc.BindingOperations.SetBinding(isReadyContext, nameof(isReadyContext.IsReady), this, nameof(IsReady));



            this.PropertyChanged += FlyAd2021_PropertyChanged;


            //每隔一个小时,改变剩下时间
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, () =>
            {
                if (!IsConnected)
                    return;
                if (Surplus < 60000)
                {
                    if (Surplus > 0)
                    {
                        Surplus--;
                        core.AddAccess(null, null);
                    }
                }
            }, TimeSpan.FromMinutes(60));


            //每隔1秒发送一次 脉冲,输入输出,状态
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, () =>
            {
                if (!IsConnected)
                    return;
                AfterConnected_GetFastData();

            }, TimeSpan.FromSeconds(60));

            //更新线速度
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD,
                () =>
                {
                    if (calSpeed.Cal(Now, out int speed1, out int speed2))
                    {
                        Speed = speed1;
                        Speed2 = speed2;
                    }

                }, TimeSpan.FromSeconds(1));

            //当状态为Running时,且每秒脉冲没变,需要 查 当前的状态, 也许Stop状态少推送了
            PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD,
                () =>
                {
                    if (!IsConnected)
                        return;
                    if (DriveStatus != DRIVE_MAN_STATUS.RUNNING)
                        return;

                    if (Position != last_position)
                    {
                        last_position = Position;
                        return;
                    }

                    if (DateTime.Now-drive_time>=TimeSpan.FromSeconds(3))//每3秒检查一次
                    {
                        drive_time = DateTime.Now;
                        GetRunResult();
                    }

                }, TimeSpan.FromSeconds(1));



        }

        void GetRunResult() 
        {
            //不需要处理回复数据。 它会作为事件被处理
            core.GetRunResult(null, null);
        }
        /// <summary>
        /// 连接后初始化, 获取快速数据
        /// </summary>
        void AfterConnected_GetFastData() {
            core.GetEncAll((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    var reponse = retData as GetEncAll_Reponse;
                    Position = reponse.pos1;
                    Position2 = reponse.pos2;
                    calSpeed.SetPos1(Now, Position);
                    calSpeed.SetPos2(Now, Position2);
                    isReadyContext.isReadyGetPos = true;
                    advPushPosAll();
                }), _retData);
            }, null);
            core.GetIn((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    var reponse = (UInt16)retData;
                    IStatus = reponse;

                    isReadyContext.isReadyGetIn = true;

                    advPushIn();
                }), _retData);
            }, null);
        }
        void GetOut() {
            core.GetOut((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    var reponse = (UInt16)retData;
                    OStatus = reponse;
                    isReadyContext.isReadyGetOut = true;
                }), _retData);
            }, null);
        }
        private void FlyAd2021_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (bShieldSetValueEx)
                return;

            if (e.PropertyName == nameof(PosOffset)) {
                core.SetSysParam_Zero(PosOffset, null, null);
            }
            else if (e.PropertyName == nameof(JogVelocity))
            {
                core.SetSysParam_Jog(JogVelocity, null, null);
            }
            else if (e.PropertyName == nameof(MotorType))
            {
                core.SetSysParam_MotorType(MotorType, null, null);
            }
            else if (e.PropertyName == nameof(Ratio01)) 
            {
                core.SetSysParam_Ratio01(Ratio01, null, null);
            }
            else if (e.PropertyName == nameof(Ratio02))
            {
                core.SetSysParam_Ratio02(Ratio02, null, null);
            }
            else if (e.PropertyName == nameof(Pos1LCShift))
            {
                core.SyncRun_SetHShift(Pos1LCShift, null, null);
            }
            else if (e.PropertyName == nameof(Pos2LCShift))
            {
                core.SyncRun_SetVShift(Pos2LCShift, null, null);
            }
        }

        private void Core_PushRunResultEvent(object sender, PushRunResultEventArgs _e)
        {
            //这个线程非主线程, 数据接收完,应该快速返回。
            //事件触发,都放在主线程操作。
            PollModule.Current.Dispatcher.BeginInvoke(new Action<PushRunResultEventArgs>((e) =>
            {
                Now = sysTickContext.ToDateTime(e.SysTick);

                Marker = (int)e.Serial;
                DriveStatus = e.Status;

                isReadyContext.isReadyGetState = true;

                advPushStatus();
            }), _e);
        }

        /// <summary>
        /// 计数32位脉冲
        /// </summary>
        /// <param name="lastPostion">上一次脉冲</param>
        /// <param name="enc">低16位脉冲</param>
        /// <returns></returns>
        int CalPosition(int lastPostion, UInt16 enc)
        {
            //Position
            UInt16 lastEnc = (UInt16)(lastPostion & 0xffff);

            Int16 d = (Int16)(enc - lastEnc);

            lastPostion += d;

            return lastPostion;
        }



        private void Core_PushDataEvent(object sender, PushDataEventArgs _e)
        {

            //这个线程非主线程, 数据接收完,应该快速返回。
            //事件触发,都放在主线程操作。
            //要有线程锁!!!!!
            PollModule.Current.Dispatcher.BeginInvoke(new Action<PushDataEventArgs>((e) =>
            {
                Now = sysTickContext.ToDateTime(e.SysTick);
                advPushAd(e.AD);

                if (e.ENC1 != null)
                {
                    if (isReadyContext.isReadyGetPos)
                    {
                        Position = CalPosition(Position, (UInt16)e.ENC1);
                        calSpeed.SetPos1(Now, Position);
                        advPushPos1();
                    }
                }
                if (e.ENC2 != null)
                {
                    if (isReadyContext.isReadyGetPos)
                    {
                        Position2 = CalPosition(Position2, (UInt16)e.ENC2);
                        calSpeed.SetPos2(Now, Position2);
                        advPushPos2();
                    }
                }
                if (e.In != null)
                {
                    IStatus = (UInt16)e.In;
                    isReadyContext.isReadyGetIn = true;
                    advPushIn();
                }
                if (e.Out != null)
                {
                    OStatus = (UInt16)e.Out;
                    isReadyContext.isReadyGetOut = true;
                }
            }), _e);
        }

        private void Core_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(core.IsConnected)) {

                if (core.IsConnected)
                {
                    ConnectCnt++;
                    AfterConnected();
                }
            }
        }


        /// <summary>
        /// 连接后初始化
        /// </summary>
        void AfterConnected()
        {

            if (IsReadParamFromDev)
            {
                //从设备读取参数

                //获取当前全部状态
                core.GetSysParam_Jog((asyncContext, retData) => JogVelocity = (UInt32)retData, null);
                core.GetSysParam_Zero((asyncContext, retData) => PosOffset = (Int16)retData, null);
                core.GetSysParam_MotorType((asyncContext, retData) => MotorType = (MOTORTYPE)retData, null);
                core.GetSysParam_Ratio01((asyncContext, retData) => Ratio01 = (UInt16)retData, null);
                core.GetSysParam_Ratio02((asyncContext, retData) => Ratio02 = (UInt16)retData, null);

                core.GetRunParam_V((asyncContext, retData) => Velocity = (UInt32)retData, null);
                core.GetRunParam_SV((asyncContext, retData) => SVelocity = (UInt32)retData, null);
                core.GetRunParam_AccTime((asyncContext, retData) => ATime = (UInt32)retData, null);
                core.GetRunParam_DecTime((asyncContext, retData) => DTime = (UInt32)retData, null);
                core.GetRunParam_HSpd1((asyncContext, retData) => HVelocity1 = (UInt32)retData, null);
                core.GetRunParam_HSpd2((asyncContext, retData) => HVelocity2 = (UInt32)retData, null);
            }
            else
            {
                //参数不保存在设备中, 保存在电脑。
                //参数写入到设备。
                NotifyPropertyChanged(nameof(MotorType));
                NotifyPropertyChanged(nameof(Ratio01));
                NotifyPropertyChanged(nameof(Ratio02));
                NotifyPropertyChanged(nameof(PosOffset));
                NotifyPropertyChanged(nameof(JogVelocity));
            }

            GetAccess();

            NotifyPropertyChanged(nameof(Pos1LCShift));//写入到AD盒
            NotifyPropertyChanged(nameof(Pos2LCShift));//写入到AD盒

            SyncClear();
            SyncEnd();

            //连接断开,复位 脉冲计数。。。,时间计数
            isReadyContext.Reset();
            sysTickContext.Reset();
            calSpeed.Reset();

            GetRunResult();
            GetOut();
            AfterConnected_GetFastData();
            
            advAfterContected();

        }
        void GetAccess() 
        {
            core.GetAccess((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    GetAccess_Reponse reponse = (GetAccess_Reponse)retData;
                    AreaStatus = reponse.status;
                    AreaRet = reponse.ret;

                    Array.Copy(reponse.code, Code, reponse.code.Length);
                    Code[6] = 0x06;
                    NotifyPropertyChanged(nameof(Code));

                    Surplus = reponse.surplus;

                    Array.Copy(reponse.access, Access, reponse.access.Length);
                    NotifyPropertyChanged(nameof(Access));
                }), _retData);

            }, null);
        }
        
        



        #region Load Save
        public bool Load()
        {
            return Load(defaultPath);
        }

        public bool Load(string jsonDbPath)
        {
            this.jsonDbPath = jsonDbPath;

            this.PropertyChanged -= FlyAD7_PropertyChanged2;

            //不需要设置到 AD盒
            bShieldSetValueEx = true;
            bool ret = FlyAd2021JsonDb.Load(jsonDbPath, this);
            bShieldSetValueEx = false;


            this.PropertyChanged += FlyAD7_PropertyChanged2;

            return ret;

        }

        /// <summary>
        /// 属性改变,自动保存
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FlyAD7_PropertyChanged2(object sender, PropertyChangedEventArgs e)
        {
            if (propertyName_save.Contains(e.PropertyName))
            {
                PollModule.Current.Poll_JustOnce(() =>
                {
                    Save();
                }, this, MARKNO_SET_SAVE);
            }
        }

        public void Save()
        {
            FlyAd2021JsonDb.Save(jsonDbPath, this);
        }
        #endregion

        #region IFlyADClient function
        /// <summary>
        /// 连接
        /// </summary>
        public void Connect() {

            Connect(Addr);
        }

        void createComm() 
        {
            if (Addr.ToLower().StartsWith("com"))
                comm = new FlyAd2021Comm_SerialPort();
            else
                comm = new FlyAd2021Comm_TcpClient();
            comm.RecMsg += (msg) => core.RecMsg(msg);
        }
        /// <summary>
        /// 连接 addr
        /// </summary>
        /// <param name="addr"></param>
        public void Connect(string addr) 
        {
            Addr = addr;
            if (comm == null)
            {
                createComm();
            }
            else {
                comm.Stop();

                if ((Addr.ToLower().StartsWith("com") && (comm is FlyAd2021Comm_SerialPort)) ||
                    (!Addr.ToLower().StartsWith("com") && (comm is FlyAd2021Comm_TcpClient)))
                {
                    //不用重建
                }
                else {
                    createComm();
                }
            }
            
            comm.Addr = addr;
            comm.Start();
        }

        /// <summary>
        /// 重连
        /// </summary>
        public void ReConnect() 
        {
            Connect();
        }
        #endregion

        #region IFlyAD function
        /// <summary>
        /// Out0 = 0, SetOutput(0x0001,0x0000);
        /// Out0 = 1, Out1 = 0, SetOutput(0x0003,0x0001);
        /// </summary>
        /// <param name="mask"></param>
        /// <param name="enable"></param>
        public void SetOutput(UInt16 mask, UInt16 enable)
        {
            core.SetOutPorts(mask, enable, (asyncConetext,_retData)=> {
                GetOut();
            }, null);
        }

        #region 运动参数
        /// <summary>
        /// 设置 运动参数
        /// </summary>
        /// <param name="posOfGrid">脉冲/grid</param>
        /// <param name="motortype">电机类型</param>
        /// <param name="ratio01">脉冲比例 分母(电机脉冲)</param>
        /// <param name="ratio02">脉冲比例 分子(编码器脉冲)</param>
        public void SetSysParam(UInt16 posOfGrid, MOTORTYPE motortype, UInt16 ratio01, UInt16 ratio02)
        {
            core.SetSysParam_MotorType(motortype, null, null);
            core.SetSysParam_Ratio01(ratio01, null, null);
            core.SetSysParam_Ratio02(ratio02, null, null);
        }


        #endregion


        #region 速度参数
        /// <summary>
        /// 设置速度参数
        /// </summary>
        /// <param name="velocity">runto 速度 pps</param>
        /// <param name="sv">开始速度 pps</param>
        /// <param name="atime">加速时间 ms</param>
        /// <param name="dtime">减速时间 ms</param>
        /// <param name="hspeed1">归0 第1段速 pps</param>
        /// <param name="hspeed2">归0 第2段速 pps</param>
        public void SetPosParam(UInt32 velocity, UInt32 sv, UInt32 atime, UInt32 dtime, UInt32 hspeed1, UInt32 hspeed2)
        {
            core.SetRunParam_V(velocity, null, null);
            core.SetRunParam_SV(sv, null, null);
            core.SetRunParam_AccTime(atime, null, null);
            core.SetRunParam_DecTime(dtime, null, null);
            core.SetRunParam_HSpd1(hspeed1, null, null);
            core.SetRunParam_HSpd2(hspeed2, null, null);

            core.GetRunParam_V((asyncContext, retData) => Velocity = (UInt32)retData, null);
            core.GetRunParam_SV((asyncContext, retData) => SVelocity = (UInt32)retData, null);
            core.GetRunParam_AccTime((asyncContext, retData) => ATime = (UInt32)retData, null);
            core.GetRunParam_DecTime((asyncContext, retData) => DTime = (UInt32)retData, null);
            core.GetRunParam_HSpd1((asyncContext, retData) => HVelocity1 = (UInt32)retData, null);
            core.GetRunParam_HSpd2((asyncContext, retData) => HVelocity2 = (UInt32)retData, null);
        }
        /// <summary>
        /// 设置速度
        /// </summary>
        /// <param name="velocity"></param>
        public void SetVelocity(UInt32 velocity)
        {
            core.SetRunParam_V(velocity, null, null);

            core.GetRunParam_V((asyncContext, retData) => Velocity = (UInt32)retData, null);
        }


        #endregion

        #region 密码

        /// <summary>
        /// 输入系统授权码
        /// </summary>
        /// <param name="access">系统授权码access[8]</param>
        public void SetAccess(byte[] access)
        {
            core.CheckAccessCode(access, (asyncContext, retData) =>
            {
                //AreaRet = (AREA_ERR)retData;
                GetAccess();
            }, null);
        }
        /// <summary>
        /// 初始化系统信息区
        /// </summary>
        public void InitArea()
        {
            core.InitArea((asyncContext, retData) =>
            {
                GetAccess();
            }, null);
        }

        #endregion

        #region 运行动作
        /// <summary>
        /// 运行至指定位置
        /// </summary>
        /// <param name="to">脉冲</param>
        public void Runto(int to)
        {
            if (Position == to)
                return;

            DriveOrder = DRIVE_MAN_ORDER.RUNTO;
            DriveStatus = DRIVE_MAN_STATUS.RUNNING;
            drive_time = DateTime.Now;

            core.RunTo(to, 123, null, null);
            
        }
        /// <summary>
        /// 归原点
        /// </summary>
        public void Origin()
        {
            DriveOrder = DRIVE_MAN_ORDER.ORIGIN;
            DriveStatus = DRIVE_MAN_STATUS.RUNNING;
            drive_time = DateTime.Now;

            core.Org(124, null, null);
            
        }
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (DriveOrder == DRIVE_MAN_ORDER.IDLE)
                return;//已经停了

            DriveOrder = DRIVE_MAN_ORDER.STOP;
            DriveStatus = DRIVE_MAN_STATUS.RUNNING;
            drive_time = DateTime.Now;

            core.Stop(null, null);
            
        }
        /// <summary>
        /// 反向,直到撞了限位
        /// </summary>
        public void Backward()
        {
            DriveOrder = DRIVE_MAN_ORDER.BACKWORD;
            DriveStatus = DRIVE_MAN_STATUS.RUNNING;
            drive_time = DateTime.Now;

            core.Backw(125, null, null);
        }
        /// <summary>
        /// 正行,直到撞了限位
        /// </summary>
        public void Forward()
        {
            DriveOrder = DRIVE_MAN_ORDER.FORWORD;
            DriveStatus = DRIVE_MAN_STATUS.RUNNING;
            drive_time = DateTime.Now;

            core.Forw(126, null, null);
        }
        #endregion



        #region 同步控制 同步状态转换规则

        /// <summary>
        /// 进入同步状态
        /// </summary>
        public void SyncBegin()
        {
            core.SyncRun_Start(null, null);
        }
        /// <summary>
        /// 退出同步状态
        /// </summary>
        public void SyncEnd()
        {
            core.SyncRun_Stop(null, null);
        }
        /// <summary>
        /// 清空同步指令
        /// </summary>
        public void SyncClear()
        {
            core.SyncRun_Clear(null, null);
        }

        #endregion
        #region 同步扫描 脚本指令

        /// <summary>
        /// 同步扫描至;
        /// D+0xE0+开始主轴位置+结束主轴位置+结束横向脉冲位置(逻辑位置)+脉冲开关(1B)+命令识标号(4B)
        /// </summary>
        /// <param name="pos2lc_begin"></param>
        /// <param name="pos2lc_end"></param>
        /// <param name="pos1lc"></param>
        /// <param name="hasDataGrid"></param>
        /// <param name="marker"></param>
        public void SyncRunAtLC(int pos2lc_begin, int pos2lc_end, int pos1lc, bool hasDataGrid, Int32 marker)
        {
            //TODO, 怎样解决 grid 数据?
            core.SyncRun_RunAtLC(pos2lc_begin, pos2lc_end, pos1lc, (UInt32)marker, null, null);
        }


        /// <summary>
        /// 位于队列头时运行,归零;
        /// D+0xE1+命令识标号(4B)
        /// </summary>
        /// <param name="marker"></param>
        public void SyncOrigin(Int32 marker)
        {
            core.SyncRun_Origin((UInt32)marker, null, null);
        }


        /// <summary>
        /// 位于队列头时运行,以速度运行至物理位置;
        /// D+0xE2+横向脉冲位置(4B:int32,物理位置)+速度(4B:int32)+脉冲开关(1B)+命令识标号(4B)
        /// </summary>
        /// <param name="pos1"></param>
        /// <param name="velocity"></param>
        /// <param name="hasDataGrid"></param>
        /// <param name="marker"></param>
        public void SyncRunTo(int pos1, UInt32 velocity, bool hasDataGrid, Int32 marker)
        {
            //TODO, 怎样解决 grid 数据?
            core.SyncRun_RunTo(pos1, velocity, (UInt32)marker, null, null);
        }


        /// <summary>
        /// 位于队列头时运行,以速度运行至逻辑位置;
        /// D+0xE3+横向脉冲位置(4B:int32,逻辑位置)+速度(4B:int32)+脉冲开关(1B)+命令识标号(4B)
        /// </summary>
        /// <param name="pos1lc"></param>
        /// <param name="velocity"></param>
        /// <param name="hasDataGrid"></param>
        /// <param name="marker"></param>
        public void SyncRunToLC(int pos1lc, UInt32 velocity, bool hasDataGrid, Int32 marker)
        {
            core.SyncRun_RunToLC(pos1lc, velocity, (UInt32)marker, null, null);
        }


        /// <summary>
        /// 等待,ms;
        /// D+0xE4+毫秒数(4B:int32)+命令识标号(4B)
        /// </summary>
        /// <param name="ms"></param>
        /// <param name="marker"></param>
        public void SyncWait(int ms, Int32 marker)
        {
            //没有实现!!!!
        }

        #endregion

        #endregion
    }


    class FlyAd2021JsonDb
    {
        static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<FlyAd2021, FlyAd2021JsonDb>().ReverseMap();
        }));
        public static void Save(string jsonDbPath, FlyAd2021 src)
        {

            if (string.IsNullOrEmpty(jsonDbPath))
                return;
            var p = Mapper.Map<FlyAd2021JsonDb>(src);
            try
            {
                File.WriteAllText(jsonDbPath, JsonConvert.SerializeObject(p, Formatting.Indented));
            }
            catch
            {
                //异常,没有json 编码失败
            }
        }
        public static bool Load(string jsonDbPath, FlyAd2021 src)
        {
            if (string.IsNullOrEmpty(jsonDbPath))
                return false;

            try
            {
                if (File.Exists(jsonDbPath))
                {
                    string json = File.ReadAllText(jsonDbPath);
                    var p = JsonConvert.DeserializeObject<FlyAd2021JsonDb>(json);
                    Mapper.Map(p, src);
                    return true;
                }
            }
            catch
            {
                //异常,没有json 解码失败
            }
            return false;
        }
        
        
        public string Addr = "192.168.251.10:20006";
        public int GridSmooth = 0;
        public int ADLag = 0;
        public int PosMin = 0;
        public int PosMax = 8900;
        public bool HasPosMaxMin = false;

        //备份参数
        public MOTORTYPE MotorType = MOTORTYPE.SERVO;
        public UInt16 PosOfGrid = 10;
        public UInt16 Ratio01 = 4;
        public UInt16 Ratio02 = 1;
        public Int16 PosOffset = 0;
        public UInt32 JogVelocity = 1000;

        /// <summary>
        /// 硬件版本
        /// version = 1,不支持 systick
        /// version = 2,支持 timeGridAdv
        /// version = 3,伟的大AD盒, io口定义不一样
        /// </summary>
        public int HardwareVersion = 2021;


        public static string[] GetMemberNames()
        {
            var type = typeof(FlyAd2021JsonDb);
            return type.GetMembers().Select(mi => mi.Name).ToArray();
        }

    }

    /// <summary>
    ///  systick 转 DateTime
    ///  不断把 从flyad7 获取的 systick 放入。  以每次systick 的差 加上 上一次 dt_1st, 得到时间。
    ///  如: 当前 获得 systick1, 之前是 systick0,   所以 dt = dt_1st+ (systick1-systick0);  dt_1st=dt;
    ///  
    /// 第1个 dt_1st, 是 从 DateTime.Now. 获取的。
    /// 
    /// 不会管 当前DateTime.Now 与 dt 差多少。 所以如果 flyad7 时间比较慢,可能会出现 3天后, DateTime.Now 与 dt 差了 1个小时。
    /// </summary>
    class SysTickContext
    {
        byte systick_1st = 0;
        DateTime dt_1st = DateTime.MinValue;

        /// <summary>
        /// 
        /// </summary>
        public event Action BeReseted;
        public void Reset()
        {
            dt_1st = DateTime.MinValue;
        }
        /// <summary>
        /// 输入从 flyad7 获取的 timemark, 转换为 本地 DateTime
        /// </summary>
        /// <param name="systick">只有低7位有效</param>
        /// <returns></returns>
        public DateTime ToDateTime(byte systick)
        {
            if (dt_1st == DateTime.MinValue)
            {
                dt_1st = DateTime.Now;
                systick_1st = systick;
                return dt_1st;
            }

            int ms = SystickSub(systick, systick_1st);
            if ((ms > 30))//记录与新的timemark 相差30ms,掉包很严重
            {
                //被复位了
                BeReseted?.Invoke();
            }

            DateTime dt = dt_1st.AddMilliseconds(ms);

            systick_1st = systick;
            dt_1st = dt;
            return dt;
        }

        /// <summary>
        /// 7位 systick1-systick2
        /// </summary>
        /// <param name="systick1"></param>
        /// <param name="systick2"></param>
        /// <returns></returns>
        int SystickSub(byte systick1, byte systick2) 
        {
            if (systick1 >= systick2)
                return systick1 - systick2;
            else {
                return systick1 + 0x80 - systick2;
            }
        }
    }

    class IsReadyContext : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public bool isReadyGetState { get; set; }
        public bool isReadyGetPos { get; set; }
        public bool isReadyGetIn { get; set; }
        public bool isReadyGetOut { get; set; }

        public bool IsReady { get; private set; }

        void updateIsReady()
        {
            if (isReadyGetState == false)
            {
                IsReady = false;
                return;
            }
            if (isReadyGetPos == false)
            {
                IsReady = false;
                return;
            }

            if (isReadyGetIn == false)
            {
                IsReady = false;
                return;
            }
            if (isReadyGetOut == false)
            {
                IsReady = false;
                return;
            }
            IsReady = true;
            return;
        }

        public IsReadyContext()
        {
            this.PropertyChanged += IsReadyContext_PropertyChanged;
        }

        private void IsReadyContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName != nameof(IsReady))
            {
                updateIsReady();
            }
        }
        public void Reset()
        {
            isReadyGetState = false;
            isReadyGetPos = false;
            isReadyGetIn = false;
            isReadyGetOut = false;
        }
    }


}