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();
}
}
}