using FLY.Thick.Blowing.IService; using Misc; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FLY.Thick.Blowing.UI.Fix.Client { public class BlowingDetectCore { /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位° /// </summary> public double RAngle; /// <summary> /// 辊周长,单位mm /// </summary> public double RollPerimeter; /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动时间, 需要初始值,以后测量出来的 /// </summary> public TimeSpan RenZiJiaPeriod; /// <summary> /// 人字架到测厚仪膜长 单位m /// </summary> public double FilmLength; /// <summary> /// 累计旋转次数 /// </summary> public int RotationCnt; /// <summary> /// 旋转架为立式,测厚仪安装在二牵引前面,膜距离 与 旋转角度 一起变化, 旋转角度越大 膜距离越大 /// </summary> public bool Is3D; /// <summary> /// 辊信号列表 /// </summary> public List<RollCell> mRollList; /// <summary> /// 转向信号列表 /// </summary> public List<LimitCell> mLimitList; /// <summary> /// 当为 立式旋转架时,膜距离与旋转角度有关系 /// 列表的长度为 200, 列表的头为信号0离开, 尾为信号1进入 /// </summary> public List<double> mFilmLength3D; /// <summary> /// /// </summary> public BlowingDetectCore() { } //public void Init(IBlowingDetect blowingDetect, // List<RollCell> rolls, // List<LimitCell> limits, // List<double> filmLength3D // ) //{ // RAngle = blowingDetect.RAngle; // RollPerimeter = blowingDetect.RollPerimeter; // RenZiJiaPeriod = blowingDetect.RenZiJiaPeriod; // FilmLength = blowingDetect.FilmLength; // Is3D = blowingDetect.Is3D; // mFilmLength3D = filmLength3D; // mRollList = rolls; // mLimitList = limits; // RotationCnt = limits.Count(); //} public void Init( double RAngle, double RollPerimeter, TimeSpan RenZiJiaPeriod, double FilmLength, bool Is3D, List<RollCell> rolls, List<LimitCell> limits, List<double> filmLength3D ) { this.RAngle = RAngle; this.RollPerimeter = RollPerimeter; this.RenZiJiaPeriod = RenZiJiaPeriod; this.FilmLength = FilmLength; this.Is3D = Is3D; mFilmLength3D = filmLength3D; mRollList = rolls; mLimitList = limits; RotationCnt = limits.Count(); } 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> /// <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(dt, out double angle, out DIRECTION direction, out int rotationcnt, out bool inCV); return GetFilmLength3D(angle); } public class FilmInfo { /// <summary> /// 上层 位于膜泡的角度 0~360° /// </summary> public double angle; /// <summary> /// 膜泡旋转方向 /// </summary> public Misc.DIRECTION direction; /// <summary> /// 线速度 /// </summary> public double filmVelocity; /// <summary> /// 累计旋转次数 /// </summary> public int rotationCnt; /// <summary> /// 在匀速阶段; 在限位信号 开始~结束 期间 inCV = false /// </summary> public bool inCV; } /// <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 filmLen = FilmLength; if (Is3D) filmLen = GetFilmLength3D(dt); filminfo = null; DateTime ago_dt; double velocity; double angle0; 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( ago_dt, out angle0, out direction, out rotationcnt, out inCV); filminfo = new FilmInfo() { angle = angle0, 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, 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="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="angle"></param> /// <param name="direction"></param> /// <param name="rotationCnt"></param> void LimitListSearch( DateTime dt, 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://发生在未来 { //检测是否需要修正 RenZiJiaPeriod if (mLimitList.Count == 0)//不允许存在 { throw new Exception("参数出错 mLimitList.Count == 0"); } else { dt_start = mLimitList[index].dt_end; no = mLimitList[index].no; } rotationCnt = LimitIndexToRotationCnt(index); if (dt > (dt_start + RenZiJiaPeriod)) { 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 (mLimitList[0].no == 0) no = 1; else no = 0; dt_start = mLimitList[0].dt_begin - (mLimitList[1].dt_begin - mLimitList[0].dt_end); dt_end = mLimitList[0].dt_begin; rotationCnt = LimitIndexToRotationCnt(-1); inCV = true; LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction); } break; case ListFindIndexResult.InCell://TODO { no = mLimitList[index].no; rotationCnt = LimitIndexToRotationCnt(index); inCV = false; if (no == 0) { angle = -RAngle / 2; direction = Misc.DIRECTION.FORWARD; } else { angle = RAngle / 2; direction = Misc.DIRECTION.BACKWARD; } } break; default: //case ListFindIndexResult.MaybeInCell: { no = mLimitList[index].no; rotationCnt = index + 1; inCV = false; if (no == 0) { angle = -RAngle / 2; direction = Misc.DIRECTION.FORWARD; } else { angle = RAngle / 2; direction = Misc.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 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)//没数据,所以是发生在未来的 throw new Exception("参数出错 mLimitList.Count == 0"); 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> /// 获取膜角度 /// </summary> /// <param name="dt">时间点</param> /// <param name="angle">上面的膜对应的膜角度</param> /// <param name="direction">人字架正转?反转? FIX, 停止!</param> /// <param name="rotationCnt">扫描次数</param> /// <param name="inCV">在恒速阶段</param> bool GetAngle( DateTime dt, out double angle, out Misc.DIRECTION direction, out int rotationCnt, out bool inCV) { inCV = true; direction = Misc.DIRECTION.FORWARD; rotationCnt = 0; //不修改旋转速度!!!!!!!!!!!! LimitListSearch( dt, out angle, out direction, out rotationCnt, out inCV); double a = 0; a += angle; if (a >= 360) a -= 360; else if (a < 0) a += 360; angle = a;; return true; } } }