#define ENABLE_MULTI_TRANS

using AutoMapper;
using FlyADBase.Inc;
using FObjBase;
using GeneralGommunication;
using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;



namespace FlyADBase
{
    public partial class FlyAd2021B2 : IFlyADClient
    {
        public Logger logger = NLog.LogManager.GetCurrentClassLogger();

        #region MARKNO
        const int MARKNO_SET_SAVE = 4;
        const int MARKNO_PARAM_APPLY = 5;
        #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>
        /// 已经从AD盒获取全部当前数据
        /// </summary>
        public bool IsReady { get; private set; }

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

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

        #endregion

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

        /// <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>
        /// 1000ms从AD卡获取(或推送)1个AD值。此AD值只用于调试,定点
        /// </summary>
        public int AD2 { 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; } = 10000;
        /// <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>
        /// timegrid 事件; 大约0.1s更新一次
        /// ad值不会被机架修正
        /// </summary>
        public event TimeGridEventHandler TimeGridEvent;

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

        /// <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();

        public FlyAd2021B2Core Core => core;

        IGeneralComm comm;
        FlyAd2021B2Core core;
        /// <summary>
        /// 不设置服务器
        /// </summary>
        bool bShieldSetValueEx;


        /// <summary>
        /// 最后一次动作时间
        /// </summary>
        DateTime orderGetRunResultTime;
        DateTime orderGetEnc1Time;
        DateTime orderGetEnc2Time;
        DateTime orderGetInTime;


        static string[] propertyName_save;

        List<EventArgs> pushEventArgs = new List<EventArgs>();

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

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

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




            core = new FlyAd2021B2Core();
            core.logger = logger;
            core.Csm.StartMeasure();

            //注册AD2021版的发送动作,把数据通过 comm模块发送出去
            core.SendMsgEvent += (s,msg) =>
            {
                comm?.Write(msg);
            };

            
            core.PushDataEvent += (sender, e) =>
            {
                //这个线程非主线程, 数据接收完,应该快速返回。
                //事件触发,都放在主线程操作。
                //要有线程锁!!!!!
                lock (pushEventArgs)
                {
                    pushEventArgs.Add(e);
                }
            };

            core.PushRunResultEvent += (sender, e) =>
            {
                //这个线程非主线程, 数据接收完,应该快速返回。
                //事件触发,都放在主线程操作。
                lock (pushEventArgs)
                {
                    pushEventArgs.Add(e);
                }
            };
            //避免推送太快,全部推送事件统一周期处理
            PollModule.Current.Poll_Config(onPoll_PushEvent, TimeSpan.FromSeconds(0.1));

            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(() =>
            {
                if (!IsConnected)
                    return;
                if (Surplus < 60000)
                {
                    if (Surplus > 0)
                    {
                        Surplus--;
                        //TODO
                    }
                }
            }, TimeSpan.FromMinutes(60));

            //1分钟,脉冲都没改变, 发送一次获取
            PollModule.Current.Poll_Config(() =>
            {
                if (!IsConnected)
                    return;
                
                if (Now - mTimeGridAdvHelper.positionChangedTime > TimeSpan.FromSeconds(60))
                {
                    if (Now - orderGetEnc1Time > TimeSpan.FromSeconds(60))
                        GetEnc1();
                }

                if (Now - mTimeGridAdvHelper.position2ChangedTime > TimeSpan.FromSeconds(60))
                {
                    if (Now - orderGetEnc2Time > TimeSpan.FromSeconds(60))
                        GetEnc2();
                }

                if (Now - mTimeGridAdvHelper.istatusChangedTime > TimeSpan.FromSeconds(60))
                {
                    if (Now - orderGetInTime > TimeSpan.FromSeconds(60))
                        GetIn();
                }
                
            }, TimeSpan.FromSeconds(1));

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

                }, TimeSpan.FromSeconds(0.5));

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

                    if (DriveStatus != DRIVE_MAN_STATUS.RUNNING)
                        return;

                    if (Now - mTimeGridAdvHelper.positionChangedTime > TimeSpan.FromSeconds(3))
                    {
                        //3秒内脉冲没改变
                        if (Now - orderGetRunResultTime >= TimeSpan.FromSeconds(3))//每3秒检查一次
                        {
                            GetRunResult();
                        }
                    }

                }, TimeSpan.FromSeconds(1));
        }

        void GetRunResult()
        {
            orderGetRunResultTime = Now;
            //不需要处理回复数据。 它会作为事件被处理
            core.GetRunResult(null, null);
        }
        void GetEnc1()
        {
            orderGetEnc1Time = Now;
            core.GetEnc1((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    //这里回复的位置,是32位的。 
                    //不需要设置到1min缓存区。 1ms推送时,肯定也会设置进去缓存。
                    var reponse = (int)retData;
                    Position = reponse;
                    calSpeed.SetPos1(Now, Position);
                    isReadyContext.isReadyGetPos1 = true;
                }), _retData);
            }, null);
        }
        void GetEnc2()
        {
            orderGetEnc2Time = Now;
            core.GetEnc2((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    //这里回复的位置,是32位的。 
                    //不需要设置到1min缓存区。 1ms推送时,肯定也会设置进去缓存。
                    var reponse = (int)retData;
                    Position2 = reponse;
                    calSpeed.SetPos2(Now, Position);
                    isReadyContext.isReadyGetPos2 = true;
                }), _retData);
            }, null);
        }
        void GetIn() 
        {
            orderGetInTime = Now;
            core.GetIn((asyncContext, _retData) =>
            {
                PollModule.Current.Dispatcher.BeginInvoke(new Action<object>((retData) =>
                {
                    //不需要设置到1min缓存区。 1ms推送时,肯定也会设置进去缓存。
                    var reponse = (UInt16)retData;
                    IStatus = reponse;
                    isReadyContext.isReadyGetIn = true;
                }), _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 (e.PropertyName == nameof(IsConnected))
            {
                if (IsConnected)
                {
                    ConnectCnt++;
                    AfterConnected();
                }
                else {
                    isReadyContext.Reset();
                }
                return;
            }

            if (bShieldSetValueEx)
                return;

            if (e.PropertyName == nameof(PosOffset))
            {
                core.SetSysParam_Zero(PosOffset, null, null);
                FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY);
            }
            else if (e.PropertyName == nameof(JogVelocity))
            {
                core.SetSysParam_Jog(JogVelocity, null, null);
                FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY);
            }
            else if (e.PropertyName == nameof(MotorType))
            {
                core.SetSysParam_MotorType(MotorType, null, null);
                FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY);
            }
            else if (e.PropertyName == nameof(Ratio01))
            {
                core.SetSysParam_Ratio01(Ratio01, null, null);
                FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY);
            }
            else if (e.PropertyName == nameof(Ratio02))
            {
                core.SetSysParam_Ratio02(Ratio02, null, null);
                FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY);
            }
        }
        /// <summary>
        /// 参数保存
        /// </summary>
        void SetSysParamApply()
        {
            core.SetSysParam_Apply(null, null);
        }

        void _core_PushRunResultEvent(PushRunResultEventArgs e)
        {
            Now = sysTickContext.ToDateTime(e.SysTick);

            DriveStatus = e.Status;

            isReadyContext.isReadyGetState = true;

            advPushStatus();
        }
        /// <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;
        }



        /// <summary>
        /// 低频处理事件
        /// </summary>
        void onPoll_PushEvent()
        {
            EventArgs[] pushEventArgs;
            lock (this.pushEventArgs) 
            {
                pushEventArgs = this.pushEventArgs.ToArray();
                this.pushEventArgs.Clear();
            }

            foreach (var e in pushEventArgs) {
                if (e is PushDataEventArgs) {
                    _core_PushDataEvent(e as PushDataEventArgs);
                }
                else if (e is PushRunResultEventArgs) 
                {
                    _core_PushRunResultEvent(e as PushRunResultEventArgs);
                }
            }

        }
        void _core_PushDataEvent(PushDataEventArgs e) {

            Now = sysTickContext.ToDateTime(e.SysTick);

            if (e.ENC1 != null)
            {
                if (isReadyContext.isReadyGetPos1)
                {
                    Position = CalPosition(Position, (UInt16)e.ENC1);//脉冲需要16位扩展为32位
                    calSpeed.SetPos1(Now, Position);
                }
            }
            if (e.ENC2 != null)
            {
                if (isReadyContext.isReadyGetPos2)
                {
                    Position2 = CalPosition(Position2, (UInt16)e.ENC2);//脉冲需要16位扩展为32位
                    calSpeed.SetPos2(Now, Position2);
                }
            }
            if (e.In != null)
            {
                IStatus = (UInt16)e.In;
                isReadyContext.isReadyGetIn = true;
            }
            if (e.Out != null)
            {
                OStatus = (UInt16)e.Out;
                isReadyContext.isReadyGetOut = true;
            }


            //脉冲,输入,没准备好,不触发
            if (!isReadyContext.isReadyGetPos1) 
                return;
            if (!isReadyContext.isReadyGetPos2)
                return;
            if (!isReadyContext.isReadyGetIn)
                return;

            advPushData(
                Now, e.AD, e.AD2);
        }
        private void Core_PushDataEvent(object sender, PushDataEventArgs _e)
        {
            //这个线程非主线程, 数据接收完,应该快速返回。
            //事件触发,都放在主线程操作。
            //要有线程锁!!!!!
            lock (pushEventArgs) {
                pushEventArgs.Add(_e);
            }
        }


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

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

                //获取当前全部状态
#if ENABLE_MULTI_TRANS
                core.BuildMultiTrans(() =>{
#endif
                    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);
#if ENABLE_MULTI_TRANS
                    });
#endif
            }
            else
            {
                //参数不保存在设备中, 保存在电脑。
                //参数写入到设备。
#if ENABLE_MULTI_TRANS
                core.BuildMultiTrans(() =>{
#endif
                    core.SetSysParam_MotorType(MotorType, null, null);
                    core.SetSysParam_Ratio01(Ratio01, null, null);
                    core.SetSysParam_Ratio02(Ratio02, null, null);
                    core.SetSysParam_Zero(PosOffset, null, null);
                    core.SetSysParam_Jog(JogVelocity, null, null);
                    core.SetSysParam_Apply(null, null);
#if ENABLE_MULTI_TRANS
                   });
#endif
            }

            GetAccess();


            //连接断开,复位 脉冲计数。。。,时间计数
            isReadyContext.Reset();
            sysTickContext.Reset();
            calSpeed.Reset();
#if ENABLE_MULTI_TRANS
            core.BuildMultiTrans(() =>{
#endif
                GetRunResult();
                GetOut();
                GetIn();
                GetEnc1();
                GetEnc2();
#if ENABLE_MULTI_TRANS
                });
#endif

            advAfterContected();

        }
        void GetAccess()
        {
            //TODO
        }





        #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);
        }
        public void Disconnect() 
        {
            if (comm != null)
            {
                comm.Stop();
                core.ResetMsg();
            }
        }

        void createComm()
        {
            if (Addr.ToLower().StartsWith("com"))
            {
                var _comm = new GComm_SerialPort();
                _comm.Init(new System.IO.Ports.SerialPort()
                {
                    BaudRate = 115200 * 2,
                    DataBits = 8,
                    Parity = System.IO.Ports.Parity.None,
                    StopBits = System.IO.Ports.StopBits.One
                });
                //_comm.logger = logger;
                comm = _comm;
            }
            else
            {
                var _comm = new GComm_TcpClient();
                //_comm.logger = logger;
                comm = _comm;
            }
            comm.DataReceived += Comm_DataReceived;
            comm.PropertyChanged += Comm_PropertyChanged;
            core.ResetMsg();
        }
        void disposeComm()
        {
            comm.DataReceived -= Comm_DataReceived;
            comm.PropertyChanged -= Comm_PropertyChanged;
            core.ResetMsg();
        }
        private void Comm_DataReceived(IGeneralComm sender, byte[] msg)
        {
            core.RecMsg(msg);
        }
        private void Comm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(comm.IsConnected))
            {
                core.ResetMsg();
            }
        }

        /// <summary>
        /// 连接 addr
        /// </summary>
        /// <param name="addr"></param>
        public void Connect(string addr)
        {
            Addr = addr;
            if (comm == null)
            {
                createComm();
            }
            else
            {
                comm.Stop();
                //只要comm.Stop()  IsConnected 就会为 False

                if ((Addr.ToLower().StartsWith("com") && (comm is GComm_SerialPort)) ||
                    (!Addr.ToLower().StartsWith("com") && (comm is GComm_TcpClient)))
                {
                    //不用重建
                }
                else
                {
                    disposeComm();
                    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, (asyncContext, retData) => {
                GetOut();
            }, null);
        }

#region 速度参数
        /// <summary>
        /// 设置速度参数
        /// </summary>
        /// <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 sv, UInt32 atime, UInt32 dtime, UInt32 hspeed1, UInt32 hspeed2)
        {
#if ENABLE_MULTI_TRANS
            core.BuildMultiTrans(() =>{
#endif
                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_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);
#if ENABLE_MULTI_TRANS
                });
#endif
        }
        /// <summary>
        /// 设置速度
        /// </summary>
        /// <param name="velocity"></param>
        public void SetVelocity(UInt32 velocity)
        {
#if ENABLE_MULTI_TRANS
            core.BuildMultiTrans(() =>{
#endif
                core.SetRunParam_V(velocity, null, null);

                core.GetRunParam_V((asyncContext, retData) => Velocity = (UInt32)retData, null);
#if ENABLE_MULTI_TRANS
            });
#endif
        }


        #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;
            orderGetRunResultTime = Now;

            core.RunTo(to, 123, null, null);

        }
        /// <summary>
        /// 归原点
        /// </summary>
        public void Origin()
        {
            DriveOrder = DRIVE_MAN_ORDER.ORIGIN;
            DriveStatus = DRIVE_MAN_STATUS.RUNNING;
            orderGetRunResultTime = 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;
            orderGetRunResultTime = Now;

            core.Stop(null, null);

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

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

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

#endregion
    }


    class FlyAd2021JsonDb
    {
        static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<FlyAd2021B2, FlyAd2021JsonDb>().ReverseMap();
        }));
        public static void Save(string jsonDbPath, FlyAd2021B2 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, FlyAd2021B2 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 AD2Lag = 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 isReadyGetPos1 { get; set; }
        public bool isReadyGetPos2 { 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 (isReadyGetPos1 == false)
            {
                IsReady = false;
                return;
            }
            if (isReadyGetPos2 == 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;
            isReadyGetPos1 = false;
            isReadyGetPos2 = false;
            isReadyGetIn = false;
            isReadyGetOut = false;
        }
    }


}