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; namespace FLY.Thick.Blowing.Server { /// <summary> /// <para>1.每次收到限位信号(有->没 and 没->有),都会触发 FilmInfoChangedEvent ;时间为上一次限位信号</para> /// <para>2.改变辊周长,膜长,都会触发 FilmInfoChangedEvent ;时间为前3次限位信号</para> /// <para>3.每次查找FilmInfo, 时间比 当前的限位信号+RenZiJiaPeriod 还要未来,修正 RenZiJiaPeriod,</para> /// <para>RenZiJiaPeriod预留10s的余量,避免不断修正</para> /// <para>并且触发FilmInfoChangedEvent 时间为上一次限位信号</para> /// </summary> public class BlowingDetect : IBlowingDetectService, INotifyPropertyChanged { #region MARKNO const int MARKNO_DELAY0 = 1; const int MARKNO_DELAY1 = 2; #endregion #region 输入口定义 /// <summary> /// 限位0 对应输入位 /// </summary> const int Limit0_bit = 9; /// <summary> /// 限位1 对应输入位 /// </summary> const int Limit1_bit = 10; /// <summary> /// 辊信号 对应输入位 /// </summary> const int Roll_bit = 11; #endregion #region 参数 /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位° /// </summary> public double RAngle { get; set; } /// <summary> /// 人字架 旋转1周 设置 时间。 /// 刚开机时,RenZiJiaPeriod = DefaultRPeriod; /// 异常时,RenZiJiaPeriod = DefaultRPeriod; /// </summary> public TimeSpan DefaultRPeriod { get; set; } /// <summary> /// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。 /// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime /// </summary> public bool IsSign0Double { get; set; } /// <summary> /// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。 /// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime /// </summary> public bool IsSign1Double { get; set; } /// <summary> /// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向) /// </summary> public BlowingSignType SignType { get; set; } = BlowingSignType.Short; /// <summary> /// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的 /// </summary> public TimeSpan LimitSignTime { get; set; } /// <summary> /// 人字架到测厚仪膜长 单位m /// </summary> public double FilmLength { get; set; } /// <summary> /// 辊周长,单位mm /// </summary> public double RollPerimeter { get; set; } /// <summary> /// 转向信号100ms 滤波 /// </summary> public bool IsSignFilter { get; set; } /// <summary> /// 缺少信号模式 /// 正反信号线接得不好,有时候会丢失了。 /// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!! /// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号 /// </summary> public bool IsLackSignMode { get; set; } /// <summary> /// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大 /// </summary> public bool Is3D { get; set; } #endregion #region 状态 /// <summary> /// 当前旋转架旋转角度 /// </summary> public double Angle { get; set; } /// <summary> /// 当前方向 /// </summary> public Misc.DIRECTION Direction { get; set; } /// <summary> /// 累计旋转次数 /// </summary> public virtual int RotationCnt { get; protected set; } /// <summary> /// 最新的限位信号序号 /// </summary> public int LastLimitNo { get; set; } /// <summary> /// 最后一个限位信号 到当前已经消耗的时间 /// </summary> public TimeSpan PastTime { get; set; } /// <summary> /// 当前线速度,单位 m/min /// </summary> public double FilmVelocity { get; set; } /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动时间, 需要初始值,以后测量出来的 /// </summary> public TimeSpan RenZiJiaPeriod { get; set; } /// <summary> /// 缓冲区拥有数据的时间长度,1s更新一次 /// </summary> public TimeSpan BufTotalTime { get; set; } #endregion #region 信号列表 /// <summary> /// 信号列表第一个记录序号 /// </summary> public int FirstSignBM { get; protected set; } = -1; /// <summary> /// 信号列表最后一个记录序号 /// </summary> public int LastSignBM { get; protected set; } = -1; #endregion #region 成员变量 /// <summary> /// 刚开机,信号列表一个信号都没有,创造一个时间点,作为信号点 /// </summary> DateTime mDefaultTime; /// <summary> /// 辊信号列表 /// </summary> RList<RollCell> mRollList; /// <summary> /// 转向信号列表 /// </summary> public RList<LimitCell> mLimitList; /// <summary> /// 与mLimitList 一样!! /// 存在的目的用于看看信号是否正常 /// </summary> RList<SignData> mSignList; /// <summary> /// 还没处理的信号0 列表 /// </summary> List<SignData> mSign0ListNoJudge = new List<SignData>(); /// <summary> /// 还没处理的信号1 列表 /// </summary> List<SignData> mSign1ListNoJudge = new List<SignData>(); /// <summary> /// 后触发,信号改变推送列表!! /// </summary> List<DateTime> mdtFilmInfoChangedEventList = new List<DateTime>(); /// <summary> /// 当为 立式旋转架时,膜距离与旋转角度有关系 /// 列表的长度为 200, 列表的头为信号0离开, 尾为信号1进入 /// </summary> List<double> mFilmLength3D = new List<double>(); #endregion /// <summary> /// /// </summary> public BlowingDetect() { RAngle = 337;//° DefaultRPeriod = TimeSpan.FromMinutes(8); RenZiJiaPeriod = DefaultRPeriod; LimitSignTime = TimeSpan.FromSeconds(4); SignType = BlowingSignType.Short; IsSign0Double = false; IsSign1Double = false; IsLackSignMode = false; RollPerimeter = 314;//mm FilmLength = 25;//m Angle = 0;//° Direction = Misc.DIRECTION.FORWARD; FilmVelocity = 0;//m/min mRollList = new Misc.RList<RollCell>(4000);//以50m/min 线速度,储存大于 30min数据 mLimitList = new Misc.RList<LimitCell>(20);//人字架转10次的信号 mSignList = new RList<SignData>(30);//人字架正反转信号 LastLimitNo = -1; RotationCnt = 0; Clear(); PropertyChanged += new PropertyChangedEventHandler(RenZiJiaAndFilmPositionDetect_PropertyChanged); } int RotationCntToLimitIndex(int rotationcnt) { if (RotationCnt == 0) return -1; else return rotationcnt - 1 - (RotationCnt - mLimitList.Count); } /// <summary> /// 正反转信号列表中的序号,转旋转次数 /// </summary> /// <param name="index"></param> /// <returns></returns> int LimitIndexToRotationCnt(int index) { if (RotationCnt == 0) return 0; else return RotationCnt - mLimitList.Count + index + 1; } /// <summary> /// 使参数合法 /// </summary> public void CheckParamErr() { if (RAngle < 100 || RAngle > 360) { RAngle = 350; } 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; } if (LimitSignTime.TotalSeconds > 30) { LimitSignTime = TimeSpan.FromSeconds(4); } } /// <summary> /// 触发膜信息修改事件 /// </summary> /// <param name="dt">时间点</param> void TouchFilmInfoChanged(DateTime dt) { if (FilmInfoChangedEvent == null) return; mdtFilmInfoChangedEventList.Add(dt); TouchFilmInfoChanged(); } /// <summary> /// 触发膜信息修改事件列表的全部事件 /// </summary> 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)) || (e.PropertyName == nameof(RAngle)) ) { TouchFilmInfoChanged_lastLimit(4); } else if (e.PropertyName == nameof(RenZiJiaPeriod)) { TouchFilmInfoChanged_lastLimit(1); } else if (e.PropertyName == nameof(DefaultRPeriod)) { RenZiJiaPeriod = DefaultRPeriod; } } /// <summary> /// 触发膜信息修改事件 /// </summary> /// <param name="last_cnt">最后旋转几次</param> 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(mDefaultTime); } else { TouchFilmInfoChanged(mLimitList[idx].dt_begin); } } /// <summary> /// 创建完对象后,必须赋值 /// </summary> /// <param name="flyad"></param> public void Init(FlyADBase.IFlyAD flyad) { flyad.IStatusChangedEvent += new FlyADBase.IStatusChangedEventHandler(flyad_IStatusChangedEvent); FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, new FObjBase.PollModule.PollHandler(OnPoll)); } /// <summary> /// 正反信号滤波时间 150ms /// </summary> const int SIGN_DELAY_MS = 150; void update_limit(FlyADBase.IStatusChangedEventArgs e) { bool bchanged = false;//信号更新? DateTime dt = e.Time;//信号更新发生的时间 int no = -1;//信号序号 bool status = false;//信号的状态 true开始, false结束 if (Misc.MyBase.CHECKBIT(e.IChanged, Limit0_bit - 1))//信号0 发生变化 { bchanged = true; no = 0; if (!Misc.MyBase.CHECKBIT(e.IStatus, Limit0_bit - 1)) { status = true; } } else if (Misc.MyBase.CHECKBIT(e.IChanged, Limit1_bit - 1))//信号1 发生变化 { bchanged = true; no = 1; if (!Misc.MyBase.CHECKBIT(e.IStatus, Limit1_bit - 1)) { status = true; } } if (!bchanged)//没有信号更新 return; SignData sd = new SignData() { No = no, Time = dt, On = status }; if (mSignList.Count() > 0) { SignData sd_last = mSignList.Last(); sd.Interval = dt - sd_last.Time; } mSignList.RAdd(sd); FirstSignBM = mSignList.Index2No(0); LastSignBM = mSignList.GetLastNo(); if (IsSignFilter) { //信号滤波 if (no == 0) { mSign0ListNoJudge.Add(mSignList.Last()); //等100ms再触发 FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, () => { PopSign(0); }, TimeSpan.FromMilliseconds(SIGN_DELAY_MS), true, false, this, MARKNO_DELAY0, true); } else { mSign1ListNoJudge.Add(mSignList.Last()); //等100ms再触发 FObjBase.PollModule.Current.Poll_Config(FObjBase.PollModule.POLL_CONFIG.ADD, () => { PopSign(1); }, TimeSpan.FromMilliseconds(SIGN_DELAY_MS), true, false, this, MARKNO_DELAY1, true); } } else { update_limit(dt, no, status); } } /// <summary> /// 判断信号是否毛刺,最后会调用update_limit /// </summary> /// <param name="no"></param> void PopSign(int no) { List<SignData> noJudgeList; if (no == 0) noJudgeList = mSign0ListNoJudge; else noJudgeList = mSign1ListNoJudge; if (noJudgeList.Count() == 0) { //没有信号,异常 noJudgeList.Clear(); return; } if (noJudgeList.Count() == 1) { //只有一个信号,它持续了100ms,是信号不是毛刺 update_limit(noJudgeList.First()); } else { SignData sd0 = noJudgeList.First(); SignData sd1 = noJudgeList.Last(); if (sd0.On == sd1.On) { //状态一样, 不应该会发生的情况,AD卡坏了? //异常处理:两次信号间的都只是毛刺,触发第1个信号 update_limit(sd0); } else { if (sd1.Time - sd0.Time >= TimeSpan.FromMilliseconds(SIGN_DELAY_MS)) { //两个信号时间差大于 100ms //触发它们 update_limit(sd0); update_limit(sd1); } else { //时间差在100ms内,只是毛刺而已 } } } //判断完,清空列表!!! noJudgeList.Clear(); } /// <summary> /// 添加正反转信号到 列表 /// </summary> /// <param name="s"></param> void update_limit(SignData s) { update_limit(s.Time, s.No, s.On); } /// <summary> /// 添加正反转信号到 列表 /// </summary> /// <param name="dt">信号更新发生的时间</param> /// <param name="no">信号序号</param> /// <param name="status">信号的状态 true开始, false结束</param> void update_limit(DateTime dt, int no, bool status) { //上一个是虚拟信号 //1.当接收的信号与虚拟信号相同时,把虚拟信号删除!!!!! //2.当接收的信号与虚拟信号不同时,更新旋转时间!!!!! if (!update_limit_virtual(dt, no, status)) { LastLimitNo = no; switch (SignType) { case BlowingSignType.Short: update_limit_short(dt, no, status); break; default: update_limit_long(dt, no, status); break; } } //更新其它参数 update_param_for_limit(dt); } /// <summary> /// 更新因为旋转信号的添加导致的变化 /// </summary> /// <param name="dt">信号变化的时间</param> void update_param_for_limit(DateTime dt) { if (mLimitList.Count() > 0) { LimitCell lc = mLimitList.Last(); if (dt == lc.dt_begin)//刚撞转向 { //RotationCnt++; //更新人字架旋转周期 if (mLimitList.Count > 1) { DateTime dt1 = mLimitList[mLimitList.Count - 1].dt_begin; DateTime dt2 = mLimitList[mLimitList.Count - 2].dt_end; RenZiJiaPeriod = dt1 - dt2;//只有这个是正确的!!! //触发修正事件!!!!! TouchFilmInfoChanged(dt2); } else if (mLimitList.Count == 1) { DateTime dt1 = mLimitList[mLimitList.Count - 1].dt_begin; mDefaultTime = dt1 - RenZiJiaPeriod; //触发修正事件!!!!! TouchFilmInfoChanged(mDefaultTime); } } else if (lc.dt_end != DateTime.MinValue) LimitSignTime = lc.dt_end - lc.dt_begin;//更新撞信号的耗时 } } /// <summary> /// 处理上一个是虚拟信号的正反转信号,它可能是多余的,返回true,新来的真实信号已经被处理 /// </summary> /// <param name="dt"></param> /// <param name="no"></param> /// <param name="status"></param> /// <returns></returns> bool update_limit_virtual(DateTime dt, int no, bool status) { if (mLimitList.Count() == 0) return false; LimitCell lc_v = mLimitList.Last(); if (lc_v.isVirtual == false)//不是虚拟信号,不用再判断了 return false; if (lc_v.no == no)//有真实的信号,与虚拟信号一样 { if (status != true) return false;//异常, 不可能在 真实的信号中间插了个虚拟信号 //上一个信号是多余的 lc_v.isVirtual = false; lc_v.dt_begin = dt; lc_v.dt_end = DateTime.MinValue; return true; } else { //更新旋转时间!!!!!! if (mLimitList.Count() > 1) { DateTime dt2 = mLimitList[mLimitList.Count - 2].dt_end; RenZiJiaPeriod = TimeSpan.FromTicks((dt - dt2 - LimitSignTime).Ticks / 2);//只有这个是正确的!!! lc_v.dt_begin = dt2 + RenZiJiaPeriod; lc_v.dt_end = lc_v.dt_begin + LimitSignTime; } } return false; } /// <summary> /// 处理短信号 /// </summary> /// <param name="dt">信号发生的时间</param> /// <param name="no">信号序号</param> /// <param name="status">信号的状态 true开始, false结束)</param> void update_limit_short(DateTime dt, int no, bool status) { //把信号加入到列表 if (status == true)//添加,写入开始时间 { if (mLimitList.Count > 0) { LimitCell lc = mLimitList.Last(); if (lc.no == no)//连续撞了两次相同限位 { if (lc.isDouble) { lc.dt_end = dt;//信号结束了 LimitSignTime = lc.dt_end - lc.dt_begin;//更新撞限位的耗时 } else { if (lc.dt_end == DateTime.MinValue)//上一个限位还没离开 { //异常,AD卡出错了,或者断开连接了,清空!!! mLimitList.Clear(); mLimitList.RAdd( new LimitCell() { dt_begin = dt, no = no, isDouble = (no == 0) ? IsSign0Double : IsSign1Double }); RotationCnt++; } else { //判断两次的时间差 if ((dt - lc.dt_end) < TimeSpan.FromSeconds(30)) { //这是同一个转向信号 lc.isDouble = true; if (lc.no == 0) IsSign0Double = lc.isDouble; else IsSign1Double = lc.isDouble; lc.dt_mid = lc.dt_end; lc.dt_end = dt; } else { //时间差很大, 异常,旋转架重新转启 //异常,AD卡出错了,或者断开连接了,清空!!! mLimitList.Clear(); mLimitList.RAdd( new LimitCell() { dt_begin = dt, no = no, isDouble = (no == 0) ? IsSign0Double : IsSign1Double }); RotationCnt++; } } } } else { //一个新的 信号 if (lc.dt_end == DateTime.MinValue)//上一个转向还没离开 { if (lc.isDouble) { if (lc.dt_mid != DateTime.MinValue) { //没事, 只是从需要撞两次,变成只撞一次就可以了 lc.isDouble = false; if (lc.no == 0) IsSign0Double = lc.isDouble; else IsSign1Double = lc.isDouble; lc.dt_end = lc.dt_mid; lc.dt_mid = DateTime.MinValue; } else { //异常 输入口没有结束 //AD卡出错了,或者断开连接了,清空!!! mLimitList.Clear(); mLimitList.RAdd( new LimitCell() { dt_begin = dt, no = no, isDouble = (no == 0) ? IsSign0Double : IsSign1Double }); RotationCnt++; } } else { //异常 输入口没有结束 //AD卡出错了,或者断开连接了,清空!!! mLimitList.Clear(); mLimitList.RAdd( new LimitCell() { dt_begin = dt, no = no, isDouble = (no == 0) ? IsSign0Double : IsSign1Double }); RotationCnt++; } } else { //一个新的信号 mLimitList.RAdd( new LimitCell() { dt_begin = dt, no = no, isDouble = (no == 0) ? IsSign0Double : IsSign1Double }); RotationCnt++; } } } else { //一个新的信号 mLimitList.RAdd( new LimitCell() { dt_begin = dt, no = no, isDouble = (no == 0) ? IsSign0Double : IsSign1Double }); RotationCnt++; } } else //写入 结束时间 { if (mLimitList.Count > 0) { LimitCell lc = mLimitList.Last(); if (lc.no == no) { if (lc.isDouble) { //需要撞2次 if (lc.dt_mid == DateTime.MinValue) { lc.dt_mid = dt;//完成撞第1次 } else if (lc.dt_end != DateTime.MinValue) { //完成撞第2次 } else { //异常, 撞第2次 还没开始,就结束了 //AD卡出错了,或者断开连接了,清空!!! mLimitList.Clear(); } } else { if (lc.dt_end == DateTime.MinValue) { lc.dt_end = dt;//完成一次信号 } else { //异常,连续2次结束 mLimitList.Clear(); } } } else { //异常,还没开始,就结束了 mLimitList.Clear(); } } else { //异常,还没开始,就结束了 } } } /// <summary> /// 处理长信号, 只关心 status=true /// </summary> /// <param name="dt">信号发生的时间</param> /// <param name="no">信号序号</param> /// <param name="status">信号的状态 true开始, false结束)</param> void update_limit_long(DateTime dt, int no, bool status) { //把信号加入到列表 if (status == true)//添加,写入开始时间 { if (mLimitList.Count > 0) { LimitCell lc = mLimitList.Last(); if (lc.no == no)//连续撞了两次相同限位, 可能重新在转 { mLimitList.Clear(); } } mLimitList.RAdd( new LimitCell() { no = no, dt_begin = dt, dt_end = dt + LimitSignTime }); RotationCnt++; } } void update_roll(FlyADBase.IStatusChangedEventArgs e) { //---------------------------------------------------------------------- //更新辊信号 if (!Misc.MyBase.CHECKBIT(e.IChanged, Roll_bit - 1)) return;//没有信号更新 //信号的状态 bool status = !Misc.MyBase.CHECKBIT(e.IStatus, Roll_bit - 1); DateTime dt = e.Time;//信号更新发生的时间 if (status)//1->0 触发!!! { mRollList.RAdd( new RollCell() { dt = dt }); //触发修正事件!!!!! TouchFilmInfoChanged(); } } void flyad_IStatusChangedEvent(object sender, FlyADBase.IStatusChangedEventArgs e) { update_limit(e); update_roll(e); } /// <summary> /// 1秒触发一次 /// </summary> void OnPoll() { //-------------------------------------------------------------------- //添加虚拟信号 if (IsLackSignMode) OnPoll_Virtual(); //-------------------------------------------------------------------- //正反向信号撞两次的超时判断 OnPoll_checkDouble(); //-------------------------------------------------------------------- //旋转架速度安全检测 OnPoll_checkRVelocity(); //-------------------------------------------------------------------- //当前旋转架角度 OnPoll_updateRV(); //-------------------------------------------------------------------- //线速度安全检测 OnPoll_checkFilmV(); //-------------------------------------------------------------------- //更新当前线速度 OnPoll_updateFilmV(); } /// <summary> /// 虚拟信号添加 /// </summary> void OnPoll_Virtual() { DateTime now = DateTime.Now; //-------------------------------------------------------------------- //正反向信号撞两次的超时判断 if (mLimitList.Count > 0) { LimitCell lc = mLimitList.Last(); if (!lc.isVirtual)//上一个不能是虚拟信号 { if (lc.dt_end != DateTime.MinValue)//上一个信号完成了 { if (now - lc.dt_end > TimeSpan.FromSeconds(RenZiJiaPeriod.TotalSeconds + 20)) { //已经大于上一次旋转信号+20s,都没有信号了 //添加虚拟信号 int no = (lc.no == 0) ? 1 : 0; DateTime dt = lc.dt_end + RenZiJiaPeriod; mLimitList.RAdd(new LimitCell() { no = no, dt_begin = dt, dt_end = dt + LimitSignTime, isVirtual = true }); RotationCnt++; LastLimitNo = no; update_param_for_limit(dt); } } } } } /// <summary> /// 正反向信号撞两次的超时判断 /// </summary> void OnPoll_checkDouble() { if (mLimitList.Count > 0) { LimitCell lc = mLimitList.Last(); //需要撞两次的超时判断!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (lc.isDouble) { if (lc.dt_end == DateTime.MinValue) { //第2次还没撞 if (DateTime.Now - lc.dt_begin > TimeSpan.FromSeconds(30)) { //长时间第2次都没撞, 看来只需要撞一次而已 lc.isDouble = false; if (lc.no == 0) IsSign0Double = lc.isDouble; else IsSign1Double = lc.isDouble; lc.dt_end = lc.dt_mid; lc.dt_mid = DateTime.MinValue; } } } } } /// <summary> /// 旋转架速度安全检测 /// </summary> void OnPoll_checkRVelocity() { DateTime dt = mDefaultTime; 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(); } } /// <summary> /// 更新旋转架速度 /// </summary> void OnPoll_updateRV() { double angle; DIRECTION direction; LimitListSearch(DateTime.Now, false, out angle, out direction, out int rotationCnt, out bool inCV); Angle = angle; Direction = direction; } /// <summary> /// 更新当前线速度 /// </summary> void OnPoll_updateFilmV() { if (mRollList.Count > 1) { DateTime dt1 = mRollList[mRollList.Count - 1].dt; int cnt = 5; if (cnt > mRollList.Count) cnt = mRollList.Count; DateTime dt2 = mRollList[mRollList.Count - cnt].dt; DateTime dt11 = mRollList[mRollList.Count - (cnt - 1)].dt; TimeSpan ts_last = TimeSpan.FromTicks((dt1 - dt2).Ticks / (cnt - 1));//5次的平均滤波 TimeSpan ts_now = TimeSpan.FromTicks((DateTime.Now - dt11).Ticks / (cnt - 1)); TimeSpan ts_max; if (ts_now > (ts_last + ts_last)) { ts_max = ts_now; } else { ts_max = ts_last; } FilmVelocity = RollPerimeter / 1000.0 / ts_max.TotalMinutes; BufTotalTime = DateTime.Now - mRollList[0].dt; } } /// <summary> /// 线速度安全检测 /// </summary> void OnPoll_checkFilmV() { if (mRollList.Count > 0) { DateTime dt = mRollList[mRollList.Count - 1].dt; TimeSpan ts = DateTime.Now - dt; if (ts.TotalMinutes > 1)//1min { //大于1分钟,也没有一个信号,复位 Clear(); } } } public void Clear() { mLimitList.Clear(); mRollList.Clear(); mDefaultTime = DateTime.Now; FilmVelocity = 0; RenZiJiaPeriod = DefaultRPeriod; RotationCnt = 0;//复位次数!!! LimitSignTime = TimeSpan.FromSeconds(4); BufTotalTime = TimeSpan.Zero; ClearEvent?.Invoke(this); } public class FilmInfo { /// <summary> /// 上层 位于膜泡的角度 0~360° /// </summary> public double angle1; /// <summary> /// 下层 位于膜泡的角度 0~360° /// </summary> public double angle2; /// <summary> /// 膜泡旋转方向 /// </summary> public Misc.DIRECTION direction; /// <summary> /// 线速度 /// </summary> public double filmVelocity; /// <summary> /// 累计旋转次数 /// </summary> public int rotationCnt; /// <summary> /// 在匀速阶段; 在限位信号 开始~结束 期间 inCV = false /// </summary> public bool inCV; } public enum GetLimitTimeResult { /// <summary> /// 获取成功 /// </summary> OK, /// <summary> /// 不存在该点 /// </summary> NOEXIST, /// <summary> /// 限位已经撞了,但膜还没走完,等吧 /// </summary> ISFURTURE } /// <summary> /// 通过角度 获取 膜距离 /// </summary> /// <param name="angle">-180~180</param> /// <returns></returns> double GetFilmLength3D(double angle) { if (mFilmLength3D == null || mFilmLength3D.Count() == 0) return FilmLength; angle += RAngle / 2; int idx = (int)(mFilmLength3D.Count() * angle / RAngle); if (idx < 0) idx = 0; else if (idx >= mFilmLength3D.Count()) idx = mFilmLength3D.Count() - 1; return FilmLength + mFilmLength3D[idx]; } /// <summary> /// /// </summary> /// <param name="dt"></param> /// <returns></returns> 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); } /// <summary> /// 计算膜长用; /// 输入膜长,获取对应的 限位点时间 /// </summary> /// <param name="rotationcnt">旋转次数</param> /// <param name="filmlength">膜长</param> /// <param name="timeOfFilmLength">膜长对应限位时间</param> /// <returns></returns> 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; } /// <summary> /// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比LimitList.Last.dt+RenZiJiaPeriod还要后,更新RenZiJiaPeriod /// 返回 -1, 当前的时间点在列表以前; /// 返回 0, 当前的时间点在列表中; /// 返回 1, 当前的时间点在列表的未来; /// </summary> /// <param name="filmWidth">被压扁后的膜宽度</param> /// <param name="filmPosH">探头所在膜的横向位置</param> /// <param name="dt">测量的时间点</param> /// <returns></returns> public int GetFilmInfo(out FilmInfo filminfo, DateTime dt, double filmWidth, double filmPosH) { double filmlen = FilmLength; if (Is3D) filmlen = GetFilmLength3D(dt); 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;//不在膜的范围内 } DateTime ago_dt; double velocity; double angle0; double angle1; Misc.DIRECTION direction; int rotationcnt; bool inCV; if (dt == DateTime.MinValue) { //TODO, BUG } int ret = GetAgo(dt, filmLen, out ago_dt, out velocity); if (ret == 1) return ret;//发生在未来不处理!!! GetAngle(a, ago_dt, out angle0, out angle1, out direction, out rotationcnt, out inCV); filminfo = new FilmInfo() { angle1 = angle0, angle2 = angle1, filmVelocity = velocity, direction = direction, rotationCnt = rotationcnt, inCV = inCV }; return ret; } #region 人字架时间点<-->24m后膜时间点 /// <summary> /// 输入当前时间点,找到 刚出人字架时的时间点, 及那时候的线速度; /// 返回 -1, 当前的时间点在列表以前; /// 返回 0, 当前的时间点在列表中; /// 返回 1, 当前的时间点在列表的未来; /// </summary> /// <param name="dt">当前时间</param> /// <param name="ago_dt">24米以前的时间</param> /// <returns></returns> int GetAgo(DateTime dt, out DateTime ago_dt, out double ago_velocity) { return GetAgo(dt, FilmLength, out ago_dt, out ago_velocity); } /// <summary> /// 输入当前时间点,找到 刚出人字架时的时间点, 及那时候的线速度; /// 返回 -1, 当前的时间点在列表以前; /// 返回 0, 当前的时间点在列表中; /// 返回 1, 当前的时间点在列表的未来; /// </summary> /// <param name="dt">当前时间</param> /// <param name="ago_dt">24米真实长度</param> /// <returns></returns> int GetAgo(DateTime dt, double filmlength, out DateTime ago_dt, out double ago_velocity) { ago_dt = DateTime.MinValue; ago_velocity = 0; int i; int ret = RollList_FindIndex(dt, out i); if (ret == 1) { return ret; } double position = RollList_GetPosition(i, dt); position -= filmlength * 1000; ret = RollList_GetDateTime(out i, position, out ago_dt); ago_velocity = RollList_GetVelocity(i); return ret; } /// <summary> /// 输入过去时间,让它再加上膜长时间,得到未来时间; /// 返回 -1, 未来的时间在列表以前; /// 返回 0, 未来的时间在列表中; /// 返回 1, 未来的时间在列表的未来; /// </summary> /// <param name="dt">过去时间</param> /// <param name="dt_future">未来时间</param> /// <returns></returns> int GetFuture(DateTime dt, out DateTime dt_future) { return GetFuture(dt, FilmLength, out dt_future); } /// <summary> /// 输入过去时间,让它再加上膜长时间,得到未来时间; /// 返回 -1, 未来的时间在列表以前; /// 返回 0, 未来的时间在列表中; /// 返回 1, 未来的时间在列表的未来; /// </summary> /// <param name="dt">过去时间</param> /// <param name="filmlength">膜长</param> /// <param name="dt_future">未来时间</param> /// <returns></returns> 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 * 1000; return RollList_GetDateTime(out i, position, out dt_future); } #endregion /// <summary> /// 计算出角度 -180~180 恒速阶段 /// </summary> /// <param name="dtEnd">旋转结束时间点</param> /// <param name="dtStart">旋转开始时间点</param> /// <param name="noStart">开始时间点对应的限位 0 or 1</param> /// <param name="dt">用于转换为角度的时间</param> /// <param name="angle">输出: 角度</param> /// <param name="direction">输出: 方向</param> 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; 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; } } /// <summary> /// 时间点查找旋转角度 -180~180 /// </summary> /// <param name="dt">时间</param> /// <param name="modifyparam">当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod</param> /// <param name="angle">角度</param> /// <param name="direction">旋转的方向</param> /// <param name="rotationCnt">旋转的次数</param> /// <param name="inCV">在恒速区</param> void LimitListSearch(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 < mDefaultTime) mDefaultTime = dt - TimeSpan.FromSeconds(10); //检测是否需要修正 RenZiJiaPeriod if (mLimitList.Count == 0) { dt_start = mDefaultTime; 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://过去 { dt_start = mDefaultTime; 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; } } #region RollList 查找 /// <summary> /// 获取 [idx,idx+1] 之间 的 位置 /// </summary> /// <param name="idx"></param> /// <param name="dt"></param> /// <returns></returns> double RollList_GetPosition(int idx, DateTime dt) { DateTime dt1 = mRollList[idx + 1].dt; double position1 = (idx + 1) * RollPerimeter; DateTime dt2 = mRollList[idx].dt; double position2 = (idx) * RollPerimeter; return (position1 - position2) * (dt - dt2).Ticks / (dt1 - dt2).Ticks + position2; } /// <summary> /// 获取 [idx,idx+1] 之间 的 速度 /// </summary> /// <param name="idx"></param> /// <returns></returns> double RollList_GetVelocity(int idx) { DateTime dt1 = mRollList[idx + 1].dt; DateTime dt2 = mRollList[idx].dt; return RollPerimeter / 1000 / dt1.Subtract(dt2).TotalMinutes; } /// <summary> /// 获取 [idx,idx+1] 之间 的 时间 /// </summary> /// <param name="idx"></param> /// <param name="position"></param> /// <returns></returns> DateTime RollList_GetDateTime(int idx, double position) { //TODO 经常出错,position 无限小,idx=0 DateTime dt1 = mRollList[idx + 1].dt; double position1 = (idx + 1) * RollPerimeter; DateTime dt2 = mRollList[idx].dt; double position2 = (idx) * RollPerimeter; return dt2.Add(new TimeSpan((long)((dt1 - dt2).Ticks * (position - position2) / (position1 - position2)))); } /// <summary> /// 通过 position 查找时间 /// 返回 -1,这个过去的时间; /// 返回 0,在列表中; /// 返回 1,发生在未来的时间; /// </summary> /// <param name="position"></param> /// <returns></returns> int RollList_GetDateTime(out int idx, double position, out DateTime dt) { idx = 0; dt = DateTime.MinValue; if (mRollList.Count == 0) return 1; for (int i = mRollList.Count - 1; i > 0; i--) { if (position >= ((i) * RollPerimeter)) { //找到了 idx = i; if (i == (mRollList.Count - 1)) return 1; else { dt = RollList_GetDateTime(i, position); return 0; } } } //TODO, 这里会出错,position无限小 idx = 0; dt = RollList_GetDateTime(0, position); //发生在过去 return -1; } /// <summary> /// dt 这个时间,在是 index 与 index+1 中间发生的 /// 返回 -1,这个过去的时间; /// 返回 0,在列表中; /// 返回 1,发生在未来的时间; /// </summary> /// <param name="dt"></param> /// <returns></returns> 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].dt) { //找到了 if (i == (mRollList.Count - 1))//这个是未来的时间点,还没发生!!! { index = i; return 1; } else { index = i; return 0; } } } //这个在过去的时间 return -1; } #endregion /// <summary> /// 时间对应旋转状态 /// </summary> enum DTSTATUS { /// <summary> /// 发生在未来的均速阶段 /// </summary> Future, /// <summary> /// 发生在过去的均速阶段 /// </summary> Past, /// <summary> /// 发生在均速阶段 /// </summary> Normal, /// <summary> /// 发生在方向切换阶段 /// </summary> Swap, } enum ListFindIndexResult { /// <summary> /// 发生在未来, index.dt_end ~ 未来, 可能 index都还没有发生 /// </summary> Future, /// <summary> /// 发生在index.dt_begin ~ index.dt_end /// </summary> InCell, /// <summary> /// 应该发生在index.dt_begin ~ index.dt_end, 但 index.dt_end还没有数据 /// </summary> MaybeInCell, /// <summary> /// 发生在 index.dt_end ~ (index+1).dt_begin /// </summary> BetweenCells, /// <summary> /// 发生在 过去 /// </summary> Past } /// <summary> /// 找到 列表中 (开始时间,结束时间) 小于 dt 的信号点 /// </summary> /// <param name="dt"></param> /// <param name="index"></param> /// <returns></returns> 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; } /// <summary> /// 获取膜角度, TODO /// </summary> /// <param name="a">与0°的偏移, 0~180</param> /// <param name="dt">时间点</param> /// <param name="angle0">上面的膜对应的膜角度</param> /// <param name="angle1">下面的膜对应的膜角度</param> /// <param name="direction">人字架正转?反转? FIX, 停止!</param> /// <param name="rotationCnt">扫描次数</param> /// <param name="inCV">在恒速阶段</param> 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; double angle; //LimitListSearch(dt, true, out angle, out direction, out rotationCnt, out inCV); //不修改旋转速度!!!!!!!!!!!! LimitListSearch(dt, false, out angle, out direction, out rotationCnt, out inCV); a += angle; if (a >= 360) a -= 360; else if (a < 0) a += 360; a_other += angle; if (a_other >= 360) a_other -= 360; else if (a_other < 0) a_other += 360; angle0 = a; angle1 = a_other; return true; } public void Apply() { Save(); } public class FilmInfoChangedEventArgs : EventArgs { public FilmInfoChangedEventArgs(DateTime time) { Time = time; } /// <summary> /// 开始时间点 /// </summary> 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; /// <summary> /// 释放资源 /// </summary> public void Dispose() { Clear(); } #region INotifyPropertyChanged 成员 public event PropertyChangedEventHandler PropertyChanged; #endregion /// <summary> /// 加载历史数据 /// </summary> /// <returns></returns> public bool Load() { LoadFilmLength3D(); if (File.Exists(file_path)) { try { string json = File.ReadAllText(file_path); var jsonDb = JsonConvert.DeserializeObject<BlowingDetectJsonDb>(json); BlowingDetectJsonDb.Mapper.Map(jsonDb, this); return true; } catch { //异常,没有json 解码失败 } return false; } else//只是为了版本兼容,以后就会删除 { var jsonDb = FLY.Thick.Blowing.Server.Update.BlowingDetect.ToJsonDb(); if (jsonDb == null) return false; BlowingDetectJsonDb.Mapper.Map(jsonDb, this); return true; } } string file_path = "blowingdetect.json"; /// <summary> /// 保存 /// </summary> public void Save() { try { var jsonDb = BlowingDetectJsonDb.Mapper.Map<BlowingDetectJsonDb>(this); string json = JsonConvert.SerializeObject(jsonDb, Formatting.Indented); File.WriteAllText(file_path, json); } catch { //异常,没有json 编码失败 } } 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<List<double>>(json); } } catch { //conf = null; //异常,没有json 解码失败 //return false; } } void SaveFilmLength3D() { try { File.WriteAllText(file_path_filmlength3d, JsonConvert.SerializeObject(this, Formatting.Indented)); } catch { //异常,没有json 编码失败 } } /// <summary> /// 获取立式旋转架的旋转角度与膜距离增量关系 /// </summary> /// <param name="asyncDelegate"></param> /// <param name="asyncContext"></param> public void GetFilmLength3D(AsyncCBHandler asyncDelegate, object asyncContext) { GetFilmLength3DReponse p = new GetFilmLength3DReponse(); p.datas = mFilmLength3D; asyncDelegate(asyncContext, p); } /// <summary> /// 设置 立式旋转架 的 膜距离增量 /// </summary> /// <param name="data"></param> public void SetFilmLength3D(List<double> data) { mFilmLength3D = data; SaveFilmLength3D(); } /// <summary> /// 获取 信号列表 /// </summary> /// <param name="asyncDelegate">retdata = GetSignListReponse</param> /// <param name="asyncContext"></param> public void GetSignList(AsyncCBHandler asyncDelegate, object asyncContext) { GetSignListReponse p = new GetSignListReponse(); p.datas = mSignList; asyncDelegate(asyncContext, p); } /// <summary> /// 获取 信号列表 /// </summary> /// <param name="begin">开始时间</param> /// <param name="asyncDelegate">retdata = GetSignListReponse</param> /// <param name="asyncContext"></param> public void GetSignList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext) { GetSignListReponse p = new GetSignListReponse(); p.datas = mSignList.FindAll(r => r.Time >= begin); asyncDelegate(asyncContext, p); } /// <summary> /// 获取 辊信号列表 /// </summary> /// <param name="begin">开始时间</param> /// <param name="asyncDelegate">retdata = GetRollListReponse</param> /// <param name="asyncContext"></param> public void GetRollList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext) { var p = new GetRollListReponse(); p.datas = mRollList.FindAll(r => r.dt >= begin); asyncDelegate(asyncContext, p); } /// <summary> /// 获取 转向信号列表 /// </summary> /// <param name="begin">开始时间</param> /// <param name="asyncDelegate">retdata = GetRollListReponse</param> /// <param name="asyncContext"></param> public void GetLimitList(DateTime begin, AsyncCBHandler asyncDelegate, object asyncContext) { var p = new GetLimitListReponse(); p.datas = mLimitList.FindAll(r => { return (r.dt_begin >= begin) || (r.dt_end >= begin); }); asyncDelegate(asyncContext, p); } } public class BlowingDetectJsonDb { public static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => { c.CreateMap<BlowingDetect, BlowingDetectJsonDb>().ReverseMap(); })); /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位° /// </summary> public double RAngle; /// <summary> /// 人字架 旋转1周 设置 时间。 /// 刚开机时,RenZiJiaPeriod = DefaultRPeriod; /// 异常时,RenZiJiaPeriod = DefaultRPeriod; /// </summary> public TimeSpan DefaultRPeriod; /// <summary> /// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。 /// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime /// </summary> public bool IsSign0Double; /// <summary> /// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。 /// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime /// </summary> public bool IsSign1Double; /// <summary> /// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向) /// </summary> public BlowingSignType SignType = BlowingSignType.Short; /// <summary> /// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的 /// </summary> public TimeSpan LimitSignTime; /// <summary> /// 人字架到测厚仪膜长 单位m /// </summary> public double FilmLength; /// <summary> /// 辊周长,单位mm /// </summary> public double RollPerimeter; /// <summary> /// 转向信号100ms 滤波 /// </summary> public bool IsSignFilter; /// <summary> /// 缺少信号模式 /// 正反信号线接得不好,有时候会丢失了。 /// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!! /// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号 /// </summary> public bool IsLackSignMode; /// <summary> /// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大 /// </summary> public bool Is3D; } } namespace FLY.Thick.Blowing.Server.Update { public class BlowingDetect : ISaveToXml { public static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => { c.CreateMap<BlowingDetect, BlowingDetectJsonDb>(); })); #region 参数 /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位° /// </summary> public double RAngle { get; set; } /// <summary> /// 人字架 旋转1周 设置 时间。 /// 刚开机时,RenZiJiaPeriod = DefaultRPeriod; /// 异常时,RenZiJiaPeriod = DefaultRPeriod; /// </summary> public TimeSpan DefaultRPeriod { get; set; } /// <summary> /// 信号0 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。 /// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime /// </summary> public bool IsSign0Double { get; set; } /// <summary> /// 信号1 撞2次。 这个能自动设置。 两个撞同一信号 间隔30秒内,算一个换向信号。 /// 当为2次撞时: StartTime = 第1次撞的StartTime, EndTime = 第2次撞的EndTime /// </summary> public bool IsSign1Double { get; set; } /// <summary> /// 信号模式, 短信号模式(只撞一下),长信号模式(只要是转着,信号就长亮,直到换向) /// </summary> public BlowingSignType SignType { get; set; } = BlowingSignType.Short; /// <summary> /// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的 /// </summary> public TimeSpan LimitSignTime { get; set; } /// <summary> /// 人字架到测厚仪膜长 单位m /// </summary> public double FilmLength { get; set; } /// <summary> /// 辊周长,单位mm /// </summary> public double RollPerimeter { get; set; } /// <summary> /// 转向信号100ms 滤波 /// </summary> public bool IsSignFilter { get; set; } /// <summary> /// 缺少信号模式 /// 正反信号线接得不好,有时候会丢失了。 /// 当前的旋转时间是上一次的1.1倍时,在以前的位置增加信号!!!! /// 当有连续两次相同的信号,且信号差刚好是旋转时间的1.9~2.1倍时,证明真的是缺少信号 /// </summary> public bool IsLackSignMode { get; set; } /// <summary> /// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大 /// </summary> public bool Is3D { get; set; } #endregion /// <summary> /// Misc.SaveToXmlHepler 使用,保存在 xml文件内的 属性名称 /// </summary> /// <returns></returns> 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<BlowingDetectJsonDb>(bDetect); } else return null; } } }