using Misc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FlyADBase { /// /// 高级timegrid助手 /// public class TimeGridAdvHelper { NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); /// /// AD数据池时间间隔为1.28ms, 肯定最少有一个数据 /// RList ADPool = new RList(60000); /// /// pos 数据池, 肯定最少有一个数据 /// RList PosPool = new RList(60000); /// /// 输入端口 数据池, 肯定最少有一个数据 /// RList IStatusPool = new RList(60000); /// /// 上一次查找istatus的序号 /// int istatus_start_idx = -1; /// /// 上一次查找pos的序号 /// int pos_start_idx = -1; void ResetSearch() { istatus_start_idx = -1; pos_start_idx = -1; } /// /// /// public void Init() { Clear(); } /// /// 添加ad数据,且判断数据量是否够触发事件 /// /// ad值 /// 开始时间 /// 每个数据的时间间隔 单位ms public void AddAD(int[] ads, DateTime start_dt, double ts_ms) { //------------------------------------------------------------------------------------------ //AD数据添加 for (int i = 0; i < ads.Count(); i++) { DateTime dt = start_dt + TimeSpan.FromTicks((long)(TimeSpan.TicksPerMillisecond * i * ts_ms)); ADPool.RAdd(new DataTimeUnit() { data = ads[i], dt = dt }); } } /// /// 添加脉冲 /// /// /// public void AddPos(int pos, DateTime time) { if (PosPool.Count() > 0) { if (time < PosPool.Last().dt) { //异常 //把前面的都删除 PosPool.Clear(); } else if (time == PosPool.Last().dt) { return; } } PosPool.RAdd(new DataTimeUnit() { data = pos, dt = time }); } /// /// 添加输入口状态 /// /// /// /// public void AddIStatus(UInt16 istatus, int pos, DateTime time) { if (IStatusPool.Count() > 0) { if (time < IStatusPool.Last().dt) { //异常 //把前面的都删除 IStatusPool.Clear(); } } IStatusPool.RAdd(new DataTimeUnit2() { istatus = istatus, pos = pos, dt = time }); //也添加到 PosPool //AddPos(pos, time); } /// /// 添加开始位置 /// /// /// public void AddPos_Default(int pos, DateTime time) { if (PosPool.Count() == 0) { AddPos(pos, time); } } /// /// 添加开始输入口状态 /// /// /// /// public void AddIStatus_Default(UInt16 istatus, int pos, DateTime time) { if (IStatusPool.Count() == 0) { AddIStatus(istatus, pos, time); } } /// /// /// public void Clear() { PosPool.Clear(); ADPool.Clear(); IStatusPool.Clear(); ResetSearch(); } /// /// 给定结束时间点,开始grid, 找到pos数据池 对应的开始序号 与 结束序号 /// /// 开始脉冲 /// 结束时间点 /// 输出 pos数据池开始序号 /// 输出 pos数据池结束序号 /// false 发生在数据池前面, true成功获取 bool GetIdxFromPosPool(int start_pos, DateTime endtime, out int start_idx, out int end_idx) { start_idx = -1; end_idx = searchIndex(PosPool, endtime, -1); if (end_idx == -1) { //endtime 发生在过去很久以前 return false; } int pos_last = int.MinValue; int i_last = -1; for (int i = end_idx; i >= 0; i--) { int pos = PosPool[i].data; //if (pos == start_pos)//找到了!!!! //{ // pos_last = pos; // continue; //} if (pos_last == int.MinValue) { pos_last = pos; i_last = i; continue; } if (pos_last == pos) { continue; } if (Math.Abs(pos - start_pos) > Math.Abs(pos_last - start_pos)) { //越找越远, //上一个就是了 start_idx = i_last; return true; } else if (Math.Abs(pos - start_pos) == Math.Abs(pos_last - start_pos)) { //跨过了 start_idx = i_last; return true; } pos_last = pos; i_last = i; } //找完 start_idx = 0; return true; } /// /// 根据时间点,获取输入口状态 /// /// /// UInt16 GetIStatus(DateTime dt) { int index = searchIndex(IStatusPool, dt, istatus_start_idx); istatus_start_idx = index; if (index < 0) return 0xffff; else return (UInt16)IStatusPool[index].istatus; } /// /// 根据时间点,获取输入口状态 /// /// /// /// UInt16 GetIStatus(DateTime dt, out int index) { index = searchIndex(IStatusPool, dt, istatus_start_idx); istatus_start_idx = index; if (index < 0) return 0xffff; else return (UInt16)IStatusPool[index].istatus; } /// /// 以 [idx,idx+1] 的比例, 给出 time 计算 pos /// /// /// /// int GetPos(DateTime time, int idx) { if (PosPool.Count() < 0) return int.MinValue; if (PosPool.Count() == 1) return PosPool[0].data; if (idx < 0) idx = 0; if (idx >= PosPool.Count()-1) idx = PosPool.Count() - 2; double d_pos = PosPool[idx].data - PosPool[idx + 1].data; TimeSpan ts = PosPool[idx].dt - PosPool[idx + 1].dt; double p = d_pos * (time - PosPool[idx + 1].dt).Ticks / ts.Ticks; return (int)(p + PosPool[idx + 1].data); } enum SearchState { ContinueLastTime, Back, ForwToCheck } /// /// 找到 时间 dt 落在 [idx,idx+1] 之间的 idx; /// 返回 -1,证明dt 在列表在前面, 或列表数据量为=0, /// 当dt 大于 列表最后一个数, 返回最后一个数据的id /// /// /// 列表 /// /// 从列表的某个位置开始向前查找 /// int searchIndex(List list, DateTime dt, int searchStartIdx ) where T: IDataTimeUnit { if (list.Count() == 0) return -1; int max_index = list.Count() - 1; if (dt >= list[max_index].dt) return max_index; if (dt < list[0].dt) return -1; int index = searchStartIdx; SearchState state = SearchState.ContinueLastTime; if ((index < 0) || (index >= max_index))//重新开始 { index = max_index; state = SearchState.Back; } else if (state == SearchState.ContinueLastTime) { if (list[index].dt <= dt) { //可能找到了 //再向正方向看看 state = SearchState.ForwToCheck; } else { state = SearchState.Back; } } if (state == SearchState.ForwToCheck) { while (true) { DateTime time = list[index].dt; if (time > dt) { //就是之前那个 index--; return index; } else { index++; } if (index > max_index) { //最后一个就是了 throw new Exception("之前没有判断出 时间点在列表后,异常!!"); //return max_index; } } } else//SearchState.Back { while (true) { DateTime time = list[index].dt; if (time <= dt) { //就是它 return index; } index--; if(index<0) { //不可能发生 throw new Exception("之前没有判断出 时间点在列表前,异常!!"); } } } } /// /// 获取时间对应的 位置点 /// /// /// int GetPos(DateTime dt) { //找到 时间落在 [idx,idx+1] 之间的 int index = searchIndex(PosPool, dt, pos_start_idx); pos_start_idx = index; if (index < 0) index = 0; return GetPos(dt, index); } /// /// 获取 高级版的 timegrid /// /// 开始时间 /// 结束时间 /// public List GetTimeGridAdv(DateTime start_dt, DateTime end_dt) { List result = new List(); for (int i = 0; i < ADPool.Count(); i++) { int idx = ADPool.Count() - 1 - i; if (ADPool[idx].dt < start_dt) { break; } else if (ADPool[idx].dt <= end_dt) { GridAdvUnit u = new GridAdvUnit(); u.dt = ADPool[idx].dt; u.ad = ADPool[idx].data; u.pos = GetPos(u.dt); u.istatus = GetIStatus(u.dt); result.Add(u); } } result.Reverse(); return result; } enum GetGridAdvState { FindEnd, FindMid, FindBegin, Finish } /// /// 从 end_dt 开始,向前查找, pos/posOfGrid 与 direction 相反方向变化, 直到 grid_start 为止 /// /// /// /// /// /// /// public List GetTimeGridAdv(DateTime end_dt, DIRECTION direction, int grid_start, int grid_len, int posOfGrid) { ResetSearch(); List result = new List(); int grid_mid = grid_start + grid_len / 2; int grid_begin = (direction == DIRECTION.FORWARD) ? grid_start : grid_start + grid_len - 1; GetGridAdvState state = GetGridAdvState.FindEnd; //肯定要经过grid_mid for (int i = 0; i < ADPool.Count(); i++) { int idx = ADPool.Count() - 1 - i; switch (state) { case GetGridAdvState.FindEnd: if (ADPool[idx].dt <= end_dt) { state = GetGridAdvState.FindMid; GridAdvUnit u = new GridAdvUnit(); u.dt = ADPool[idx].dt; u.ad = ADPool[idx].data; u.pos = GetPos(u.dt); u.istatus = GetIStatus(u.dt); result.Add(u); } break; case GetGridAdvState.FindMid: { GridAdvUnit u = new GridAdvUnit(); u.dt = ADPool[idx].dt; u.ad = ADPool[idx].data; u.pos = GetPos(u.dt); u.istatus = GetIStatus(u.dt); result.Add(u); int grid = u.pos / posOfGrid; if (direction == DIRECTION.FORWARD) { if (grid < grid_mid) { //经过了一半数据,肯定没有错了 state = GetGridAdvState.FindBegin; } } else { if (grid > grid_mid) { //经过了一半数据,肯定没有错了 state = GetGridAdvState.FindBegin; } } } break; case GetGridAdvState.FindBegin: { GridAdvUnit u = new GridAdvUnit(); u.dt = ADPool[idx].dt; u.ad = ADPool[idx].data; u.pos = GetPos(u.dt); u.istatus = GetIStatus(u.dt); result.Add(u); int grid = u.pos / posOfGrid; if (direction == DIRECTION.FORWARD) { if (grid <= grid_begin) { //找完了 state = GetGridAdvState.Finish; goto _finish; } } else { if (grid >= grid_begin) { //找完了 state = GetGridAdvState.Finish; goto _finish; } } } break; } } _finish: if (state != GetGridAdvState.Finish) { logger.Error($"GetGridAdv({end_dt},{direction},start={grid_start},len={grid_len},posOfGrid={posOfGrid}) 不能在缓冲区找到完整的数据"); } result.Reverse(); return result; } void toGrid_step1(int[] grids_sum, int[] grids_cnt, int gridLen, int grid_idx, int ad) { if ((grid_idx < 0) || (grid_idx >= gridLen)) { return; } if (!Misc.MyBase.ISVALIDATA(ad)) return; grids_cnt[grid_idx]++; grids_sum[grid_idx] += ad; } /// /// 把 TimeGridAdv 转为 Grid /// /// /// /// /// public int[] ToGrid(IEnumerable gridAdvUnits, int posOfGrid, int gridLen) { int[] grids_sum = new int[gridLen]; int[] grids_cnt = new int[gridLen]; int grid_idx_last = -1; //grid_idx 应该要连续变化,不是,那就插值 foreach (var gridAdvUnit in gridAdvUnits) { int grid_idx = gridAdvUnit.pos / posOfGrid; if (grid_idx_last != -1 && (Math.Abs(grid_idx_last - grid_idx)>1)) { //步子太大,需要插值 if (grid_idx_last < grid_idx) { for (int i = grid_idx_last; i < grid_idx; i++) { toGrid_step1(grids_sum, grids_cnt, gridLen, i, gridAdvUnit.ad); } } else { for (int i = grid_idx_last; i > grid_idx; i--) { toGrid_step1(grids_sum, grids_cnt, gridLen, i, gridAdvUnit.ad); } } } toGrid_step1(grids_sum, grids_cnt, gridLen, grid_idx, gridAdvUnit.ad); grid_idx_last = grid_idx; } for(int i=0;i< gridLen;i++) { if (grids_cnt[i] == 0) grids_sum[i] = Misc.MyBase.NULL_VALUE; else grids_sum[i] = (int)(grids_sum[i] / grids_cnt[i]); } return grids_sum; } /// /// 把 TimeGridAdv 转为 Grid, adLag, 把AD值滞后 n * 1.28ms /// /// /// /// /// /// public int[] ToGrid(IEnumerable gridAdvUnits, int posOfGrid, int gridLen, int adLag) { int[] grids_sum = new int[gridLen]; int[] grids_cnt = new int[gridLen]; int grid_idx_last = -1; //grid_idx 应该要连续变化,不是,那就插值 for (int j = 0; j < gridAdvUnits.Count(); j++) { var gridAdvUnit = gridAdvUnits.ElementAt(j); int j_lag = j - adLag; if (j_lag < 0) j_lag = 0; else if (j_lag > gridAdvUnits.Count() - 1) j_lag = gridAdvUnits.Count() - 1; var gridAdvUnit_lag = gridAdvUnits.ElementAt(j_lag); int ad = gridAdvUnit_lag.ad; int grid_idx = gridAdvUnit.pos / posOfGrid; if (grid_idx_last != -1 && (Math.Abs(grid_idx_last - grid_idx) > 1)) { //步子太大,需要插值 if (grid_idx_last < grid_idx) { for (int i = grid_idx_last; i < grid_idx; i++) { toGrid_step1(grids_sum, grids_cnt, gridLen, i, ad); } } else { for (int i = grid_idx_last; i > grid_idx; i--) { toGrid_step1(grids_sum, grids_cnt, gridLen, i, ad); } } } toGrid_step1(grids_sum, grids_cnt, gridLen, grid_idx, ad); grid_idx_last = grid_idx; } for (int i = 0; i < gridLen; i++) { if (grids_cnt[i] == 0) grids_sum[i] = Misc.MyBase.NULL_VALUE; else grids_sum[i] = (int)(grids_sum[i] / grids_cnt[i]); } return grids_sum; } #region 子功能,grid 的 istatus 变化 class GridIstatusPush { public int istatus_no; public event GridIStatusEventHander GridIStatusEvent; public void Notify(object sender, GridIStatusEventArgs e) { GridIStatusEvent?.Invoke(sender, e); } public bool IsNoEvent() { if (GridIStatusEvent == null) return true; else return false; } } List mGridIStatusEventList = new List(); /// /// 添加事件 /// /// /// public void GridIStatusEventAdd(int istatus_no, GridIStatusEventHander func) { var gs = from _g in mGridIStatusEventList where _g.istatus_no == istatus_no select _g; GridIstatusPush g; if (gs.Count() > 0) { g = gs.First(); g.GridIStatusEvent += func; } else { g = new GridIstatusPush() { istatus_no = istatus_no }; g.GridIStatusEvent += func; mGridIStatusEventList.Add(g); } } /// /// 删除事件 /// /// /// public void GridIStatusEventDel(int istatus_no, GridIStatusEventHander func) { var gs = from _g in mGridIStatusEventList where _g.istatus_no == istatus_no select _g; GridIstatusPush g; if (gs.Count() > 0) { g = gs.First(); g.GridIStatusEvent -= func; if (g.IsNoEvent()) { mGridIStatusEventList.Remove(g); } } } /// /// 触发GridIStatusEvent事件 /// /// /// /// /// /// /// public void NotifyGridIStatusEvent(DIRECTION direction, int pos_start, int pos_len, int marker, DateTime dt, object sender) { if (mGridIStatusEventList.Count() <= 0) return; int start_idx; int end_idx; int start_pos; int end_pos; if (direction == DIRECTION.FORWARD) { start_pos = pos_start; end_pos = pos_start + pos_len - 1; } else { start_pos = pos_start + pos_len - 1; end_pos = pos_start; } bool ret = GetIdxFromPosPool( start_pos, dt, out start_idx, out end_idx); if (ret == false)//异常 return; DateTime start_dt = PosPool[start_idx].dt; DateTime end_dt = dt; foreach (GridIstatusPush g in mGridIStatusEventList) { List data0; List data1; GetIStatusRange( g.istatus_no, start_dt, end_dt, direction, start_pos, end_pos, out data0, out data1); g.Notify(sender, new GridIStatusEventArgs() { direction = direction, istatus_no = g.istatus_no, marker = marker, pos_start = pos_start, pos_len = pos_len, data_0 = data0, data_1 = data1 }); } } void GetIStatusRange( int istatus_no, DateTime start_dt, DateTime end_dt, DIRECTION direction, int start_pos, int end_pos, out List data0, out List data1) { data0 = new List(); data1 = new List(); UInt16 istatus = GetIStatus(start_dt, out int istatus_idx); if (istatus_idx == -1)//异常,列表没有数据 { //全部都是1 if (direction == DIRECTION.FORWARD) data1.Add(new Range() { Begin = start_pos, End = end_pos }); else data1.Add(new Range() { Begin = end_pos, End = start_pos }); return; } bool istatus_no_b = Misc.MyBase.CHECKBIT(istatus, istatus_no ); Range r = new Range(); r.Begin = start_pos; for (int i = istatus_idx + 1; i < IStatusPool.Count(); i++) { istatus = IStatusPool[i].istatus; int pos = IStatusPool[i].pos; bool b = Misc.MyBase.CHECKBIT(istatus, istatus_no ); if (IStatusPool[i].dt >= end_dt) { //结束,这是最后!!! break; } else { if (b != istatus_no_b) { r.End = pos; if (istatus_no_b) data1.Add(r); else data0.Add(r); istatus_no_b = b; r = new Range(); r.Begin = pos; } } } r.End = end_pos; if (istatus_no_b) data1.Add(r); else data0.Add(r); if (direction == DIRECTION.FORWARD) { for (int i = 0; i < data0.Count(); i++) { data0[i].End -= 1; } for (int i = 0; i < data1.Count(); i++) { data1[i].End -= 1; } } else { for (int i = 0; i < data0.Count(); i++) { data0[i].End += 1; int t = data0[i].End; data0[i].End = data0[i].Begin; data0[i].Begin = t; } data0.Reverse(); for (int i = 0; i < data1.Count(); i++) { data1[i].End += 1; int t = data1[i].End; data1[i].End = data1[i].Begin; data1[i].Begin = t; } data1.Reverse(); } } #endregion } interface IDataTimeUnit { DateTime dt { get; set; } } struct DataTimeUnit: IDataTimeUnit { public int data; public DateTime dt { get; set; } public override string ToString() { return data.ToString() + " |" + dt.Ticks.ToString(); } } struct DataTimeUnit2: IDataTimeUnit { public UInt16 istatus; public int pos; public DateTime dt { get; set; } public override string ToString() { return pos.ToString() + "(" + istatus.ToString("X2") + ") |" + dt.ToString(); } } }