using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Misc;
using FLY.Thick.Base.IService;
using FLY.Thick.Blowing.IService;
using FObjBase;
using System.IO;
using Newtonsoft.Json;
using AutoMapper;
using FlyADBase;
using FLY.Thick.Blowing.Common;
using FLY.Thick.Blowing.RotaryMachine.Client;
using FLY.Thick.Blowing.RotaryMachine.IService;
namespace FLY.Thick.Blowing.Server
{
///
/// 1.每次收到限位信号(有->没 and 没->有),都会触发 FilmInfoChangedEvent ;时间为上一次限位信号
/// 2.改变辊周长,膜长,都会触发 FilmInfoChangedEvent ;时间为前3次限位信号
/// 3.每次查找FilmInfo, 时间比 当前的限位信号+RenZiJiaPeriod 还要未来,修正 RenZiJiaPeriod,
/// RenZiJiaPeriod预留10s的余量,避免不断修正
/// 并且触发FilmInfoChangedEvent 时间为上一次限位信号
///
public partial class BlowingDetect : IBlowingDetectInServer, IBlowingDetectService, INotifyPropertyChanged
{
#region 参数
///
/// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
///
public double RAngle { get; set; } = 337;//°
///
/// 人字架 旋转1周 设置 时间。
/// 刚开机时,RenZiJiaPeriod = DefaultRPeriod;
/// 异常时,RenZiJiaPeriod = DefaultRPeriod;
///
public TimeSpan DefaultRPeriod { get; set; } = TimeSpan.FromMinutes(8);
///
/// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
public bool IsSign0Double { get; set; }
///
/// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
public bool IsSign1Double { get; set; }
///
/// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向)
///
public BlowingSignType SignType { get; set; } = BlowingSignType.Short;
///
/// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的
///
public TimeSpan LimitSignTime { get; set; } = TimeSpan.FromSeconds(4);
///
/// 人字架到测厚仪膜长 单位m
///
public double FilmLength { get; set; } = 25;//m
///
/// 辊周长,单位mm
///
public double RollPerimeter { get; set; }= 314;//mm
///
/// 辊周长,单位m
///
double MofR => RollPerimeter / 1000.0;
///
/// 转向信号100ms 滤波
///
public bool IsSignFilter { get; set; }
///
/// 缺少信号模式
/// 正反信号线接得不好,有时候会丢失了。
/// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!!
/// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号
///
public bool IsLackSignMode { get; set; }
///
/// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大
///
public bool Is3D { get; set; }
///
/// 使用收卷PLC的数据,得到旋转位置,速度数据
///
public bool IsPlcMode { get; set; }
///
/// 旋转服务地址
///
public string PlcAddr { get; set; } = "127.0.0.1:20004";
///
/// 使用编码器检测旋转架旋转位置模式
///
public bool IsRPosMode { get; set; }
///
/// 人字架 离开限位 到 撞下一个限位 的 旋转架转动总脉冲 单位 脉冲
///
public int RPosOfR { get; set; } = 50000;
///
/// 自定义 输入口
///
public bool IsInNoCustomized { get; set; }
///
/// 自定义 转向信号 输入口序号 从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; protected set; }
///
/// 当前方向
///
public Misc.DIRECTION Direction { get; protected set; } = Misc.DIRECTION.FORWARD;
///
/// 累计旋转次数
///
public virtual int RotationCnt { get; protected set; }
///
/// 最新的限位信号序号
///
public int LastLimitNo { get; protected set; } = -1;
///
/// 最后一个限位信号 到当前已经消耗的时间
///
public TimeSpan PastTime { get; protected set; }
///
/// 当前线速度,单位 m/min
///
public double FilmVelocity { get; protected set; }
///
/// 离开限位 到 撞下一个限位 的 旋转架转动时间, 需要初始值,以后测量出来的
///
public TimeSpan RenZiJiaPeriod { get; protected set; }
///
/// 缓冲区拥有数据的时间长度,1s更新一次
///
public TimeSpan BufTotalTime { get; protected 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; }
///
/// 刚开机,信号列表一个信号都没有,创造一个时间点,作为信号点
///
public DateTime DefaultTime { get; protected set; }
#endregion
///
/// 每圈数据列表
///
public RList mLimitList;
///
/// 辊信号列表,保存一个小时数据
///
RList mRollList;
///
/// 转向信号列表
///
RList mSignList;
///
/// 后触发,信号改变推送列表!!
///
List mdtFilmInfoChangedEventList = new List();
///
/// 当为 立式旋转架时,膜距离与旋转角度有关系.
/// 列表的长度为 360, 1°一个膜距离增量。
/// 列表的头为-180°,尾为180°
///
/// FilmLength+mFilmLength3D[angle] 为真实膜距离
///
List mFilmLength3D = new List();
///
/// 旋转脉冲列表
///
RList mRPosList;
///
/// 转向信号处理
///
LimitCellCollection mLimitCellCollection;
///
/// 原点旋转脉冲 是否有效
///
OrgRPosValidLevel OrgRPosValidLv;
enum OrgRPosValidLevel
{
///
/// 刚开机,OrgRPos = 0
///
Invalid,
///
/// 没有撞任何一个转向信号,使用列表第1个作为OrgRPos
///
JustListHeader,
///
/// 只撞转向信号1, 先修正 OrgRPos = GlobalRPos - RPosOfR
///
Sign1,
///
/// 撞转向信号0,真正的 OrgRPos = GlobalRPos
///
Sign0
}
protected IFlyADClientAdv flyad;
protected IRotarySerivce rotarySerivce;
///
///
///
public BlowingDetect()
{
RAngle = 337;//°
RenZiJiaPeriod = DefaultRPeriod;
//以数量与时间限制下面的列表大小
mRollList = new Misc.RList(50 * 60 * 1000 / 314);//以50m/min 线速度,储存大于 60min数据
mSignList = new RList(30);//人字架正反转信号
mRPosList = new RList(60 * 60);//人字架脉冲数据 1秒一个 60分钟数据
//---------------------------------------------------------------------------------------------------
//LimitCellCollection配置
mLimitCellCollection = new LimitCellCollection();
mLimitCellCollection.AddEvent += LimitCellCollection_AddEvent;
mLimitList = mLimitCellCollection.mLimitList;
//输入
Misc.BindingOperations.SetBinding(this, nameof(IsSignFilter), mLimitCellCollection, nameof(mLimitCellCollection.IsSignFilter));
Misc.BindingOperations.SetBinding(this, nameof(IsLackSignMode), mLimitCellCollection, nameof(mLimitCellCollection.IsLackSignMode));
Misc.BindingOperations.SetBinding(this, nameof(SignType), mLimitCellCollection, nameof(mLimitCellCollection.SignType));
Misc.BindingOperations.SetBinding(this, nameof(IsSign0Double), mLimitCellCollection, nameof(mLimitCellCollection.IsSign1Double), BindingOperations.BindingMode.TwoWay);
Misc.BindingOperations.SetBinding(this, nameof(IsSign1Double), mLimitCellCollection, nameof(mLimitCellCollection.IsSign1Double), BindingOperations.BindingMode.TwoWay);
Misc.BindingOperations.SetBinding(this, nameof(DefaultRPeriod), mLimitCellCollection, nameof(mLimitCellCollection.DefaultRPeriod) );
//输出
Misc.BindingOperations.SetBinding(mLimitCellCollection, nameof(mLimitCellCollection.RenZiJiaPeriod), this, nameof(RenZiJiaPeriod));
Misc.BindingOperations.SetBinding(mLimitCellCollection, nameof(mLimitCellCollection.LimitSignTime), this, nameof(LimitSignTime));
ClearBuf();
PropertyChanged += new PropertyChangedEventHandler(RenZiJiaAndFilmPositionDetect_PropertyChanged);
}
private void LimitCellCollection_AddEvent(LimitCell obj)
{
LastLimitNo = obj.no;
Direction = LastLimitNo == 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
RotationCnt++;
//TODO
update_param_for_limit(obj.dt_begin) ;
}
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 (RAngle < 100 || RAngle > 360)
{
RAngle = 350;
}
if ((DefaultRPeriod.TotalMinutes < 4) || (DefaultRPeriod.TotalMinutes > 25))
{
DefaultRPeriod = TimeSpan.FromMinutes(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));
}
}
}
}
string[] resetRollListAngle_propertyNames = new string[] {
nameof(RAngle),
nameof(RenZiJiaPeriod),
nameof(DefaultTime),
nameof(RPosOfR),
nameof(OrgRPos),
nameof(Is3D)
};
//TODO
void RenZiJiaAndFilmPositionDetect_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ((e.PropertyName == nameof(FilmLength)) ||
(e.PropertyName == nameof(RollPerimeter)) ||
(e.PropertyName == nameof(RAngle))
)
{
TouchFilmInfoChanged_lastLimit(4);
}
else if (e.PropertyName == nameof(RenZiJiaPeriod))
{
TouchFilmInfoChanged_lastLimit(1);
}
if (resetRollListAngle_propertyNames.Contains(e.PropertyName))
{
if(Is3D)
RollList_ResetAngle();
}
}
///
/// 触发膜信息修改事件
///
/// 最后旋转几次
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;
this.flyad.IStatusChangedEvent += new FlyADBase.IStatusChangedEventHandler(flyad_IStatusChangedEvent);
FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, OnPoll, TimeSpan.FromSeconds(1));
if (IsPlcMode) {
Init_plc();
}
}
///
///
///
///
void Init_plc()
{
string connName = "rotary";
FObjServiceClientManager.Instance.ConnAddrs.Add(new ConnAddr() { ConnName = connName, Addr = PlcAddr });
rotarySerivce = new RotarySerivceClient(FLY.Thick.Blowing.RotaryMachine.OBJ_INTERFACE.OBJ_INTERFACE_ID.RotarySerivce_OBJ_ID, connName);
update_orgRPos_plc();
Misc.BindingOperations.SetBinding(rotarySerivce, nameof(rotarySerivce.RPosGlobal), () =>
{
GlobalRPos = (int)(rotarySerivce.RPosGlobal);
});
Misc.BindingOperations.SetBinding(rotarySerivce, nameof(rotarySerivce.RSpeed), () =>
{
RSpeed = (int)(rotarySerivce.RSpeed);
});
rotarySerivce.PropertyChanged += RotarySerivce_PropertyChanged;
}
void update_orgRPos_plc()
{
if (!double.IsNaN(rotarySerivce.RPosAtTurnSign0))
{
OrgRPos = (int)rotarySerivce.RPosAtTurnSign0;
//脉冲有效了
OrgRPosValidLv = OrgRPosValidLevel.Sign0;
}
else if (!double.IsNaN(rotarySerivce.RPosAtTurnSign1))
{
OrgRPos = (int)rotarySerivce.RPosAtTurnSign1 - RPosOfR;
//只撞了信号1,好过没有
OrgRPosValidLv = OrgRPosValidLevel.Sign1;
}
else if (!double.IsNaN(rotarySerivce.RPosAtOrgSign))
{
OrgRPos = (int)rotarySerivce.RPosAtOrgSign - RPosOfR / 2;
//只撞了归零信号,好过没有
OrgRPosValidLv = OrgRPosValidLevel.Sign1;
}
}
string[] propertyNameOfUpdateOrgRPos = new string[] {
nameof(IRotarySerivce.RPosAtTurnSign0),
nameof(IRotarySerivce.RPosAtTurnSign1),
nameof(IRotarySerivce.RPosAtOrgSign)
};
private void RotarySerivce_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(IRotarySerivce.IsTurnSign0))
{
SignData sd = new SignData()
{
No = 0,
Time = DateTime.Now,
On = rotarySerivce.IsTurnSign0,
GlobalRPos = GlobalRPos
};
mSignList.RAdd(sd);
mLimitCellCollection.AddLimit(sd);
//更新其它参数
update_param_for_limit(sd.Time);
}
else if (e.PropertyName == nameof(IRotarySerivce.IsTurnSign1))
{
SignData sd = new SignData()
{
No = 1,
Time = DateTime.Now,
On = rotarySerivce.IsTurnSign1,
GlobalRPos = GlobalRPos
};
mSignList.RAdd(sd);
mLimitCellCollection.AddLimit(sd);
//更新其它参数
update_param_for_limit(sd.Time);
}
else if (e.PropertyName == nameof(IRotarySerivce.IsOrgSign))
{
SignData sd = new SignData()
{
No = 2,
Time = DateTime.Now,
On = rotarySerivce.IsOrgSign,
GlobalRPos = GlobalRPos
};
mSignList.RAdd(sd);
}
else if (propertyNameOfUpdateOrgRPos.Contains(e.PropertyName))
{
update_orgRPos_plc();
}
}
void update_limit(FlyADBase.IStatusChangedEventArgs e)
{
bool bchanged = false;//信号更新?
//TODO,不使用AD盒的时间
//DateTime dt = e.Time;//信号更新发生的时间
DateTime dt = DateTime.Now;
int no = -1;//信号序号
bool status = false;//信号的状态 true开始, false结束
int position2 = e.Position2;
int inNo_Limit0 = IsInNoCustomized ? InNo_Limit0 : Common.FlyADIODefine.Instance.InNo_Limit0;
int inNo_Limit1 = IsInNoCustomized ? InNo_Limit1 : Common.FlyADIODefine.Instance.InNo_Limit1;
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() { No = no, Time = dt, On = status, GlobalRPos = position2 };
mSignList.RAdd(sd);
if(IsRPosMode)//使用编码器测量旋转角度
update_orgRPos(sd);
mLimitCellCollection.AddLimit(sd);
//更新其它参数
update_param_for_limit(sd.Time);
}
void update_orgRPos(SignData sd)
{
if (!sd.On)
return;
if (sd.No == 0)
{
OrgRPos = sd.GlobalRPos;
//脉冲有效了
OrgRPosValidLv = OrgRPosValidLevel.Sign0;
}
else if (sd.No == 1)
{
if (OrgRPosValidLv != OrgRPosValidLevel.Sign0)
{
OrgRPos = sd.GlobalRPos - RPosOfR;
//还没撞 信号0, 先用着信号1吧。好过没有
OrgRPosValidLv = OrgRPosValidLevel.Sign1;
}
}
}
///
/// 更新因为旋转信号的添加导致的变化
///
/// 信号变化的时间
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 dt2 = mLimitList[mLimitList.Count - 2].dt_end;
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)
{
int inNo_Roll = IsInNoCustomized ? InNo_Roll : Common.FlyADIODefine.Instance.InNo_Roll;
//----------------------------------------------------------------------
//更新辊信号
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)
{
if (IsPlcMode)
{
//PLC模式不采集旋转信号
}
else
{
update_limit(e);
}
update_roll(e);
}
///
/// 1秒触发一次
///
void OnPoll()
{
if (!flyad.IsReady || !flyad.IsConnected)
return;
//--------------------------------------------------------------------
//转向信号处理
mLimitCellCollection.OnPoll();
//--------------------------------------------------------------------
//旋转架速度安全检测
OnPoll_checkRVelocity();
//--------------------------------------------------------------------
if (IsPlcMode) //PLC模式
{
//更新旋转脉冲 & 角度
OnPoll_updateRPos_plc();
}
else if (IsRPosMode) //编码器模式
{
//更新旋转脉冲 & 角度
OnPoll_updateRPos();
}
else {
//--------------------------------------------------------------------
//当前旋转架角度
OnPoll_updateAngle();
}
//--------------------------------------------------------------------
//线速度安全检测
OnPoll_checkFilmV();
//--------------------------------------------------------------------
//更新当前线速度
OnPoll_updateFilmV();
//--------------------------------------------------------------------
//限制列表大小
KeepSize();
}
void OnPoll_updateRPos_plc()
{
//没连接上
{
var client = rotarySerivce as FObjBase.Reflect.Reflect_SeviceClient;
if (!client.IsSynced)
return;
}
DateTime now = DateTime.Now;
DateTime sysTime = flyad.Now;
int globalRPos = GlobalRPos;
int speed = RSpeed;
if (mRPosList.Count() > 0)//1秒记录一次
{
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;
}
//必须在 0~RPosOfR 范围内
Angle = GetAngleByGlobalRPos(GlobalRPos);
}
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;
//必须在 0~RPosOfR 范围内
Angle = GetAngleByGlobalRPos(GlobalRPos);
}
public double GetAngleByGlobalRPos(int globalRPos)
{
//OrgRPos 为 -RAngle/2
int rPos = globalRPos - OrgRPos;
//360°对应的旋转脉冲
int rPosOf360 = (int)(RPosOfR * 360 / RAngle);
while (rPos < 0)
rPos += rPosOf360;
while (rPos > rPosOf360)
rPos -= rPosOf360;
double angle = RAngle * rPos / RPosOfR;
//角度范围是 -180°~ +180°
angle -= RAngle / 2;
return angle;
}
///
/// 旋转架速度安全检测
///
void OnPoll_checkRVelocity()
{
DateTime dt = DefaultTime;
if (mLimitList.Count > 0)
{
LimitCell lc = mLimitList.Last();
dt = lc.dt_begin;
}
//因为没有使用AD盒的时间,所以可以用DateTime.Now 比较
PastTime = DateTime.Now - dt;
if (PastTime > TimeSpan.FromMinutes(25))//25min
{
//大于25分钟,也没有一个信号,复位
Clear();
}
}
///
/// 更新旋转架速度
///
void OnPoll_updateAngle()
{
GetAngle(DateTime.Now, false, out double angle, out DIRECTION direction, out int rotationCnt, out bool inCV);
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.Last().Time;
//因为没有使用AD盒的时间,所以可以用DateTime.Now 比较
TimeSpan ts = DateTime.Now - dt;
if (ts.TotalMinutes > 0.5)
{
//大于0.5分钟,也没有一个信号,复位,但不会清空调试数据
//辊周长314mm, 0.5min = 0.6m/min, 太慢了
Clear();
}
}
}
///
/// 删除旋转数据
///
public void Clear()
{
mLimitCellCollection.Clear();
DefaultTime = DateTime.Now;
RotationCnt = 0;//复位次数!!!
ClearEvent?.Invoke(this);
}
///
/// 删除调试数据,与旋转数据
///
void ClearBuf()
{
FilmVelocity = 0;
BufTotalTime = TimeSpan.Zero;
mRollList.Clear();
mSignList.Clear();
mRPosList.Clear();
OrgRPos = 0;
OrgRPosValidLv = OrgRPosValidLevel.Invalid;
Clear();
}
public enum GetLimitTimeResult
{
///
/// 获取成功
///
OK,
///
/// 不存在该点
///
NOEXIST,
///
/// 限位已经撞了,但膜还没走完,等吧
///
ISFURTURE
}
///
/// 通过角度 获取 膜距离
///
/// -180~180
///
double GetFilmLength3D(double angle)
{
if (mFilmLength3D == null || mFilmLength3D.Count() == 0)
return FilmLength;
angle += 180;
int idx = (int)(mFilmLength3D.Count() * angle / 360);
if (idx < 0) idx = 0;
else if (idx >= mFilmLength3D.Count()) idx = mFilmLength3D.Count() - 1;
return FilmLength + mFilmLength3D[idx];
}
///
///
///
///
///
protected double GetFilmLength3D(DateTime dt)
{
GetAngle(0, dt, out double angle0, out double angle1, out DIRECTION direction, out int rotationcnt, out bool inCV);
return GetFilmLength3D(angle0);
}
///
/// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比LimitList.Last.dt+RenZiJiaPeriod还要后,更新RenZiJiaPeriod
/// 返回 -1, 当前的时间点在列表以前;
/// 返回 0, 当前的时间点在列表中;
/// 返回 1, 当前的时间点在列表的未来;
///
/// 被压扁后的膜宽度
/// 探头所在膜的横向位置
/// 测量的时间点
///
public virtual int GetFilmInfo(out FilmInfo filminfo, DateTime dt, double filmWidth, double filmPosH)
{
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);
if (ret > 0)
return ret;//发生在未来不处理!!!
bool ret2 = GetAngle(a, ago_dt, out double angle0, out double angle1, out Misc.DIRECTION direction, out int rotationcnt, out bool inCV);
if (!ret2)
return -1;//异常!!!
double velocity = RollList_GetVelocity(ago_index);
filminfo = new FilmInfo()
{
angle1 = angle0,
angle2 = angle1,
filmVelocity = velocity,
direction = direction,
rotationCnt = rotationcnt,
inCV = inCV
};
return ret;
}
#region 人字架时间点<-->24m后膜时间点
///
/// 3D旋转架,向前找,filmPosition >= filmPos0 + filmLength0 膜距离前的点
///
/// 开始查找的序号
/// 纵向位置
/// 返回查找到的序号
int RollList_FindIndex3d24mPre(int index, double filmPosition)
{
for (int i = index; i >= 0; i--)
{
double angle = RollList_GetAngle(i);
if (double.IsNaN(angle))
return -1;//不能找到角度
double filmPos0 = i * MofR;
double filmLength0 = GetFilmLength3D(angle);
//膜距离后的纵向位置
double pos24m0 = filmPos0 + filmLength0;
if (filmPosition >= pos24m0)
{
return i;
}
}
return -1;
}
///
/// 3D旋转架,向后找,filmPosition <= filmPos1 + filmLength1 膜距离前的点
///
/// 开始查找的序号
/// 纵向位置
/// 返回查找到的序号
int RollList_FindIndex3d24mNext(int index, double filmPosition)
{
for (int i = index; i < mRollList.Count; i++)
{
double angle = RollList_GetAngle(i);
if (double.IsNaN(angle))
return -1;//不能找到角度
double filmPos1 = i * MofR;
double filmLength1 = GetFilmLength3D(angle);
//膜距离后的纵向位置
double pos24m1 = filmPos1 + filmLength1;
if (filmPosition <= pos24m1)
{
return i;
}
}
return 1;
}
///
/// 输入当前时间点,找到 刚出人字架时的时间点, 及那时候的线速度;
/// 返回 -1, 当前的时间点在列表以前;
/// 返回 0, 当前的时间点在列表中;
/// 返回 1, 当前的时间点在列表的未来;
///
/// 当前时间
/// 24米真实长度
///
int GetAgo(DateTime dt, out DateTime ago_dt, out int ago_index)
{
if (Is3D)
{
int ret = GetAgo3D(dt, out ago_dt, out ago_index);
if (ret != -1) return ret;
//发生在过去的,顺便猜个值出来
return GetAgoFix(dt, out ago_dt, out ago_index);
}
else
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;
}
///
/// 3d旋转架的 获取膜距离前的时间点
///
///
///
///
///
int GetAgo3D(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);
//快速移动到 开始查找rollList 的序号
int beginIndex = (int)((filmPosition - FilmLength) / MofR);
if (beginIndex < 0)
return -1;//发生在过去,什么都不干
int index0;
int index1;
index = RollList_FindIndex3d24mPre(beginIndex, filmPosition);
if (index == beginIndex)
{
//第0个就找到,还需要再向后找确定
index = RollList_FindIndex3d24mNext(beginIndex + 1, filmPosition);
index0 = index - 1;
index1 = index;
}
else
{
if (index < 0)
{
return -1;//找不到,发生在过去
}
index0 = index;
index1 = index + 1;
}
if (index0 < 0)
return -1;
if (index1 >= mRollList.Count())
return 1;
DateTime dt0 = mRollList[index0].Time;
double angle0 = RollList_GetAngle(index0);
if (double.IsNaN(angle0))
return -1;//不能找到角度
double filmPos0 = index0 * MofR;
double filmLength0 = GetFilmLength3D(angle0);
//膜距离后的纵向位置
double pos24m0 = filmPos0 + filmLength0;
DateTime dt1 = mRollList[index1].Time;
double angle1 = RollList_GetAngle(index1);
if (double.IsNaN(angle1))
return -1;//不能找到角度
double filmPos1 = (index1) * MofR;
double filmLength1 = GetFilmLength3D(angle1);
//膜距离后的纵向位置
double pos24m1 = filmPos1 + filmLength1;
ago_dt = dt0.Add(new TimeSpan((long)((dt1 - dt0).Ticks * (filmPosition - pos24m0) / (pos24m1 - pos24m0))));
ago_index = index0;
return 0;
}
///
/// 输入过去时间,让它再加上膜长时间,得到未来时间;
/// 返回 -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
///
/// 计算出角度 -180~180 恒速阶段
///
/// 旋转结束时间点
/// 旋转开始时间点
/// 开始时间点对应的限位 0 or 1
/// 用于转换为角度的时间
/// 输出: 角度
/// 输出: 方向
void LimitListCal_ConstantVelocity(DateTime dtEnd, DateTime dtStart, int noStart, DateTime dt, out double angle, out Misc.DIRECTION direction)
{
TimeSpan ts1 = dt - dtStart;
TimeSpan ts2 = dtEnd - dtStart;
if (ts2.TotalMinutes < 1 || ts1.TotalMinutes <= 0)
{
//异常
angle = 0;
}
else
{
angle = RAngle * ts1.Ticks / ts2.Ticks;
}
if (noStart == 0)
{
angle = -RAngle / 2 + angle;
direction = Misc.DIRECTION.FORWARD;
}
else
{
angle = RAngle / 2 - angle;
direction = Misc.DIRECTION.BACKWARD;
}
}
///
/// 时间点查找旋转角度 -180~180
///
/// 时间
/// 当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod
/// 角度
/// 旋转的方向
/// 旋转的次数
/// 在恒速区
bool GetAngle(DateTime dt, bool modifyparam, out double angle, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
{
if (IsRPosMode)
{
return GetAngleByRPos(dt, out angle, out direction, out rotationCnt, out inCV);
}
else {
return GetAngleByTime(dt, modifyparam, out angle, out direction, out rotationCnt, out inCV);
}
}
///
/// 时间点查找旋转角度 -180~180
///
/// 时间点
/// 输出 旋转角度
bool GetAngle(DateTime dt, out double angle)
{
if (dt == DateTime.MinValue)
{
angle = 0;
return false;
}
//不修改旋转速度!!!!!!!!!!!!
bool ret = GetAngle(dt, false, out angle, out DIRECTION direction, out int rotationCnt, out bool inCV);
if (ret == false)
return false;
return true;
}
///
/// 时间点查找旋转角度 -180~180 时间预测版
///
/// 时间
/// 当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod
/// 角度
/// 旋转的方向
/// 旋转的次数
/// 在恒速区
bool GetAngleByTime(DateTime dt, bool modifyparam, out double angle, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
{
int index;
ListFindIndexResult ret = LimitList_FindIndex(dt, out index);
DateTime dt_start;
DateTime dt_end;
int no;
switch (ret)
{
case ListFindIndexResult.Future://发生在未来
{
if (dt < DefaultTime)
DefaultTime = dt - TimeSpan.FromSeconds(10);
//检测是否需要修正 RenZiJiaPeriod
if (mLimitList.Count == 0)
{
dt_start = DefaultTime;
no = 0;
}
else
{
dt_start = mLimitList[index].dt_end;
no = mLimitList[index].no;
}
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;
inCV = true;
LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
}
break;
case ListFindIndexResult.BetweenCells:
{
dt_start = mLimitList[index].dt_end;
dt_end = mLimitList[index + 1].dt_begin;
no = mLimitList[index].no;
rotationCnt = LimitIndexToRotationCnt(index);
inCV = true;
LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
}
break;
case ListFindIndexResult.Past://过去
{
if (DefaultTime >= mLimitList[0].dt_begin)
{
//异常?!!!!
DefaultTime = mLimitList[0].dt_begin - RenZiJiaPeriod;
}
dt_start = DefaultTime;
dt_end = mLimitList[0].dt_begin;
no = (mLimitList[0].no == 0) ? 1 : 0;
rotationCnt = LimitIndexToRotationCnt(-1);
inCV = true;
LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
}
break;
//case ListFindIndexResult.InCell:
//case ListFindIndexResult.MaybeInCell:
default:
{
//发生在 撞换向信号中间, 简单当它没有动就算了
inCV = false;
no = mLimitList[index].no;
rotationCnt = LimitIndexToRotationCnt(index);
if (no == 0)
{
angle = -RAngle / 2;
direction = DIRECTION.FORWARD;
}
else
{
angle = RAngle / 2;
direction = DIRECTION.BACKWARD;
}
}
break;
}
return true;
}
///
/// 时间点查找旋转角度 -180~180 脉冲版
///
/// 时间
/// 角度
/// 旋转的方向
/// 旋转的次数
/// 在恒速区
bool GetAngleByRPos(DateTime dt, out double angle, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
{
angle = Angle;
rotationCnt = RotationCnt;
direction = Direction;
inCV = true;
int retPos = RPosList_FindIndex(dt, out int p_index);
ListFindIndexResult ret = LimitList_FindIndex(dt, out int l_index);
if (retPos == 1) {
//发生在未来
return false;
}
var posData = mRPosList[p_index + 1];
TimeSpan ts = posData.Time - dt;
int globalRPos = (int)(posData.GlobalRPos - ts.TotalSeconds * posData.Speed);
int no;
switch (ret)
{
case ListFindIndexResult.Future://发生在未来
{
//检测是否需要修正 RenZiJiaPeriod
if (mLimitList.Count == 0)
no = 0;
else
no = mLimitList[l_index].no;
angle = GetAngleByGlobalRPos(globalRPos);
rotationCnt = LimitIndexToRotationCnt(l_index);
direction = no == 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
inCV = true;
}
break;
case ListFindIndexResult.BetweenCells:
{
no = mLimitList[l_index].no;
angle = GetAngleByGlobalRPos(globalRPos);
rotationCnt = LimitIndexToRotationCnt(l_index);
direction = no == 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
inCV = true;
}
break;
case ListFindIndexResult.Past://过去
{
no = mLimitList[0].no == 0 ? 1 : 0;
angle = GetAngleByGlobalRPos(globalRPos);
rotationCnt = LimitIndexToRotationCnt(-1);
direction = no == 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
inCV = true;
}
break;
//case ListFindIndexResult.InCell:
//case ListFindIndexResult.MaybeInCell:
default:
{
//发生在 撞换向信号中间
no = mLimitList[l_index].no;
angle = GetAngleByGlobalRPos(globalRPos);
rotationCnt = LimitIndexToRotationCnt(l_index);
direction = no == 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD;
}
break;
}
return true;
}
#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 (mRollList.Count == 0)
return 1;
if (filmPosition < 0)
{
//发生在过去。 只能猜
dt = RollList_GetDateTime(idx, filmPosition);
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;
}
void RollList_ResetAngle()
{
if (mRollList == null || mRollList.Count() == 0)
return;
for (int i = 0; i < mRollList.Count; i++)
{
mRollList[i].Angle = double.NaN;
}
}
double RollList_GetAngle(int index)
{
if (!double.IsNaN(mRollList[index].Angle))
{
return mRollList[index].Angle;
}
else
{
if (!GetAngle(mRollList[index].Time, out double angle))
{
//不能找到角度
return double.NaN;
}
mRollList[index].Angle = angle;
return mRollList[index].Angle;
}
}
#endregion
///
/// 时间对应旋转状态
///
enum DTSTATUS
{
///
/// 发生在未来的均速阶段
///
Future,
///
/// 发生在过去的均速阶段
///
Past,
///
/// 发生在均速阶段
///
Normal,
///
/// 发生在方向切换阶段
///
Swap,
}
protected enum ListFindIndexResult
{
///
/// 发生在未来, index.dt_end ~ 未来, 可能 index都还没有发生
///
Future,
///
/// 发生在index.dt_begin ~ index.dt_end
///
InCell,
///
/// 应该发生在index.dt_begin ~ index.dt_end, 但 index.dt_end还没有数据
///
MaybeInCell,
///
/// 发生在 index.dt_end ~ (index+1).dt_begin
///
BetweenCells,
///
/// 发生在 过去
///
Past
}
///
/// 找到 列表中 (开始时间,结束时间) 小于 dt 的信号点
///
///
///
///
protected 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 (lc.dt_end == DateTime.MinValue)
{
return ListFindIndexResult.MaybeInCell;
}
else if (dt <= mLimitList[i].dt_end)
{
return ListFindIndexResult.InCell;
}
else if (i == (mLimitList.Count - 1))//这个是未来的时间点,还没发生!!!
{
return ListFindIndexResult.Future;
}
else
{
return ListFindIndexResult.BetweenCells;
}
}
}
//这个在过去的时间
return ListFindIndexResult.Past;
}
///
/// dt 这个时间,在是 index 与 index+1 中间发生的
/// 返回 -1,这个过去的时间;
/// 返回 0,在列表中;
/// 返回 1,发生在未来的时间;
///
///
///
int RPosList_FindIndex(DateTime dt, out int index)
{
index = 0;
if (mRPosList.Count <= 1)//没数据,所以是发生在未来的
return 1;
for (int i = mRPosList.Count - 1; i >= 0; i--)
{
if (dt >= mRPosList[i].Time)
{
//找到了
if (i == (mRPosList.Count - 1))//这个是未来的时间点,还没发生!!!
{
index = i;
return 1;
}
else
{
index = i;
return 0;
}
}
}
//这个在过去的时间
return -1;
}
///
/// 获取膜角度, TODO
///
/// 与0°的偏移, 0~180
/// 时间点
/// 上面的膜对应的膜角度 [0~360)
/// 下面的膜对应的膜角度 [0~360)
/// 人字架正转?反转? FIX, 停止!
/// 扫描次数
/// 在恒速阶段
bool GetAngle(double a, DateTime dt, out double angle0, out double angle1, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV)
{
angle0 = 0;
angle1 = 0;
direction = Misc.DIRECTION.FORWARD;
rotationCnt = 0;
inCV = true;
if (a > 180 || a < 0)
return false;
//压扁对面的角度
double a_other = 360 - a;
if (dt == DateTime.MinValue)
{
return false;
}
//不修改旋转速度!!!!!!!!!!!!
bool ret = GetAngle(dt, false, out double angle, out direction, out rotationCnt, out inCV);
if (ret == false)
return false;
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 event FilmInfoChangedEventHandler FilmInfoChangedEvent;
public event ClearEventHandler ClearEvent;
///
/// 释放资源
///
public void Dispose()
{
Clear();
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
#endregion
///
/// 加载历史数据
///
///
public bool Load()
{
LoadFilmLength3D();
if (BlowingDetectJsonDb.Load(this, file_path))
{
return true;
}
else//只是为了版本兼容,以后就会删除
{
var jsonDb = FLY.Thick.Blowing.Server.Update.BlowingDetect.ToJsonDb();
if (jsonDb == null)
return false;
try
{
string json = JsonConvert.SerializeObject(jsonDb, Formatting.Indented);
File.WriteAllText(file_path, json);
}
catch {
return false;
}
return BlowingDetectJsonDb.Load(this, file_path);
}
}
string file_path = "blowingdetect.json";
///
/// 保存
///
public bool Save()
{
return BlowingDetectJsonDb.Save(this, file_path);
}
string file_path_filmlength3d = "filmlength3d.json";
void LoadFilmLength3D()
{
try
{
if (File.Exists(file_path_filmlength3d))
{
string json = File.ReadAllText(file_path_filmlength3d);
mFilmLength3D = JsonConvert.DeserializeObject>(json);
}
}
catch
{
//conf = null;
//异常,没有json 解码失败
//return false;
}
}
void SaveFilmLength3D()
{
try
{
File.WriteAllText(file_path_filmlength3d, JsonConvert.SerializeObject(mFilmLength3D, Formatting.Indented));
}
catch
{
//异常,没有json 编码失败
}
}
///
/// 获取立式旋转架的旋转角度与膜距离增量关系
///
///
///
public void GetFilmLength3D(AsyncCBHandler asyncDelegate, object asyncContext)
{
GetFilmLength3DReponse p = new GetFilmLength3DReponse();
p.datas = mFilmLength3D;
asyncDelegate(asyncContext, p);
}
///
/// 设置 立式旋转架 的 膜距离增量
///
///
public void SetFilmLength3D(List data)
{
mFilmLength3D = data;
SaveFilmLength3D();
}
///
/// 获取 信号列表
///
/// 开始时间
/// 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 class BlowingDetectJsonDb
{
static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => {
c.CreateMap().ReverseMap();
}));
///
/// 加载历史数据
///
///
public static bool Load(BlowingDetect 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(BlowingDetect 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;
}
///
/// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
///
public double RAngle;
///
/// 人字架 旋转1周 设置 时间。
/// 刚开机时,RenZiJiaPeriod = DefaultRPeriod;
/// 异常时,RenZiJiaPeriod = DefaultRPeriod;
///
public TimeSpan DefaultRPeriod;
///
/// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
public bool IsSign0Double;
///
/// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
public bool IsSign1Double;
///
/// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向)
///
public BlowingSignType SignType = BlowingSignType.Short;
///
/// 人字架到测厚仪膜长 单位m
///
public double FilmLength;
///
/// 辊周长,单位mm
///
public double RollPerimeter;
///
/// 转向信号100ms 滤波
///
public bool IsSignFilter;
///
/// 缺少信号模式
/// 正反信号线接得不好,有时候会丢失了。
/// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!!
/// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号
///
public bool IsLackSignMode;
///
/// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大
///
public bool Is3D;
///
/// 使用编码器检测旋转架旋转位置模式
///
public bool IsRPosMode;
///
/// 人字架 离开限位 到 撞下一个限位 的 旋转架转动总脉冲 单位 脉冲
///
public int RPosOfR=50000;
///
/// 使用收卷PLC的数据,得到旋转位置,速度数据
///
public bool IsPlcMode;
///
/// 旋转服务地址
///
public string PlcAddr = "127.0.0.1:20004";
///
/// 自定义 转向信号 输入口序号 从0开始 默认9-1
///
public int InNo_Limit0 = 9 - 1;
///
/// 自定义 转向信号 输入口序号 从0开始 默认10-1
///
public int InNo_Limit1 = 10 - 1;
///
/// 自定义 辊速度 输入口序号 从0开始 默认11-1
///
public int InNo_Roll = 11 - 1;
///
/// 自定义 输入口
///
public bool IsInNoCustomized;
}
}
namespace FLY.Thick.Blowing.Server.Update
{
public class BlowingDetect : ISaveToXml
{
public static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => {
c.CreateMap();
}));
#region 参数
///
/// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
///
public double RAngle { get; set; }
///
/// 人字架 旋转1周 设置 时间。
/// 刚开机时,RenZiJiaPeriod = DefaultRPeriod;
/// 异常时,RenZiJiaPeriod = DefaultRPeriod;
///
public TimeSpan DefaultRPeriod { get; set; }
///
/// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
public bool IsSign0Double { get; set; }
///
/// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。
/// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime
///
public bool IsSign1Double { get; set; }
///
/// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向)
///
public BlowingSignType SignType { get; set; } = BlowingSignType.Short;
///
/// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的
///
public TimeSpan LimitSignTime { get; set; }
///
/// 人字架到测厚仪膜长 单位m
///
public double FilmLength { get; set; }
///
/// 辊周长,单位mm
///
public double RollPerimeter { get; set; }
///
/// 转向信号100ms 滤波
///
public bool IsSignFilter { get; set; }
///
/// 缺少信号模式
/// 正反信号线接得不好,有时候会丢失了。
/// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!!
/// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号
///
public bool IsLackSignMode { get; set; }
///
/// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大
///
public bool Is3D { get; set; }
#endregion
///
/// Misc.SaveToXmlHepler 使用,保存在 xml文件内的 属性名称
///
///
public string[] GetSavePropertyNames()
{
return new string[]{
nameof(RAngle),
nameof(LimitSignTime),
nameof(FilmLength),
nameof(RollPerimeter),
nameof(DefaultRPeriod),
nameof(IsSign0Double),
nameof(IsSign1Double),
nameof(SignType),
nameof(IsSignFilter),
nameof(IsLackSignMode),
nameof(Is3D)
};
}
public static BlowingDetectJsonDb ToJsonDb()
{
if (File.Exists("blowingdetect.xml"))
{//只是为了版本兼容,以后就会删除
BlowingDetect bDetect = new BlowingDetect();
Misc.SaveToXmlHepler.Load("blowingdetect.xml", bDetect);
return Mapper.Map(bDetect);
}
else
return null;
}
}
}