using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using FObjBase; using System.IO; using System.ComponentModel; using System.Xml.Serialization; using FlyADBase; using static FLY.Simulation.Flyad7.FLYAD7; using AutoMapper; using Newtonsoft.Json; namespace FLY.Simulation.Flyad7 { public class FLYAD7 : IFlyAD, INotifyPropertyChanged { public IPEndPoint LocalEP { get { return Misc.StringConverter.ToIPEndPoint(Addr); } } public string Addr { get; set; } = "127.0.0.1:20008"; public int Version { get; } = 2; /// /// 更新率为 1s 的AD值 /// public int ADForShow { get; set; } /// /// 更新率为 0.1s 的脉冲值 /// public int PosForShow { get; set; } /// /// 更新率为 0.1s 的脉冲值2 /// public int Pos2ForShow { get; set; } private int ADSum; private int ADCnt; public List TimeGrid = new List(); public double TimeSpan1ms { get; set; } /// /// 原始脉冲值 /// public int PositionOrg=0; #region 模拟部分 SimDrive mSimDrive = new SimDrive(); public OrderMan mOrderMan = new OrderMan(); public enum SIM_MODE { /// /// 未知 /// Unknown, /// /// 吹膜 /// Blowing, /// /// 涂布 /// Coating, /// /// 头尾部检测 /// HeaderAndTailer, /// /// 流延 /// Casting } /// /// 系统重启时,mSimModeCurr = SimMode ,之后怎么变化都不会影响 mSimModeCurr /// public SIM_MODE SimMode { get; set; } = SIM_MODE.Coating; /// /// 当前模拟类型 /// SIM_MODE mSimMode; public FLY.Simulation.ISimulationGageAD mSimGageAD; //public SimDrive_VF0 mSimDriveVF0 = new SimDrive_VF0(); //public SimDrive_Servo mSimDriveServo = new SimDrive_Servo(); #endregion public FLYAD7() { if (!Load()) Save(); } public FLYAD7(string filePath) { file_path = filePath; if (!Load()) Save(); } public void Init(FLY.Simulation.ISimulationGageAD simulationGageAD) { mSimGageAD = simulationGageAD; Init2(); } public void Init() { mSimMode = SimMode; switch (mSimMode) { case SIM_MODE.Blowing: mSimGageAD = new FLY.Simulation.Blowing.GageAD(); break; case SIM_MODE.HeaderAndTailer: mSimGageAD = new FLY.Simulation.HeaderAndTailer.GageAD(); break; case SIM_MODE.Casting: mSimGageAD = new FLY.Simulation.Casting.GageAD(); break; case SIM_MODE.Coating: mSimGageAD = new FLY.Simulation.Coating.GageAD(); break; default: { mSimGageAD = new FLY.Simulation.Blowing.GageAD(); mSimMode = SIM_MODE.Blowing; break; } } Init2(); } void Init2() { HVelocity1 = 3000; HVelocity2 = 100; Velocity = 3000; SVelocity = 50; JogVelocity = 3000; ATime = 500; DTime = 500; mSimDrive.MotorType = MotorType; mSimDrive.MaxPos = (int)(mSimGageAD.TotalLength / mSimGageAD.Mmpp); mSimDrive.MinPos = 0; mSimDrive.OrgPos = (int)(50 / mSimGageAD.Mmpp); mSimDrive.AccTime = ATime; mSimDrive.DecTime = DTime; mSimDrive.SVelocity = SVelocity / Ratio; mSimDrive.PosOffset = PosOffset; mOrderMan.Init(this, mSimDrive); mOrderMan.order_org.Velocity1 = HVelocity1 / Ratio; mOrderMan.order_org.Velocity2 = HVelocity2 / Ratio; mOrderMan.order_runto.Velocity = Velocity / Ratio; mOrderMan.order_forw.Velocity = JogVelocity / Ratio; mOrderMan.order_backw.Velocity = JogVelocity / Ratio; mOrderMan.order_sync.mSyncOrderList = mSyncOrderList; mOrderMan.FinishEvent += new OrderMan.FinishEventHandler(mSimDriveMan_FinishEvent); this.PropertyChanged += new PropertyChangedEventHandler(FLYAD7_PropertyChanged); PollModule.Current.Poll_Config( PollModule.POLL_CONFIG.ADD, new PollModule.PollHandler(OnPoll), new TimeSpan((long)(TimeSpan.TicksPerMillisecond * 1.28))); //一定只能用TimeSpan.FromTicks(12800) //TimeSpan.FromMilliseconds(1.28) 会被取整,最后是 1ms } void mSimDriveMan_FinishEvent(bool isNormalStop) { if (isNormalStop) DriveStatus = DRIVE_MAN_STATUS.STOP; else DriveStatus = DRIVE_MAN_STATUS.LIMIT; DriveOrder = DRIVE_MAN_ORDER.IDLE; } void FLYAD7_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(DriveOrder)) { switch (DriveOrder) { case DRIVE_MAN_ORDER.FORWORD: DriveStatus = DRIVE_MAN_STATUS.RUNNING; mOrderMan.Forw(); break; case DRIVE_MAN_ORDER.BACKWORD: DriveStatus = DRIVE_MAN_STATUS.RUNNING; mOrderMan.Backw(); break; case DRIVE_MAN_ORDER.ORIGIN: DriveStatus = DRIVE_MAN_STATUS.RUNNING; mOrderMan.Org(); break; case DRIVE_MAN_ORDER.RUNTO: DriveStatus = DRIVE_MAN_STATUS.RUNNING; mOrderMan.Runto(DriveRuntoTarget); break; case DRIVE_MAN_ORDER.SYNC: DriveStatus = DRIVE_MAN_STATUS.RUNNING; mOrderMan.Sync(); break; case DRIVE_MAN_ORDER.STOP: DriveStatus = DRIVE_MAN_STATUS.RUNNING; mOrderMan.Stop(); break; case DRIVE_MAN_ORDER.IDLE: DriveStatus = DRIVE_MAN_STATUS.STOP; break; } } else if(e.PropertyName == nameof(PosOffset)) { mSimDrive.PosOffset = PosOffset; } else if (e.PropertyName == nameof(MotorType)) { mSimDrive.MotorType = MotorType; } else if (e.PropertyName == nameof(Ratio)) { mOrderMan.order_org.Velocity1 = HVelocity1 / Ratio; mOrderMan.order_org.Velocity2 = HVelocity2 / Ratio; mOrderMan.order_runto.Velocity = Velocity / Ratio; mOrderMan.order_forw.Velocity = JogVelocity / Ratio; mOrderMan.order_backw.Velocity = JogVelocity / Ratio; mSimDrive.SVelocity = SVelocity / Ratio; } else if (e.PropertyName == nameof(JogVelocity)) { mOrderMan.order_forw.Velocity = JogVelocity / Ratio; mOrderMan.order_backw.Velocity = JogVelocity / Ratio; } else if (e.PropertyName == nameof(Velocity)) { mOrderMan.order_runto.Velocity = Velocity / Ratio; } else if (e.PropertyName == nameof(SVelocity)) { mSimDrive.SVelocity = SVelocity / Ratio; } else if (e.PropertyName == nameof(HVelocity1)) { mOrderMan.order_org.Velocity1 = HVelocity1 / Ratio; } else if (e.PropertyName == nameof(HVelocity2)) { mOrderMan.order_org.Velocity2 = HVelocity2 / Ratio; } else if (e.PropertyName == nameof(DTime)) { mSimDrive.DecTime = DTime; } else if (e.PropertyName == nameof(ATime)) { mSimDrive.AccTime = ATime; } } class SimulationDataCell { public UInt16 istatus; public UInt32 ad; public override string ToString() { return "ad=" + ad.ToString() + " istatus=" + istatus.ToString("X3"); } } string file_path = "simulation_flyad7.json"; public bool Save() { return FlyAd7JsonDb.Save(this, file_path); } public bool Load() { return FlyAd7JsonDb.Load(this, file_path); } double systick; public Int32 GetSysTick() { //以读到的 mSimulationDatas_idx 作为时间, 一个为1.28ms return (Int32)systick; } public Int32 GetSysTick(DateTime dt) { return (Int32)(systick + (dt - Now).TotalMilliseconds); } //1.28ms 触发一次 void OnPoll() { if (Now == DateTime.MinValue) { Now = DateTime.Now; systick = 0; } else { Now += TimeSpan.FromTicks((long)(1.28 * TimeSpan.TicksPerMillisecond)); systick += 1.28; } UpdateAD(); UpdatePos(); UpdateIO(); UpdateDrive(); } DateTime dt = DateTime.MinValue; void UpdateTimeSpan1ms() { DateTime d = DateTime.Now; if (dt == DateTime.MinValue) dt = d; else { TimeSpan1ms = (d-dt).TotalMilliseconds; dt = d; } } Random random = new Random(); void UpdateAD() { mSimGageAD.OnPoll(Now); int mm = (int)(mSimGageAD.Mmpp * mSimDrive.PhysicalPos); int ad = mSimGageAD.GetAD(mm); //添加随机数 double p = random.NextDouble(); ad = (int)(ad * (1 + (p - 0.5) * 0.001)); if (ad < 0) ad = 0; else if (ad > 65535) ad = 65535; ADSum += ad; ADCnt++; AD = ad; TimeGrid.Add(ad); if (TimeGrid.Count >= 200) { ADForShow = ADSum / ADCnt; ADSum = 0; ADCnt = 0; UpdateTimeSpan1ms(); if (TimeGridEvent != null) { TimeGridEvent.Invoke(this, new TimeGridEventArgs( Now, TimeSpan.FromTicks((long)(1.28 * TimeSpan.TicksPerMillisecond)), TimeGrid.ToArray())); } TimeGrid.Clear(); } } public class PosGrid { public int[] Datas = new int[1000]; List gridsum = new List(); int grid_index_last = -1;//上一个grid_index bool isEnable; int grid_start = 0; public void Start() { isEnable = true; grid_start = grid_index_last; } public void End() { if (isEnable) { if (gridsum.Count > 0) { Datas[grid_index_last] = (int)(gridsum.Average()); gridsum.Clear(); } int grid_end = grid_index_last; Misc.DIRECTION dir = Misc.DIRECTION.FORWARD; if (grid_end < grid_start) { int t = grid_start; grid_start = grid_end; grid_end = t; dir = Misc.DIRECTION.BACKWARD; } int len = grid_end - grid_start + 1; int[] data = Datas.Skip(grid_start).Take(len).ToArray(); if (PushEvent != null) PushEvent(false, dir, grid_start, data); } isEnable = false; } public delegate void PushEventHandler(bool isMini, Misc.DIRECTION dir, int grid_start, int[] data); public event PushEventHandler PushEvent; public void UpdateGrid(int grid_index, int ad) { if (grid_index >= 0 && grid_index < Datas.Count()) { if (grid_index_last == grid_index) { gridsum.Add(ad); } else { Misc.DIRECTION dir = Misc.DIRECTION.FORWARD; if (grid_index < grid_index_last) dir = Misc.DIRECTION.BACKWARD; if (gridsum.Count > 0) { Datas[grid_index_last] = (int)(gridsum.Average()); gridsum.Clear(); //TODO 推送MINIGRID if (isEnable) { int[] data = new int[1]; data[0] = Datas[grid_index_last]; if (PushEvent != null) PushEvent(true, dir, grid_index_last, data); } } grid_index_last = grid_index; } } } } public PosGrid mPosGrid = new PosGrid(); void UpdatePos() { Position = mSimDrive.CurrPos; //Speed = mSimDrive.Speed; mPosGrid.UpdateGrid(Position / PosOfGrid, AD); Position2Org = mSimGageAD.GetPosition2(); //同步模式 if (mSyncPos2List.Position2Changed(Position2, out int pos2)) { Position2 = pos2; } Speed2 = mSimGageAD.GetSpeed2(); } void UpdateIO() { mSimGageAD.SetOutput(OStatus); UInt16 istatus = mSimGageAD.GetInput(); if (mSimDrive.IsMaxLimit) Misc.MyBase.SIGNBIT(ref istatus, IODefinition.IN_MAXLIMIT); else Misc.MyBase.CLEARBIT(ref istatus, IODefinition.IN_MAXLIMIT); if (mSimDrive.IsMinLimit) Misc.MyBase.SIGNBIT(ref istatus, IODefinition.IN_MINLIMIT); else Misc.MyBase.CLEARBIT(ref istatus, IODefinition.IN_MINLIMIT); if (mSimDrive.IsOrg) Misc.MyBase.CLEARBIT(ref istatus, IODefinition.IN_ORG); else Misc.MyBase.SIGNBIT(ref istatus, IODefinition.IN_ORG); //同步模式下才有这个 UInt16 inchange = (UInt16)(istatus ^ IStatus); if (Misc.MyBase.CHECKBIT(inchange, IODefinition.IN_VSENSOR)) { //处理position2 if (mSyncPos2List.VSensorChanged(Misc.MyBase.CHECKBIT(istatus, IODefinition.IN_VSENSOR), Position2, out int pos2)) { Position2 = pos2; } } IStatus = istatus; } class SyncPos2Collection { List list = new List(); int curr_offset = int.MaxValue; public void Add(SyncPos2Item item) { list.Add(item); } public void Clear() { list.Clear(); } /// /// 信号改变时调用 /// /// /// public bool VSensorChanged(bool vsensor, int position2, out int correct_pos2) { correct_pos2 = position2; if (list.Count() == 0) return false;//列表是空的,什么都不干 SyncPos2Item spitem = list.First();//当前在处理的项 if (vsensor == spitem.At01) { int offset = position2 - spitem.pos2; if (Math.Abs(curr_offset) > Math.Abs(offset)) { curr_offset = offset; return false; } else { //已经找到最近的一个 correct_pos2 = spitem.pos2 + curr_offset; curr_offset = int.MaxValue; list.RemoveAt(0); if (list.Count() > 0) { spitem = list.First();//当前在处理的项 if (vsensor == spitem.At01) curr_offset = position2 - spitem.pos2; } return true; } } return false; } /// /// 脉冲改变时调用 /// /// /// /// public bool Position2Changed(int position2, out int correct_pos2) { correct_pos2 = position2; if (list.Count() == 0) return false;//列表是空的,什么都不干 SyncPos2Item spitem = list.First();//当前在处理的项 if(curr_offset!=int.MaxValue)//之前有一个信号 { int offset = position2 - spitem.pos2; if (Math.Abs(curr_offset) > Math.Abs(offset)) { return false; } else { //已经找到最近的一个 correct_pos2 = spitem.pos2 + curr_offset; curr_offset = int.MaxValue; list.RemoveAt(0); return true; } } return false; } } void UpdateDrive() { mSimDrive.OnPoll(Now); mOrderMan.OnPoll(); } #region INotifyPropertyChanged 成员 protected void NotifyPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; #endregion #region IFlyAD接口 /// /// systick 转换的时间 /// public DateTime Now { get; set; } = DateTime.MinValue; public int Position { get; set; } public int Speed { get; set; } public int AD { get; set; } public int ADMax { get { return 0xffff; } } public UInt16 IStatus { get; set; } public UInt16 OStatus { get; set; } = 0xf; public void SetOutput(ushort mask, ushort enable) { UInt16 ostatus = OStatus; for (int i = 0; i < 16; i++) { if (Misc.MyBase.CHECKBIT(mask, i)) { if (Misc.MyBase.CHECKBIT(enable, i)) Misc.MyBase.SIGNBIT(ref ostatus, i); else Misc.MyBase.CLEARBIT(ref ostatus, i); } } OStatus = ostatus; } public void UpdateParam() { //throw new NotImplementedException(); } #region 配置参数 public void SetSysParam(ushort posOfGrid, FlyADBase.MOTORTYPE motortype, ushort ratio01, ushort ratio02) { //不需要实现 } public MOTORTYPE MotorType { get; set; } = MOTORTYPE.VF0; //1个Grid=?脉冲数(2B) public UInt16 PosOfGrid { get; set; } = 10; public double Ratio => 1.0 * Ratio01 / Ratio02; /// /// //电机比例(2B) /// public UInt16 Ratio01 { get; set; } = 1; /// /// 脉冲比例(2B) /// public UInt16 Ratio02 { get; set; } = 1; #endregion public Int16 PosOffset { get; set; } public UInt32 JogVelocity { get; set; } = 500; #region 速度 public UInt32 Velocity { get; set; } public uint SVelocity { get; set; } UInt32 atime; public UInt32 ATime { get; set; } public UInt32 DTime { get; set; } public UInt32 HVelocity1 { get; set; } public UInt32 HVelocity2 { get; set; } public void SetPosParam(uint velocity, uint sv, uint atime, uint dtime, uint hspeed1, uint hspeed2) { } #endregion #region 密码 public AREA_STATUS AreaStatus { get; set; } public AREA_ERR AreaRet { get; set; } public byte[] Code { get; } = new byte[7] { 0x74, 0xB7, 0x07, 0x4E, 0x52, 0x3D, 0x06 }; public int Surplus { get; set; } = 8000; public byte[] Access { get; } = new byte[8] { 0x19, 0x4A, 0xA5, 0x21, 0x7B, 0x74, 0x1A, 0x00 }; public void SetAccess(byte[] access) { if (access.Length != 8) return; Array.Copy(access, Access, 8); AreaStatus = AREA_STATUS.VALID; AreaRet = AREA_ERR.NO_ERR; Surplus = 8000; } public void InitArea() { throw new NotImplementedException(); } #endregion public void GetGrid(Misc.DIRECTION direction, int grid_start, int grid_len, out int[] dat) { dat = null; } public void GetGrid(Misc.DIRECTION direction, out int[] dat) { GetGrid(direction, 0, 1000,out dat); } public event TimeGridEventHandler TimeGridEvent; public event MiniGridEventHandler MiniGridEvent; public event MiniGridEventHandler GridEvent; public event IStatusChangedEventHandler IStatusChangedEvent; public event DriveStatusChangedEventHandler DriveStatusChangedEvent; public event PositionChangedEventHandler PositionChangedEvent; public event PositionChangedEventHandler Position2ChangedEvent; public void Runto(int to) { Drive(DRIVE_MAN_ORDER.RUNTO, to); } public void Origin() { Drive(DRIVE_MAN_ORDER.ORIGIN); } public void Stop() { Drive(DRIVE_MAN_ORDER.STOP); } public void Backward() { Drive(DRIVE_MAN_ORDER.BACKWORD); } public void Forward() { Drive(DRIVE_MAN_ORDER.FORWORD); } public DRIVE_MAN_ORDER DriveOrder { get; set; } public DRIVE_MAN_STATUS DriveStatus { get; set; } int DriveRuntoTarget; void Drive(DRIVE_MAN_ORDER order, object param) { if (order == DRIVE_MAN_ORDER.RUNTO) { DriveRuntoTarget = (int)param; } DriveOrder = order; } void Drive(DRIVE_MAN_ORDER order) { Drive(order, null); } #endregion #region 同步 /// /// 同步用标示 /// public Int32 Marker { get; set; } #region 横向脉冲 /// /// 逻辑横向脉冲偏移 Pos1 + Pos1LCShift = Pos1LC /// public int Pos1LCShift { get; set; } public void SetPos1LCShift(int value) { Pos1LCShift = value; } #endregion #region 主轴脉冲 private int position2_org; int Position2Org { get { return position2_org; } set { if (position2_org != value) { position2_org = value; cal_position2(); } } } /// /// 逻辑横向脉冲偏移 Pos1 + Pos1LCShift = Pos1LC /// public int Pos2LCShift { get; set; } private int position2; /// /// 纵向脉冲,也叫主轴脉冲 /// public int Position2 { get { return position2; } set { if (position2 != value) { position2 = value; Pos2Shift = position2 - Position2Org; } } } void cal_position2() { position2 = Position2Org + Pos2Shift; } public int Speed2 { get; set; } int pos2shift=0; /// /// 纵向偏移 /// int Pos2Shift { get { return pos2shift; } set { if (pos2shift != value) { pos2shift = value; cal_position2(); } } } public void SetPos2Offset(int offest) { Pos2Shift += offest; } public int HardwareVersion { get; set; } = 2; struct SyncPos2Item { public bool At01;//? or At10 public int pos2; public bool immediately; } SyncPos2Collection mSyncPos2List = new SyncPos2Collection(); /// /// 纵向同步事件,0-1事件 /// /// public void SetPos2At01(int pos2,bool immediately) { mSyncPos2List.Add(new SyncPos2Item() { At01 = true, pos2 = pos2, immediately = immediately }); } #endregion #region 同步控制 同步状态转换规则 /// /// 进入同步状态 /// /// public void SyncBegin(int pos2) { SetPos2(pos2); SyncBegin(); } /// /// 进入同步状态 /// public void SyncBegin() { Drive(DRIVE_MAN_ORDER.SYNC); } /// /// 退出同步状态 /// public void SyncEnd() { Drive(DRIVE_MAN_ORDER.STOP); } /// /// 清空同步指令 /// public void SyncClear() { mSyncOrderList.Clear(); } /// /// 清空POS2同步列表 /// public void SyncPos2Clear() { mSyncPos2List.Clear(); } public UInt16 GetSyncOrderCnt() { return (UInt16)mSyncOrderList.Count(); } public SYNC_STATUS GetSyncStatus() { if (DriveOrder == DRIVE_MAN_ORDER.SYNC) return SYNC_STATUS.SYNCST_YES; else return SYNC_STATUS.SYNCST_NO; } #endregion #region 同步扫描 脚本指令 List mSyncOrderList = new List(); //同步扫描至 //D+0xE0+开始主轴位置+结束主轴位置+结束横向脉冲位置(逻辑位置)+脉冲开关(1B)+命令识标号(4B) public void SyncRunAtLC(int pos2_begin, int pos2_end, int pos1lc, bool hasDataGrid, Int32 marker) { mSyncOrderList.Add( new SyncOrder_RunAtLC() { pos2_begin = pos2_begin, pos2_end = pos2_end, pos1lc_end = pos1lc, hasDataGrid = hasDataGrid, marker = marker }); } //7.2 位于队列头时运行,归零 //D+0xE1+命令识标号(4B) public void SyncOrigin(Int32 marker) { mSyncOrderList.Add( new SyncOrder_Origin() { marker = marker }); } //7.3 位于队列头时运行,以速度运行至物理位置 //D+0xE2+横向脉冲位置(4B:int32,物理位置)+速度(4B:int32)+脉冲开关(1B)+命令识标号(4B) public void SyncRunTo(int pos1, UInt32 velocity, bool hasDataGrid, Int32 marker) { mSyncOrderList.Add( new SyncOrder_RunTo() { pos1 = pos1, velocity = velocity, hasDataGrid = hasDataGrid, marker = marker }); } //7.3 位于队列头时运行,以速度运行至逻辑位置 //D+0xE3+横向脉冲位置(4B:int32,逻辑位置)+速度(4B:int32)+脉冲开关(1B)+命令识标号(4B) public void SyncRunToLC(int pos1lc, UInt32 velocity, bool hasDataGrid, Int32 marker) { mSyncOrderList.Add( new SyncOrder_RunToLC() { pos1lc = pos1lc, velocity = velocity, hasDataGrid = hasDataGrid, marker = marker }); } //7.4 等待,ms //D+0xE4+毫秒数(4B:int32)+命令识标号(4B) public void SyncWait(int ms, Int32 marker) { mSyncOrderList.Add( new SyncOrder_Wait() { ms = ms, marker = marker }); } #endregion #endregion public void SetVelocity(uint velocity) { Velocity = velocity; } public void SetPos2(int postion2) { Position2 = postion2; } } public class FlyAd7JsonDb { static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => { c.CreateMap().ForMember(d => d.Addr, opt => { opt.MapFrom(s=>s.LocalEP.ToString()); }).ReverseMap().ForMember(d => d.LocalEP, opt => { opt.MapFrom(s => Misc.StringConverter.ToIPEndPoint(s.Addr)); }); })); public static bool Save(FLYAD7 src, string filePath) { var p = Mapper.Map(src); try { File.WriteAllText(filePath, JsonConvert.SerializeObject(p, Formatting.Indented)); } catch { //异常,没有json 编码失败 return false; } return true; } public static bool Load(FLYAD7 src, string filePath) { try { if (File.Exists(filePath)) { string json = File.ReadAllText(filePath); var p = JsonConvert.DeserializeObject(json); Mapper.Map(p, src); return true; } } catch { //异常,没有json 解码失败 return false; } return false; } public string Addr; public SIM_MODE SimMode; public MOTORTYPE MotorType; public UInt16 PosOfGrid; /// /// //电机比例(2B) /// public UInt16 Ratio01; /// /// 脉冲比例(2B) /// public UInt16 Ratio02; public UInt32 JogVelocity; public Int16 PosOffset; } public static class FLYAD7_IODefinition { public const int IN_ORG = 2; public const int IN_MINLIMIT = 3; public const int IN_MAXLIMIT = 4; public const int IN_FORW = 7; public const int IN_BACKW = 8; public const int IN_VSENSOR = 12; } }