领胜LDS 键盘AOI检测项目
wells.liu
2020-06-29 0d97500a3aac13b642fc93fae2e5dd01e1086b21
暂存 固高板卡
3个文件已添加
5个文件已修改
2850 ■■■■■ 已修改文件
src/Bro.Common.Model/Helper/EnumHelper.cs 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/Bro.Device.GTSCard.csproj 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/GTS800.cfg 793 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/GTSCardAPI.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/GTSCardConfig.cs 868 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/GTSCardDriver.cs 1025 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/gts.dll 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Device.GTSCard/packages.config 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.Common.Model/Helper/EnumHelper.cs
@@ -443,5 +443,145 @@
            [Description("相机坐标系相对运动")]
            CameraRelativeMove = 12,
        }
        /// <summary>
        /// 马达/运动板卡运行模式
        /// </summary>
        public enum MotorMoveMode
        {
            /// <summary>
            /// 普通点位运动
            /// </summary>
            [Description("普通点位运动")]
            Normal = 1,
            ///// <summary>
            ///// 高速等距运动
            ///// </summary>
            //[Description("高速等距运动")]
            //HighSpeedStep = 2,
            ///// <summary>
            ///// 高速不等距运动
            ///// </summary>
            //[Description("高速不等距运动")]
            //HighSpeed = 3,
            /// <summary>
            /// 找正限位运动
            /// </summary>
            [Description("找正限位运动")]
            FindPositive = 4,
            /// <summary>
            /// 离开正限位
            /// </summary>
            [Description("离开正限位")]
            LeavePositive = 5,
            /// <summary>
            /// 找负限位运动
            /// </summary>
            [Description("找负限位运动")]
            FindNegative = 6,
            /// <summary>
            /// 离开负限位
            /// </summary>
            [Description("离开负限位")]
            LeaveNegative = 7,
            /// <summary>
            /// 找原点运动
            /// </summary>
            [Description("找原点运动")]
            FindOri = 8,
            /// <summary>
            /// Jog模式
            /// </summary>
            [Description("Jog模式")]
            Jog = 9,
            /// <summary>
            /// 读数头找原点方式
            /// </summary>
            [Description("读数头找原点方式")]
            FindOriIndex = 10,
            /// <summary>
            /// 插补模式
            /// </summary>
            [Description("插补模式")]
            Coordinate = 11,
            ///// <summary>
            ///// 刀向更随功能
            ///// </summary>
            //[Description("刀向更随功能")]
            //BufMove = 12,
        }
        /// <summary>
        /// 马达IO定义类型
        /// </summary>
        public enum MotorIODefine
        {
            Nothing,
            InputStartLeft,
            InputStartRight,
            InputCurtainLeft,
            InputCurtainRight,
            InputSafeDoor,
            InputEmergency,
            InputReset,
            InputScrew1,
            InputScrew2,
            InputSuckLeft,
            InputSuckRight,
            InputSuckXYZ,
            InputCylinderLeftLimitFront,
            InputCylinderLeftLimitBack,
            InputCylinderRightLimitFront,
            InputCylinderRightLimitBack,
            OutputYellow,
            OutputGreen,
            OutputRed,
            OutputBeep,
            OutputLight,
            OutputMotorPower,
            OutputInitOK,
            OutputZLock,
            OutputLeftCylinder,
            OutputRightCylinder,
            OutputLeftSuck,
            OutputRightSuck,
            OutputScrewBlow,
            OutputCamera,
            OutputScrewSuck,
        }
        /// <summary>
        /// GTS运动板卡控制返回控制码
        /// </summary>
        public enum GTSRetCode
        {
            [Description("指令执行成功")]
            GRCRunOK = 0,         // 指令执行成功
            [Description("指令执行错误")]
            GRCRunErr = 1,                          // 指令执行错误
            [Description("icense不支持")]
            GRCNotSupport = 2,                      // icense不支持
            [Description("指令参数错误")]
            GRCInvalidParam = 7,                    // 指令参数错误
            [Description("主机和运动控制器通讯失败")]
            GRCCommErr = -1,                         // 主机和运动控制器通讯失败
            [Description("打开控制器失败")]
            GRCOpenErr = -6,                         // 打开控制器失败
            [Description("运动控制器没有响应")]
            GRCNotAck = -7                           // 运动控制器没有响应
        }
    }
}
src/Bro.Device.GTSCard/Bro.Device.GTSCard.csproj
@@ -31,6 +31,9 @@
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Core" />
@@ -63,5 +66,12 @@
      <Name>Bro.Common.Model</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="gts.dll" />
  </ItemGroup>
  <ItemGroup>
    <None Include="GTS800.cfg" />
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
src/Bro.Device.GTSCard/GTS800.cfg
New file
@@ -0,0 +1,793 @@
[profile1]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile2]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile3]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile4]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile5]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile6]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile7]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[profile8]
active=1
decSmoothStop=1.000000
decAbruptStop=1000.000000
[axis1]
active=1
alarmType=2
alarmIndex=1
limitPositiveType=0
limitPositiveIndex=1
limitNegativeType=1
limitNegativeIndex=1
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=5
prfMap=0x1
encMap=0x1
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis2]
active=1
alarmType=2
alarmIndex=2
limitPositiveType=0
limitPositiveIndex=2
limitNegativeType=1
limitNegativeIndex=2
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=5
prfMap=0x2
encMap=0x2
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis3]
active=1
alarmType=2
alarmIndex=3
limitPositiveType=0
limitPositiveIndex=3
limitNegativeType=1
limitNegativeIndex=3
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=5
prfMap=0x4
encMap=0x4
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis4]
active=1
alarmType=2
alarmIndex=4
limitPositiveType=0
limitPositiveIndex=4
limitNegativeType=1
limitNegativeIndex=4
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=5
prfMap=0x8
encMap=0x8
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis5]
active=1
alarmType=2
alarmIndex=5
limitPositiveType=0
limitPositiveIndex=5
limitNegativeType=1
limitNegativeIndex=5
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=-1
prfMap=0x10
encMap=0x10
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis6]
active=1
alarmType=2
alarmIndex=6
limitPositiveType=0
limitPositiveIndex=6
limitNegativeType=1
limitNegativeIndex=6
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=-1
prfMap=0x20
encMap=0x20
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis7]
active=1
alarmType=2
alarmIndex=7
limitPositiveType=0
limitPositiveIndex=7
limitNegativeType=1
limitNegativeIndex=7
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=-1
prfMap=0x40
encMap=0x40
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[axis8]
active=1
alarmType=2
alarmIndex=8
limitPositiveType=0
limitPositiveIndex=8
limitNegativeType=1
limitNegativeIndex=8
smoothStopType=4
smoothStopIndex=-1
abruptStopType=4
abruptStopIndex=-1
prfMap=0x80
encMap=0x80
prfMapAlpha1=1
prfMapBeta1=1
prfMapAlpha2=1
prfMapBeta2=1
encMapAlpha1=1
encMapBeta1=1
encMapAlpha2=1
encMapBeta2=1
[dac1]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac2]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac3]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac4]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac5]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac6]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac7]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac8]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac9]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac10]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac11]
active=1
control=-1
reverse=0
bias=0
limit=32767
[dac12]
active=1
control=-1
reverse=0
bias=0
limit=32767
[step1]
active=1
axis=1
mode=0
parameter=0
reverse=0
[step2]
active=1
axis=2
mode=0
parameter=0
reverse=0
[step3]
active=1
axis=3
mode=0
parameter=0
reverse=0
[step4]
active=1
axis=4
mode=0
parameter=0
reverse=0
[encoder1]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder2]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder3]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder4]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder5]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder6]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder7]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder8]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder9]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder10]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[encoder11]
active=1
reverse=1
filterType=0
captureSource=0
captureHomeSense=0
captureIndexSense=0
[enable1]
active=1
axis=1
axisItem=-1
reverse=1
[enable2]
active=1
axis=2
axisItem=-1
reverse=1
[enable3]
active=1
axis=3
axisItem=-1
reverse=1
[enable4]
active=1
axis=4
axisItem=-1
reverse=1
[enable5]
active=1
axis=5
axisItem=-1
reverse=1
[enable6]
active=1
axis=6
axisItem=-1
reverse=1
[enable7]
active=1
axis=7
axisItem=-1
reverse=1
[enable8]
active=1
axis=8
axisItem=-1
reverse=1
[clear1]
active=1
axis=-1
axisItem=-1
reverse=0
[clear2]
active=1
axis=-1
axisItem=-1
reverse=0
[clear3]
active=1
axis=-1
axisItem=-1
reverse=0
[clear4]
active=1
axis=-1
axisItem=-1
reverse=0
[clear5]
active=1
axis=-1
axisItem=-1
reverse=0
[clear6]
active=1
axis=-1
axisItem=-1
reverse=0
[clear7]
active=1
axis=-1
axisItem=-1
reverse=0
[clear8]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo1]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo2]
active=1
axis=-1
axisItem=-1
reverse=1
[gpo3]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo4]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo5]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo6]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo7]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo8]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo9]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo10]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo11]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo12]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo13]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo14]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo15]
active=1
axis=-1
axisItem=-1
reverse=0
[gpo16]
active=1
axis=-1
axisItem=-1
reverse=0
[limitPositive1]
active=1
reverse=0
filterTime=3
[limitPositive2]
active=1
reverse=0
filterTime=3
[limitPositive3]
active=1
reverse=0
filterTime=3
[limitPositive4]
active=1
reverse=0
filterTime=3
[limitPositive5]
active=1
reverse=0
filterTime=3
[limitPositive6]
active=1
reverse=0
filterTime=3
[limitPositive7]
active=1
reverse=0
filterTime=3
[limitPositive8]
active=1
reverse=0
filterTime=3
[limitNegative1]
active=1
reverse=0
filterTime=3
[limitNegative2]
active=1
reverse=0
filterTime=3
[limitNegative3]
active=1
reverse=0
filterTime=3
[limitNegative4]
active=1
reverse=0
filterTime=3
[limitNegative5]
active=1
reverse=0
filterTime=3
[limitNegative6]
active=1
reverse=0
filterTime=3
[limitNegative7]
active=1
reverse=0
filterTime=3
[limitNegative8]
active=1
reverse=0
filterTime=3
[alarm1]
active=1
reverse=0
filterTime=3
[alarm2]
active=1
reverse=0
filterTime=3
[alarm3]
active=1
reverse=0
filterTime=3
[alarm4]
active=1
reverse=0
filterTime=3
[alarm5]
active=1
reverse=0
filterTime=3
[alarm6]
active=1
reverse=0
filterTime=3
[alarm7]
active=1
reverse=0
filterTime=3
[alarm8]
active=1
reverse=0
filterTime=3
[home1]
active=1
reverse=0
filterTime=3
[home2]
active=1
reverse=0
filterTime=3
[home3]
active=1
reverse=0
filterTime=3
[home4]
active=1
reverse=0
filterTime=3
[home5]
active=1
reverse=0
filterTime=3
[home6]
active=1
reverse=0
filterTime=3
[home7]
active=1
reverse=0
filterTime=3
[home8]
active=1
reverse=0
filterTime=3
[gpi1]
active=1
reverse=0
filterTime=3
[gpi2]
active=1
reverse=0
filterTime=3
[gpi3]
active=1
reverse=0
filterTime=3
[gpi4]
active=1
reverse=0
filterTime=3
[gpi5]
active=1
reverse=0
filterTime=3
[gpi6]
active=1
reverse=0
filterTime=3
[gpi7]
active=1
reverse=0
filterTime=3
[gpi8]
active=1
reverse=0
filterTime=3
[gpi9]
active=1
reverse=0
filterTime=3
[gpi10]
active=1
reverse=0
filterTime=3
[gpi11]
active=1
reverse=0
filterTime=3
[gpi12]
active=1
reverse=0
filterTime=3
[gpi13]
active=1
reverse=0
filterTime=3
[gpi14]
active=1
reverse=0
filterTime=3
[gpi15]
active=1
reverse=0
filterTime=3
[gpi16]
active=1
reverse=0
filterTime=3
[arrive1]
active=1
reverse=0
filterTime=3
[arrive2]
active=1
reverse=0
filterTime=3
[arrive3]
active=1
reverse=0
filterTime=3
[arrive4]
active=1
reverse=0
filterTime=3
[arrive5]
active=1
reverse=0
filterTime=3
[arrive6]
active=1
reverse=0
filterTime=3
[arrive7]
active=1
reverse=0
filterTime=3
[arrive8]
active=1
reverse=0
filterTime=3
[mpg1]
active=1
reverse=0
filterTime=3
[mpg2]
active=1
reverse=0
filterTime=3
[mpg3]
active=1
reverse=0
filterTime=3
[mpg4]
active=1
reverse=0
filterTime=3
[mpg5]
active=1
reverse=0
filterTime=3
[mpg6]
active=1
reverse=0
filterTime=3
[mpg7]
active=1
reverse=0
filterTime=3
src/Bro.Device.GTSCard/GTSCardAPI.cs
@@ -143,6 +143,16 @@
        public const short LASER_CTRL_VOLTAGE = 2;
        public const short LASER_CTRL_MODE_PWM2 = 3;
        //指令返回值
        public const short ResultSuccess = 0;
        public const short ResultError = 1;
        public const short ResultNotSupported = 2;
        public const short ParameterError = 7;
        public const short CommunicationFailed = -1;
        public const short OpenFailed = -6;
        public const short NoResponse = -7;
        public struct TTrapPrm
        {
            public double acc;
src/Bro.Device.GTSCard/GTSCardConfig.cs
@@ -1,247 +1,675 @@
using Bro.Common.Base;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Drawing.Design;
using System.Linq;
using System.Threading;
using static Bro.Common.Helper.EnumHelper;
namespace Bro.Device.GTSCard
{
    public abstract class GTSCardOperationConfigBase : OperationConfigBase
    [Device("GTSCard", "固高板卡", EnumHelper.DeviceAttributeType.OperationConfig)]
    public class GTSCardOperationConfig : OperationConfigBase, IComplexDisplay
    {
        [Category("板卡运动配置")]
        [DisplayName("运动配置集合")]
        [Description("MovingOps:运动配置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<MovingOption>), typeof(UITypeEditor))]
        public ObservableCollection<MovingOption> MovingOps { get; set; } = new ObservableCollection<MovingOption>();
        public string GetDisplayText()
        {
            return MovingOps.Count() == 0 ? "--" : string.Join(";", MovingOps.Select(m => m.GetDisplayText()));
        }
    }
    [Device("GTSCard", "固高板卡", EnumHelper.DeviceAttributeType.OperationConfig)]
    public class GTSCardInitialConfig : InitialConfigBase, IMonitorConfig
    {
        [Category("板卡配置")]
        [DisplayName("卡号")]
        [Description("CardNum:卡号")]
        public int CardNum { get; set; }
        //[Category("板卡配置")]
        //[Description("IO卡号")]
        //public int IOCardNum { get; set; }
        [Category("板卡配置")]
        [DisplayName("初始配置文件路径")]
        [Description("InitialConfigFilePath:初始配置文件路径")]
        [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
        public string InitialConfigFilePath { get; set; }
        [Category("IO扫描配置")]
        [DisplayName("扫描间隔")]
        [Description("ScanInterval:扫描间隔,以毫秒为单位")]
        public int ScanInterval { get; set; } = 100;
        [Category("轴配置")]
        [DisplayName("轴数量")]
        [Description("AxisNum:轴数量")]
        public int AxisNum { get; set; } = 2;
        [Category("轴配置")]
        [DisplayName("轴配置信息集合")]
        [Description("AxisSettings:轴配置信息集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<AxisSetting>), typeof(UITypeEditor))]
        public List<AxisSetting> AxisSettings { get; set; } = new List<AxisSetting>();
        [Category("轴配置")]
        [DisplayName("轴速度比率")]
        [Description("AxisVelocityRatio:轴速度比率")]
        public double AxisVelocityRatio { get; set; } = 1;
        [Category("延时配置")]
        [DisplayName("动作完成后延迟")]
        [Description("ActionAfterDelay:动作完成后延迟")]
        public int ActionAfterDelay { get; set; } = 100;
        public bool IsEnableMonitor { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public int MonitorInterval { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public int MonitorTimeout { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public List<IMonitorSet> MonitorSetCollection { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public List<IMonitorSet> GetAllMonitorSet()
        {
            throw new NotImplementedException();
        }
    }
    /// <summary>
    /// 轴配置
    /// </summary>
    public class AxisSetting : IComplexDisplay
    {
        [Category("轴配置")]
        [DisplayName("轴号索引")]
        [Description("AxisIndex:轴号索引")]
        public int AxisIndex { get; set; }
        [Category("轴配置")]
        [DisplayName("轴名称")]
        [Description("AxisName:轴名称")]
        public string AxisName { get; set; }
        [Category("轴配置")]
        [DisplayName("轴是否启用")]
        [Description("IsAxisEnabled:轴是否启用")]
        public bool IsAxisEnabled { get; set; } = false;
        [Category("换算配置")]
        [DisplayName("脉冲数换算比例")]
        [Description("脉冲数和其他计量单位的换算比例,例如设置为1000,表示1000个脉冲等于1uint")]
        public int PulseRatio { get; set; } = 1;
        [Category("速度配置")]
        [DisplayName("默认速度参数")]
        [Description("VelocityPara:默认速度参数")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
        public VelocityPara VelocityPara { get; set; } = new VelocityPara();
        [Category("回原点设置")]
        [DisplayName("回原点模式")]
        [Description("HomeMode:回原点模式。0:一般模式 1:极限限位模式 2:外部触发模式")]
        public int HomeMode { get; set; } = 0;
        [Category("回原点设置")]
        [DisplayName("回原点方向")]
        [Description("IsHomePositive:回原点方向。 true:正方向开始  false:负方向开始")]
        public bool IsHomePositive { get; set; } = true;
        [Category("回原点设置")]
        [DisplayName("是否自动回原点")]
        [Description("IsAutoGoHome:是否自动回原点。 true:是  false:否")]
        public bool IsAutoGoHome { get; set; } = false;
        [Category("超时设置")]
        [DisplayName("回原点超时")]
        [Description("TimeOutHome:回原点超时,单位毫秒")]
        public int TimeOutHome { get; set; } = 30000;
        [Category("超时设置")]
        [DisplayName("运动超时")]
        [Description("TimeOutMove:运动超时,单位毫秒")]
        public int TimeOutMove { get; set; } = 10000;
        [Category("开闭环设置")]
        [DisplayName("是否使用Cmmd回馈")]
        [Description("IsUseCmmdPosition:是否使用Cmmd回馈")]
        public bool IsUseCmmdPosition { get; set; } = false;
        [Category("开闭环设置")]
        [DisplayName("是否使用板卡反馈停止信号")]
        [Description("IsUseMDNStopSignal:是否使用板卡反馈停止信号")]
        public bool IsUseMDNStopSignal { get; set; } = false;
        [Category("开闭环设置")]
        [DisplayName("是否启用报警")]
        [Description("IsUseWarning:是否启用报警")]
        public bool IsUseWarning { get; set; } = false;
        [Category("暂停配置")]
        [DisplayName("是否启用立即暂停")]
        [Description("IsImmediatePause:是否启用立即暂停")]
        public bool IsImmediatePause { get; set; } = false;
        [Category("暂停配置")]
        [DisplayName("轴位置处于区间内时适用的暂停操作")]
        [Description("ImmediatePauseSections:当轴位置处于区间内时适用的暂停操作")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<Section>), typeof(UITypeEditor))]
        public List<Section> ImmediatePauseSections { get; set; } = new List<Section>();
        public string GetDisplayText()
        {
            return AxisIndex + "-" + AxisName + "-" + (IsAxisEnabled ? "启用" : "禁用");
        }
    }
    /// <summary>
    /// 位置对象
    /// </summary>
    public class Section : IComplexDisplay
    {
        [Category("暂停区间")]
        [DisplayName("起始位置")]
        [Description("StartPosition:起始位置")]
        public int StartPosition { get; set; }
        [Category("暂停区间")]
        [DisplayName("结束位置")]
        [Description("EndPosition:结束位置")]
        public int EndPosition { get; set; }
        public string GetDisplayText()
        {
            return $"{StartPosition}--{EndPosition}";
        }
    }
    /// <summary>
    /// 速度参数对象
    /// </summary>
    public class VelocityPara
    {
        [Category("速度配置")]
        [DisplayName("速度")]
        [Description("Velocity:速度,为0时表示不修改当前设置")]
        public double Velocity { get; set; } = 0;
        [Category("速度配置")]
        [DisplayName("加速度")]
        [Description("Acc:加速度,为0时表示不修改当前设置")]
        public double Acc { get; set; } = 0;
        [Category("速度配置")]
        [DisplayName("减速度")]
        [Description("减速度,为0时表示不修改当前设置。回原点模式时设置为曲线参数")]
        public double Dec { get; set; } = 0;
    }
    /// <summary>
    /// 运动对象
    /// </summary>
    public class MovingOption : INotifyPropertyChanged, IComplexDisplay
    {
        private int axisIndex = 0;
        [Category("运动配置")]
        [DisplayName("运动轴索引")]
        [Description("AxisIndex:运动轴索引")]
        [TypeConverter(typeof(AxisIndexConvert))]
        public int AxisIndex
        {
            get => axisIndex;
            set
            {
                if (axisIndex != value)
                {
                    axisIndex = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("AxisIndex"));
                }
                axisIndex = value;
            }
        }
        private MotorMoveMode moveMode = MotorMoveMode.Normal;
        [Category("运动配置")]
        [DisplayName("运动模式")]
        [Description("MoveMode:运动模式")]
        public MotorMoveMode MoveMode
        {
            get => moveMode;
            set
            {
                if (moveMode != value)
                {
                    moveMode = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MoveMode"));
                }
                moveMode = value;
            }
        }
        private bool isAbsolute = true;
        [Category("运动配置")]
        [DisplayName("是否绝对运动")]
        [Description("IsAbsolute:是否绝对运动")]
        public bool IsAbsolute
        {
            get => isAbsolute;
            set
            {
                if (isAbsolute != value)
                {
                    isAbsolute = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsAbsolute"));
                }
                isAbsolute = value;
            }
        }
        private int destination = 0;
        [Category("运动配置")]
        [DisplayName("目的地")]
        [Description("Destination:目的地")]
        public int Destination
        {
            get => destination;
            set
            {
                if (destination != value)
                {
                    destination = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Destination"));
                }
                destination = value;
            }
        }
        private VelocityPara velocityPara = new VelocityPara();
        [Category("运动配置")]
        [DisplayName("速度参数")]
        [Description("VelocityPara:速度参数")]
        [TypeConverter(typeof(ComplexObjectConvert))]
        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
        public VelocityPara VelocityPara
        {
            get => velocityPara;
            set
            {
                velocityPara = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("VelocityPara"));
            }
        }
        internal List<AxisSetting> _axisSettingList = new List<AxisSetting>();
        public void SetAxisSetting(List<AxisSetting> settings)
        {
            if (settings != null)
                _axisSettingList = settings;
        }
        public string GetDisplayText()
        {
            string axisName = AxisIndex.ToString();
            var axisSet = _axisSettingList.FirstOrDefault(a => a.AxisIndex == AxisIndex);
            if (axisSet != null)
            {
                axisName += ("-" + axisSet.AxisName);
            }
            return axisName + "," + MoveMode.ToString() + "," + (IsAbsolute ? "Abs" : "Rel") + "," + Destination;
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
    public class AxisIndexConvert : TypeConverter
    {
        Dictionary<int, string> _indexNameDict = new Dictionary<int, string>();
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            MovingOption mo = context.Instance as MovingOption;
            _indexNameDict = mo._axisSettingList.ToDictionary(a => a.AxisIndex, a => a.AxisIndex + "-" + a.AxisName);
            return new StandardValuesCollection(_indexNameDict.Keys);
        }
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object v)
        {
            if (v is string)
            {
                foreach (var indexName in _indexNameDict)
                {
                    if (indexName.Value == v.ToString())
                    {
                        return indexName.Key;
                    }
                }
                return Convert.ToInt32(v);
            }
            return base.ConvertFrom(context, culture, v);
        }
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object v, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                if (_indexNameDict.ContainsKey(Convert.ToInt32(v)))
                {
                    return _indexNameDict[Convert.ToInt32(v)];
                }
            }
            return base.ConvertTo(context, culture, v, destinationType);
        }
        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return false;
        }
    }
    public class AxisConflictSet : IComplexDisplay
    {
        [Category("1.轴冲突条件")]
        [Description("轴冲突条件,满足全部条件时轴运动需要检查冲突")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<AxisLimit>), typeof(UITypeEditor))]
        public List<AxisLimit> AxisOptions { get; set; } = new List<AxisLimit>();
        [Category("1.轴冲突条件")]
        [Description("IO冲突条件,满足全部条件时轴运动需要检查冲突")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<IOLimit>), typeof(UITypeEditor))]
        public List<IOLimit> IOOptions { get; set; } = new List<IOLimit>();
        [Category("2.轴冲突限制")]
        [Description("轴冲突限制,轴运动允许区间")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<AxisLimit>), typeof(UITypeEditor))]
        public List<AxisLimit> AxisLimits { get; set; } = new List<AxisLimit>();
        [Category("2.轴冲突限制")]
        [Description("IO允许条件,允许输出的IO")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<IOLimit>), typeof(UITypeEditor))]
        public List<IOLimit> IOOutputs { get; set; } = new List<IOLimit>();
        [Category("3.轴冲突启用")]
        [Description("true:启用轴冲突限制 false:不启用轴冲突限制")]
        public bool IsEnabled { get; set; } = true;
        public string GetDisplayText()
        {
            string optionStr = "Options:" + String.Join(";", AxisOptions.Select(a => a.GetDisplayText()));
            string limitStr = "Limits:" + String.Join(";", AxisLimits.Select(a => a.GetDisplayText()));
            return optionStr + "|" + limitStr;
        }
    }
    public class AxisLimit : IComplexDisplay
    {
        [Category("1.轴号")]
        [Description("1.轴号")]
        public int AxisIndex { get; set; }
        [Category("2.轴最小限制")]
        [Description("2.轴最小限制")]
        public int LimitMin { get; set; }
        [Category("3.轴最大限制")]
        [Description("3.轴最大限制")]
        public int LimitMax { get; set; }
        private int currentPosition = 0;
        [Browsable(false)]
        [JsonIgnore]
        public int CurrentPosition
        {
            get => currentPosition;
            set
            {
                if (currentPosition != value)
                {
                    if (value >= LimitMin && value <= LimitMax)
                    {
                        IsInLimit = true;
                    }
                    else
                    {
                        IsInLimit = false;
                    }
                }
                currentPosition = value;
            }
        }
        [Browsable(false)]
        [JsonIgnore]
        public bool IsInLimit { get; set; }
        public string GetDisplayText()
        {
            return String.Format("Index:{0},{1}->{2}", AxisIndex, LimitMin, LimitMax);
        }
    }
    public class IOLimit : IComplexDisplay
    {
        [Category("1.IO限制")]
        [Description("IO索引")]
        public int IOIndex { get; set; }
        [Category("1.IO限制")]
        [Description("IO限制值。输入判断时该值作为启用判断值,输出判断时该值作为允许输出值")]
        public bool IOSignal { get; set; }
        public string GetDisplayText()
        {
            return IOIndex + "--" + IOSignal.ToString();
        }
    }
    public class AxisMovingStay
    {
        public int Position { get; set; }
        public int Velocity { get; set; }
        public AutoResetEvent MoveHandle { get; set; } = new AutoResetEvent(false);
        public AutoResetEvent MoveSendHandle { get; set; } = new AutoResetEvent(false);
    }
        ///// <summary>
        ///// 需要操作的PLC项
    ///// 点位类型
        ///// </summary>
        //public List<PLCItem> Items { get; set; } = new List<PLCItem>();
    }
    //public class GTSCardInitialConfigBase : InitialConfigBase, IMonitorConfig
    //public enum PosType
    //{
    //    [Category("驱动类型")]
    //    [Description("驱动类型")]
    //    [DisplayName("驱动类型")]
    //    [TypeConverter(typeof(PLCTypeConverter))]
    //    public override string DriverType { get; set; }
    //    [Category("警报配置")]
    //    [Description("警报配置列表")]
    //    [DisplayName("警报配置")]
    //    [TypeConverter(typeof(CollectionCountConvert))]
    //    [Editor(typeof(WarningSetsEditor), typeof(UITypeEditor))]
    //    public List<WarningSet> WarningSetCollection { get; set; } = new List<WarningSet>();
    //    #region IMonitorConfig
    //    [Category("监听设置")]
    //    [Description("监听操作配置集合")]
    //    [DisplayName("监听配置")]
    //    [TypeConverter(typeof(CollectionCountConvert))]
    //    [Editor(typeof(ComplexCollectionEditor<MonitorSet>), typeof(UITypeEditor))]
    //    public List<IMonitorSet> MonitorSetCollection { get; set; } = new List<IMonitorSet>();
    //    [Category("监听设置")]
    //    [Description("true:启动监听 false:关闭监听")]
    //    [DisplayName("监听启用")]
    //    public bool IsEnableMonitor { get; set; } = true;
    //    [Category("监听设置")]
    //    [Description("扫描间隔时间,单位:ms")]
    //    [DisplayName("扫描间隔")]
    //    public int MonitorInterval { get; set; } = 100;
    //    [Category("监听设置")]
    //    [Description("超时设置,单位:ms")]
    //    [DisplayName("监听超时")]
    //    public int MonitorTimeout { get; set; } = 500;
    //    [Category("事件地址设置")]
    //    [Description("事件开始地址,PLC的实际寄存器地址。十进制,不包含功能码。")]
    //    [DisplayName("监听开始地址")]
    //    public int EventStartAddress { get; set; } = 8000;
    //    [Category("事件地址设置")]
    //    [Description("事件地址长度,最大长度128")]
    //    [DisplayName("监听长度")]
    //    public int EventLength { get; set; } = 120;
    //    public List<IMonitorSet> GetAllMonitorSet()
    //    {
    //        WarningSetCollection.ForEach(m => m.Source = this.Name);
    //        MonitorSetCollection.ForEach(m => m.SourceDevice = this.Id);
    //        return MonitorSetCollection;
    //    }
    //    #endregion
    //    #region IMotion Related
    //    [Category("运动配置")]
    //    [Description("运动轴状态集合")]
    //    [DisplayName("运动轴状态集合")]
    //    [TypeConverter(typeof(CollectionCountConvert))]
    //    [Editor(typeof(ComplexCollectionEditor<PLCMotionDefinition_State>), typeof(UITypeEditor))]
    //    public List<PLCMotionDefinition_State> MotionStateCollection { get; set; } = new List<PLCMotionDefinition_State>();
    //    #endregion
    //    /// <summary>
    //    /// 直线插补
    //    /// </summary>
    //    Line = 1,
    //    /// <summary>
    //    /// 圆弧插补(半径)
    //    /// </summary>
    //    CircleRadius,
    //    /// <summary>
    //    /// 圆弧插补(圆心)
    //    /// </summary>
    //    CircleCenter
    //}
    /// <summary>
    /// 点位类型
    /// </summary>
    public enum PosType
    {
        /// <summary>
        /// 直线插补
        /// </summary>
        Line = 1,
        /// <summary>
        /// 圆弧插补(半径)
        /// </summary>
        CircleRadius,
        /// <summary>
        /// 圆弧插补(圆心)
        /// </summary>
        CircleCenter
    }
    public static class GTSCardParameter
    {
        #region 运动参数
        public static int Dangliang = 1;
        public static int AxisCount = 2;//运动轴数量
        public static short CardNum = Convert.ToInt16(ConfigurationManager.AppSettings["cardNum"]);
        public static short fifo = Convert.ToInt16(ConfigurationManager.AppSettings["fifo"]);
        public static int FlySpeed = Convert.ToInt32(ConfigurationManager.AppSettings["flySpeed"]);
        public static double FlyAcc = Convert.ToDouble(ConfigurationManager.AppSettings["flyAcc"]);
        public static int P2PSpeed = Convert.ToInt32(ConfigurationManager.AppSettings["p2pSpeed"]);
        public static double P2PAcc = Convert.ToDouble(ConfigurationManager.AppSettings["p2pAcc"]);
        public static double P2PDec = Convert.ToDouble(ConfigurationManager.AppSettings["p2pDec"]);
        public static int FreeSpeed = Convert.ToInt32(ConfigurationManager.AppSettings["freeSpeed"]);
        public static int VelEnd = Convert.ToInt32(ConfigurationManager.AppSettings["velEnd"]);//飞拍结束速度
        public static int HomeSpeed = Convert.ToInt32(ConfigurationManager.AppSettings["homeSpeed"]);
        public static int Loading = Convert.ToInt32(ConfigurationManager.AppSettings["loading"]);
        //public const short cardn = 0;//运动控制器卡号 默认为:0
        //public const short crdn = 1;//坐标系号 取值范围:[1, 2]
        //public const short fifo = 0;//插补缓存区号 取值范围:[0, 1],默认值为:0
        //public const int flySpeed = 250;//飞拍速度
        //public const double flyAcc = 0.5;//飞拍加速度
        //public const int gocatorSpeed = 150;//3D检测轴运动速度
        //public const int p2pSpeed = 250;//P2P速度
        //public const double p2pAcc = 1;//P2P加速度
        //public const double p2pDec = 1;//P2P减速度
        //public const int calibrationSpeed = 10;//标定速度
        //public const int calibrationZ = 19336;//标定Z轴高度
        //public const int barcodeSpeed = 250;//条码检测轴运动速度
        //public const int freeSpeed = 250;//非检测时轴运动速度
        //public const int velEnd = 0;//飞拍结束速度
        //public const int homeSpeed = 50;//回零速度
        //public const int loading = 80000;//上料位置
        #endregion
    //public static class GTSCardParameter
    //{
    //    #region 运动参数
    //    public static int Dangliang = 1;
    //    public static int AxisCount = 2;//运动轴数量
    //    public static short CardNum = Convert.ToInt16(ConfigurationManager.AppSettings["cardNum"]);
    //    public static short fifo = Convert.ToInt16(ConfigurationManager.AppSettings["fifo"]);
    //    public static int FlySpeed = Convert.ToInt32(ConfigurationManager.AppSettings["flySpeed"]);
    //    public static double FlyAcc = Convert.ToDouble(ConfigurationManager.AppSettings["flyAcc"]);
    //    public static int P2PSpeed = Convert.ToInt32(ConfigurationManager.AppSettings["p2pSpeed"]);
    //    public static double P2PAcc = Convert.ToDouble(ConfigurationManager.AppSettings["p2pAcc"]);
    //    public static double P2PDec = Convert.ToDouble(ConfigurationManager.AppSettings["p2pDec"]);
    //    public static int FreeSpeed = Convert.ToInt32(ConfigurationManager.AppSettings["freeSpeed"]);
    //    public static int VelEnd = Convert.ToInt32(ConfigurationManager.AppSettings["velEnd"]);//飞拍结束速度
    //    public static int HomeSpeed = Convert.ToInt32(ConfigurationManager.AppSettings["homeSpeed"]);
    //    public static int Loading = Convert.ToInt32(ConfigurationManager.AppSettings["loading"]);
    //    //public const short cardn = 0;//运动控制器卡号 默认为:0
    //    //public const short crdn = 1;//坐标系号 取值范围:[1, 2]
    //    //public const short fifo = 0;//插补缓存区号 取值范围:[0, 1],默认值为:0
    //    //public const int flySpeed = 250;//飞拍速度
    //    //public const double flyAcc = 0.5;//飞拍加速度
    //    //public const int gocatorSpeed = 150;//3D检测轴运动速度
    //    //public const int p2pSpeed = 250;//P2P速度
    //    //public const double p2pAcc = 1;//P2P加速度
    //    //public const double p2pDec = 1;//P2P减速度
    //    //public const int calibrationSpeed = 10;//标定速度
    //    //public const int calibrationZ = 19336;//标定Z轴高度
    //    //public const int barcodeSpeed = 250;//条码检测轴运动速度
    //    //public const int freeSpeed = 250;//非检测时轴运动速度
    //    //public const int velEnd = 0;//飞拍结束速度
    //    //public const int homeSpeed = 50;//回零速度
    //    //public const int loading = 80000;//上料位置
    //    #endregion
        #region IO
        /// <summary>
        /// 输入IO默认值
        /// </summary>
        public const int InDefaultValue = 0xFFDA;
        /// <summary>
        /// 夹紧气缸
        /// </summary>
        public const short EXO_1 = 100;//夹紧气缸
        /// <summary>
        /// 日光灯
        /// </summary>
        public const short EXO_2 = 101;//日光灯
        /// <summary>
        /// 光源切换
        /// </summary>
        public const short EXO_3 = 102;//光源切换
        /// <summary>
        /// 红灯
        /// </summary>
        public const short EXO_4 = 103;//红灯
        /// <summary>
        /// 黄灯
        /// </summary>
        public const short EXO_5 = 104;//黄灯
        /// <summary>
        /// 绿灯
        /// </summary>
        public const short EXO_6 = 105;//绿灯
        /// <summary>
        /// 蜂鸣器
        /// </summary>
        public const short EXO_7 = 106;//蜂鸣器
        /// <summary>
        /// Gocator X
        /// </summary>
        public const short EXO_8 = 107;//Gocator X
    //    #region IO
    //    /// <summary>
    //    /// 输入IO默认值
    //    /// </summary>
    //    public const int InDefaultValue = 0xFFDA;
    //    /// <summary>
    //    /// 夹紧气缸
    //    /// </summary>
    //    public const short EXO_1 = 100;//夹紧气缸
    //    /// <summary>
    //    /// 日光灯
    //    /// </summary>
    //    public const short EXO_2 = 101;//日光灯
    //    /// <summary>
    //    /// 光源切换
    //    /// </summary>
    //    public const short EXO_3 = 102;//光源切换
    //    /// <summary>
    //    /// 红灯
    //    /// </summary>
    //    public const short EXO_4 = 103;//红灯
    //    /// <summary>
    //    /// 黄灯
    //    /// </summary>
    //    public const short EXO_5 = 104;//黄灯
    //    /// <summary>
    //    /// 绿灯
    //    /// </summary>
    //    public const short EXO_6 = 105;//绿灯
    //    /// <summary>
    //    /// 蜂鸣器
    //    /// </summary>
    //    public const short EXO_7 = 106;//蜂鸣器
    //    /// <summary>
    //    /// Gocator X
    //    /// </summary>
    //    public const short EXO_8 = 107;//Gocator X
        /// <summary>
        /// 正面光(左)
        /// </summary>
        public const short EXO_9 = 108;//正面光(左)
    //    /// <summary>
    //    /// 正面光(左)
    //    /// </summary>
    //    public const short EXO_9 = 108;//正面光(左)
        /// <summary>
        /// 正面光(后)
        /// </summary>
        public const short EXO_10 = 109;//正面光(后)
    //    /// <summary>
    //    /// 正面光(后)
    //    /// </summary>
    //    public const short EXO_10 = 109;//正面光(后)
        /// <summary>
        /// 正面光(右)
        /// </summary>
        public const short EXO_11 = 110;//正面光(右)
    //    /// <summary>
    //    /// 正面光(右)
    //    /// </summary>
    //    public const short EXO_11 = 110;//正面光(右)
        /// <summary>
        /// 正面光(前)
        /// </summary>
        public const short EXO_12 = 111;//正面光(前)
    //    /// <summary>
    //    /// 正面光(前)
    //    /// </summary>
    //    public const short EXO_12 = 111;//正面光(前)
        /// <summary>
        /// Gocator Y
        /// </summary>
        public const short EXO_16 = 115;//Gocator Y
    //    /// <summary>
    //    /// Gocator Y
    //    /// </summary>
    //    public const short EXO_16 = 115;//Gocator Y
        /// <summary>
        /// 输出IO默认值
        /// </summary>
        public const int OutDefaultValue = 0xFFF;
        /// <summary>
        /// 左启动
        /// </summary>
        public const short EXI0 = 0;//左起动
        /// <summary>
        /// 右启动
        /// </summary>
        public const short EXI1 = 1;//右起动
        /// <summary>
        /// 停止
        /// </summary>
        public const short EXI2 = 2;//停止
        /// <summary>
        /// 复位
        /// </summary>
        public const short EXI3 = 3;//复位
        /// <summary>
        /// 急停
        /// </summary>
        public const short EXI4 = 4;//急停
        /// <summary>
        /// 门开关
        /// </summary>
        public const short EXI5 = 5;//门开关
        /// <summary>
        /// 安全光幕
        /// </summary>
        public const short EXI6 = 6;//安全光幕
    //    /// <summary>
    //    /// 输出IO默认值
    //    /// </summary>
    //    public const int OutDefaultValue = 0xFFF;
    //    /// <summary>
    //    /// 左启动
    //    /// </summary>
    //    public const short EXI0 = 0;//左起动
    //    /// <summary>
    //    /// 右启动
    //    /// </summary>
    //    public const short EXI1 = 1;//右起动
    //    /// <summary>
    //    /// 停止
    //    /// </summary>
    //    public const short EXI2 = 2;//停止
    //    /// <summary>
    //    /// 复位
    //    /// </summary>
    //    public const short EXI3 = 3;//复位
    //    /// <summary>
    //    /// 急停
    //    /// </summary>
    //    public const short EXI4 = 4;//急停
    //    /// <summary>
    //    /// 门开关
    //    /// </summary>
    //    public const short EXI5 = 5;//门开关
    //    /// <summary>
    //    /// 安全光幕
    //    /// </summary>
    //    public const short EXI6 = 6;//安全光幕
        public const short EXI7 = 7;//
        /// <summary>
        /// 夹紧气缸原位
        /// </summary>
        public const short EXI8 = 8;//夹紧气缸原位
        /// <summary>
        /// 夹紧气缸到位
        /// </summary>
        public const short EXI9 = 9;//夹紧气缸到位
        #endregion
    }
    //    public const short EXI7 = 7;//
    //    /// <summary>
    //    /// 夹紧气缸原位
    //    /// </summary>
    //    public const short EXI8 = 8;//夹紧气缸原位
    //    /// <summary>
    //    /// 夹紧气缸到位
    //    /// </summary>
    //    public const short EXI9 = 9;//夹紧气缸到位
    //    #endregion
    //}
}
src/Bro.Device.GTSCard/GTSCardDriver.cs
@@ -1,27 +1,101 @@
using Bro.Common.Base;
using Bro.Common.Helper;
using Bro.Common.Interface;
using Bro.Common.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Bro.Device.GTSCard
{
    [Device("GTSCard", "固高板卡", EnumHelper.DeviceAttributeType.Device)]
    public class GTSCardDriver : DeviceBase,IMonitor, IMotion
    {
        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 GTSCardInitialConfig IConfig
        {
            get
            {
                return InitialConfig as GTSCardInitialConfig;
            }
        }
        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();
        /// <summary>
        /// 是否复位标志
        /// </summary>
        bool _isResetting = false;
        public void SetResetFlag(bool isReset)
        {
            _isResetting = isReset;
        }
        public List<AxisInfo> GetCurrentAxisInfo(params string[] axisName)
        {
            throw new NotImplementedException();
        }
        #region DeviceBase
        protected override void Init()
        {
            throw new NotImplementedException();
            InitialMotionCard((short)IConfig.CardNum, IConfig.InitialConfigFilePath);
            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);
        }
        protected override void Pause()
@@ -43,43 +117,874 @@
        {
            throw new NotImplementedException();
        }
        #endregion
        /// <summary>
        /// 点位到点位 运动
        /// </summary>
        /// <param name="cardNum">卡号</param>
        /// <param name="axisNum">轴号</param>
        /// <param name="prfPosition">规划位置,单位毫米</param>
        /// <param name="prfVelocity">规划速度,单位米每秒</param>
        public void P2P(short cardNum, short axisNum, int prfPosition, int prfVelocity)
        #region GTSCard
        public void ClearPosition(short cardNum, short axisNum)
        {
            GTSCardAPI.TTrapPrm trapprm;
            GTSCardAPI.GT_PrfTrap(cardNum, axisNum);
            GTSCardAPI.GT_GetTrapPrm(cardNum, axisNum, out trapprm);
            trapprm.acc = GTSCardParameter.P2PAcc;
            trapprm.dec = GTSCardParameter.P2PDec;
            trapprm.smoothTime = 1;
            GTSCardAPI.GT_SetTrapPrm(cardNum, axisNum, ref trapprm);
            GTSCardAPI.GT_SetPos(cardNum, axisNum, prfPosition * GTSCardParameter.Dangliang);
            GTSCardAPI.GT_SetVel(cardNum, axisNum, prfVelocity * GTSCardParameter.Dangliang);
            GTSCardAPI.GT_Update(cardNum, 1 << (axisNum - 1));
            int ret = GTSCardAPI.GT_SetPos(cardNum, axisNum, 0);
        }
        /// <summary>
        /// Jog运动
        /// Load Motion Card parameter from file
        /// </summary>
        /// <param name="cardNum"></param>
        /// <param name="axisNum"></param>
        /// <param name="velocity">规划速度,单位米每秒</param>
        public void Jog(short cardNum, short axisNum, double velocity)
        /// <param name="fileName">Invalid Parameter</param>
        /// <returns></returns>
        public void InitialMotionCard(short cardNum, string fileName)
        {
            var res = GTSCardAPI.GT_LoadConfig(cardNum, fileName);
            if (res != GTSCardAPI.ResultSuccess)
            {
                throw new Exception("板卡载入配置文件异常,错误码:" + res);
            }
        }
        /// <summary>
        /// 单个轴 点位到点位运动(异步)
        /// </summary>
        /// <param name="item">运动对象</param>
        /// <returns></returns>
        public async Task SingleAxisMovingAsync(MovingOption item)
        {
            await Task.Run(() =>
            {
                SingleAxisMoving(item);
            });
        }
        /// <summary>
        /// 单个轴 点位到点位运动(异步)
        /// </summary>
        /// <param name="item">运动对象</param>
        public void SingleAxisMoving(MovingOption item)
        {
            if (IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == item.AxisIndex)?.IsAxisEnabled ?? false)
            {
                axisImmediatePauseHandleDict[item.AxisIndex].WaitOne();
                VelocityPara vel = new VelocityPara();
                if (item.VelocityPara.Velocity != 0)
                {
                    velIndexDict[item.AxisIndex] = vel = item.VelocityPara;
                }
                else
                {
                    vel = velIndexDict[item.AxisIndex];
                }
                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;
        }
        /// <summary>
        ///Get single Axis Feedback position
        /// </summary>
        /// <param name="axisNum">Axis number</param>
        /// <param name="nPosition">Feedback/Encorde position </param>
        /// <returns></returns>
        public void GetPosition(int axisNum, ref int nPosition)
        {
            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);
            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)
            {
                throw new Exception("轴" + axisNum + "设置参数异常,错误码:" + ret);
            }
        }
        /// <summary>
        /// Set Single Axis Do Jog Move
        /// </summary>
        /// <param name="axisNum">AxisNo</param>
        /// <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)
        {
            GTSCardAPI.TJogPrm jogprm = new GTSCardAPI.TJogPrm();
            short rtn = GTSCardAPI.GT_PrfJog(cardNum, axisNum);
            short rtn = GTSCardAPI.GT_PrfJog((short)IConfig.CardNum, (short)axisNum);
            jogprm.acc = 1;
            jogprm.dec = 1;
            GTSCardAPI.GT_SetJogPrm(cardNum, axisNum, ref jogprm);//设置jog运动参数
            GTSCardAPI.GT_SetVel(cardNum, axisNum, velocity);//设置目标速度
            GTSCardAPI.GT_Update(cardNum, 1 << (axisNum - 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)
            {
                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)
        {
            try
            {
                if (CurrentState == EnumHelper.DeviceState.DSExcept)
                {
                    LogAsync(DateTime.Now, "板卡异常状态", "轴" + axisNum + "试图异常状态运动");
                    return;
                }
                if (CurrentState != EnumHelper.DeviceState.DSOpen)
                {
                    return;
                }
                int currentPosition = GetPosition(axisNum);
                if (OnAxisStartToCheckConfliction != null && OnAxisStartToCheckConfliction.Invoke(axisNum, currentPosition, currentPosition + nDistance))
                {
                    return;
                }
                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)
                {
                    throw new Exception("轴" + axisNum + "启动相对运动异常,错误码:" + ret);
                }
                RunFinish(axisNum, false);
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(ex);
            }
        }
        /// <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)
        {
            try
            {
            ReMove:
                MoveAbsAsync(axisNum, nPosition);
                var runFinish = Task.Run(() => RunFinish(axisNum), axisMoveCancelDict[axisNum].Token);
                try
                {
                    runFinish.Wait(axisMoveCancelDict[axisNum].Token);
                }
                catch (OperationCanceledException ex)
                {
                    goto ReMove;
                }
                //// 删除记录
                //if (currentCommandDic.ContainsKey(axisNum))
                //{
                //    currentCommandDic.TryRemove(axisNum, out int[] temp);
                //}
            }
            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;
                        }
                    }
                }
            }
        }
        /// <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)
        {
            int ret = GTSCardAPI.GT_Stop((short)IConfig.CardNum, 1 << (axisNum - 1), option);
            if (ret != (Int32)APS_Define.ResultSuccess)
            {
                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)
                {
                    return false;
                }
                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)
                {
                    return false;
                }
                bool isAlarm = Convert.ToBoolean(nMotionIoSts & (1 << (int)MOTION_IOSTATUS.ALM));
                if (strcMotionIoSts.ALM != isAlarm)
                {
                    strcMotionIoSts.ALM = isAlarm;
                    OnMotionAlarm?.Invoke(nAxis, isAlarm);
                }
                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);
            });
        }
        /// <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)
        {
            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);  //启动回零
                while (true)
                {
                    Thread.Sleep(5);
                    GTSCardAPI.GT_GetHomeStatus(cardn, axisn, out homests);
                    if (homests.run == 0)
                    {
                        if (homests.error == 0)
                        {
                            Thread.Sleep(200);
                            GTSCardAPI.GT_ZeroPos(cardn, axisn, 1);
                        }
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                MoveStop();
                OnExceptionRaised?.Invoke(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>
@@ -99,36 +1004,45 @@
        /// <summary>
        /// 输出
        /// </summary>
        /// <param name="cardNumo">卡号</param>
        /// <param name="cardNum">卡号</param>
        /// <param name="index">输出口,返回1-16</param>
        /// <param name="value">0表示输出,1表示关闭</param>
        public void WriteOut(short cardNumo, short index, bool value)
        /// <param name="value">false表示输出,true表示关闭</param>
        public void WriteOut(short cardNum, short index, bool value)
        {
            short outNum = (short)(index % 100 + 1);
            switch (value)
            if (value)
            {
                case true:
                    {
                        GTSCardAPI.GT_SetDoBit(cardNumo, GTSCardAPI.MC_GPO, outNum, 0);//按位输出,0表示输出,1表示关闭
                GTSCardAPI.GT_SetDoBit(cardNum, GTSCardAPI.MC_GPO, outNum, 0);
                    }
                    break;
                case false:
            else
                    {
                        GTSCardAPI.GT_SetDoBit(cardNumo, GTSCardAPI.MC_GPO, outNum, 1);//按位输出,0表示输出,1表示关闭
                    }
                    break;
                GTSCardAPI.GT_SetDoBit(cardNum, GTSCardAPI.MC_GPO, outNum, 1);
            }
        }
        /// <summary>
        /// 停止 某个轴
        /// </summary>
        /// <param name="cardNum"></param>
        /// <param name="axisNum"></param>
        /// <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>
@@ -137,21 +1051,15 @@
        /// <param name="cardNum"></param>
        /// <param name="axisNum">轴号</param>
        /// <param name="value">停止方式,false表示平滑停止,true表示紧急停止</param>
        public void Stop(short cardNum, short axisNum, bool value)
        public void Stop(short cardNum, short axisNum, bool emergencyStop)
        {
            switch (value)
            {
                case false:
                    {
                        GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), 0);
                    }
                    break;
                case true:
            if (emergencyStop)
                    {
                        GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), 1 << (axisNum - 1));
                    }
                    break;
            else
            {
                GTSCardAPI.GT_Stop(cardNum, 1 << (axisNum - 1), 0);
            }
        }
@@ -190,20 +1098,20 @@
        /// <summary>
        /// 读取IO输出状态
        /// </summary>
        /// <param name="cardNumo"></param>
        /// <param name="cardNum"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool GetDoSts(short cardNumo, short index)
        public bool GetDoSts(short cardNum, short index)
        {
            short outNum = 0;
            int outSts;
            outNum = (short)(index % 100);
            GTSCardAPI.GT_GetDo(cardNumo, GTSCardAPI.MC_GPO, out outSts);
            GTSCardAPI.GT_GetDo(cardNum, GTSCardAPI.MC_GPO, out outSts);
            if ((outSts & (1 << outNum)) == 0) return true;
            else return false;
        }
        static object lockObj = new object();
      
        /// <summary>
@@ -218,7 +1126,7 @@
            {
                double prfpos = 0; uint pclock = 0;
                GTSCardAPI.GT_GetPrfPos(cardNum, axisNum, out prfpos, 1, out pclock);
                return prfpos / GTSCardParameter.Dangliang;
                return prfpos / IConfig.AxisVelocityRatio;
            }
        }
@@ -239,6 +1147,7 @@
                else return false;              //运行中返回false
            }
        }
        #endregion
        public void Monitor()
        {
src/Bro.Device.GTSCard/gts.dll
Binary files differ
src/Bro.Device.GTSCard/packages.config
New file
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />
</packages>