领胜LDS 键盘AOI检测项目
src/Bro.Device.GTSCard/GTSCardDriver.cs
@@ -6,25 +6,24 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static Bro.Common.Helper.EnumHelper;
namespace Bro.Device.GTSCard
{
    [Device("GTSCard", "固高板卡", EnumHelper.DeviceAttributeType.Device)]
    public class GTSCardDriver : DeviceBase, IMonitor, IMotion
    public class GTSCardDriver : MotionCardBase
    {
        public event Action<DateTime, string, IDevice, MonitorSet> OnMonitorInvoke;
        public event Action<DateTime, IDevice, WarningSet> OnMonitorAlarm;
        public delegate bool OnAxisStartToCheckDelegate(int axisIndex, int startPosition, int endPosition);
        // 异常事件
        public Action<Exception> OnExceptionRaised;
        //public Action<Exception> OnExceptionRaised;
        public GTSCardInitialConfig IConfig
        public GTSCardInitialConfig IIConfig
        {
            get
            {
@@ -32,317 +31,544 @@
            }
        }
        static Dictionary<int, object> axisMoveLockDict = new Dictionary<int, object>();
        /// <summary>
        /// 轴运动开始时的检测,true:有冲突 不可继续执行 false:无冲突,可继续执行
        /// </summary>
        public event OnAxisStartToCheckDelegate OnAxisStartToCheckConfliction;
        /// <summary>
        /// 暂停(线程同步事件)
        /// </summary>
        Dictionary<int, ManualResetEvent> axisImmediatePauseHandleDict = new Dictionary<int, ManualResetEvent>();
        Dictionary<int, CancellationTokenSource> axisMoveCancelDict = new Dictionary<int, CancellationTokenSource>();
        /// <summary>
        /// 运行过程中的线程等待
        /// </summary>
        private Dictionary<int, ManualResetEvent> runningEventDic = new Dictionary<int, ManualResetEvent>();
        private Dictionary<int, AutoResetEvent> axisMovingHandleDict = new Dictionary<int, AutoResetEvent>();
        private ConcurrentDictionary<int, int> axisDestination = new ConcurrentDictionary<int, int>();
        private ObservableCollection<int> _commandAxisList = new ObservableCollection<int>();
        public Action<bool> CommandAxisCountChangedAction = null;
        private Dictionary<int, VelocityPara> velIndexDict = new Dictionary<int, VelocityPara>();
        ManualResetEvent _pauseHandle = new ManualResetEvent(true);
        static object lockObj = new object();
        static object _commandAxisLock = new object();
        static object moveLock = new object();
        /// <summary>
        /// 是否复位标志
        /// </summary>
        bool _isResetting = false;
        /// <summary>
        /// 是否暂停中
        /// </summary>
        bool _isPause = false;
        /// <summary>
        /// 运动轴立即暂停
        /// </summary>
        Dictionary<int, ManualResetEvent> axisImmediatePauseHandleDict = new Dictionary<int, ManualResetEvent>();
        Dictionary<int, bool> axisImmediatePauseFlag = new Dictionary<int, bool>();
        Dictionary<int, bool> axisPauseResumeFlag = new Dictionary<int, bool>();
        //Dictionary<int, CancellationTokenSource> axisMoveCancelDict = new Dictionary<int, CancellationTokenSource>();
        public void SetResetFlag(bool isReset)
        {
            _isResetting = isReset;
        }
        public List<AxisInfo> GetCurrentAxisInfo(params string[] axisName)
        public override List<AxisInfo> GetCurrentAxisInfo(params string[] axisName)
        {
            throw new NotImplementedException();
            List<AxisInfo> axisInfos = new List<AxisInfo>();
            IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled).ForEach(axisSetting =>
            {
                AxisInfo axisInfo = new AxisInfo();
                axisInfo.AxisName = axisSetting.AxisName;
                var axisMovingStatus = AxisStatusList.FirstOrDefault(u => u.AxisIndex == axisSetting.AxisIndex);
                axisInfo.AxisLocation = axisMovingStatus == null ? 0 : Convert.ToDouble(axisMovingStatus.CurPosition);
                axisInfos.Add(axisInfo);
            });
            return axisInfos;
        }
        #region DeviceBase
        protected override void Init()
        {
            InitialMotionCard((short)IConfig.CardNum, IConfig.InitialConfigFilePath);
            InitialMotionCard();
            axisImmediatePauseHandleDict = IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled).ToDictionary(a => a.AxisIndex, a => new ManualResetEvent(true));
            //axisMoveCancelDict = IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled).ToDictionary(a => a.AxisIndex, a => new CancellationTokenSource());
            axisMoveLockDict = IConfig.AxisSettings.ToDictionary(a => a.AxisIndex, a => new object());
            runningEventDic = IConfig.AxisSettings.ToDictionary(a => a.AxisIndex, a => new ManualResetEvent(false));
            axisMovingHandleDict = IConfig.AxisSettings.ToDictionary(a => a.AxisIndex, a => new AutoResetEvent(true));
            axisImmediatePauseHandleDict = IConfig.AxisSettings.ToDictionary(a => a.AxisIndex, a => new ManualResetEvent(true));
            axisMoveCancelDict = IConfig.AxisSettings.ToDictionary(a => a.AxisIndex, a => new CancellationTokenSource());
            axisMoveCancelDict.Values.ToList().ForEach(c =>
            {
                c = new CancellationTokenSource();
            });
            _commandAxisList.CollectionChanged -= CommandAxisList_CollectionChanged;
            _commandAxisList.CollectionChanged += CommandAxisList_CollectionChanged;
        }
        private void CommandAxisList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            CommandAxisCountChangedAction?.Invoke(_commandAxisList.Count > 0);
            //axisMoveCancelDict.Values.ToList().ForEach(c =>
            //{
            //    c = new CancellationTokenSource();
            //});
        }
        protected override void Pause()
        {
            throw new NotImplementedException();
        }
        protected override void Resume()
        {
            throw new NotImplementedException();
        }
        protected override void Start()
        {
            throw new NotImplementedException();
            AllAxisOn();
            base.Start();
        }
        protected override void Stop()
        {
            throw new NotImplementedException();
            AllMoveStop();
            AllAxisOff();
        }
        /// <summary>
        /// 设备 运行(执行 板卡系列的操作的 集合)
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public override ResponseMessage Run(IOperationConfig config)
        {
            ResponseMessage responseMessage = new ResponseMessage();
            if (config is MotionCardOperationConfigBase motionCardOperationConfig)
            {
                foreach (var operationSet in motionCardOperationConfig.OperationCollection)
                {
                    if (operationSet.DelayBefore > 0)
                        Thread.Sleep(operationSet.DelayBefore);
                    responseMessage = RunOperationSet(operationSet);
                    if (!responseMessage.Result)
                    {
                        return responseMessage;
                    }
                    if (operationSet.DelayAfter > 0)
                        Thread.Sleep(operationSet.DelayAfter);
                }
            }
            return responseMessage;
        }
        /// <summary>
        /// 执行 一个系列的操作
        /// </summary>
        private ResponseMessage RunOperationSet(MotionCardOperationSet operationSet)
        {
            ResponseMessage responseMessage = new ResponseMessage();
            // 1.预检查
            if (CurrentState == DeviceState.DSOpen)
            {
                foreach (var preCheck in operationSet.PreCheckIOCollection)
                {
                    _pauseHandle.Wait();
                    IOValue? ioData = null;
                    if (CurrentState == DeviceState.DSOpen)
                    {
                        int timeout = operationSet.PreCheckIOTimeout;
                        while (CurrentState == DeviceState.DSOpen)
                        {
                            Thread.Sleep(10);
                            ioData = MonitorValues.FirstOrDefault(u => u.IONum == preCheck.IOItem.IONum && u.IOType == preCheck.IOItem.IOType)?.Value;//IO 是开、关 从MonitorValues 获取
                            timeout -= 10;
                            if (preCheck.CheckValue == ioData || (operationSet.PreCheckIOTimeout > 0 && timeout < 0))
                            {
                                break;
                            }
                        }
                    }
                    if (preCheck.CheckValue != ioData)
                    {
                        responseMessage.Result = false;
                        responseMessage.Message = $"预检查不通过,配置:{preCheck.GetDisplayText()},当前值:{ioData}";
                        return responseMessage;
                    }
                }
            }
            // 2.板卡运动
            if (CurrentState == DeviceState.DSOpen)
            {
                _pauseHandle.Wait();
                if (CurrentState == DeviceState.DSOpen)
                {
                    responseMessage = MoveToPoint(new MotionOperationCollection() { MovingOps = operationSet.MovingOps });
                    if (!responseMessage.Result)
                    {
                        return responseMessage;
                    }
                }
            }
            // 3.IO输出 不需要超时
            if (CurrentState == DeviceState.DSOpen)
            {
                foreach (var ioOutput in operationSet.IOOutputCollection)
                {
                    _pauseHandle.Wait();
                    if (CurrentState == DeviceState.DSOpen)
                    {
                        WriteOutput((short)ioOutput.IOItem.IONum, ioOutput.CheckValue);
                        //var ioData = MonitorValues.FirstOrDefault(u => u.IONum == ioOutput.IOItem.IONum && u.IOType == ioOutput.IOItem.IOType)?.Value;//IO 是开、关 从MonitorValues 获取
                        //if (ioOutput.CheckValue != ioData)
                        //{
                        //    responseMessage.Result = false;
                        //    responseMessage.Message = $"IO输出不通过,配置:{ioOutput.GetDisplayText()},当前值:{ioData}";
                        //    return responseMessage;
                        //}
                    }
                }
            }
            // 4.IO确认
            if (CurrentState == DeviceState.DSOpen)
            {
                foreach (var ioConfirm in operationSet.IOConfirmCollection)
                {
                    int timeout = operationSet.IOConfirmTimeout;
                    IOValue? ioData = null;
                    while (CurrentState == DeviceState.DSOpen)
                    {
                        Thread.Sleep(10);
                        ioData = MonitorValues.FirstOrDefault(u => u.IONum == ioConfirm.IOItem.IONum && u.IOType == ioConfirm.IOItem.IOType)?.Value;//IO 是开、关 从MonitorValues 获取
                        timeout -= 10;
                        if (ioConfirm.CheckValue == ioData || (operationSet.IOConfirmTimeout > 0 && timeout < 0))
                        {
                            break;
                        }
                    }
                    if (ioConfirm.CheckValue != ioData)
                    {
                        responseMessage.Result = false;
                        responseMessage.Message = $"IO确认不通过,配置:{ioConfirm.GetDisplayText()},当前值:{ioData}";
                        return responseMessage;
                    }
                }
            }
            return responseMessage;
        }
        #endregion
        #region ImmediatePause
        ManualResetEventSlim _pauseHandle = new ManualResetEventSlim(true);
        /// <summary>
        /// 启动立即暂停
        /// </summary>
        public override void SetImmediatePause()
        {
            if (!_isResetting)
            {
                var immediatePauseAxis = IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled && a.IsImmediatePause).Select(u => u.AxisIndex).ToList();
                _pauseHandle.Reset();
                immediatePauseAxis.ForEach(async axisIndex =>
                {
                    axisImmediatePauseHandleDict[axisIndex].Reset();
                    axisImmediatePauseFlag[axisIndex] = true;
                    await MoveStop(axisIndex, 0);//所有轴都暂停
                });
            }
        }
        /// <summary>
        /// 恢复立即暂停
        /// </summary>
        public override void ResetImmediatePause(bool isResumeMoving)
        {
            var immediatePauseAxis = IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled && a.IsImmediatePause).Select(u => u.AxisIndex).ToList();
            _pauseHandle.Set();
            immediatePauseAxis.ForEach(axisIndex =>
            {
                axisImmediatePauseFlag[axisIndex] = false;
                axisImmediatePauseHandleDict[axisIndex].Set();
                if (isResumeMoving)
                {
                    axisPauseResumeFlag[axisIndex] = true;
                }
                else
                {
                    axisPauseResumeFlag[axisIndex] = false;
                }
            });
        }
        #endregion
        #region GTSCard
        public void ClearPosition(short cardNum, short axisNum)
        {
            int ret = GTSCardAPI.GT_SetPos(cardNum, axisNum, 0);
        }
        /// <summary>
        /// Load Motion Card parameter from file
        /// </summary>
        /// <param name="fileName">Invalid Parameter</param>
        /// <returns></returns>
        public void InitialMotionCard(short cardNum, string fileName)
        public void InitialMotionCard()
        {
            var res = GTSCardAPI.GT_LoadConfig(cardNum, fileName);
            if (res != GTSCardAPI.ResultSuccess)
            var res = GTSCardAPI.GT_Open((short)IConfig.CardNum, 0, 1); //打开运动控制器。参数必须为(0,1),不能修改。
            res += GTSCardAPI.GT_LoadConfig((short)IConfig.CardNum, IConfig.InitialConfigFilePath);
            ClearStatus(1, IConfig.AxisSettings.FindAll(u => u.IsAxisEnabled).Count);
            if (res != (short)GTSRetCode.GRCRunOK)
            {
                throw new Exception("板卡载入配置文件异常,错误码:" + res);
                throw new ProcessException("板卡载入配置文件异常,错误码:" + res);
            }
        }
        public override bool AllAxisOn()
        {
            List<Task<bool>> taskList = new List<Task<bool>>(); ;
            // 如果是多个轴的运动 等每个轴开启
            IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled).ForEach(axisNum =>
            {
                var task = AxisOnAsync(axisNum.AxisIndex);
                taskList.Add(task);
            });
            Task.WaitAll(taskList.ToArray());
            var resultOK = taskList.All(u => u.GetAwaiter().GetResult());
            return resultOK;
        }
        public override bool AllAxisOff()
        {
            List<Task<bool>> taskList = new List<Task<bool>>(); ;
            // 如果是多个轴的运动 等每个轴关闭
            IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled).ForEach(axisNum =>
            {
                var task = AxisOffAsync(axisNum.AxisIndex);
                taskList.Add(task);
            });
            Task.WaitAll(taskList.ToArray());
            var resultOK = taskList.All(u => u.GetAwaiter().GetResult());
            return resultOK;
        }
        /// <summary>
        /// 单个轴 点位到点位运动(异步)
        /// 单个轴开启
        /// </summary>
        /// <param name="item">运动对象</param>
        /// <returns></returns>
        public async Task SingleAxisMovingAsync(MovingOption item)
        public override async Task<bool> AxisOnAsync(int axisNum)
        {
            await Task.Run(() =>
            return await Task.Run(() =>
            {
                SingleAxisMoving(item);
                var ret = GTSCardAPI.GT_AxisOn((short)IConfig.CardNum, (short)axisNum);
                return ret == (short)GTSRetCode.GRCRunOK;
            });
        }
        /// <summary>
        /// 单个轴 点位到点位运动(异步)
        /// 单个轴关闭
        /// </summary>
        /// <returns></returns>
        public override async Task<bool> AxisOffAsync(int axisNum)
        {
            return await Task.Run(() =>
            {
                var ret = GTSCardAPI.GT_AxisOff((short)IConfig.CardNum, (short)axisNum);
                return ret == (short)GTSRetCode.GRCRunOK;
            });
        }
        /// <summary>
        /// 点位到点位运动
        /// </summary>
        /// <param name="item">运动对象</param>
        public void SingleAxisMoving(MovingOption item)
        /// <returns>运动控制+停止判断</returns>
        public override ResponseMessage MoveToPoint(IOperationConfig opConfig)
        {
            if (IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == item.AxisIndex)?.IsAxisEnabled ?? false)
            ResponseMessage responseMessage = new ResponseMessage();
            if (opConfig is MotionOperationCollection gtsOperationCollection)
            {
                axisImmediatePauseHandleDict[item.AxisIndex].WaitOne();
                VelocityPara vel = new VelocityPara();
                if (item.VelocityPara.Velocity != 0)
                List<bool> resultList = new List<bool>();
                Parallel.ForEach(gtsOperationCollection.MovingOps, movingOp =>
                {
                    velIndexDict[item.AxisIndex] = vel = item.VelocityPara;
                }
                else
                    axisImmediatePauseFlag[movingOp.AxisIndex] = false;
                    axisPauseResumeFlag[movingOp.AxisIndex] = true;
                    resultList.Add(SingleAxisMoving(movingOp).Result);
                });
                responseMessage.Result = resultList.All(u => u == true);
                if (!responseMessage.Result)
                {
                    vel = velIndexDict[item.AxisIndex];
                    responseMessage.Message = $"点位运动异常";
                }
                string _position = "";
                string motionType = item.MoveMode == EnumHelper.MotorMoveMode.Normal ? (item.IsAbsolute ? "Abs" : "Rel") : item.MoveMode.ToString();
                _position = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")},{item.AxisIndex},{motionType},{GetCmdOrPosition(item.AxisIndex, 0).ToString()},{GetCmdOrPosition(item.AxisIndex, 1).ToString()},{item.Destination},";
                lock (axisMoveLockDict[item.AxisIndex])
                {
                    Task.Run(() =>
                    {
                        lock (_commandAxisLock)
                        {
                            try
                            {
                                if (!_commandAxisList.Contains(item.AxisIndex))
                                {
                                    _commandAxisList.Add(item.AxisIndex);
                                }
                            }
                            catch (Exception)
                            {
                            }
                        }
                    });
                    switch (item.MoveMode)
                    {
                        case EnumHelper.MotorMoveMode.Normal:
                            {
                                if (_isResetting)
                                {
                                    LogAsync(DateTime.Now, "复位中启动运动异常", item.AxisIndex + "启动运动异常");
                                    return;
                                }
                                SetAxisParam(item.AxisIndex, vel);
                                if (item.IsAbsolute)
                                {
                                    MoveAbs(item.AxisIndex, item.Destination, (int)(vel.Velocity * IConfig.AxisVelocityRatio));
                                }
                                else
                                {
                                    MoveRel(item.AxisIndex, item.Destination, (int)(vel.Velocity * IConfig.AxisVelocityRatio));
                                }
                            }
                            break;
                        case EnumHelper.MotorMoveMode.FindOri:
                            {
                                AxisSetting setting = IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == item.AxisIndex);
                                StartHoming(item.AxisIndex, setting.HomeMode, setting.IsHomePositive ? 1 : 0, item.VelocityPara.Dec, item.VelocityPara.Acc, item.VelocityPara.Velocity);
                            }
                            break;
                    }
                    Task.Run(() =>
                    {
                        lock (_commandAxisLock)
                        {
                            try
                            {
                                _commandAxisList.Remove(item.AxisIndex);
                            }
                            catch (Exception)
                            {
                            }
                        }
                    });
                }
                _position += $"{GetCmdOrPosition(item.AxisIndex, 0).ToString()},";
                _position += $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}";
                LogAsync(DateTime.Now, "", _position);
            }
        }
        public int GetCmdOrPosition(int axisNum, int flag = 0)
        {
            int position = 0;
            if (flag == 0)
            {
                GetPosition(axisNum, ref position);
            }
            else
            {
                GetCmdPosition(axisNum, ref position);
            }
            return position;
            return responseMessage;
        }
        /// <summary>
        ///Get single Axis Feedback position
        /// 点到点运动设置参数
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <param name="nPosition">Feedback/Encorde position </param>
        /// <param name="optionPara">运动参数对象</param>
        /// <returns></returns>
        public void GetPosition(int axisNum, ref int nPosition)
        private bool SetAxisParam(MovingOption optionPara)
        {
            lock (lockObj)
            {
                double prfpos = 0; uint pclock = 0;
                var ret = GTSCardAPI.GT_GetPrfPos((short)IConfig.CardNum, (short)axisNum, out prfpos, 1, out pclock);
                if (ret != GTSCardAPI.ResultSuccess)
                {
                    throw new Exception("轴" + axisNum + "获取当前位置异常,错误码:" + ret);
                }
                nPosition = prfpos / IConfig.AxisVelocityRatio;
            }
        }
        public int GetPosition(int axisNum)
        {
            int position = 0;
            if (!IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == axisNum).IsUseCmmdPosition)
            {
                GetPosition(axisNum, ref position);
            }
            else
            {
                GetCmdPosition(axisNum, ref position);
            }
            return position;
        }
        /// <summary>
        ///Get single Axis Command position
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <param name="nPosition">Command position </param>
        /// <returns></returns>
        public void GetCmdPosition(int axisNum, ref int nPosition)
        {
            var ret = GTSCardAPI.APS_get_command(axisNum, ref nPosition);
            if (ret != (Int32)GTSCardParameter.ResultSuccess)
            {
                throw new Exception("轴" + axisNum + "获取当前位置异常,错误码:" + ret);
            }
            nPosition = prfpos / IConfig.AxisVelocityRatio;
        }
        /// <summary>
        /// Set AxisParam
        /// </summary>
        /// <param name="axisNo"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public void SetAxisParam(int axisNum, VelocityPara param)
        {
            int ret = 0;
            GTSCardAPI.TTrapPrm trapprm;
            GTSCardAPI.GT_PrfTrap((short)IConfig.CardNum, (short)axisNum);
            GTSCardAPI.GT_GetTrapPrm((short)IConfig.CardNum, (short)axisNum, out trapprm);
            List<short> resultCode = new List<short>() { 0 };
            GTSCardAPI.TTrapPrm trapprm = new GTSCardAPI.TTrapPrm();
            short axisIndex = short.Parse(optionPara.AxisIndexStr);
            resultCode.Add(GTSCardAPI.GT_PrfTrap((short)IConfig.CardNum, axisIndex));
            resultCode.Add(GTSCardAPI.GT_GetTrapPrm((short)IConfig.CardNum, axisIndex, out trapprm));
            trapprm.smoothTime = 1;
            if (param.Acc != 0)
            {
                trapprm.acc = param.Acc;
            }
            if (param.Dec != 0)
            {
                trapprm.dec = param.Dec;
            }
            ret += GTSCardAPI.GT_SetTrapPrm((short)IConfig.CardNum, (short)axisNum, ref trapprm);
            ret += GTSCardAPI.GT_SetVel((short)IConfig.CardNum, (short)axisNum, param.Velocity * IConfig.AxisVelocityRatio);
            if (ret != (int)APS_Define.ResultSuccess)
            if (optionPara.VelocityPara.Acc != 0)
            {
                throw new Exception("轴" + axisNum + "设置参数异常,错误码:" + ret);
                trapprm.acc = optionPara.VelocityPara.Acc;
            }
            if (optionPara.VelocityPara.Dec != 0)
            {
                trapprm.dec = optionPara.VelocityPara.Dec;
            }
            resultCode.Add(GTSCardAPI.GT_SetTrapPrm((short)IConfig.CardNum, axisIndex, ref trapprm));
            if (optionPara.VelocityPara.Velocity != 0)
            {
                resultCode.Add(GTSCardAPI.GT_SetVel((short)IConfig.CardNum, axisIndex, optionPara.VelocityPara.Velocity * IConfig.AxisVelocityRatio));
            }
            var resultOK = resultCode.All(u => u == (short)GTSRetCode.GRCRunOK);
            if (!resultOK)
            {
                throw new ProcessException("轴" + optionPara.AxisIndex + "设置参数异常,错误码:" + string.Join(",", resultCode));
            }
            return resultOK;
        }
        TaskFactory taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.LongRunning);
        /// <summary>
        /// 单个轴 运动(点到点 jog 回零...)
        /// </summary>
        /// <param name="optionPara">运动参数对象</param>
        public override Task<bool> SingleAxisMoving(MovingOption optionPara)
        {
            return Task.Run(() =>
            {
                bool isSuccessAndStop = false;
                do
                {
                    axisImmediatePauseHandleDict[optionPara.AxisIndex].WaitOne();
                    if (!axisPauseResumeFlag[optionPara.AxisIndex])
                        return true;
                    try
                    {
                        if (IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == optionPara.AxisIndex)?.IsAxisEnabled ?? false)
                        {
                            string motionType = optionPara.MoveMode == EnumHelper.MotionMode.Normal ? (optionPara.IsAbsolute ? "Abs" : "Rel") : optionPara.MoveMode.ToString();
                            switch (optionPara.MoveMode)
                            {
                                case MotionMode.Normal:
                                    {
                                        if (_isResetting)
                                        {
                                            LogAsync(DateTime.Now, "复位中启动运动异常", optionPara.AxisIndex + "启动运动异常");
                                            return false;
                                        }
                                        if (optionPara.IsAbsolute)
                                        {
                                            isSuccessAndStop = P2PMoveAbs(optionPara);
                                        }
                                        //else
                                        //{
                                        //    isSuccessAndStop = P2PMoveRel(optionPara);
                                        //}
                                    }
                                    break;
                                case MotionMode.FindOri:
                                    {
                                        //isSuccessAndStop = GoHome(optionPara);
                                        isSuccessAndStop = P2PGoHome(optionPara);
                                    }
                                    break;
                                case MotionMode.Jog:
                                    {
                                        isSuccessAndStop = JogMove(optionPara);
                                    }
                                    break;
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        isSuccessAndStop = false;
                        LogAsync(DateTime.Now, $"轴{optionPara.AxisIndex}运动异常", ex.GetExceptionMessage());
                    }
                } while (axisImmediatePauseFlag[optionPara.AxisIndex]);
                return isSuccessAndStop;
            });
        }
        /// <summary>
        /// 获取规划位置(要去的位置)
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <returns></returns>
        public double GetPrfPosition(int axisNum)
        {
            double position = 0;
            double prfpos = 0; uint pclock = 0;
            var ret = GTSCardAPI.GT_GetPrfPos((short)IConfig.CardNum, (short)axisNum, out prfpos, 1, out pclock);
            if (ret != (short)GTSRetCode.GRCRunOK)
            {
                throw new ProcessException("轴" + axisNum + "获取规划位置异常,错误码:" + ret);
            }
            //var AxisRatio = IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == axisNum) == null ? 1 : IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == axisNum).AxisRatio;
            position = prfpos;
            return position;
        }
        /// <summary>
        /// 获取目前当前位置
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <returns></returns>
        public double GetPosition(int axisNum)
        {
            lock (moveLock)
            {
                double position = 0;
                double pPos = 0;
                var ret = GTSCardAPI.GT_GetPrfPos((short)IConfig.CardNum, (short)axisNum, out pPos, 1, out uint pclock);
                if (ret != (short)GTSRetCode.GRCRunOK)
                {
                    throw new ProcessException("轴" + axisNum + "获取目标位置异常,错误码:" + ret);
                }
                //var AxisRatio = IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == axisNum) == null ? 1 : IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == axisNum).AxisRatio;
                position = pPos;
                return position;
            }
        }
        /// <summary>
        /// 获取规划速度
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <returns>速度脉冲</returns>
        public double GetPrfVelocity(int axisNum)
        {
            double prfVel = 0;
            uint pclock = 0;
            var ret = GTSCardAPI.GT_GetPrfVel((short)IConfig.CardNum, (short)axisNum, out prfVel, 1, out pclock);
            if (ret != (short)GTSRetCode.GRCRunOK)
            {
                throw new ProcessException("轴" + axisNum + "获取规划速度异常,错误码:" + ret);
            }
            return prfVel;
        }
        /// <summary>
        /// 获取当前速度
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <returns>速度脉冲</returns>
        public double GetVelocity(int axisNum)
        {
            double vel = 0;
            var ret = GTSCardAPI.GT_GetVel((short)IConfig.CardNum, (short)axisNum, out vel);
            if (ret != (short)GTSRetCode.GRCRunOK)
            {
                throw new ProcessException("轴" + axisNum + "获取当前速度异常,错误码:" + ret);
            }
            return vel;
        }
        /// <summary>
@@ -352,643 +578,410 @@
        /// <param name="nDirection">Motion Direction 0: Negative, 1: Positive</param>
        /// <param name="nMaxVel">max velocity</param>
        /// <returns></returns>
        public bool StartJog(int axisNum, int nDirection, int velocity)
        public bool JogMove(MovingOption optionPara)
        {
            GTSCardAPI.TJogPrm jogprm = new GTSCardAPI.TJogPrm();
            short rtn = GTSCardAPI.GT_PrfJog((short)IConfig.CardNum, (short)axisNum);
            jogprm.acc = 1;
            jogprm.dec = 1;
            GTSCardAPI.GT_SetJogPrm((short)IConfig.CardNum, (short)axisNum, ref jogprm);//设置jog运动参数
            GTSCardAPI.GT_SetVel((short)IConfig.CardNum, (short)axisNum, velocity);//设置目标速度
            int ret = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (axisNum - 1));//更新轴运动
            if (ret != (int)APS_Define.ResultSuccess)
            try
            {
                GTSCardAPI.TJogPrm jogprm = new GTSCardAPI.TJogPrm();
                short ret = 0;
                int repeatTime = 100;
                do
                {
                    ret = GTSCardAPI.GT_PrfJog((short)IConfig.CardNum, (short)optionPara.AxisIndex);
                    jogprm.acc = optionPara.VelocityPara.Acc;
                    jogprm.dec = optionPara.VelocityPara.Dec;
                    ret = GTSCardAPI.GT_SetJogPrm((short)IConfig.CardNum, (short)optionPara.AxisIndex, ref jogprm);//设置jog运动参数
                    ret = GTSCardAPI.GT_SetVel((short)IConfig.CardNum, (short)optionPara.AxisIndex, optionPara.VelocityPara.Velocity);//设置目标速度
                    ret = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (optionPara.AxisIndex - 1));//更新轴运动
                    if (ret != (short)GTSRetCode.GRCRunOK)
                    {
                        LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "JogMove异常", "错误码:" + ret + ";" + "重试次数:" + repeatTime);
                        Thread.Sleep(10);
                    }
                    repeatTime--;
                } while (ret != (short)GTSRetCode.GRCRunOK && repeatTime > 0);
                return (ret == (short)GTSRetCode.GRCRunOK);
            }
            catch (Exception ex)
            {
                AllMoveStop(true);
                OnExceptionOccured?.Invoke(DateTime.Now, ex);
                return false;
            }
            return true;
        }
        /// <summary>
        /// Set Single Axis Do stop Jog Move
        /// </summary>
        /// <param name="axisNum">AxisNo</param>
        /// <returns></returns>
        public bool StopJog(int axisNum)
        {
            MoveStop();
            return IsStop((short)IConfig.CardNum, (short)axisNum);
        }
        /// <summary>
        /// Set Single Axis Do Rel Move
        /// 相对位置运动
        /// </summary>
        /// <param name="axisNum">AxisNo</param>
        /// <param name="nDistance">run distance</param>
        /// <returns></returns>
        public void MoveRel(int axisNum, int nDistance, int nMaxVel)
        public bool P2PMoveRel(MovingOption optionPara)
        {
            try
            {
                if (_isResetting)
                {
                    LogAsync(DateTime.Now, "复位过程异常", "轴" + optionPara.AxisIndex + "试图在复位过程中运动");
                    throw new ProcessException("轴" + optionPara.AxisIndex + "试图在复位过程中运动");
                }
                int repeatTime = 30;
                while (CurrentState != EnumHelper.DeviceState.DSOpen && repeatTime > 0)
                {
                    Thread.Sleep(10);
                    repeatTime--;
                }
                if (CurrentState == EnumHelper.DeviceState.DSExcept)
                {
                    LogAsync(DateTime.Now, "板卡异常状态", "轴" + axisNum + "试图异常状态运动");
                    return;
                    LogAsync(DateTime.Now, "板卡异常状态", "轴" + optionPara.AxisIndex + "试图异常状态运动");
                    return false;
                }
                if (CurrentState != EnumHelper.DeviceState.DSOpen)
                {
                    return;
                    LogAsync(DateTime.Now, "非正常状态异常", "轴" + optionPara.AxisIndex + "试图在非正常状态运动");
                    throw new ProcessException("轴" + optionPara.AxisIndex + "试图在非正常状态运动", null);
                }
                int currentPosition = GetPosition(axisNum);
                if (OnAxisStartToCheckConfliction != null && OnAxisStartToCheckConfliction.Invoke(axisNum, currentPosition, currentPosition + nDistance))
                LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "开始运动", "目标坐标:" + optionPara.Destination);
                short ret = 0;
                bool isSuccessSetAxisParam = false;
                int currentPosition = (int)GetPosition(optionPara.AxisIndex);
                int dPosition = optionPara.Destination + currentPosition;
                int timeout = optionPara.MovingTimeout;
                var AxisRatio = IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == optionPara.AxisIndex) == null ? 1 : IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == optionPara.AxisIndex).AxisRatio;
                while (CurrentState == DeviceState.DSOpen)
                {
                    return;
                    //设置 运动参数
                    isSuccessSetAxisParam = SetAxisParam(optionPara);
                    ret = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)optionPara.AxisIndex, (int)(dPosition));// 设置目的位置
                    ret = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (optionPara.AxisIndex - 1));//更新运动
                    if (ret != (short)GTSRetCode.GRCRunOK)
                    {
                        LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "APS_absolute_move异常", "错误码:" + ret + ";" + "重试次数:" + repeatTime);
                        Thread.Sleep(50);
                    }
                    timeout -= 50;
                    if ((ret == (short)GTSRetCode.GRCRunOK && isSuccessSetAxisParam) || (optionPara.MovingTimeout > 0 && timeout < 0))
                    {
                        break;
                    }
                }
                int ret = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)axisNum, nDistance * IConfig.AxisVelocityRatio);
                ret += GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (axisNum - 1));
                if (ret != (Int32)APS_Define.ResultSuccess)
                //运动开始后 检查运动是否停止
                bool isStop = false;
                repeatTime = 1000;
                do
                {
                    throw new Exception("轴" + axisNum + "启动相对运动异常,错误码:" + ret);
                }
                    Thread.Sleep(50);
                    isStop = IsStop((short)optionPara.AxisIndex);
                    repeatTime--;
                } while (!isStop && repeatTime > 0);
                RunFinish(axisNum, false);
                return (ret == (short)GTSRetCode.GRCRunOK) && isStop;
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(ex);
                AllMoveStop(true);
                OnExceptionOccured?.Invoke(DateTime.Now, ex);
                return false;
            }
        }
        /// <summary>
        ///  Set Single Axis Do Absolute Move
        ///  绝对位置运动
        /// </summary>
        /// <param name="axisNum">AxisNo</param>
        /// <param name="nDistance">run distance</param>
        /// <param name="nMaxVel">max velocity</param>
        /// <returns></returns>
        public void MoveAbs(int axisNum, int nPosition, int nMaxVel)
        /// <param name="optionPara">运动参数对象</param>
        public bool P2PMoveAbs(MovingOption optionPara)
        {
            try
            {
            ReMove:
                MoveAbsAsync(axisNum, nPosition);
                axisImmediatePauseHandleDict[optionPara.AxisIndex].WaitOne();
                var runFinish = Task.Run(() => RunFinish(axisNum), axisMoveCancelDict[axisNum].Token);
                try
                if (_isResetting)
                {
                    runFinish.Wait(axisMoveCancelDict[axisNum].Token);
                    LogAsync(DateTime.Now, "复位过程异常", "轴" + optionPara.AxisIndex + "试图在复位过程中运动");
                    throw new ProcessException("轴" + optionPara.AxisIndex + "试图在复位过程中运动");
                }
                catch (OperationCanceledException ex)
                int repeatTime = 30;
                while (CurrentState != EnumHelper.DeviceState.DSOpen && repeatTime > 0)
                {
                    goto ReMove;
                    Thread.Sleep(10);
                    repeatTime--;
                }
                if (CurrentState == EnumHelper.DeviceState.DSExcept)
                {
                    LogAsync(DateTime.Now, "板卡异常状态", "轴" + optionPara.AxisIndex + "试图异常状态运动");
                    return false;
                }
                //// 删除记录
                //if (currentCommandDic.ContainsKey(axisNum))
                //{
                //    currentCommandDic.TryRemove(axisNum, out int[] temp);
                //}
                if (CurrentState != EnumHelper.DeviceState.DSOpen)
                {
                    LogAsync(DateTime.Now, "非正常状态异常", "轴" + optionPara.AxisIndex + "试图在非正常状态运动");
                    throw new ProcessException("轴" + optionPara.AxisIndex + "试图在非正常状态运动", null);
                }
                LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "开始运动", "目标坐标:" + optionPara.Destination);
                short ret = 0;
                bool isSuccessSetAxisParam = false;
                int timeout = optionPara.MovingTimeout;
                //var AxisRatio = IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == optionPara.AxisIndex) == null ? 1 : IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == optionPara.AxisIndex).AxisRatio;
                while (CurrentState == DeviceState.DSOpen && !_isPause)
                {
                    //设置 运动参数
                    isSuccessSetAxisParam = SetAxisParam(optionPara);
                    ret = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)optionPara.AxisIndex, (int)(optionPara.Destination));// 设置目标位置
                    ret = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (optionPara.AxisIndex - 1));//更新运动
                    if (ret != (short)GTSRetCode.GRCRunOK)
                    {
                        LogAsync(DateTime.Now, "轴" + optionPara.AxisIndex + "APS_absolute_move异常", "错误码:" + ret + ";" + "重试次数:" + repeatTime);
                        Thread.Sleep(50);
                    }
                    timeout -= 50;
                    if ((ret == (short)GTSRetCode.GRCRunOK && isSuccessSetAxisParam) || (optionPara.MovingTimeout > 0 && timeout < 0))
                    {
                        break;
                    }
                }
                bool isStop = false;
                repeatTime = 1000;
                while (!isStop && repeatTime > 0)
                {
                    Thread.Sleep(50);
                    isStop = IsStop((short)optionPara.AxisIndex);
                    repeatTime--;
                }
                return (ret == (short)GTSRetCode.GRCRunOK) && isStop;
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(ex);
            }
        }
        static object moveLock = new object();
        /// <summary>
        /// 绝对运动(异步)
        /// </summary>
        /// <param name="axisNum"></param>
        /// <param name="nPosition"></param>
        public void MoveAbsAsync(int axisNum, int nPosition)
        {
            try
            {
                lock (moveLock)
                {
                    axisImmediatePauseHandleDict[axisNum].WaitOne();
                    _pauseHandle.WaitOne();
                    int currentPosition = GetPosition(axisNum);
                    if (OnAxisStartToCheckConfliction != null && OnAxisStartToCheckConfliction.Invoke(axisNum, currentPosition, nPosition))
                    {
                        return;
                    }
                    if (_isResetting)
                    {
                        LogAsync(DateTime.Now, "复位过程异常", "轴" + axisNum + "试图在复位过程中运动");
                        throw new Exception("轴" + axisNum + "试图在复位过程中运动");
                    }
                    int repeatTime = 30;
                    while (CurrentState != EnumHelper.DeviceState.DSOpen && repeatTime > 0)
                    {
                        Thread.Sleep(10);
                        repeatTime--;
                    }
                    if (CurrentState == EnumHelper.DeviceState.DSExcept)
                    {
                        LogAsync(DateTime.Now, "板卡异常状态", "轴" + axisNum + "试图异常状态运动");
                        return;
                    }
                    if (CurrentState != EnumHelper.DeviceState.DSOpen)
                    {
                        LogAsync(DateTime.Now, "非正常状态异常", "轴" + axisNum + "试图在非正常状态运动");
                        throw new Exception("轴" + axisNum + "试图在非正常状态运动", null);
                    }
                    int ret = 0;
                    repeatTime = 50;
                    do
                    {
                        LogAsync(DateTime.Now, "轴" + axisNum + "启动运动", "目标坐标:" + nPosition);
                        ret = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)axisNum, nPosition * IConfig.AxisVelocityRatio);
                        ret += GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (axisNum - 1));
                        if (ret != (Int32)APS_Define.ResultSuccess)
                        {
                            LogAsync(DateTime.Now, "轴" + axisNum + "APS_absolute_move异常", "返回值:" + ret + ";重试次数:" + repeatTime);
                            Thread.Sleep(50);
                        }
                        repeatTime--;
                    } while (ret != (Int32)APS_Define.ResultSuccess && repeatTime > 0);
                    if (ret != (Int32)APS_Define.ResultSuccess)
                    {
                        LogAsync(DateTime.Now, "轴" + axisNum + "启动绝对运动异常", "错误码:" + ret);
                        throw new Exception("轴" + axisNum + "启动绝对运动异常,错误码:" + ret);
                    }
                }
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(ex);
            }
        }
        Dictionary<int, AxisMovingStay> _axisStayDict = new Dictionary<int, AxisMovingStay>();
        private async void MoveAbsStay(int axisNum)
        {
            await Task.Run(() =>
            {
                while (CurrentState != EnumHelper.DeviceState.DSClose)
                {
                    if (!_axisStayDict.ContainsKey(axisNum))
                    {
                        _axisStayDict[axisNum] = new AxisMovingStay();
                    }
                    _axisStayDict[axisNum].MoveHandle.WaitOne();
                    MoveAbsAsync(axisNum, _axisStayDict[axisNum].Position);
                    _axisStayDict[axisNum].MoveSendHandle.Set();
                }
            });
        }
        /// <summary>
        /// 动作结束
        /// </summary>
        /// <param name="axisNum"></param>
        /// <returns></returns>
        public void RunFinish(int axisNum, bool isAbs = true)
        {
            MOTION_IO_STATUS MotionIoSts = new MOTION_IO_STATUS();
            MOTION_BOOL_STATUS MotionSts = new MOTION_BOOL_STATUS();
            var axisSetting = IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == axisNum);
            int startTime = System.Environment.TickCount;
            //MDN信号
            if (axisSetting.IsUseMDNStopSignal)
            {
                while (GetMotionIoStatus(axisNum, ref MotionIoSts) && GetMotionStatus(axisNum, ref MotionSts))
                {
                    if (MotionIoSts.EMG)
                    {
                        LogAsync(DateTime.Now, "轴" + axisNum + "急停", "");
                        throw new Exception("轴" + axisNum + "急停");
                    }
                    if (MotionSts.MDN)
                    {
                        int stopCode = -1;
                        APS168.APS_get_stop_code(axisNum, ref stopCode);
                        LogAsync(DateTime.Now, "轴" + axisNum + "StopCode:", stopCode.ToString());
                        //0 正常停止 4 正极限 5 负极限
                        if (new List<int>() { 0, 4, 5 }.Contains(stopCode))
                        {
                            if (new List<int>() { 4, 5 }.Contains(stopCode) && axisSetting.IsUseWarning)
                            {
                                MoveStop(true);
                                OnExceptionRaised?.Invoke(new Exception("轴" + axisNum + "运动至极限位置,stopCode:" + stopCode.ToString(), null));
                                return;
                            }
                            if (IConfig.ActionAfterDelay > 0)
                            {
                                Thread.Sleep(IConfig.ActionAfterDelay);
                            }
                            int feedBack = 0;
                            GetPosition(axisNum, ref feedBack);
                            LogAsync(DateTime.Now, "轴" + axisNum + "运动结束", "当前位置:" + feedBack);
                            break;
                        }
                    }
                }
            }
            else    //INP信号
            {
                while (GetMotionIoStatus(axisNum, ref MotionIoSts) && GetMotionStatus(axisNum, ref MotionSts))
                {
                    if (MotionIoSts.EMG)
                    {
                        LogAsync(DateTime.Now, "轴" + axisNum + "急停", "");
                        throw new Exception("轴" + axisNum + "急停");
                    }
                    if (MotionSts.MDN && MotionIoSts.INP)
                    {
                        int stopCode = -1;
                        APS168.APS_get_stop_code(axisNum, ref stopCode);
                        LogAsync(DateTime.Now, "轴" + axisNum + "StopCode:", stopCode.ToString());
                        //0 正常停止 4 正极限 5 负极限
                        if (new List<int>() { 0, 4, 5 }.Contains(stopCode))
                        {
                            if (new List<int>() { 4, 5 }.Contains(stopCode) && axisSetting.IsUseWarning)
                            {
                                MoveStop(true);
                                OnExceptionRaised?.Invoke(new AMPRunException("轴" + axisNum + "运动至极限位置,stopCode:" + stopCode.ToString(), null));
                                return;
                            }
                            if (IConfig.ActionAfterDelay > 0)
                            {
                                Thread.Sleep(IConfig.ActionAfterDelay);
                            }
                            int feedBack = 0;
                            GetPosition(axisNum, ref feedBack);
                            LogAsync(DateTime.Now, "轴" + axisNum + "运动结束", "当前位置:" + feedBack);
                            break;
                        }
                    }
                }
                AllMoveStop(true);
                OnExceptionOccured?.Invoke(DateTime.Now, ex);
                return false;
            }
        }
        /// <summary>
        /// Stop single axis move
        /// 某个轴运动停止
        /// </summary>
        /// <param name="axisNum">axisNo</param>
        /// <param name="option">0表示平滑停止,1表示紧急停止</param>
        /// <returns></returns>
        public void MoveStop(int axisNum, int option)
        public override async Task<bool> MoveStop(int axisNum, int option)
        {
            int ret = GTSCardAPI.GT_Stop((short)IConfig.CardNum, 1 << (axisNum - 1), option);
            if (ret != (Int32)APS_Define.ResultSuccess)
            return await Task.Run(() =>
            {
                LogAsync(DateTime.Now, "轴" + axisNum + "运动停止异常", "错误码:" + ret);
                throw new Exception("轴" + axisNum + "运动停止异常,错误码:" + ret);
            }
            else
            {
                LogAsync(DateTime.Now, "轴" + axisNum + "运动停止", "");
            }
        }
        static object motionIOLock = new object();
        /// <summary>
        /// Get single Axis  Motion_Io status and motion status
        /// </summary>
        /// <param name="nAxis"></param>
        /// <param name="nMotionSts"></param>
        /// <returns></returns>
        public bool GetMotionStatus(int nAxis, ref MOTION_BOOL_STATUS strcMotionSts)
        {
            lock (motionIOLock)
            {
                int nMotionSts = 0;
                nMotionSts = APS168.APS_motion_status(nAxis);
                if (nMotionSts < 0)
                bool isStop = false;
                if (option == 1)
                {
                    return false;
                    StateChange(EnumHelper.DeviceState.DSExcept);
                }
                strcMotionSts.ASTP = Convert.ToBoolean(nMotionSts & (1 << (int)MOTION_STATUS.ASTP));
                strcMotionSts.HMV = Convert.ToBoolean(nMotionSts & (1 << (int)MOTION_STATUS.HMV));
                strcMotionSts.MDN = Convert.ToBoolean(nMotionSts & (1 << (int)MOTION_STATUS.MDN));
                strcMotionSts.DIR = Convert.ToBoolean(nMotionSts & (1 << (int)MOTION_STATUS.DIR));
            }
            return true;
        }
        /// <summary>
        /// Get single Axis  Motion_Io status and motion status
        /// </summary>
        /// <param name="nAxis"></param>
        /// <param name="nMotionIoSts"></param>
        /// <returns></returns>
        public bool GetMotionIoStatus(int nAxis, ref MOTION_IO_STATUS strcMotionIoSts)
        {
            lock (motionIOLock)
            {
                //Thread.Sleep(15);
                int nMotionIoSts = 0;
                nMotionIoSts = APS168.APS_motion_io_status(nAxis);
                if (nMotionIoSts < 0)
                var ret = GTSCardAPI.GT_Stop((short)IConfig.CardNum, 1 << (axisNum - 1), option);
                if (ret != (short)GTSRetCode.GRCRunOK)
                {
                    return false;
                    LogAsync(DateTime.Now, "轴" + axisNum + "运动停止异常", "错误码:" + ret);
                    throw new ProcessException("轴" + axisNum + "运动停止异常,错误码:" + ret);
                }
                bool isAlarm = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.ALM));
                if (strcMotionIoSts.ALM != isAlarm)
                else
                {
                    strcMotionIoSts.ALM = isAlarm;
                    OnMotionAlarm?.Invoke(nAxis, isAlarm);
                    LogAsync(DateTime.Now, "轴" + axisNum + "运动停止", "");
                }
                int repeatTime = 100;
                do
                {
                    Thread.Sleep(10);
                    isStop = IsStop((short)axisNum);
                    repeatTime--;
                } while (!isStop && repeatTime > 0);
                strcMotionIoSts.PEL = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.PEL));
                strcMotionIoSts.MEL = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.MEL));
                strcMotionIoSts.ORG = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.ORG));
                strcMotionIoSts.EMG = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.EMG));
                strcMotionIoSts.INP = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.INP));
                strcMotionIoSts.SVON = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.SVON));
            }
            return true;
        }
        public void MoveStop(bool emergencyStop = false)
        {
            int option = 0;
            if (emergencyStop)
            {
                option = 1;
                StateChange(EnumHelper.DeviceState.DSExcept);
            }
            IConfig.AxisSettings.Where(a => a.IsAxisEnabled).ToList().ForEach(axisNum =>
            {
                MoveStop(axisNum.AxisIndex, option);
                return (ret == (short)GTSRetCode.GRCRunOK) && isStop;
            });
        }
        /// <summary>
        /// 回原点
        /// 所有开启的轴停止
        /// </summary>
        /// <param name="cardn">卡号</param>
        /// <param name="axisn">轴号</param>
        /// <param name="homests">轴回原点状态</param>
        public bool GoHome(short cardn, short axisn, short home_mode, short home_dir, int homeoffset)
        /// <param name="emergencyStop"></param>
        public void AllMoveStop(bool emergencyStop = false)
        {
            int option = emergencyStop ? 1 : 0;
            List<Task<bool>> taskList = new List<Task<bool>>(); ;
            // 如果是多个轴的运动 等每个轴运动结束
            IConfig.AxisSettings.Where(a => a.IsAxisEnabled).ToList().ForEach(axisNum =>
            {
                var task = MoveStop(axisNum.AxisIndex, option);
                taskList.Add(task);
            });
            Task.WaitAll(taskList.ToArray());
            var resultOK = taskList.All(u => u.GetAwaiter().GetResult());
        }
        ///// <summary>
        ///// 回原点
        ///// </summary>
        ///// <param name="movingOption">卡号</param>
        ///// <param name="axisn">轴号</param>
        ///// <param name="homests">轴回原点状态</param>
        //public bool GoHome(MovingOption movingOption)
        //{
        //    try
        //    {
        //        PositionReset(movingOption.AxisIndex, 1);
        //        GTSCardAPI.THomePrm thomeprm;
        //        GTSCardAPI.THomeStatus homests;
        //        // 启动Home捕获
        //        short rtn = GTSCardAPI.GT_SetCaptureMode((short)IConfig.CardNum, (short)movingOption.AxisIndex, GTSCardAPI.CAPTURE_HOME);
        //        // 切换到点位运动模式
        //        rtn = GTSCardAPI.GT_PrfTrap((short)IConfig.CardNum, (short)movingOption.AxisIndex);
        //        // 读取点位模式运动参数
        //        rtn = GTSCardAPI.GT_GetHomePrm((short)IConfig.CardNum, (short)movingOption.AxisIndex, out thomeprm);
        //        thomeprm.mode = movingOption.GoHomePara.HomeMode;//回零方式
        //        thomeprm.moveDir = movingOption.GoHomePara.HomeDir;//回零方向
        //        thomeprm.edge = movingOption.GoHomePara.Edge;
        //        thomeprm.velHigh = movingOption.GoHomePara.HighVelocity;
        //        thomeprm.velLow = movingOption.GoHomePara.LowVelocity;
        //        thomeprm.acc = movingOption.VelocityPara.Acc;
        //        thomeprm.dec = movingOption.VelocityPara.Dec;
        //        thomeprm.searchHomeDistance = movingOption.GoHomePara.SearchHomeDistance;//搜索距离
        //        thomeprm.homeOffset = movingOption.GoHomePara.HomeOffset;  //偏移距离
        //        thomeprm.escapeStep = movingOption.GoHomePara.EscapeStep;
        //        rtn = GTSCardAPI.GT_GoHome((short)IConfig.CardNum, (short)movingOption.AxisIndex, ref thomeprm);  //启动回零
        //        bool isStop = false;
        //        int repeatTime = 1000;
        //        do
        //        {
        //            Thread.Sleep(10);
        //            GTSCardAPI.GT_GetHomeStatus((short)IConfig.CardNum, (short)movingOption.AxisIndex, out homests);
        //            isStop = homests.run == 0;
        //            if (isStop && homests.error == 0)
        //            {
        //                Thread.Sleep(200);
        //                PositionReset(movingOption.AxisIndex, 1);
        //            }
        //            repeatTime--;
        //        } while (!isStop && repeatTime > 0);
        //        return isStop;
        //    }
        //    catch (Exception ex)
        //    {
        //        AllMoveStop(true);
        //        OnExceptionOccured?.Invoke(DateTime.Now, ex);
        //        return false;
        //    }
        //}
        /// <summary>
        /// P2P方式回原点
        /// </summary>
        /// <param name="movingOption">运动参数</param>
        public bool P2PGoHome(MovingOption movingOption)
        {
            try
            {
                GTSCardAPI.GT_ZeroPos(cardn, axisn, 1);
                GTSCardAPI.THomePrm thomeprm;
                GTSCardAPI.THomeStatus homests;
                short rtn = GTSCardAPI.GT_GetHomePrm(cardn, axisn, out thomeprm);
                thomeprm.mode = home_mode;//回零方式
                thomeprm.moveDir = home_dir;//回零方向
                thomeprm.edge = 0;
                thomeprm.velHigh = 50;
                thomeprm.velLow = 50;
                thomeprm.acc = 50;
                thomeprm.dec = 50;
                thomeprm.searchHomeDistance = 9999999;//搜搜距离
                thomeprm.homeOffset = 0;  //偏移距离
                thomeprm.escapeStep = 1000;
                rtn = GTSCardAPI.GT_GoHome(cardn, axisn, ref thomeprm);  //启动回零
                StartCapture:
                while (true)
                PositionReset(movingOption.AxisIndex, 1);
                ClearStatus(movingOption.AxisIndex, 1);
                GTSCardAPI.TTrapPrm trapPrm;
                bool isStop = false;
                //搜索距离 阶段1
                // 启动Home捕获
                short sRtn = GTSCardAPI.GT_SetCaptureMode((short)IConfig.CardNum, (short)movingOption.AxisIndex, GTSCardAPI.CAPTURE_HOME);
                // 切换到点位运动模式
                sRtn = GTSCardAPI.GT_PrfTrap((short)IConfig.CardNum, (short)movingOption.AxisIndex);
                // 读取点位模式运动参数
                sRtn = GTSCardAPI.GT_GetTrapPrm((short)IConfig.CardNum, (short)movingOption.AxisIndex, out trapPrm);
                trapPrm.acc = movingOption.VelocityPara.Acc;
                trapPrm.dec = movingOption.VelocityPara.Dec;
                // 设置点位模式运动参数
                sRtn = GTSCardAPI.GT_SetTrapPrm((short)IConfig.CardNum, (short)movingOption.AxisIndex, ref trapPrm);
                // 设置点位模式目标速度,即回原点速度
                sRtn = GTSCardAPI.GT_SetVel((short)IConfig.CardNum, (short)movingOption.AxisIndex, movingOption.GoHomePara.HighVelocity);
                // 设置点位模式目标位置,即原点搜索距离
                sRtn = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)movingOption.AxisIndex, movingOption.GoHomePara.HomeDir == 1 ? 99999999 : -99999999);
                // 启动运动
                sRtn = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (movingOption.AxisIndex - 1));
                int repeatTime = 1000;
                short capture;
                int pos;
                uint clk;//时钟参数
                do
                {
                    Thread.Sleep(5);
                    GTSCardAPI.GT_GetHomeStatus(cardn, axisn, out homests);
                    if (homests.run == 0)
                    Thread.Sleep(20);
                    // 读取捕获状态
                    GTSCardAPI.GT_GetCaptureStatus((short)IConfig.CardNum, (short)movingOption.AxisIndex, out capture, out pos, 1, out clk);
                    isStop = IsStop((short)movingOption.AxisIndex);
                    repeatTime--;
                } while (!(isStop || capture == 1 || repeatTime <= 0));
                if (repeatTime <= 0)
                {
                    MoveStop((short)movingOption.AxisIndex, 0);
                    throw new ProcessException($"运动轴{movingOption.AxisIndex} ,回原点超时异常");
                }
                if (isStop && capture != 1)
                {
                    ClearStatus((short)movingOption.AxisIndex, 1);
                    var axisStatus = AxisStatusList.FirstOrDefault(u => u.AxisIndex == movingOption.AxisIndex);
                    if (((axisStatus.AxisStatus & 0x20) != 0) || ((axisStatus.AxisStatus & 0x40) != 0))
                    {
                        if (homests.error == 0)
                        {
                            Thread.Sleep(200);
                            GTSCardAPI.GT_ZeroPos(cardn, axisn, 1);
                        }
                        return true;
                        movingOption.GoHomePara.HomeDir = (short)(movingOption.GoHomePara.HomeDir == 1 ? -1 : 1);
                        goto StartCapture;
                    }
                    throw new ProcessException($"运动轴{movingOption.AxisIndex} ,回原点超时异常");
                }
                if (capture == 1)
                {
                    //先stop
                    MoveStop((short)movingOption.AxisIndex, 0);
                    ClearStatus((short)movingOption.AxisIndex, 1);
                    //已经捕获到Home才可以回零 阶段2
                    // 运动到"捕获位置+偏移量"
                    sRtn = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)movingOption.AxisIndex, pos + movingOption.GoHomePara.HomeOffset);
                    // 在运动状态下更新目标位置
                    sRtn = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (movingOption.AxisIndex - 1));
                    isStop = false;
                    repeatTime = 1000;
                    do
                    {
                        Thread.Sleep(20);
                        isStop = IsStop((short)movingOption.AxisIndex);
                        repeatTime--;
                    } while (!isStop && repeatTime > 0);
                    PositionReset(movingOption.AxisIndex, 1);
                    return (sRtn == (short)GTSRetCode.GRCRunOK) && isStop;
                }
                return false;
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(ex);
                AllMoveStop(true);
                OnExceptionOccured?.Invoke(DateTime.Now, ex);
                return false;
            }
        }
        /// <summary>
        /// 回原点
        /// </summary>
        /// <param name="axisId"></param>
        public void StartHoming(int axisId)
        {
            try
            {
                //servo on
                APS168.APS_set_servo_on(axisId, 1);
                Thread.Sleep(500); // Wait stable.
                // 2. Start home move
                APS168.APS_home_move(axisId);
                WaitHomeFinish(axisId);
                axisDestination[axisId] = 0;
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(ex);
            }
        }
        /// <summary>
        /// 回原点
        /// </summary>
        /// <param name="axisId"></param>
        /// <param name="homeMode"></param>
        /// <param name="homeDir"></param>
        /// <param name="praCurve"></param>
        /// <param name="praAcc"></param>
        /// <param name="praVm"></param>
        public void StartHoming(int axisId, int homeMode, int homeDir, double praCurve, double praAcc, double praVm)
        {
            // 1. Select home mode and config home parameters
            APS168.APS_set_axis_param(axisId, (Int32)APS_Define.PRA_HOME_MODE, homeMode); // Set home mode
            APS168.APS_set_axis_param(axisId, (Int32)APS_Define.PRA_HOME_DIR, homeDir); // Set home direction
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_CURVE, praCurve); // Set acceleration paten (T-curve)
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_ACC, praAcc); // Set homing acceleration rate
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_VM, praVm); // Set homing maximum velocity.
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_VO, praVm / 5); // Set homing VO speed
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_EZA, 0); // Set EZ signal alignment (yes or no)
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_SHIFT, 0); // Set home position shfit distance.
            APS168.APS_set_axis_param_f(axisId, (Int32)APS_Define.PRA_HOME_POS, 0); // Set final home position.
            StartHoming(axisId);
        }
        /// <summary>
        /// 回原点结束
        /// </summary>
        /// <param name="axisNum"></param>
        /// <returns></returns>
        public void WaitHomeFinish(int axisNum)
        {
            MOTION_IO_STATUS MotionIoSts = new MOTION_IO_STATUS();
            MOTION_BOOL_STATUS MotionSts = new MOTION_BOOL_STATUS();
            var axisSetting = IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == axisNum);
            int startTime = System.Environment.TickCount;
            while ((GetMotionIoStatus(axisNum, ref MotionIoSts)) && (GetMotionStatus(axisNum, ref MotionSts)))
            {
                if (MotionIoSts.EMG)
                {
                    LogAsync(DateTime.Now, "轴复位中" + axisNum + "急停", "");
                    throw new Exception("轴复位中" + axisNum + "急停");
                }
                if (axisSetting.HomeMode == 0)
                {
                    if (!MotionSts.HMV && MotionIoSts.ORG)
                        break;
                }
                else
                {
                    if (!MotionSts.HMV)
                        break;
                }
                if (axisSetting.TimeOutHome < System.Environment.TickCount - startTime)
                {
                    LogAsync(DateTime.Now, "轴复位中" + axisNum + "回原点超时", "");
                    MoveStop(axisNum);
                    throw new Exception("轴复位中" + axisNum + "回原点超时");
                }
            }
            //ClearPosition(axisNum);
        }
        static object _ioLock = new object();
        /// <summary>
        /// 设置IO卡输出
        /// </summary>
        /// <param name="DINo">IO端口序号</param>
        /// <param name="Value">设置值(true:高电平, false:低电平)</param>
        /// <returns></returns>
        public bool SetOutput(int DINo, bool Value)
        {
            if (OnCheckOutputAllowed?.Invoke(DINo, Value, new Dictionary<int, int>(axisDestination.ToDictionary(u => u.Key, u => u.Value))) ?? false)
            {
                OnExceptionRaised?.Invoke(new AMPRunException(DINo + "输出" + Value.ToString() + "不被允许", null));
                return false;
            }
            int ret = -1;
            lock (_ioLock)
            {
                ret = APS168.APS_write_d_channel_output(0, 0, DINo, Value ? 1 : 0);
            }
            if (ret < 0)
            {
                return false;
            }
            return true;
        }
        /// <summary>
        /// 获取输入信号
        /// </summary>
        /// <param name="stateType"></param>
        /// <returns></returns>
        public bool[] GetInputState()
        {
            int diVaule = 0;
            lock (_ioLock)
            {
                APS168.APS_read_d_input(0, 0, ref diVaule);
            }
            return GetBoolSig(diVaule);
            //return new bool[16];
        }
        /// <summary>
        /// 获取输出信号
        /// </summary>
        /// <param name="stateType"></param>
        /// <returns></returns>
        public bool[] GetOutputState()
        {
            int doVaule = 0;
            lock (_ioLock)
            {
                APS168.APS_read_d_output(0, 0, ref doVaule);
            }
            return GetBoolSig(doVaule);
        }
        /// <summary>
        /// 将无符号整型转换为Bool型数组
        /// </summary>
        /// <param name="sigStr"></param>
        /// <returns></returns>
        private bool[] GetBoolSig(int sigStr)
        {
            bool[] state = new bool[32];
            for (int i = 0; i < 32; i++)
            {
                state[i] = (sigStr & (1 << i)) != 0;
            }
            return state;
        }
        /// <summary>
        /// 将bool数组转换成无符号整型
        /// </summary>
        /// <param name="sigArray"></param>
        /// <returns></returns>
        private int GetintSig(bool[] sigArray)
        {
            int state = -1;
            for (int i = 31; i > -1; i--)
            {
                state = (state << 1) + (sigArray[i] ? 1 : 0);
            }
            return state;
        }
        /// <summary>
        /// 读取输入
        /// 读取IO输入
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="index">输入口</param>
@@ -997,166 +990,392 @@
        {
            int value;
            GTSCardAPI.GT_GetDi(cardNum, GTSCardAPI.MC_GPI, out value);
            if ((value & 1 << index) == 0) return true;//有输入返回true
            if ((value & (1 << index)) == 0) return true;//有输入返回true
            else return false;          //无输入返回false
        }
        /// <summary>
        /// 输出
        /// 读取IO输出
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="index">输出口,返回1-16</param>
        /// <param name="value">false表示输出,true表示关闭</param>
        public void WriteOut(short cardNum, short index, bool value)
        {
            short outNum = (short)(index % 100 + 1);
            if (value)
            {
                GTSCardAPI.GT_SetDoBit(cardNum, GTSCardAPI.MC_GPO, outNum, 0);
            }
            else
            {
                GTSCardAPI.GT_SetDoBit(cardNum, GTSCardAPI.MC_GPO, outNum, 1);
            }
        }
        /// <summary>
        /// 停止 某个轴
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="axisNum">轴号</param>
        /// <param name="option">0表示平滑停止,1表示紧急停止</param>
        public void Stop(short cardNum, short axisNum, short option)
        {
            GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), option);
        }
        /// <summary>
        /// 停止 某个轴(异步)
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="axisNum">轴号</param>
        /// <param name="option">0表示平滑停止,1表示紧急停止</param>
        public async Task StopAsync(short cardNum, short axisNum, short option)
        {
            await Task.Run(() =>
           {
               GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), option);
           });
        }
        /// <summary>
        /// 停止 某个轴
        /// </summary>
        /// <param name="cardNum"></param>
        /// <param name="axisNum">轴号</param>
        /// <param name="value">停止方式,false表示平滑停止,true表示紧急停止</param>
        public void Stop(short cardNum, short axisNum, bool emergencyStop)
        {
            if (emergencyStop)
            {
                GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), 1 << (axisNum - 1));
            }
            else
            {
                GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), 0);
            }
        }
        /// <summary>
        /// 停止 全部轴
        /// </summary>
        /// <param name="cardNum"></param>
        /// <param name="value">停止方式,false表示平滑停止,true表示紧急停止</param>
        public void StopAll(short cardNum, bool value)
        {
            for (short i = 1; i <= GTSCardParameter.AxisCount; i++)
            {
                Stop(cardNum, i, value);
            }
        }
        /// <summary>
        /// IO输出
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="mdl">模块号</param>
        /// <param name="index">IO输出</param>
        /// <param name="value">true表示输出,false表示无输出</param>
        public void MC_WriteDigitalOutput(short cardNum, short mdl, short index, bool value)
        {
            if (value)
            {
                GTSCardAPI.GT_SetExtIoBit(cardNum, mdl, index, 0);
            }
            else
            {
                GTSCardAPI.GT_SetExtIoBit(cardNum, mdl, index, 1);
            }
        }
        /// <summary>
        /// 读取IO输出状态
        /// </summary>
        /// <param name="cardNum"></param>
        /// <param name="index"></param>
        /// <param name="index">io索引</param>
        /// <returns></returns>
        public bool GetDoSts(short cardNum, short index)
        public bool GetDoSts(short index)
        {
            short outNum = 0;
            int outSts;
            outNum = (short)(index % 100);
            GTSCardAPI.GT_GetDo(cardNum, GTSCardAPI.MC_GPO, out outSts);
            short outNum = (short)(index % 100);
            GTSCardAPI.GT_GetDo((short)IConfig.CardNum, GTSCardAPI.MC_GPO, out outSts);
            if ((outSts & (1 << outNum)) == 0) return true;
            else return false;
        }
        /// <summary>
        /// 读取当前值
        /// 按位设置数字 IO 输出状态
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="axisNum">轴号</param>
        /// <returns>返回当前值,单位毫米</returns>
        public double GetPosMM(short cardNum, short axisNum)
        /// <param name="index">输出口,返回1-16</param>
        /// <param name="value">false表示关,true表示开,板卡要设置取反</param>
        public override void WriteOutput(short index, IOValue value)
        {
            lock (lockObj)
            short outNum = (short)(index % 100 + 1);
            if ((int)value <= 1)
            {
                double prfpos = 0; uint pclock = 0;
                GTSCardAPI.GT_GetPrfPos(cardNum, axisNum, out prfpos, 1, out pclock);
                return prfpos / IConfig.AxisVelocityRatio;
                GTSCardAPI.GT_SetDoBit((short)IConfig.CardNum, GTSCardAPI.MC_GPO, outNum, IConfig.IsOutputReversed ? (short)(value == IOValue.TRUE ? 0 : 1) : (short)value);
            }
            else
            {
                var currentValue = (int)MonitorValues.FirstOrDefault(u => u.IONum == outNum && u.IOType == IOType.OUTPUT).Value;
                GTSCardAPI.GT_SetDoBit((short)IConfig.CardNum, GTSCardAPI.MC_GPO, outNum, (short)(currentValue == 1 ? 0 : 1));
            }
        }
        /// <summary>
        /// 读取轴状态,判断电机是否停止
        /// </summary>
        /// <param name="cardNum"></param>
        /// <param name="axisNum"></param>
        /// <param name="cardNum">板卡号</param>
        /// <param name="axisNum">轴号</param>
        /// <returns></returns>
        public bool IsStop(short cardNum, short axisNum)
        public bool IsStop(short axisNum)
        {
            lock (lockObj)
            int sts = AxisStatusList.FirstOrDefault(u => u.AxisIndex == axisNum).AxisStatus;
            if ((sts & 0x400) == 0) return true;//停止返回true
            else return false;              //运行中返回false
        }
        /// <summary>
        /// 读取轴状态
        /// </summary>
        /// <param name="axisNum">轴号</param>
        /// <returns></returns>
        public override int GetAxisStatus(int axisNum)
        {
            lock (moveLock)
            {
                int sts = 0;
                uint pclock = 0;
                GTSCardAPI.GT_GetSts(cardNum, axisNum, out sts, 1, out pclock);
                if ((sts & 0x400) == 0) return true;//停止返回true
                else return false;              //运行中返回false
                GTSCardAPI.GT_GetSts((short)IConfig.CardNum, (short)axisNum, out sts, 1, out pclock);
                return sts;
            }
        }
        public override bool ClearStatus(int startAxisIndex, int count)
        {
            var rtn = GTSCardAPI.GT_ClrSts((short)IConfig.CardNum, (short)startAxisIndex, (short)count);
            return rtn == (short)GTSRetCode.GRCRunOK;
        }
        /// <summary>
        /// 位置回零
        /// </summary>
        /// <param name="startAxisIndex"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public override bool PositionReset(int startAxisIndex, int count)
        {
            lock (moveLock)
            {
                var rtn = GTSCardAPI.GT_ZeroPos((short)IConfig.CardNum, (short)startAxisIndex, (short)count);
                return rtn == (short)GTSRetCode.GRCRunOK;
            }
        }
        #endregion
        public void Monitor()
        #region IMonitor
        public List<AxisMovingStatus> GetAxisMovingStatus()
        {
            throw new NotImplementedException();
            List<AxisMovingStatus> axisMovingStatusesList = new List<AxisMovingStatus>();
            foreach (var axisConfig in IConfig.AxisSettings.FindAll(u => u.IsAxisEnabled))
            {
                AxisMovingStatus axisMovingStatus = new AxisMovingStatus();
                axisMovingStatus.AxisIndex = axisConfig.AxisIndex;
                axisMovingStatus.AxisName = axisConfig.AxisName;
                //axisMovingStatus.CurPosition = Convert.ToInt32(GetPosition(axisMovingStatus.AxisIndex));
                axisMovingStatus.Destination = Convert.ToInt32(GetPrfPosition(axisMovingStatus.AxisIndex));
                //axisMovingStatus.CurVelocity = GetVelocity(axisMovingStatus.AxisIndex);
                //axisMovingStatus.PrfVelocity = GetPrfVelocity(axisMovingStatus.AxisIndex);
                axisMovingStatus.AxisStatus = GetAxisStatus(axisMovingStatus.AxisIndex);
                axisMovingStatusesList.Add(axisMovingStatus);
            }
            return axisMovingStatusesList;
        }
        public void ResetAlarm()
        public List<IOItem> GetMonitorValues()
        {
            throw new NotImplementedException();
            var result = new List<IOItem>();
            //读取IO输入
            int inValue;
            GTSCardAPI.GT_GetDi((short)IConfig.CardNum, GTSCardAPI.MC_GPI, out inValue);
            //读取IO输出
            int outValue;
            GTSCardAPI.GT_GetDo((short)IConfig.CardNum, GTSCardAPI.MC_GPO, out outValue);
            //解析结果
            for (var index = 0; index < 16; index++)
            {
                IOItem inItem = new IOItem()
                {
                    IONum = index,
                    Value = (inValue & (1 << index)) == 0 ? IOValue.TRUE : IOValue.FALSE,
                    IOType = IOType.INPUT
                };
                IOItem outItem = new IOItem()
                {
                    IONum = index,
                    Value = (outValue & (1 << index)) == 0 ? IOValue.TRUE : IOValue.FALSE,
                    IOType = IOType.OUTPUT
                };
                result.Add(inItem);
                result.Add(outItem);
            }
            return result;
        }
        public async override void Monitor()
        {
            await Task.Run(() =>
            {
                while (CurrentState != EnumHelper.DeviceState.DSClose && CurrentState != EnumHelper.DeviceState.DSExcept && CurrentState != EnumHelper.DeviceState.DSUninit)
                {
                    try
                    {
                        if (!IConfig.IsEnableMonitor)
                            return;
                        var newValues = GetMonitorValues();
                        var newAxisMovingStatus = GetAxisMovingStatus();
                        if (newValues == null || newValues.Count == 0 || newAxisMovingStatus == null || newAxisMovingStatus.Count == 0)
                            continue;
                        //Stopwatch sw = new Stopwatch();
                        //sw.Start();
                        if (MonitorValues.Count == newValues.Count)
                        {
                            //var tempNew = newValues.DeepSerializeClone();//clone
                            //var tempOld = MonitorValues.DeepSerializeClone();
                            var tempNew = new List<IOItem>(newValues);//clone
                            var tempOld = new List<IOItem>(MonitorValues);
                            MonitorCheckAndInvoke(tempNew, tempOld);
                        }
                        if (AxisStatusList.Count == newAxisMovingStatus.Count)
                        {
                            var tempNew = new List<AxisMovingStatus>(newAxisMovingStatus);//clone
                            var tempOld = new List<AxisMovingStatus>(AxisStatusList);
                            AxisStatusCheck(tempNew, tempOld);
                        }
                        AxisStatusList = new List<AxisMovingStatus>(newAxisMovingStatus);
                        MonitorValues = new List<IOItem>(newValues);
                        //sw.Stop();
                        //if (sw.ElapsedMilliseconds > 20)
                        //{
                        //    LogAsync(DateTime.Now, $"{this.Name}轮询时间:{sw.ElapsedMilliseconds}", "");
                        //}
                        if (IConfig.MonitorInterval > 0)
                        {
                            Thread.Sleep(IConfig.MonitorInterval);
                        }
                    }
                    catch (Exception ex)
                    {
                        if (CurrentState == DeviceState.DSOpen)
                        {
                            LogAsync(DateTime.Now, $"{this.Name}监听异常", ex.GetExceptionMessage());
                        }
                    }
                }
            });
        }
        private async void AxisStatusCheck(List<AxisMovingStatus> tempNew, List<AxisMovingStatus> tempOld)
        {
            await Task.Run(() =>
            {
                foreach (var newSts in tempNew)
                {
                    var oldSts = tempOld.FirstOrDefault(u => u.AxisIndex == newSts.AxisIndex);
                    if (oldSts != null)
                    {
                        if (oldSts.AxisStatus != newSts.AxisStatus)
                        {
                            AxisStatusChanged(newSts.AxisIndex, oldSts.AxisStatus, newSts.AxisStatus);
                        }
                        if (((newSts.AxisStatus >> 1 & 1) == 1) && ((oldSts.AxisStatus >> 1 & 1) == 0)) //初次报警
                        {
                            AxisAlarmRaised(newSts.AxisStatus, $"轴{newSts.AxisIndex}:{newSts.AxisName}轴伺服报警");
                        }
                    }
                }
            });
        }
        public override void OnMethodInvoked(IAsyncResult ar)
        {
            //MotionCardMonitorSet monitorSet = ar.AsyncState as MotionCardMonitorSet;
            //ProcessResponse resValues = monitorSet.Response;
            //if (resValues.ResultValue == (int)ReplyValue.IGNORE)
            //{
            //    return;
            //}
            //Stopwatch sw = new Stopwatch();
            //sw.Start();
            //// 将指定IOItem写入板卡
            //foreach (var replyIOData in monitorSet.ReplyIODatas)
            //{
            //    //写入IO输出
            //    if (replyIOData.IOType == IOType.OUTPUT)
            //    {
            //        GTSCardAPI.GT_SetDoBit((short)IConfig.CardNum, GTSCardAPI.MC_GPI, (short)replyIOData.IONum, (short)replyIOData.Value);
            //    }
            //    // in只读不能写
            //}
            //sw.Stop();
            //LogAsync(DateTime.Now, $"{Name}反馈完成,耗时{sw.ElapsedMilliseconds}ms", $"{resValues.GetDisplayText()}");
        }
        protected void MonitorCheckAndInvoke(List<IOItem> tempNew, List<IOItem> tempOld)
        {
            #region 警报信息
            Parallel.ForEach(IConfig.WarningSetCollection, wSet =>
            {
                MotionCardWarningSet warningSet = wSet as MotionCardWarningSet;
                bool isOn = (((int)((tempNew.FirstOrDefault(u => u.IONum == warningSet.TriggerIndex && u.IOType == warningSet.WarningIOModel)?.Value)) >> warningSet.TriggerIndex) & 1) == (warningSet.TriggerValue ? 1 : 0);
                if (warningSet.CurrentStatus != isOn)
                {
                    warningSet.CurrentStatus = isOn;
                    warningSet.TriggerTime = DateTime.Now;
                    SaveAlarmCSVAsync(DateTime.Now, this.Name, warningSet);
                    ExcuteMonitorAlarm(DateTime.Now, this, warningSet);
                }
            });
            #endregion
            #region 监听信息
            Parallel.ForEach(IConfig.MonitorSetCollection, mSet =>
            {
                MotionCardMonitorSet monitorSet = mSet as MotionCardMonitorSet;
                if (monitorSet.TriggerIndex < 0 || monitorSet.TriggerIndex > tempNew.Count)
                {
                    return;
                }
                var newIOItem = tempNew.FirstOrDefault(u => u.IONum == monitorSet.TriggerIndex);
                var oldIOItem = tempOld.FirstOrDefault(u => u.IONum == monitorSet.TriggerIndex);
                if (newIOItem?.Value != oldIOItem?.Value)
                {
                    if (monitorSet.TriggerValue == -999 || (int)newIOItem.Value == monitorSet.TriggerValue)
                    {
                        if (monitorSet.OpConfig == null)
                        {
                            monitorSet.OpConfig = new OperationConfigBase();
                        }
                        //monitorSet.OpConfig.InputPara = monitorSet.InputDataIndex.ConvertAll(index =>
                        //{
                        //    return tempNew[index].Value == IOValue.TRUE ? 1 : 0;
                        //}).ToList();
                        monitorSet.OpConfig.InputPara = new List<int>() { (int)newIOItem.Value };
                        ExcuteMonitorInvoke(DateTime.Now, monitorSet.InvokeDevice, this, monitorSet);
                    }
                }
            });
            #endregion
        }
        public override void ResetAlarm()
        {
            int axis_sts;
            var axisSettings = IConfig.AxisSettings.FindAll(u => u.IsAxisEnabled);
            ClearStatus(1, axisSettings.Count);
            if (AxisStatusList.Count == 0)
            {
                Thread.Sleep(10);
            }
            foreach (var axisSetting in axisSettings)
            {
                //axis_sts = GetAxisStatus((short)axisSetting.AxisIndex);
                axis_sts = AxisStatusList.FirstOrDefault(u => u.AxisIndex == axisSetting.AxisIndex)?.AxisStatus ?? 0;
                if ((axis_sts & 0x200) == 0)
                {
                    var rst = GTSCardAPI.GT_AxisOn((short)IConfig.CardNum, (short)axisSetting.AxisIndex);
                }
                //// 所有位置请零
                //PositionReset(1, axisSettings.Count);
                // 正极限报警
                if ((axis_sts & 0x20) != 0)
                {
                    // 负向移动
                    MovingOption movingOption = new MovingOption();
                    movingOption.AxisIndex = (short)axisSetting.AxisIndex;
                    movingOption.Destination = -50; // 负向移动
                    movingOption.VelocityPara.Velocity = 50;
                    P2PMoveAbs(movingOption);
                }
                // 负极限报警
                if ((axis_sts & 0x40) != 0)
                {
                    // 正向移动
                    MovingOption movingOption = new MovingOption();
                    movingOption.AxisIndex = (short)axisSetting.AxisIndex;
                    movingOption.Destination = 50; // 负向移动
                    movingOption.VelocityPara.Velocity = 50;
                    P2PMoveAbs(movingOption);
                }
            }
            // 清除状态
            ClearStatus(1, axisSettings.Count);
        }
        object _alarmLock = new object();
        private async void SaveAlarmCSVAsync(DateTime now, string plcName, IWarningSet ws)
        {
            await Task.Run(() =>
            {
                lock (_alarmLock)
                {
                    DirectoryInfo dir = new DirectoryInfo(this.IConfig.LogPath);
                    if (!dir.Exists)
                    {
                        dir.Create();
                    }
                    string path = Path.Combine(IConfig.LogPath, $"Alarm_{Name}_{now.ToString("yyyyMMdd")}.csv");
                    bool fileExist = File.Exists(path);
                    using (StreamWriter writer = new StreamWriter(path, true, System.Text.Encoding.UTF8))
                    {
                        if (!fileExist)
                        {
                            writer.WriteLine("Time,Source,AlarmCode,AlarmDescription,AlarmStatus");
                        }
                        writer.WriteLine($"{now.ToString("HH:mm:ss.fff")},{plcName},{ws.WarningCode},{ws.WarningDescription},{(ws.CurrentStatus ? "报警" : "停止")}");
                        writer.Flush();
                        writer.Close();
                    }
                }
            });
        }
        #endregion
    }
}