using AutoMapper;
using FLY.Thick.Blowing;
using FLY.Thick.Blowing.IService;
using FLY.Thick.Blowing360.IService;
using FlyADBase;
using FObjBase;
using Misc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FLY.Thick.Blowing360.Server
{
///
/// 1.每次收到限位信号(有->没 and 没->有),都会触发 FilmInfoChangedEvent ;时间为上一次限位信号
/// 2.改变辊周长,膜长,都会触发 FilmInfoChangedEvent ;时间为前3次限位信号
/// 3.每次查找FilmInfo, 时间比 当前的限位信号+RenZiJiaPeriod 还要未来,修正 RenZiJiaPeriod,
/// RenZiJiaPeriod预留10s的余量,避免不断修正
/// 并且触发FilmInfoChangedEvent 时间为上一次限位信号
///
public class BlowingDetect360 : IBlowingDetectInServer, IBlowingDetectService, INotifyPropertyChanged
{
#region 输入口定义
///
/// 旋转架转一圈的定位信号, 正向
///
const int Limit0_bit = 9;
///
/// 旋转架转一圈的定位信号, 反向
///
const int Limit1_bit = 10;
///
/// 辊信号 对应输入位
///
const int Roll_bit = 11;
#endregion
#region 无效接口
///
/// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
///
[Obsolete("360连续旋转 此接口无效")]
public double RAngle { get { return 360; } set { } }
///
/// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
[Obsolete("360连续旋转 此接口无效")]
public bool IsSign0Double { get; set; }
///
/// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
[Obsolete("360连续旋转 此接口无效")]
public bool IsSign1Double { get; set; }
///
/// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向)
///
[Obsolete("360连续旋转 此接口无效")]
public BlowingSignType SignType { get; set; } = BlowingSignType.Short;
///
/// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的
///
[Obsolete("360连续旋转 此接口无效")]
public TimeSpan LimitSignTime { get; set; } = TimeSpan.FromSeconds(4);
///
/// 转向信号100ms 滤波
///
[Obsolete("360连续旋转 此接口无效")]
public bool IsSignFilter { get; set; }
///
/// 缺少信号模式
/// 正反信号线接得不好,有时候会丢失了。
/// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!!
/// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号
///
[Obsolete("360连续旋转 此接口无效")]
public bool IsLackSignMode { get; set; }
///
/// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大
///
[Obsolete("360连续旋转 此接口无效")]
public bool Is3D { get; set; }
///
/// 使用编码器检测旋转架旋转位置模式
///
public bool IsRPosMode { get; set; }
///
/// 人字架 离开限位 到 撞下一个限位 的 旋转架转动总脉冲 单位 脉冲
///
public int RPosOfR { get; set; } = 50000;
///
/// 刚开机,信号列表一个信号都没有,创造一个时间点,作为信号点
///
public DateTime DefaultTime { get; set; }
#endregion
#region 参数
///
/// 人字架 旋转1周 设置 时间。
/// 刚开机时,RenZiJiaPeriod = DefaultRPeriod;
/// 异常时,RenZiJiaPeriod = DefaultRPeriod;
///
public TimeSpan DefaultRPeriod { get; set; }
///
/// 人字架到测厚仪膜长 单位m
///
public double FilmLength { get; set; } = 25;//m
///
/// 辊周长,单位mm
///
public double RollPerimeter { get; set; } = 314;//mm
///
/// 辊周长,单位m
///
double MofR => RollPerimeter / 1000.0;
///
/// 自定义 转向信号 输入口序号 从0开始 默认9-1
///
public int InNo_Limit0 { get; set; } = 9 - 1;
///
/// 自定义 转向信号 输入口序号 从0开始 默认10-1
///
public int InNo_Limit1 { get; set; } = 10 - 1;
///
/// 自定义 辊速度 输入口序号 从0开始 默认11-1
///
public int InNo_Roll { get; set; } = 11 - 1;
#endregion
#region 状态
///
/// 当前旋转架旋转角度
///
public double Angle { get; private set; }
///
/// 当前方向
///
public Misc.DIRECTION Direction { get; private set; }
///
/// 累计旋转次数
///
public virtual int RotationCnt { get; protected set; }
///
/// 最新的限位信号序号
///
public int LastLimitNo { get; private set; }
///
/// 最后一个限位信号 到当前已经消耗的时间
///
public TimeSpan PastTime { get; private set; }
///
/// 当前线速度,单位 m/min
///
public double FilmVelocity { get; private set; }
///
/// 离开限位 到 撞下一个限位 的 旋转架转动时间, 需要初始值,以后测量出来的
///
public TimeSpan RenZiJiaPeriod { get; private set; }
///
/// 缓冲区拥有数据的时间长度,1s更新一次
///
public TimeSpan BufTotalTime { get; private set; }
///
/// 原点旋转脉冲 上一次撞限位开关0时, 旋转架脉冲
///
public int OrgRPos { get; protected set; }
///
///旋转架全局脉冲, 当前旋转架脉冲 = GlobalRPos - OrgRPos;
///
public int GlobalRPos { get; protected set; }
///
/// 旋转架脉冲速度, 旋转架角速度 = RSpeed * RAngle / RPosOfR
///
public int RSpeed { get; protected set; }
#endregion
#region 成员变量
///
/// 每圈数据列表
///
public RList mLimitList;
///
/// 辊信号列表
///
RList mRollList;
///
/// 转向信号列表
///
RList mSignList;
///
/// 后触发,信号改变推送列表!!
///
List mdtFilmInfoChangedEventList = new List();
///
/// 旋转脉冲列表
///
RList mRPosList;
///
/// 原点旋转脉冲 是否有效
///
OrgRPosValidLevel OrgRPosValidLv;
enum OrgRPosValidLevel
{
///
/// 刚开机,OrgRPos = 0
///
Invalid,
///
/// 没有撞任何一个转向信号,使用列表第1个作为OrgRPos
///
JustListHeader,
///
/// 撞转向信号0,真正的 OrgRPos = GlobalRPos
///
Sign0
}
IFlyADClientAdv flyad;
#endregion
///
///
///
public BlowingDetect360()
{
DefaultRPeriod = TimeSpan.FromMinutes(8);
RenZiJiaPeriod = DefaultRPeriod;
RollPerimeter = 314;//mm
FilmLength = 25;//m
Angle = 0;//°
FilmVelocity = 0;//m/min
mRollList = new Misc.RList(50 * 60 * 1000 / 314);//以50m/min 线速度,储存大于 60min数据
mLimitList = new Misc.RList(10);//人字架转10次的信号
mSignList = new RList(10);//人字架正反转信号
mRPosList = new RList(60 * 60);//人字架脉冲数据 1秒一个 60分钟数据
RotationCnt = 0;
Clear();
PropertyChanged += new PropertyChangedEventHandler(RenZiJiaAndFilmPositionDetect_PropertyChanged);
}
int RotationCntToLimitIndex(int rotationcnt)
{
if (RotationCnt == 0)
return -1;
else
return rotationcnt - 1 - (RotationCnt - mLimitList.Count);
}
///
/// 正反转信号列表中的序号,转旋转次数
///
///
///
int LimitIndexToRotationCnt(int index)
{
if (RotationCnt == 0)
return 0;
else
return RotationCnt - mLimitList.Count + index + 1;
}
///
/// 使参数合法
///
public void CheckParamErr()
{
if ((DefaultRPeriod.TotalMinutes < 4) || (DefaultRPeriod.TotalMinutes > 25))
{
DefaultRPeriod = TimeSpan.FromMinutes(5.8);
}
if ((FilmLength < 0) || (FilmLength > 100))
{
FilmLength = 25;
}
if ((RollPerimeter < 100) || (RollPerimeter > 1000))
{
RollPerimeter = 314;
}
}
///
/// 触发膜信息修改事件
///
/// 时间点
void TouchFilmInfoChanged(DateTime dt)
{
if (FilmInfoChangedEvent == null)
return;
mdtFilmInfoChangedEventList.Add(dt);
TouchFilmInfoChanged();
}
///
/// 触发膜信息修改事件列表的全部事件
///
void TouchFilmInfoChanged()
{
if (FilmInfoChangedEvent == null)
return;
while (mdtFilmInfoChangedEventList.Count > 0)
{
DateTime dt = mdtFilmInfoChangedEventList.First();
DateTime dt_future;
int ret = GetFuture(dt, out dt_future);
if (ret == 1)//发生在信号列表的未来,留着,以后再触发!!
{
break;
}
else if (ret == 0)//发生在信号列表的中间
{
//只需要触发最老的那个就够了!!!!!
mdtFilmInfoChangedEventList.Clear();
FilmInfoChangedEvent(this, new FilmInfoChangedEventArgs(dt_future));
}
else//发生在信号列表的以前
{
//以前的事件不触发,除非就只有这么一个
mdtFilmInfoChangedEventList.RemoveAt(0);
if (mdtFilmInfoChangedEventList.Count == 0)
{
FilmInfoChangedEvent(this, new FilmInfoChangedEventArgs(dt_future));
}
}
}
}
//TODO
void RenZiJiaAndFilmPositionDetect_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ((e.PropertyName == nameof(FilmLength)) ||
(e.PropertyName == nameof(RollPerimeter))
)
{
TouchFilmInfoChanged_lastLimit(4);
}
else if (e.PropertyName == nameof(RenZiJiaPeriod))
{
TouchFilmInfoChanged_lastLimit(1);
}
else if (e.PropertyName == nameof(DefaultRPeriod))
{
RenZiJiaPeriod = DefaultRPeriod;
}
}
///
/// 触发膜信息修改事件
///
/// 最后旋转几次
void TouchFilmInfoChanged_lastLimit(int last_cnt)
{
if (mLimitList.Count < last_cnt)
{
last_cnt = mLimitList.Count;
}
int idx = mLimitList.Count - last_cnt - 1;
if (idx <= 0)
{
TouchFilmInfoChanged(DefaultTime);
}
else
{
TouchFilmInfoChanged(mLimitList[idx].dt_begin);
}
}
///
/// 创建完对象后,必须赋值
///
///
public void Init(IFlyADClientAdv flyad)
{
this.flyad = flyad;
flyad.IStatusChangedEvent += new FlyADBase.IStatusChangedEventHandler(flyad_IStatusChangedEvent);
FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, new FObjBase.PollModule.PollHandler(OnPoll));
}
void update_limit(FlyADBase.IStatusChangedEventArgs e)
{
bool bchanged = false;//信号更新?
//TODO,不使用AD盒的时间
//DateTime dt = e.Time;//信号更新发生的时间
DateTime dt = DateTime.Now;
int no = 0;
bool status = false;//信号的状态 true开始, false结束
int position2 = e.Position2;
if (Misc.MyBase.CHECKBIT(e.IChanged, InNo_Limit0))//信号0 发生变化
{
bchanged = true;
no = 0;
if (!Misc.MyBase.CHECKBIT(e.IStatus, InNo_Limit0))
{
status = true;
}
}
else if (Misc.MyBase.CHECKBIT(e.IChanged, InNo_Limit1))//信号1 发生变化
{
bchanged = true;
no = 1;
if (!Misc.MyBase.CHECKBIT(e.IStatus, InNo_Limit1))
{
status = true;
}
}
if (!bchanged)//没有信号更新
return;
SignData sd = new SignData() { Time = dt, On = status, No = no };
mSignList.RAdd(sd);
if (IsRPosMode)
{
//使用编码器测量旋转角度
//方向 由编码器决定
update_orgRPos(sd);
}
else
{
//没有编码器,方向由 信号类型决定
Direction = no == 0 ? Misc.DIRECTION.FORWARD : Misc.DIRECTION.BACKWARD;
}
LastLimitNo = no;
update_limit(dt, status);
}
void update_orgRPos(SignData sd)
{
if (!sd.On)
return;
OrgRPosValidLv = OrgRPosValidLevel.Sign0;
OrgRPos = sd.GlobalRPos;
int speed = flyad.Speed2;
if (speed > 0)
Direction = DIRECTION.FORWARD;
else if (speed < 0)
Direction = DIRECTION.BACKWARD;
}
///
/// 添加正反转信号到 列表
///
/// 信号更新发生的时间
/// 信号的状态 true开始, false结束
void update_limit(DateTime dt, bool status)
{
//把信号加入到列表
if (status == true)//添加,写入开始时间
{
mLimitList.RAdd(new LimitCell() { dt_begin = dt,dt_end = dt });
RotationCnt++;
}
else //写入 结束时间
{
if (mLimitList.Count > 0)
{
LimitCell lc = mLimitList.Last();
lc.dt_end = dt;//完成一次信号
}
else
{
//异常,还没开始,就结束了
mLimitList.RAdd(new LimitCell() { dt_begin = dt, dt_end = dt });
RotationCnt++;
}
}
//更新其它参数
update_param_for_limit(dt);
}
///
/// 更新因为旋转信号的添加导致的变化
///
/// 信号变化的时间
void update_param_for_limit(DateTime dt)
{
if (mLimitList.Count() > 0)
{
LimitCell lc = mLimitList.Last();
if (dt == lc.dt_begin)//刚撞转向
{
//更新人字架旋转周期
if (mLimitList.Count > 1)
{
DateTime dt1 = mLimitList[mLimitList.Count - 1].dt_begin;
DateTime dt2 = mLimitList[mLimitList.Count - 2].dt_begin;
RenZiJiaPeriod = dt1 - dt2;//只有这个是正确的!!!
//触发修正事件!!!!!
TouchFilmInfoChanged(dt2);
}
else if (mLimitList.Count == 1)
{
DateTime dt1 = mLimitList[mLimitList.Count - 1].dt_begin;
DefaultTime = dt1 - RenZiJiaPeriod;
//触发修正事件!!!!!
TouchFilmInfoChanged(DefaultTime);
}
}
}
}
void update_roll(FlyADBase.IStatusChangedEventArgs e)
{
//----------------------------------------------------------------------
//更新辊信号
if (!Misc.MyBase.CHECKBIT(e.IChanged, InNo_Roll))
return;//没有信号更新
//信号的状态
bool status = !Misc.MyBase.CHECKBIT(e.IStatus, InNo_Roll);
if (status)//1->0 触发!!!
{
mRollList.RAdd(
new RollCell2()
{
Time = DateTime.Now,//用于时间定位
SysTime = e.Time,//用于算速度
});
//触发修正事件!!!!!
TouchFilmInfoChanged();
}
}
void flyad_IStatusChangedEvent(object sender, FlyADBase.IStatusChangedEventArgs e)
{
update_limit(e);
update_roll(e);
}
///
/// 1秒触发一次
///
void OnPoll()
{
if (!flyad.IsReady || !flyad.IsConnected)
return;
//--------------------------------------------------------------------
//旋转架速度安全检测
OnPoll_checkRVelocity();
//--------------------------------------------------------------------
if (IsRPosMode)
{
//更新旋转脉冲
OnPoll_updateRPos();
//更新旋转角度
OnPoll_updateAngleByRPos();
}
else
{
//--------------------------------------------------------------------
//当前旋转架角度
OnPoll_updateAngle();
}
//--------------------------------------------------------------------
//线速度安全检测
OnPoll_checkFilmV();
//--------------------------------------------------------------------
//更新当前线速度
OnPoll_updateFilmV();
//--------------------------------------------------------------------
//限制列表大小
KeepSize();
}
void KeepSize()
{
DateTime keepTime = DateTime.Now - TimeSpan.FromMinutes(60);
//限制数量
List dts = new List();
dts.Add(KeepSize(mRollList, keepTime));
dts.Add(KeepSize(mSignList, keepTime));
dts.Add(KeepSize(mRPosList, keepTime));
var earlyTime = dts.Min();
if (DateTime.Now > earlyTime)
{
BufTotalTime = DateTime.Now - earlyTime;
}
else
{
BufTotalTime = TimeSpan.Zero;
}
}
DateTime KeepSize(List list, DateTime keepTime) where T : ITimeCell
{
if (list.Count() > 0)
{
while (true)
{
var cell = list.First();
if (cell.Time > keepTime)
break;
list.RemoveAt(0);
if (list.Count() == 0)
break;
}
}
if (list.Count() > 0)
{
return list.First().Time;
}
return DateTime.MaxValue;
}
void OnPoll_updateRPos()
{
DateTime now = DateTime.Now;
DateTime sysTime = flyad.Now;
int globalRPos = flyad.Position2;
int speed = flyad.Speed2;
if (mRPosList.Count() > 0)
{
var lastPosData = mRPosList.Last();
if (now - lastPosData.Time < TimeSpan.FromSeconds(1))
{
return;
}
}
mRPosList.RAdd(new RPosData() { Time = now, SysTime = sysTime, Speed = speed, GlobalRPos = globalRPos });
if (OrgRPosValidLv == OrgRPosValidLevel.Invalid)
{
OrgRPos = globalRPos;
//第1次, 赋值为第1个全局旋转脉冲
OrgRPosValidLv = OrgRPosValidLevel.JustListHeader;
}
GlobalRPos = globalRPos;
RSpeed = speed;
}
///
/// 旋转架速度换算
///
void OnPoll_updateAngleByRPos()
{
//必须在 0~RPosOfR 范围内
Angle = GetAngleByGlobalRPos(GlobalRPos);
}
public double GetAngleByGlobalRPos(int globalRPos)
{
//OrgRPos 为 -RAngle/2
int rPos = globalRPos - OrgRPos;
//360°对应的旋转脉冲
int rPosOf360 = RPosOfR;
while (rPos < 0)
rPos += rPosOf360;
while (rPos > rPosOf360)
rPos -= rPosOf360;
double angle = 360 * rPos / RPosOfR;
//角度范围是 0°~ 360°
return angle;
}
///
/// 旋转架速度安全检测
///
void OnPoll_checkRVelocity()
{
DateTime dt = DefaultTime;
if (mLimitList.Count > 0)
{
LimitCell lc = mLimitList.Last();
if (lc.dt_end != DateTime.MinValue)
dt = lc.dt_end;
else
dt = lc.dt_begin;
}
PastTime = DateTime.Now - dt;
if (PastTime > TimeSpan.FromMinutes(25))//25min
{
//大于25分钟,也没有一个信号,复位
Clear();
}
}
///
/// 更新旋转架速度
///
void OnPoll_updateAngle()
{
GetAngle(DateTime.Now, false, out double angle, out int rotationCnt);
Angle = angle;
}
///
/// 更新当前线速度
///
void OnPoll_updateFilmV()
{
if (mRollList.Count > 1)
{
DateTime sysTime1 = mRollList[mRollList.Count - 1].SysTime;//最后一次的sysTime
int cnt = 5;//5次均值
if (cnt > mRollList.Count)
cnt = mRollList.Count;
DateTime sysTime2 = mRollList[mRollList.Count - cnt].SysTime;//5次前的sysTime
TimeSpan ts_last = TimeSpan.FromTicks((sysTime1 - sysTime2).Ticks / (cnt - 1));//5次的平均滤波, 使用AD盒的时间算出来的时间差,最准
DateTime dt11 = mRollList[mRollList.Count - (cnt - 1)].Time;//4次前的PC时间
//因为没有使用AD盒的时间,所以可以用DateTime.Now 比较
TimeSpan ts_now = TimeSpan.FromTicks((DateTime.Now - dt11).Ticks / (cnt - 1));//使用 电脑的时间,算出来的时间差,大概而已
TimeSpan ts_max;
if (ts_now > (ts_last + ts_last))//超过了2倍的时间,肯定是慢下来了
{
ts_max = ts_now;
}
else
{
ts_max = ts_last;
}
FilmVelocity = MofR / ts_max.TotalMinutes;
}
}
///
/// 线速度安全检测
///
void OnPoll_checkFilmV()
{
if (mRollList.Count > 0)
{
DateTime dt = mRollList[mRollList.Count - 1].Time;
TimeSpan ts = DateTime.Now - dt;
if (ts.TotalMinutes > 1)//1min
{
//大于1分钟,也没有一个信号,复位
Clear();
}
}
}
public void Clear()
{
mLimitList.Clear();
mRollList.Clear();
DefaultTime = DateTime.Now;
FilmVelocity = 0;
RenZiJiaPeriod = DefaultRPeriod;
RotationCnt = 0;//复位次数!!!
LastLimitNo = -1;
BufTotalTime = TimeSpan.Zero;
ClearEvent?.Invoke(this);
}
//public class FilmInfo
//{
// ///
// /// 上层 位于膜泡的角度 0~360°
// ///
// public double angle1;
// ///
// /// 下层 位于膜泡的角度 0~360°
// ///
// public double angle2;
// ///
// /// 膜泡旋转方向
// ///
// public Misc.DIRECTION direction;
// ///
// /// 线速度
// ///
// public double filmVelocity;
// ///
// /// 累计旋转次数
// ///
// public int rotationCnt;
//}
public enum GetLimitTimeResult
{
///
/// 获取成功
///
OK,
///
/// 不存在该点
///
NOEXIST,
///
/// 限位已经撞了,但膜还没走完,等吧
///
ISFURTURE
}
///
/// 计算膜长用;
/// 输入膜长,获取对应的 限位点时间
///
/// 旋转次数
/// 膜长
/// 膜长对应限位时间
///
public GetLimitTimeResult GetLimitTime(int rotationcnt, double filmlength, out DateTime timeOfFilmLength)
{
timeOfFilmLength = DateTime.MinValue;
int index = RotationCntToLimitIndex(rotationcnt);
if (index == -1)
return GetLimitTimeResult.NOEXIST;
if (index >= mLimitList.Count)
return GetLimitTimeResult.ISFURTURE;
DateTime dt = mLimitList[index].dt_begin;
int ret = GetFuture(dt, filmlength, out timeOfFilmLength);
if (ret == 1)
return GetLimitTimeResult.ISFURTURE;
return GetLimitTimeResult.OK;
}
///
/// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比LimitList.Last.dt+RenZiJiaPeriod还要后,更新RenZiJiaPeriod
/// 返回 -1, 当前的时间点在列表以前;
/// 返回 0, 当前的时间点在列表中;
/// 返回 1, 当前的时间点在列表的未来;
///
/// 被压扁后的膜宽度
/// 探头所在膜的横向位置
/// 测量的时间点
///
public int GetFilmInfo(out FilmInfo filminfo, DateTime dt, double filmWidth, double filmPosH)
{
double filmlen = FilmLength;
return GetFilmInfo(out filminfo, dt, filmWidth, filmPosH, filmlen);
}
int GetFilmInfo(out FilmInfo filminfo, DateTime dt, double filmWidth, double filmPosH, double filmLen)
{
filminfo = null;
//与0°的偏移
double a = filmPosH / filmWidth * 180;
if ((a < 0) || (a > 180))
{
return -1;//不在膜的范围内
}
if (dt == DateTime.MinValue)
{
//TODO, BUG
return -1;//异常!!!
}
int ret = GetAgo(dt, out DateTime ago_dt, out int ago_index);
bool ret2 = GetAngle(a, ago_dt, out double angle0, out double angle1, out int rotationcnt);
if (!ret2)
return -1;//异常!!!
double velocity = RollList_GetVelocity(ago_index);
filminfo = new FilmInfo()
{
angle1 = angle0,
angle2 = angle1,
filmVelocity = velocity,
direction = Direction,
rotationCnt = rotationcnt,
inCV = true
};
return ret;
}
#region 人字架时间点<-->24m后膜时间点
///
/// 输入当前时间点,找到 刚出人字架时的时间点, 及那时候的线速度;
/// 返回 -1, 当前的时间点在列表以前;
/// 返回 0, 当前的时间点在列表中;
/// 返回 1, 当前的时间点在列表的未来;
///
/// 当前时间
/// 24米真实长度
///
int GetAgo(DateTime dt, out DateTime ago_dt, out int ago_index)
{
return GetAgoFix(dt, out ago_dt, out ago_index);
}
///
/// 膜距离固定 获取膜距离前的时间点
///
///
///
///
///
int GetAgoFix(DateTime dt, out DateTime ago_dt, out int ago_index)
{
ago_dt = DateTime.MinValue;
ago_index = -1;
int index;
int ret = RollList_FindIndex(dt, out index);
if (ret == 1)//发生在未来,什么都不干
return ret;
//纵向位置
double filmPosition = RollList_GetPosition(index, dt);
double filmPos0 = filmPosition - FilmLength;
ret = RollList_GetDateTime(filmPos0, out index, out ago_dt);
ago_index = index;
return ret;
}
///
/// 输入过去时间,让它再加上膜长时间,得到未来时间;
/// 返回 -1, 未来的时间在列表以前;
/// 返回 0, 未来的时间在列表中;
/// 返回 1, 未来的时间在列表的未来;
///
/// 过去时间
/// 未来时间
///
int GetFuture(DateTime dt, out DateTime dt_future)
{
return GetFuture(dt, FilmLength, out dt_future);
}
///
/// 输入过去时间,让它再加上膜长时间,得到未来时间;
/// 返回 -1, 未来的时间在列表以前;
/// 返回 0, 未来的时间在列表中;
/// 返回 1, 未来的时间在列表的未来;
///
/// 过去时间
/// 膜长
/// 未来时间
///
int GetFuture(DateTime dt, double filmlength, out DateTime dt_future)
{
dt_future = DateTime.MinValue;
double position;
int i;
int ret = RollList_FindIndex(dt, out i);
if (ret == -1)
position = RollList_GetPosition(0, dt);
else if (ret == 0)
position = RollList_GetPosition(i, dt);
else
return ret;
position += filmlength;
return RollList_GetDateTime(position, out i, out dt_future);
}
#endregion
///
/// 计算出角度 0~360° 恒速阶段
///
/// 旋转结束时间点
/// 旋转开始时间点
/// 用于转换为角度的时间
/// 输出: 角度
void LimitListCal_ConstantVelocity(DateTime dtEnd, DateTime dtStart, DateTime dt, out double angle)
{
TimeSpan ts1 = dt - dtStart;
TimeSpan ts2 = dtEnd - dtStart;
angle = 360 * ts1.Ticks / ts2.Ticks;
}
///
/// 时间点查找旋转角度 0~360°
///
/// 时间
/// 当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod
/// 角度
/// 旋转的方向
/// 旋转的次数
/// 在恒速区
void GetAngle(DateTime dt, bool modifyparam, out double angle, out int rotationCnt)
{
int index;
ListFindIndexResult ret = LimitList_FindIndex(dt, out index);
DateTime dt_start;
DateTime dt_end;
switch (ret)
{
case ListFindIndexResult.Future://发生在未来
{
if (dt < DefaultTime)
DefaultTime = dt - TimeSpan.FromSeconds(10);
//检测是否需要修正 RenZiJiaPeriod
if (mLimitList.Count == 0)
{
dt_start = DefaultTime;
}
else
{
dt_start = mLimitList[index].dt_begin;
}
rotationCnt = LimitIndexToRotationCnt(index);
if (dt > (dt_start + RenZiJiaPeriod))
{
if (modifyparam)
{
//修正!!!!
RenZiJiaPeriod = dt - dt_start + TimeSpan.FromSeconds(10);
}
else
{
dt = dt_start + RenZiJiaPeriod;//只是为了看,随便了。 时间停止下来
}
}
dt_end = dt_start + RenZiJiaPeriod;
LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle);
}
break;
case ListFindIndexResult.BetweenCells:
{
dt_start = mLimitList[index].dt_begin;
dt_end = mLimitList[index + 1].dt_begin;
rotationCnt = LimitIndexToRotationCnt(index);
LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle);
}
break;
//case ListFindIndexResult.Past://过去
default:
{
dt_start = DefaultTime;
dt_end = mLimitList[0].dt_begin;
rotationCnt = LimitIndexToRotationCnt(-1);
LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle);
}
break;
}
if (Direction == DIRECTION.BACKWARD)
angle = 360 - angle;
}
#region RollList 查找
///
/// 获取 [idx,idx+1] 之间 的 位置, 单位m
///
///
///
///
double RollList_GetPosition(int idx, DateTime dt)
{
DateTime dt1 = mRollList[idx + 1].Time;
double filmPos1 = (idx + 1) * MofR;
DateTime dt0 = mRollList[idx].Time;
double filmPos0 = (idx) * MofR;
return (filmPos1 - filmPos0) * (dt - dt0).Ticks / (dt1 - dt0).Ticks + filmPos0;
}
///
/// 获取 [idx,idx+1] 之间 的 速度 m/min
///
///
///
double RollList_GetVelocity(int idx)
{
DateTime dt1 = mRollList[idx + 1].SysTime;
DateTime dt2 = mRollList[idx].SysTime;
return MofR / dt1.Subtract(dt2).TotalMinutes;
}
///
/// 获取 [idx,idx+1] 之间 的 时间
///
///
/// 纵向位置m
///
DateTime RollList_GetDateTime(int idx, double filmPosition)
{
//TODO 经常出错,position 无限小,idx=0
DateTime dt1 = mRollList[idx + 1].Time;
double filmPos1 = (idx + 1) * MofR;
DateTime dt2 = mRollList[idx].Time;
double filmPos2 = (idx) * MofR;
return dt2.Add(new TimeSpan((long)((dt1 - dt2).Ticks * (filmPosition - filmPos2) / (filmPos1 - filmPos2))));
}
///
/// 通过 filmPosition 查找时间
/// 返回 -1,这个过去的时间;
/// 返回 0,在列表中;
/// 返回 1,发生在未来的时间;
///
/// 单位m
///
int RollList_GetDateTime(double filmPosition, out int idx, out DateTime dt)
{
idx = 0;
dt = DateTime.MinValue;
if (filmPosition < 0)
return -1;
if (mRollList.Count == 0)
return 1;
idx = (int)(filmPosition / MofR);
if (idx >= mRollList.Count - 1)
{
return 1;
}
dt = RollList_GetDateTime(idx, filmPosition);
return 0;
}
///
/// dt 这个时间,在是 index 与 index+1 中间发生的
/// 返回 -1,这个过去的时间;
/// 返回 0,在列表中;
/// 返回 1,发生在未来的时间;
///
///
///
int RollList_FindIndex(DateTime dt, out int index)
{
index = 0;
if (mRollList.Count <= 1)//没数据,所以是发生在未来的
return 1;
for (int i = mRollList.Count - 1; i >= 0; i--)
{
if (dt >= mRollList[i].Time)
{
//找到了
if (i == (mRollList.Count - 1))//这个是未来的时间点,还没发生!!!
{
index = i;
return 1;
}
else
{
index = i;
return 0;
}
}
}
//这个在过去的时间
return -1;
}
#endregion
///
/// 时间对应旋转状态
///
enum DTSTATUS
{
///
/// 发生在未来的均速阶段
///
Future,
///
/// 发生在过去的均速阶段
///
Past,
///
/// 发生在均速阶段
///
Normal,
///
/// 发生在方向切换阶段
///
Swap,
}
enum ListFindIndexResult
{
///
/// 发生在未来, index.dt_end ~ 未来, 可能 index都还没有发生
///
Future,
///
/// 发生在 index.dt_end ~ (index+1).dt_begin
///
BetweenCells,
///
/// 发生在 过去
///
Past
}
///
/// 找到 列表中 (开始时间,结束时间) 小于 dt 的信号点
///
///
///
///
ListFindIndexResult LimitList_FindIndex(DateTime dt, out int index)
{
index = 0;
if (mLimitList.Count <= 0)//没数据,所以是发生在未来的
return ListFindIndexResult.Future;
for (int i = mLimitList.Count - 1; i >= 0; i--)
{
LimitCell lc = mLimitList[i];
if (dt >= lc.dt_begin)
{
//找到了
index = i;
if (i == (mLimitList.Count - 1))//这个是未来的时间点,还没发生!!!
{
return ListFindIndexResult.Future;
}
else
{
return ListFindIndexResult.BetweenCells;
}
}
}
//这个在过去的时间
return ListFindIndexResult.Past;
}
///
/// 获取膜角度
///
/// 与0°的偏移, 0~180
/// 时间点
/// 上面的膜对应的膜角度
/// 下面的膜对应的膜角度
/// 扫描次数
bool GetAngle(double a, DateTime dt, out double angle0, out double angle1, out int rotationCnt)
{
angle0 = 0;
angle1 = 0;
rotationCnt = 0;
if (a > 180 || a < 0)
return false;
//压扁对面的角度
double a_other = 360 - a;
double angle;
//不修改旋转速度!!!!!!!!!!!!
GetAngle(dt, false, out angle, out rotationCnt);
a = GetValidAngle(a + angle);
a_other = GetValidAngle(a_other + angle);
angle0 = a;
angle1 = a_other;
return true;
}
///
/// 限制角度在 [0~360)
///
///
///
double GetValidAngle(double a)
{
if (a >= 360)
a -= 360;
else if (a < 0)
a += 360;
return a;
}
public void Apply()
{
Save();
}
//public class FilmInfoChangedEventArgs : EventArgs
//{
// public FilmInfoChangedEventArgs(DateTime time)
// {
// Time = time;
// }
// ///
// /// 开始时间点
// ///
// public DateTime Time
// {
// get;
// set;
// }
//}
//public delegate void FilmInfoChangedEventHandler(object sender, FilmInfoChangedEventArgs e);
public event FilmInfoChangedEventHandler FilmInfoChangedEvent;
//public delegate void ClearEventHandler(object sender);
public event ClearEventHandler ClearEvent;
///
/// 释放资源
///
public void Dispose()
{
Clear();
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
#endregion
///
/// 加载历史数据
///
///
public bool Load()
{
return BlowingDetect360JsonDb.Load(this, file_path);
}
string file_path = "blowingdetect360.json";
///
/// 保存
///
public bool Save()
{
return BlowingDetect360JsonDb.Save(this, file_path);
}
///
/// 获取 信号列表
///
/// retdata = GetSignListReponse
///
public void GetSignList(AsyncCBHandler asyncDelegate, object asyncContext)
{
GetSignListReponse p = new GetSignListReponse();
p.datas = mSignList;
asyncDelegate(asyncContext, p);
}
///
/// 获取 信号列表
///
/// 开始时间
/// retdata = GetSignListReponse
///
public void GetSignList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext)
{
GetSignListReponse p = new GetSignListReponse();
p.datas = mSignList.FindAll(r => r.Time >= begin);
asyncDelegate(asyncContext, p);
}
///
/// 获取 辊信号列表
///
/// 开始时间
/// retdata = GetRollListReponse
///
public void GetRollList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext)
{
var p = new GetRollListReponse();
p.datas = mRollList.FindAll(r => r.Time > begin).Select(r => new RollCell() { Time = r.Time, SysTime = r.SysTime }).ToList();
asyncDelegate(asyncContext, p);
}
///
/// 获取 转向信号列表
///
/// 开始时间
/// retdata = GetRollListReponse
///
public void GetLimitList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext)
{
var p = new GetLimitListReponse();
p.datas = mLimitList.FindAll(r =>
{
return (r.dt_end != DateTime.MinValue) && (r.dt_begin > begin);
});
asyncDelegate(asyncContext, p);
}
///
/// 获取 旋转位置列表
///
/// 开始时间
///
///
public void GetRPosList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext)
{
var p = new GetRPosListReponse();
p.datas = mRPosList.FindAll(r => r.Time > begin);
asyncDelegate(asyncContext, p);
}
public void GetFilmLength3D(AsyncCBHandler asyncDelegate, object asyncContext)
{
//不实现
//throw new NotImplementedException();
}
public void SetFilmLength3D(List data)
{
//不实现
//throw new NotImplementedException();
}
}
public class BlowingDetect360JsonDb
{
static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => {
c.CreateMap().ReverseMap();
}));
///
/// 加载历史数据
///
///
public static bool Load(BlowingDetect360 src, string file_path)
{
if (File.Exists(file_path))
{
try
{
string json = File.ReadAllText(file_path);
var jsonDb = JsonConvert.DeserializeObject(json);
Mapper.Map(jsonDb, src);
return true;
}
catch
{
//异常,没有json 解码失败
}
}
return false;
}
///
/// 保存
///
public static bool Save(BlowingDetect360 src, string file_path)
{
try
{
var jsonDb = Mapper.Map(src);
string json = JsonConvert.SerializeObject(jsonDb, Formatting.Indented);
File.WriteAllText(file_path, json);
return true;
}
catch
{
//异常,没有json 编码失败
}
return false;
}
///
/// 人字架 旋转1周 设置 时间。
/// 刚开机时,RenZiJiaPeriod = DefaultRPeriod;
/// 异常时,RenZiJiaPeriod = DefaultRPeriod;
///
public TimeSpan DefaultRPeriod;
///
/// 人字架到测厚仪膜长 单位m
///
public double FilmLength;
///
/// 辊周长,单位mm
///
public double RollPerimeter;
///
/// 旋转反向
///
public bool IsReversed;
///
/// 使用编码器检测旋转架旋转位置模式
///
public bool IsRPosMode;
///
/// 人字架 离开限位 到 撞下一个限位 的 旋转架转动总脉冲 单位 脉冲
///
public int RPosOfR = 50000;
}
}