#define ENABLE_MULTI_TRANS using AutoMapper; using FlyADBase.Inc; using FObjBase; using GeneralGommunication; using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; namespace FlyADBase { public partial class FlyAd2021B2 : IFlyADClient { public Logger logger = NLog.LogManager.GetCurrentClassLogger(); #region MARKNO const int MARKNO_SET_SAVE = 4; const int MARKNO_PARAM_APPLY = 5; #endregion #region INotifyPropertyChanged 成员 protected void NotifyPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public event PropertyChangedEventHandler PropertyChanged; #endregion #region IFlyADClient property /// /// 连接成功 /// public bool IsConnected { get; private set; } /// /// 重连次数 /// public int ConnectCnt { get; private set; } /// /// 已经从AD盒获取全部当前数据 /// public bool IsReady { get; private set; } /// /// 通过判断systick,判断AD盒是否发生重启; /// BeResetTime为上一次被复位时间, 此为 电脑的时间 /// public DateTime BeResetTime { get; private set; } /// /// 只要connect成功,获取systick被复位,都会从AD盒设备读取参数; /// 否则, 设置参数 到 AD盒 /// public bool IsReadParamFromDev { get; set; } #endregion #region IFlyAD property /// /// AD盒地址, 当为 TCP通讯时,为 192.168.251.200:20020; /// 当为 串口通讯时, 为 COM6 /// public string Addr { get; set; } = "192.168.251.10:20006"; /// /// 从flyad7 获取 的systick 转换的时间 /// public DateTime Now { get; private set; } /// /// AD盒版本号 版本1 没有systick;版本2 有systick; /// 通过通讯读取 /// public int Version { get; } = 2021; /// /// 硬件版本,设置的!!! /// public int HardwareVersion { get; set; } = 2021; /// /// 脉冲 /// public int Position { get; private set; } /// /// 速度 pps /// public int Speed { get; private set; } /// /// 纵向脉冲,也叫主轴脉冲 /// public int Position2 { get; private set; } /// /// 纵向速度 /// public int Speed2 { get; private set; } /// /// 1000ms从AD卡获取(或推送)1个AD值。此AD值只用于调试,定点 /// public int AD { get; private set; } /// /// 1000ms从AD卡获取(或推送)1个AD值。此AD值只用于调试,定点 /// public int AD2 { get; private set; } /// /// AD值最大值 ,恒定返回65535 /// public int ADMax => 0xFFFF; /// /// 输入口状态,只有12位有效 /// public UInt16 IStatus { get; private set; } = 0xffff; /// /// 输出口状态,只有4位有效 /// public UInt16 OStatus { get; private set; } = 0xffff; /// /// 电机类型 /// public MOTORTYPE MotorType { get; set; } = MOTORTYPE.SERVO; /// /// 脉冲/grid, 最大1000个grid /// private UInt16 posofgrid = 10; /// /// 脉冲/grid, 最大1000个grid /// public UInt16 PosOfGrid { get { return posofgrid; } set { if (value < 1) value = 1; if (posofgrid != value) posofgrid = value; } } /// /// 脉冲比例 分母(电机脉冲) /// public UInt16 Ratio01 { get; set; } = 4; /// /// 脉冲比例 分子(编码器脉冲) /// public UInt16 Ratio02 { get; set; } = 1; /// /// 脉冲平移(0位处脉冲值) /// public Int16 PosOffset { set; get; } /// /// 手动速度 pps /// public UInt32 JogVelocity { get; set; } = 5000; /// /// runto 速度 pps /// public UInt32 Velocity { get; private set; } = 8000; /// /// 开始速度 pps /// public UInt32 SVelocity { get; private set; } = 200; /// /// 加速时间 ms /// public UInt32 ATime { get; private set; } = 300; /// /// 减速时间 ms /// public UInt32 DTime { get; private set; } = 200; /// /// 归0 第1段速 pps /// public UInt32 HVelocity1 { get; private set; } = 4000; /// /// 归0 第2段速 pps /// public UInt32 HVelocity2 { get; private set; } = 500; /// /// 使用码区状态 /// public AREA_STATUS AreaStatus { get; private set; } /// /// 使用码区出错码 /// public AREA_ERR AreaRet { get; private set; } /// /// 序列码byte[6], AD盒只需要 6个byte,但flysecute 软件需要 7byte, code[6]=0x06 /// public byte[] Code { get; private set; } = new byte[7]; /// /// 系统剩余时间 /// public int Surplus { get; private set; } = 10000; /// /// 系统授权码access[8] /// public byte[] Access { get; private set; } = new byte[8]; /// /// 当前运行指令 /// public DRIVE_MAN_ORDER DriveOrder { get; set; } = DRIVE_MAN_ORDER.IDLE; /// /// 之前运行动作的状态 /// public DRIVE_MAN_STATUS DriveStatus { get; set; } = DRIVE_MAN_STATUS.STOP; /// /// timegrid 事件; 大约0.1s更新一次 /// ad值不会被机架修正 /// public event TimeGridEventHandler TimeGridEvent; /// /// runto 中,每经过1个grid 推送数据 /// public event MiniGridEventHandler MiniGridEvent; /// /// 输入状态改变事件 /// public event IStatusChangedEventHandler IStatusChangedEvent; /// /// 指令状态改变事件 /// public event DriveStatusChangedEventHandler DriveStatusChangedEvent; /// /// 脉冲1改变事件 /// public event PositionChangedEventHandler PositionChangedEvent; /// /// 脉冲2改变事件 /// public event PositionChangedEventHandler Position2ChangedEvent; #endregion IsReadyContext isReadyContext = new IsReadyContext(); SysTickContext sysTickContext = new SysTickContext(); CalSpeed calSpeed = new CalSpeed(); public FlyAd2021B2Core Core => core; IGeneralComm comm; FlyAd2021B2Core core; /// /// 不设置服务器 /// bool bShieldSetValueEx; /// /// 最后一次动作时间 /// DateTime orderGetRunResultTime; DateTime orderGetEnc1Time; DateTime orderGetEnc2Time; DateTime orderGetInTime; static string[] propertyName_save; List pushEventArgs = new List(); string defaultPath = "flyad.json"; string jsonDbPath; static FlyAd2021B2() { propertyName_save = FlyAd2021JsonDb.GetMemberNames(); } public FlyAd2021B2() { constructor(); advConstructor(); } void constructor() { Now = DateTime.Now; sysTickContext.BeReseted += () => BeResetTime = DateTime.Now; core = new FlyAd2021B2Core(); core.logger = logger; core.Csm.StartMeasure(); //注册AD2021版的发送动作,把数据通过 comm模块发送出去 core.SendMsgEvent += (s,msg) => { comm?.Write(msg); }; core.PushDataEvent += (sender, e) => { //这个线程非主线程, 数据接收完,应该快速返回。 //事件触发,都放在主线程操作。 //要有线程锁!!!!! lock (pushEventArgs) { pushEventArgs.Add(e); } }; core.PushRunResultEvent += (sender, e) => { //这个线程非主线程, 数据接收完,应该快速返回。 //事件触发,都放在主线程操作。 lock (pushEventArgs) { pushEventArgs.Add(e); } }; //避免推送太快,全部推送事件统一周期处理 PollModule.Current.Poll_Config(onPoll_PushEvent, TimeSpan.FromSeconds(0.1)); Misc.BindingOperations.SetBinding(core, nameof(core.IsConnected), this, nameof(IsConnected)); Misc.BindingOperations.SetBinding(isReadyContext, nameof(isReadyContext.IsReady), this, nameof(IsReady)); this.PropertyChanged += FlyAd2021_PropertyChanged; //每隔一个小时,改变剩下时间 PollModule.Current.Poll_Config(() => { if (!IsConnected) return; if (Surplus < 60000) { if (Surplus > 0) { Surplus--; //TODO } } }, TimeSpan.FromMinutes(60)); //1分钟,脉冲都没改变, 发送一次获取 PollModule.Current.Poll_Config(() => { if (!IsConnected) return; if (Now - mTimeGridAdvHelper.positionChangedTime > TimeSpan.FromSeconds(60)) { if (Now - orderGetEnc1Time > TimeSpan.FromSeconds(60)) GetEnc1(); } if (Now - mTimeGridAdvHelper.position2ChangedTime > TimeSpan.FromSeconds(60)) { if (Now - orderGetEnc2Time > TimeSpan.FromSeconds(60)) GetEnc2(); } if (Now - mTimeGridAdvHelper.istatusChangedTime > TimeSpan.FromSeconds(60)) { if (Now - orderGetInTime > TimeSpan.FromSeconds(60)) GetIn(); } }, TimeSpan.FromSeconds(1)); //更新线速度 PollModule.Current.Poll_Config(() => { if (calSpeed.Cal(Now, out int speed1, out int speed2)) { Speed = speed1; Speed2 = speed2; } }, TimeSpan.FromSeconds(0.5)); //当状态为Running时,且每秒脉冲没变,需要 查 当前的状态, 也许Stop状态少推送了 PollModule.Current.Poll_Config(() => { if (!IsConnected) return; if (DriveStatus != DRIVE_MAN_STATUS.RUNNING) return; if (Now - mTimeGridAdvHelper.positionChangedTime > TimeSpan.FromSeconds(3)) { //3秒内脉冲没改变 if (Now - orderGetRunResultTime >= TimeSpan.FromSeconds(3))//每3秒检查一次 { GetRunResult(); } } }, TimeSpan.FromSeconds(1)); } void GetRunResult() { orderGetRunResultTime = Now; //不需要处理回复数据。 它会作为事件被处理 core.GetRunResult(null, null); } void GetEnc1() { orderGetEnc1Time = Now; core.GetEnc1((asyncContext, _retData) => { PollModule.Current.Dispatcher.BeginInvoke(new Action((retData) => { //这里回复的位置,是32位的。 //不需要设置到1min缓存区。 1ms推送时,肯定也会设置进去缓存。 var reponse = (int)retData; Position = reponse; calSpeed.SetPos1(Now, Position); isReadyContext.isReadyGetPos1 = true; }), _retData); }, null); } void GetEnc2() { orderGetEnc2Time = Now; core.GetEnc2((asyncContext, _retData) => { PollModule.Current.Dispatcher.BeginInvoke(new Action((retData) => { //这里回复的位置,是32位的。 //不需要设置到1min缓存区。 1ms推送时,肯定也会设置进去缓存。 var reponse = (int)retData; Position2 = reponse; calSpeed.SetPos2(Now, Position); isReadyContext.isReadyGetPos2 = true; }), _retData); }, null); } void GetIn() { orderGetInTime = Now; core.GetIn((asyncContext, _retData) => { PollModule.Current.Dispatcher.BeginInvoke(new Action((retData) => { //不需要设置到1min缓存区。 1ms推送时,肯定也会设置进去缓存。 var reponse = (UInt16)retData; IStatus = reponse; isReadyContext.isReadyGetIn = true; }), _retData); }, null); } void GetOut() { core.GetOut((asyncContext, _retData) => { PollModule.Current.Dispatcher.BeginInvoke(new Action((retData) => { var reponse = (UInt16)retData; OStatus = reponse; isReadyContext.isReadyGetOut = true; }), _retData); }, null); } private void FlyAd2021_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(IsConnected)) { if (IsConnected) { ConnectCnt++; AfterConnected(); } else { isReadyContext.Reset(); } return; } if (bShieldSetValueEx) return; if (e.PropertyName == nameof(PosOffset)) { core.SetSysParam_Zero(PosOffset, null, null); FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY); } else if (e.PropertyName == nameof(JogVelocity)) { core.SetSysParam_Jog(JogVelocity, null, null); FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY); } else if (e.PropertyName == nameof(MotorType)) { core.SetSysParam_MotorType(MotorType, null, null); FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY); } else if (e.PropertyName == nameof(Ratio01)) { core.SetSysParam_Ratio01(Ratio01, null, null); FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY); } else if (e.PropertyName == nameof(Ratio02)) { core.SetSysParam_Ratio02(Ratio02, null, null); FObjBase.PollModule.Current.Poll_JustOnce(SetSysParamApply, this, MARKNO_PARAM_APPLY); } } /// /// 参数保存 /// void SetSysParamApply() { core.SetSysParam_Apply(null, null); } void _core_PushRunResultEvent(PushRunResultEventArgs e) { Now = sysTickContext.ToDateTime(e.SysTick); DriveStatus = e.Status; isReadyContext.isReadyGetState = true; advPushStatus(); } /// /// 计数32位脉冲 /// /// 上一次脉冲 /// 低16位脉冲 /// int CalPosition(int lastPostion, UInt16 enc) { //Position UInt16 lastEnc = (UInt16)(lastPostion & 0xffff); Int16 d = (Int16)(enc - lastEnc); lastPostion += d; return lastPostion; } /// /// 低频处理事件 /// void onPoll_PushEvent() { EventArgs[] pushEventArgs; lock (this.pushEventArgs) { pushEventArgs = this.pushEventArgs.ToArray(); this.pushEventArgs.Clear(); } foreach (var e in pushEventArgs) { if (e is PushDataEventArgs) { _core_PushDataEvent(e as PushDataEventArgs); } else if (e is PushRunResultEventArgs) { _core_PushRunResultEvent(e as PushRunResultEventArgs); } } } void _core_PushDataEvent(PushDataEventArgs e) { Now = sysTickContext.ToDateTime(e.SysTick); if (e.ENC1 != null) { if (isReadyContext.isReadyGetPos1) { Position = CalPosition(Position, (UInt16)e.ENC1);//脉冲需要16位扩展为32位 calSpeed.SetPos1(Now, Position); } } if (e.ENC2 != null) { if (isReadyContext.isReadyGetPos2) { Position2 = CalPosition(Position2, (UInt16)e.ENC2);//脉冲需要16位扩展为32位 calSpeed.SetPos2(Now, Position2); } } if (e.In != null) { IStatus = (UInt16)e.In; isReadyContext.isReadyGetIn = true; } if (e.Out != null) { OStatus = (UInt16)e.Out; isReadyContext.isReadyGetOut = true; } //脉冲,输入,没准备好,不触发 if (!isReadyContext.isReadyGetPos1) return; if (!isReadyContext.isReadyGetPos2) return; if (!isReadyContext.isReadyGetIn) return; advPushData( Now, e.AD, e.AD2); } private void Core_PushDataEvent(object sender, PushDataEventArgs _e) { //这个线程非主线程, 数据接收完,应该快速返回。 //事件触发,都放在主线程操作。 //要有线程锁!!!!! lock (pushEventArgs) { pushEventArgs.Add(_e); } } /// /// 连接后初始化 /// void AfterConnected() { if (IsReadParamFromDev) { //从设备读取参数 //获取当前全部状态 #if ENABLE_MULTI_TRANS core.BuildMultiTrans(() =>{ #endif core.GetSysParam_Jog((asyncContext, retData) => JogVelocity = (UInt32)retData, null); core.GetSysParam_Zero((asyncContext, retData) => PosOffset = (Int16)retData, null); core.GetSysParam_MotorType((asyncContext, retData) => MotorType = (MOTORTYPE)retData, null); core.GetSysParam_Ratio01((asyncContext, retData) => Ratio01 = (UInt16)retData, null); core.GetSysParam_Ratio02((asyncContext, retData) => Ratio02 = (UInt16)retData, null); core.GetRunParam_V((asyncContext, retData) => Velocity = (UInt32)retData, null); core.GetRunParam_SV((asyncContext, retData) => SVelocity = (UInt32)retData, null); core.GetRunParam_AccTime((asyncContext, retData) => ATime = (UInt32)retData, null); core.GetRunParam_DecTime((asyncContext, retData) => DTime = (UInt32)retData, null); core.GetRunParam_HSpd1((asyncContext, retData) => HVelocity1 = (UInt32)retData, null); core.GetRunParam_HSpd2((asyncContext, retData) => HVelocity2 = (UInt32)retData, null); #if ENABLE_MULTI_TRANS }); #endif } else { //参数不保存在设备中, 保存在电脑。 //参数写入到设备。 #if ENABLE_MULTI_TRANS core.BuildMultiTrans(() =>{ #endif core.SetSysParam_MotorType(MotorType, null, null); core.SetSysParam_Ratio01(Ratio01, null, null); core.SetSysParam_Ratio02(Ratio02, null, null); core.SetSysParam_Zero(PosOffset, null, null); core.SetSysParam_Jog(JogVelocity, null, null); core.SetSysParam_Apply(null, null); #if ENABLE_MULTI_TRANS }); #endif } GetAccess(); //连接断开,复位 脉冲计数。。。,时间计数 isReadyContext.Reset(); sysTickContext.Reset(); calSpeed.Reset(); #if ENABLE_MULTI_TRANS core.BuildMultiTrans(() =>{ #endif GetRunResult(); GetOut(); GetIn(); GetEnc1(); GetEnc2(); #if ENABLE_MULTI_TRANS }); #endif advAfterContected(); } void GetAccess() { //TODO } #region Load Save public bool Load() { return Load(defaultPath); } public bool Load(string jsonDbPath) { this.jsonDbPath = jsonDbPath; this.PropertyChanged -= FlyAD7_PropertyChanged2; //不需要设置到 AD盒 bShieldSetValueEx = true; bool ret = FlyAd2021JsonDb.Load(jsonDbPath, this); bShieldSetValueEx = false; this.PropertyChanged += FlyAD7_PropertyChanged2; return ret; } /// /// 属性改变,自动保存 /// /// /// private void FlyAD7_PropertyChanged2(object sender, PropertyChangedEventArgs e) { if (propertyName_save.Contains(e.PropertyName)) { PollModule.Current.Poll_JustOnce(() => { Save(); }, this, MARKNO_SET_SAVE); } } public void Save() { FlyAd2021JsonDb.Save(jsonDbPath, this); } #endregion #region IFlyADClient function /// /// 连接 /// public void Connect() { Connect(Addr); } public void Disconnect() { if (comm != null) { comm.Stop(); core.ResetMsg(); } } void createComm() { if (Addr.ToLower().StartsWith("com")) { var _comm = new GComm_SerialPort(); _comm.Init(new System.IO.Ports.SerialPort() { BaudRate = 115200 * 2, DataBits = 8, Parity = System.IO.Ports.Parity.None, StopBits = System.IO.Ports.StopBits.One }); //_comm.logger = logger; comm = _comm; } else { var _comm = new GComm_TcpClient(); //_comm.logger = logger; comm = _comm; } comm.DataReceived += Comm_DataReceived; comm.PropertyChanged += Comm_PropertyChanged; core.ResetMsg(); } void disposeComm() { comm.DataReceived -= Comm_DataReceived; comm.PropertyChanged -= Comm_PropertyChanged; core.ResetMsg(); } private void Comm_DataReceived(IGeneralComm sender, byte[] msg) { core.RecMsg(msg); } private void Comm_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(comm.IsConnected)) { core.ResetMsg(); } } /// /// 连接 addr /// /// public void Connect(string addr) { Addr = addr; if (comm == null) { createComm(); } else { comm.Stop(); //只要comm.Stop() IsConnected 就会为 False if ((Addr.ToLower().StartsWith("com") && (comm is GComm_SerialPort)) || (!Addr.ToLower().StartsWith("com") && (comm is GComm_TcpClient))) { //不用重建 } else { disposeComm(); createComm(); } } comm.Addr = addr; comm.Start(); } /// /// 重连 /// public void ReConnect() { Connect(); } #endregion #region IFlyAD function /// /// Out0 = 0, SetOutput(0x0001,0x0000); /// Out0 = 1, Out1 = 0, SetOutput(0x0003,0x0001); /// /// /// public void SetOutput(UInt16 mask, UInt16 enable) { core.SetOutPorts(mask, enable, (asyncContext, retData) => { GetOut(); }, null); } #region 速度参数 /// /// 设置速度参数 /// /// 开始速度 pps /// 加速时间 ms /// 减速时间 ms /// 归0 第1段速 pps /// 归0 第2段速 pps public void SetPosParam(UInt32 sv, UInt32 atime, UInt32 dtime, UInt32 hspeed1, UInt32 hspeed2) { #if ENABLE_MULTI_TRANS core.BuildMultiTrans(() =>{ #endif core.SetRunParam_SV(sv, null, null); core.SetRunParam_AccTime(atime, null, null); core.SetRunParam_DecTime(dtime, null, null); core.SetRunParam_HSpd1(hspeed1, null, null); core.SetRunParam_HSpd2(hspeed2, null, null); core.GetRunParam_SV((asyncContext, retData) => SVelocity = (UInt32)retData, null); core.GetRunParam_AccTime((asyncContext, retData) => ATime = (UInt32)retData, null); core.GetRunParam_DecTime((asyncContext, retData) => DTime = (UInt32)retData, null); core.GetRunParam_HSpd1((asyncContext, retData) => HVelocity1 = (UInt32)retData, null); core.GetRunParam_HSpd2((asyncContext, retData) => HVelocity2 = (UInt32)retData, null); #if ENABLE_MULTI_TRANS }); #endif } /// /// 设置速度 /// /// public void SetVelocity(UInt32 velocity) { #if ENABLE_MULTI_TRANS core.BuildMultiTrans(() =>{ #endif core.SetRunParam_V(velocity, null, null); core.GetRunParam_V((asyncContext, retData) => Velocity = (UInt32)retData, null); #if ENABLE_MULTI_TRANS }); #endif } #endregion #region 运行动作 /// /// 运行至指定位置 /// /// 脉冲 public void Runto(int to) { if (Position == to) return; DriveOrder = DRIVE_MAN_ORDER.RUNTO; DriveStatus = DRIVE_MAN_STATUS.RUNNING; orderGetRunResultTime = Now; core.RunTo(to, 123, null, null); } /// /// 归原点 /// public void Origin() { DriveOrder = DRIVE_MAN_ORDER.ORIGIN; DriveStatus = DRIVE_MAN_STATUS.RUNNING; orderGetRunResultTime = Now; core.Org(124, null, null); } /// /// 停止 /// public void Stop() { if (DriveOrder == DRIVE_MAN_ORDER.IDLE) return;//已经停了 DriveOrder = DRIVE_MAN_ORDER.STOP; DriveStatus = DRIVE_MAN_STATUS.RUNNING; orderGetRunResultTime = Now; core.Stop(null, null); } /// /// 反向,直到撞了限位 /// public void Backward() { DriveOrder = DRIVE_MAN_ORDER.BACKWORD; DriveStatus = DRIVE_MAN_STATUS.RUNNING; orderGetRunResultTime = Now; core.Backw(125, null, null); } /// /// 正行,直到撞了限位 /// public void Forward() { DriveOrder = DRIVE_MAN_ORDER.FORWORD; DriveStatus = DRIVE_MAN_STATUS.RUNNING; orderGetRunResultTime = Now; core.Forw(126, null, null); } #endregion #endregion } class FlyAd2021JsonDb { static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => { c.CreateMap().ReverseMap(); })); public static void Save(string jsonDbPath, FlyAd2021B2 src) { if (string.IsNullOrEmpty(jsonDbPath)) return; var p = Mapper.Map(src); try { File.WriteAllText(jsonDbPath, JsonConvert.SerializeObject(p, Formatting.Indented)); } catch { //异常,没有json 编码失败 } } public static bool Load(string jsonDbPath, FlyAd2021B2 src) { if (string.IsNullOrEmpty(jsonDbPath)) return false; try { if (File.Exists(jsonDbPath)) { string json = File.ReadAllText(jsonDbPath); var p = JsonConvert.DeserializeObject(json); Mapper.Map(p, src); return true; } } catch { //异常,没有json 解码失败 } return false; } public string Addr = "192.168.251.10:20006"; public int GridSmooth = 0; public int ADLag = 0; public int AD2Lag = 0; public int PosMin = 0; public int PosMax = 8900; public bool HasPosMaxMin = false; //备份参数 public MOTORTYPE MotorType = MOTORTYPE.SERVO; public UInt16 PosOfGrid = 10; public UInt16 Ratio01 = 4; public UInt16 Ratio02 = 1; public Int16 PosOffset = 0; public UInt32 JogVelocity = 1000; /// /// 硬件版本 /// version = 1,不支持 systick /// version = 2,支持 timeGridAdv /// version = 3,伟的大AD盒, io口定义不一样 /// public int HardwareVersion = 2021; public static string[] GetMemberNames() { var type = typeof(FlyAd2021JsonDb); return type.GetMembers().Select(mi => mi.Name).ToArray(); } } /// /// systick 转 DateTime /// 不断把 从flyad7 获取的 systick 放入。 以每次systick 的差 加上 上一次 dt_1st, 得到时间。 /// 如: 当前 获得 systick1, 之前是 systick0, 所以 dt = dt_1st+ (systick1-systick0); dt_1st=dt; /// /// 第1个 dt_1st, 是 从 DateTime.Now. 获取的。 /// /// 不会管 当前DateTime.Now 与 dt 差多少。 所以如果 flyad7 时间比较慢,可能会出现 3天后, DateTime.Now 与 dt 差了 1个小时。 /// class SysTickContext { byte systick_1st = 0; DateTime dt_1st = DateTime.MinValue; /// /// /// public event Action BeReseted; public void Reset() { dt_1st = DateTime.MinValue; } /// /// 输入从 flyad7 获取的 timemark, 转换为 本地 DateTime /// /// 只有低7位有效 /// public DateTime ToDateTime(byte systick) { if (dt_1st == DateTime.MinValue) { dt_1st = DateTime.Now; systick_1st = systick; return dt_1st; } int ms = SystickSub(systick, systick_1st); if ((ms > 30))//记录与新的timemark 相差30ms,掉包很严重 { //被复位了 BeReseted?.Invoke(); } DateTime dt = dt_1st.AddMilliseconds(ms); systick_1st = systick; dt_1st = dt; return dt; } /// /// 7位 systick1-systick2 /// /// /// /// int SystickSub(byte systick1, byte systick2) { if (systick1 >= systick2) return systick1 - systick2; else { return systick1 + 0x80 - systick2; } } } class IsReadyContext : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public bool isReadyGetState { get; set; } public bool isReadyGetPos1 { get; set; } public bool isReadyGetPos2 { get; set; } public bool isReadyGetIn { get; set; } public bool isReadyGetOut { get; set; } public bool IsReady { get; private set; } void updateIsReady() { if (isReadyGetState == false) { IsReady = false; return; } if (isReadyGetPos1 == false) { IsReady = false; return; } if (isReadyGetPos2 == false) { IsReady = false; return; } if (isReadyGetIn == false) { IsReady = false; return; } if (isReadyGetOut == false) { IsReady = false; return; } IsReady = true; return; } public IsReadyContext() { this.PropertyChanged += IsReadyContext_PropertyChanged; } private void IsReadyContext_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName != nameof(IsReady)) { updateIsReady(); } } public void Reset() { isReadyGetState = false; isReadyGetPos1 = false; isReadyGetPos2 = false; isReadyGetIn = false; isReadyGetOut = false; } } }