using FlyADBase; using FObjBase; using Misc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FlyAd2021 { public partial class FlyAd2021 : IFlyADClientAdv { #region IFlyADClientAdv property /// /// 通过脉冲计算速度,不使用AD盒的输出 /// [Obsolete("已经无效,必须通过电脑计算速度")] public bool IsCalSpeed { get; set; } = true; /// /// grid数据平滑 /// public int GridSmooth { get; set; } /// /// 机架总长 /// public int PosLen { get; set; } = 8900; /// /// 使用独立的脉冲最大最小值 /// public bool HasPosMaxMin { get; set; } /// /// 最小脉冲,默认是0 /// public int PosMin { get; set; } = 0; /// /// 最大脉冲,默认于PosLen 一样 /// public int PosMax { get; set; } = 8900; public int GridLen => PosLen / PosOfGrid; /// /// Speed1 = Velocity * Speed1Scale /// public double Speed1Scale => (double)Ratio02 / Ratio01; /// /// 动作完成 /// public bool IsFinish { get; set; } /// /// 机架修正 /// public CorrectADsHandler CorrectADs { get; set; } /// /// ad滞后修正 单位ms /// public int ADLag { get; set; } //一共有1.AD数据池(由timegrid提供) 1min //2.pos数据池(pos推送提供) 1min //4.当接收的grid事件数据。它有 (direction, grid_start,grid_len, systick ) //systick 就是结束的时间点。 当AD数据池出现了这个时间点 //pos数据池向前找。 pos 在 grid_start*posOfGrid 范围的数据。 //找到开始的systick 后,整合3个数据池的数据。 //5.最后代替 grid 推送出去。 public TimeGridAdvHelper mTimeGridAdvHelper { get; } = new TimeGridAdvHelper(); /// /// 动作指令完成,准备推送 timegridadv 事件 /// public bool IsTimeToPushTimeGridAdv { get; private set; } = false; /// /// 以timegrid 为单位,推送数据 /// public event TimeGridAdv2EventHandler TimeGridAdv2Event; #endregion DateTime lastUpdateADTime; int lastGrid = Misc.MyBase.NULL_VALUE; DateTime lastGridTime; SGrid fGrid = new SGrid(); void advConstructor() { fGrid.SetSize(PosLen / PosOfGrid); TimeGridAdvHelperExt.ad_ts_ms = 1; mTimeGridAdvHelper.Init(); this.PropertyChanged += FlyAD7_PropertyChanged1; //更新线速度 PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, () => { if (calSpeed.Cal(Now, out int speed1, out int speed2)) { Speed = speed1; Speed2 = speed2; } }, TimeSpan.FromSeconds(1)); //轮询事件触发 PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, OnPoll); } private void FlyAD7_PropertyChanged1(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(DriveStatus)) { switch (DriveStatus) { case DRIVE_MAN_STATUS.STOP_MANUAL: case DRIVE_MAN_STATUS.STOP: case DRIVE_MAN_STATUS.LIMIT: IsFinish = true; break; case DRIVE_MAN_STATUS.RUNNING: IsFinish = false; break; } } else if ((e.PropertyName == nameof(PosLen)) || (e.PropertyName == nameof(PosOfGrid))) { fGrid.SetSize(PosLen / PosOfGrid); } else if (e.PropertyName == nameof(Marker)) { //if (DriveOrder == DRIVE_MAN_ORDER.SYNC) { //同步运行中 //检测与哪个指令一致, //该指令置为 Doing, 前面的全部置为 Finish,并且删除 for (int i = 0; i < SyncOrders.Count(); i++) { if (SyncOrders[i].Marker == Marker) { SyncOrders[i].State = SyncOrderState.Doing; for (int j = 0; j < i; j++) { SyncOrders[0].State = SyncOrderState.Finish; SyncOrders.RemoveAt(0); } } } } } } void advAfterContected() { mTimeGridAdvHelper.Clear(); lastUpdateADTime = DateTime.MinValue; lastGrid = Misc.MyBase.NULL_VALUE; lastGridTime = DateTime.MinValue; } #region IFlyADClientAdv function /// /// Runto(0), 不同于 Backward /// public void RuntoMin() { if (HasPosMaxMin) Runto(PosMin); else Runto(0); } /// /// Runto(PosLen), 不同于 Forward /// public void RuntoMax() { if (HasPosMaxMin) Runto(PosMax); else Runto(PosLen); } /// /// 设置输出 /// /// /// public void SetOutputBit(int index, bool is1) { if (index > 15)//4) return; if (index < 0) return; if (is1) { SetOutput( (UInt16)Misc.MyBase.BIT(index), (UInt16)Misc.MyBase.BIT(index)); } else { SetOutput( (UInt16)Misc.MyBase.BIT(index), (UInt16)~Misc.MyBase.BIT(index)); } } /// /// 从正反缓存区, 获取grid数据 /// /// 方向, 只有 正,反 /// grid 开始位置 /// grid 长度 /// grid 数据 public void GetGrid(Misc.DIRECTION direction, int grid_start, int grid_len, out int[] dat) { int index = 0; if (direction == Misc.DIRECTION.BACKWARD) index = 1; dat = new int[grid_len]; for (int i = 0; i < dat.Length; i++) { int grid_num = grid_start + i; if (grid_num >= fGrid.data[index].Length) break; if (GridSmooth > 0) { int sum = 0; int cnt = 0; for (int j = 0; j < (GridSmooth * 2 + 1); j++) { int idx = grid_num - GridSmooth + j; if (idx < 0) continue; if (idx >= fGrid.data[index].Length) break; if (Misc.MyBase.ISVALIDATA(fGrid.data[index][idx])) { sum += fGrid.data[index][idx]; cnt++; } } if (cnt > 0) dat[i] = sum / cnt; else dat[i] = Misc.MyBase.NULL_VALUE; } else { dat[i] = fGrid.data[index][grid_num]; } } CorrectADs?.Invoke(direction, grid_start, dat); return; } /// /// 从正反缓存区, 获取全部grid数据 /// /// 方向, 只有 正,反 /// grid 数据 public void GetGrid(Misc.DIRECTION direction, out int[] dat) { GetGrid(direction, 0, fGrid.data[0].Length, out dat); } #endregion void advPushPosAll() { advPushPos1(); advPushPos2(); } void advPushPos1() { if (mTimeGridAdvHelper.AddPos(Now, Position)) { PositionChangedEvent?.Invoke(this, new PositionChangedEventArgs() { Time = Now, Position = Position }); } } void advPushPos2() { if (mTimeGridAdvHelper.AddPos2(Now, Position2)) { Position2ChangedEvent?.Invoke(this, new PositionChangedEventArgs() { Time = Now, Position = Position2 }); } } void advPushIn() { if (isReadyContext.isReadyGetPos) { mTimeGridAdvHelper.AddIStatus(Now, IStatus); } else { if (mTimeGridAdvHelper.AddIStatus(Now, IStatus, Position, Position2, out UInt16 ichanged)) { IStatusChangedEvent?.Invoke(this, new IStatusChangedEventArgs(Now, IStatus, ichanged, Position, Position2)); } } } void advPushAd(int ad) { mTimeGridAdvHelper.AddAD(Now - TimeSpan.FromMilliseconds(ADLag), new int[] { ad }); } void advPushStatus() { if (mTimeGridAdvHelper.AddDriveStatus(Now, DriveOrder, DriveStatus, Marker)) { //状态变了 if (DriveStatus != DRIVE_MAN_STATUS.RUNNING) { //通知 mTimeGridAdvHelper 下次触发 timegridadv IsTimeToPushTimeGridAdv = true; } DriveStatusChangedEvent?.Invoke(this, new DriveStatusChangedEventArgs() { Time = Now, DriveOrder = DriveOrder, DriveStatus = DriveStatus, Marker = Marker }); } } /// /// ScanMotion 使用。给定按扫描的时间点。 当探头停下来后,获取数据 /// /// /// public TimeGridAdv2EventArgs GetTimeGridAdv2Event(DateTime beginTime) { //获取ad列表 var adList = mTimeGridAdvHelper.GetAD(beginTime); if (adList.Count() == 0) return null; DateTime endTime = mTimeGridAdvHelper.NewestTime; //获取pos var posList = mTimeGridAdvHelper.GetPos(mTimeGridAdvHelper.NewestTime, adList.Count()); Misc.DIRECTION direction; //判断运动方向 if (posList.Last() > posList.First()) { //正向 direction = DIRECTION.FORWARD; } else { direction = DIRECTION.BACKWARD; } //机架修正 if (CorrectADs != null) { for (int i = 0; i < adList.Count(); i++) { adList[i] = CorrectAD(posList[i] / PosOfGrid, adList[i]); } } TimeGridAdv2EventArgs eventArgs = new TimeGridAdv2EventArgs(); eventArgs.Direction = direction; eventArgs.EndTime = endTime; eventArgs.AdList = adList; eventArgs.PosList = posList; return eventArgs; } void OnPoll() { if (!IsConnected) return; if (!IsReady) return; OnPoll_TimeGrid(); OnPoll_TimeGridAdv(); OnPoll_miniGrid(); } void OnPoll_TimeGrid() { if (lastUpdateADTime == DateTime.MinValue) { lastUpdateADTime = Now; return; } if (Now - lastUpdateADTime < TimeSpan.FromSeconds(0.2)) return; //获取0.2s到现在的数据 var adList = mTimeGridAdvHelper.GetAD(lastUpdateADTime); //算AD均值 int ad = (int)(adList.Average()); //机架修改处理 ad = CorrectAD(Position / PosOfGrid, ad); AD = ad; //推送timegrid事件 TimeGridEvent?.Invoke(this, new TimeGridEventArgs() { Time = lastUpdateADTime, Data = adList.ToArray(), Ts = TimeGridAdvHelperExt.ad_ts }); lastUpdateADTime = Now; } void OnPoll_miniGrid() { int grid = Position / PosOfGrid; if (!Misc.MyBase.ISVALIDATA(lastGrid)) { lastGrid = grid; lastGridTime = Now; return; } if (lastGrid == grid) { if (Now - lastGridTime > TimeSpan.FromSeconds(2)) { //太慢,超过2秒还没走完一个grid //强制缩少数据 lastGridTime = Now; } return; } //方向 Misc.DIRECTION direction; if (lastGrid < grid) direction = Misc.DIRECTION.FORWARD; else direction = Misc.DIRECTION.BACKWARD; //开始位置 var adList = mTimeGridAdvHelper.GetAD(lastGridTime); var posList = mTimeGridAdvHelper.GetPos(Now, adList.Count()); var gridList = TimeGridAdvHelperExt.ToGrid(adList, posList, PosOfGrid, GridLen, out int grid_start, out int grid_end, isMiniGrid:true); if (gridList == null) return; int index = (direction == Misc.DIRECTION.BACKWARD) ? 1 : 0; if (grid_start >= fGrid.data[index].Length) return; if (grid_end >= fGrid.data[index].Length) grid_end = fGrid.data[index].Length - 1; int len = grid_end - grid_start + 1; Array.Copy(gridList, 0, fGrid.data[index], grid_start, len); //清空后面的数据 if (direction == Misc.DIRECTION.BACKWARD) { for (int i = 0; i < grid_start; i++) { fGrid.data[index][i] = Misc.MyBase.NULL_VALUE; } } else { for (int i = (grid_end + 1); i < fGrid.data[index].Length; i++) { fGrid.data[index][i] = Misc.MyBase.NULL_VALUE; } } var data = gridList.ToArray(); CorrectADs?.Invoke(direction, grid_start, data); //需要推送grid MiniGridEvent?.Invoke(this, new MiniGridEventArgs() { direction = direction, grid_start = grid_start, posOfGrid = PosOfGrid, buf = data, marker = Marker }); lastGrid = grid; lastGridTime = Now; } void OnPoll_TimeGridAdv() { if (!IsTimeToPushTimeGridAdv) return; IsTimeToPushTimeGridAdv = false; //触发全部高级版的 timegrid bool ret = mTimeGridAdvHelper.GetLastRunningTime(out DateTime beginTime, out DateTime endTime, out int marker, out DRIVE_MAN_ORDER order); if (!ret) return; //获取ad列表 var adList = mTimeGridAdvHelper.GetAD(beginTime, endTime, out DateTime reponse_endTime); int adCnt = adList.Count(); if (adCnt == 0) return; //获取pos var posList = mTimeGridAdvHelper.GetPos(reponse_endTime, adCnt); Misc.DIRECTION direction; //判断运动方向 if (posList.Last() > posList.First()) { //正向 direction = DIRECTION.FORWARD; } else { direction = DIRECTION.BACKWARD; } //机架修正 if (CorrectADs != null) { for (int i = 0; i < adList.Count(); i++) { adList[i] = CorrectAD(posList[i] / PosOfGrid, adList[i]); } } //生成grid图 var gridList = TimeGridAdvHelperExt.ToGrid(adList, posList, PosOfGrid, PosLen / PosOfGrid); //放入对应方向缓存区 int index = (direction == Misc.DIRECTION.BACKWARD) ? 1 : 0; int len = gridList.Length; if (len > fGrid.data[index].Length) len = fGrid.data[index].Length - 1; Array.Copy(gridList, 0, fGrid.data[index], 0, len); TimeGridAdv2Event?.Invoke(this, new TimeGridAdv2EventArgs { Direction = direction, EndTime = reponse_endTime, AdList = adList, PosList = posList, Marker = marker }); GridEvent?.Invoke(this, new MiniGridEventArgs() { direction = direction, marker = Marker, buf = gridList, posOfGrid = PosOfGrid, grid_start = 0 }); } int CorrectAD(int grid, int ad) { int[] d = new int[1]; d[0] = ad; CorrectADs?.Invoke(Misc.DIRECTION.FIX, grid, d); return d[0]; } } class CalSpeed { DateTime last_now; DateTime pos1_changed_time; DateTime pos2_changed_time; DateTime last_pos1_changed_time; DateTime last_pos2_changed_time; int last_pos1; int last_pos2; int pos1; int pos2; public bool Cal(DateTime now, out int speed1, out int speed2) { speed1 = 0; speed2 = 0; if (last_now == DateTime.MinValue && now != DateTime.MinValue) { last_now = now; last_pos1_changed_time = pos1_changed_time; last_pos2_changed_time = pos2_changed_time; last_pos1 = pos1; last_pos2 = pos2; return false; } if (now - last_now < TimeSpan.FromSeconds(1)) return false; if (pos1_changed_time != last_pos1_changed_time && last_pos1_changed_time != DateTime.MinValue) { speed1 = (int)((pos1 - last_pos1) / (pos1_changed_time - last_pos1_changed_time).TotalSeconds); } else { //停了 } if (pos2_changed_time != last_pos2_changed_time && last_pos2_changed_time != DateTime.MinValue) { speed2 = (int)((pos2 - last_pos2) / (pos2_changed_time - last_pos2_changed_time).TotalSeconds); } else { //停了 } last_now = now; last_pos1_changed_time = pos1_changed_time; last_pos2_changed_time = pos2_changed_time; last_pos1 = pos1; last_pos2 = pos2; return true; } public void SetPos1(DateTime changedTime, int pos) { pos1_changed_time = changedTime; pos1 = pos; } public void SetPos2(DateTime changedTime, int pos) { pos2_changed_time = changedTime; pos2 = pos; } public void Reset() { last_now = DateTime.MinValue; pos1_changed_time = DateTime.MinValue; pos1 = 0; last_pos1_changed_time = DateTime.MinValue; last_pos1 = 0; pos2_changed_time = DateTime.MinValue; pos2 = 0; last_pos2_changed_time = DateTime.MinValue; last_pos2 = 0; } } /// /// 正反转 grid数据 缓存区 /// class SGrid { public const int GRID_MAX_SIZE = 1000; int size; public int[][] data = new int[2][];//data[0]=forword, data[1]=backward public SGrid() { //清空所有数据 SetSize(GRID_MAX_SIZE); } public void SetSize(int size) { this.size = size; data[0] = new int[size]; data[1] = new int[size]; Clear(); } public void Clear() { //清空所有数据 for (int i = 0; i < size; i++) { data[0][i] = Misc.MyBase.NULL_VALUE; data[1][i] = Misc.MyBase.NULL_VALUE; } } } }