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