using FLY.Thick.Blowing; using FLY.Thick.Blowing.IService; using Misc; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace FLY.Thick.Blowing360.UI { public class BlowingDetect360Core { /// <summary> /// 辊周长,单位mm /// </summary> public double RollPerimeter; /// <summary> /// 辊周长,单位m /// </summary> double MofR => RollPerimeter / 1000.0; /// <summary> /// 离开限位 到 撞下一个限位 的 旋转架转动时间, 需要初始值,以后测量出来的 /// </summary> public TimeSpan RenZiJiaPeriod; /// <summary> /// 人字架到测厚仪膜长 单位m /// </summary> public double FilmLength; /// <summary> /// 累计旋转次数 /// </summary> public int RotationCnt; /// <summary> /// 使用编码器检测旋转架旋转位置模式 /// </summary> public bool IsRPosMode; /// <summary> /// 人字架 离开限位 到 撞下一个限位 的 旋转架转动总脉冲 单位 脉冲 /// </summary> public int RPosOfR; /// <summary> /// 原点旋转脉冲 上一次撞限位开关0时, 旋转架脉冲 /// </summary> public int OrgRPos; /// <summary> /// 刚开机,信号列表一个信号都没有,创造一个时间点,作为信号点 /// </summary> public DateTime DefaultTime; /// <summary> /// 旋转脉冲列表 /// </summary> public List<RPosData> mRPosList; /// <summary> /// 辊信号列表 /// </summary> public List<RollCell2> mRollList; /// <summary> /// 转向信号列表 /// </summary> public List<LimitCell> mLimitList; public Misc.DIRECTION Direction; /// <summary> /// /// </summary> public BlowingDetect360Core() { } public void Init( double rollPerimeter, TimeSpan renZiJiaPeriod, double filmLength, List<RollCell> rollList, List<LimitCell> limitList, DateTime defaultTime, bool isRPosMode, int orgRPos, int rPosOfR, List<RPosData> rPosList, Misc.DIRECTION direction ) { Direction = direction; RollPerimeter = rollPerimeter; RenZiJiaPeriod = renZiJiaPeriod; FilmLength = filmLength; IsRPosMode = isRPosMode; RPosOfR = rPosOfR; DefaultTime = defaultTime; OrgRPos = orgRPos; mRPosList = rPosList; mLimitList = limitList; RotationCnt = limitList.Count(); //组装 rPosList 与 rollList mRollList = rollList.Select(r => new RollCell2() { Time = r.Time, SysTime = r.SysTime }).ToList(); } 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> /// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比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; filminfo = null; Misc.DIRECTION direction = Direction; if (dt == DateTime.MinValue) { //TODO, BUG return -1;//不在膜的范围内 } int ret = GetAgo(dt, out DateTime ago_dt, out int ago_index); if (ret == 1) return ret;//发生在未来不处理!!! GetAngle( ago_dt, out double angle0, out int rotationcnt); double velocity = RollList_GetVelocity(ago_index); filminfo = new FilmInfo() { angle1 = angle0, angle2 = angle0, filmVelocity = velocity, direction = direction, rotationCnt = rotationcnt, inCV = true }; 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 int ago_index) { return GetAgoFix(dt, out ago_dt, out ago_index); } /// <summary> /// 膜距离固定 获取膜距离前的时间点 /// </summary> /// <param name="dt"></param> /// <param name="ago_dt"></param> /// <param name="ago_index"></param> /// <returns></returns> 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; } /// <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; return RollList_GetDateTime(position, out i, out dt_future); } #endregion /// <summary> /// 计算出角度 0~360° 恒速阶段 /// </summary> /// <param name="dtEnd">旋转结束时间点</param> /// <param name="dtStart">旋转开始时间点</param> /// <param name="dt">用于转换为角度的时间</param> /// <param name="angle">输出: 角度</param> void LimitListCal_ConstantVelocity(DateTime dtEnd, DateTime dtStart, DateTime dt, out double angle) { TimeSpan ts1 = dt - dtStart; TimeSpan ts2 = dtEnd - dtStart; angle = 360 * ts1.Ticks / ts2.Ticks; } /// <summary> /// 时间点查找旋转角度 0~360° /// </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 GetAngle(DateTime dt, bool modifyparam, out double angle, out int rotationCnt) { int index; ListFindIndexResult ret = LimitList_FindIndex(dt, out index); DateTime dt_start; DateTime dt_end; switch (ret) { case ListFindIndexResult.Future://发生在未来 { if (dt < DefaultTime) DefaultTime = dt - TimeSpan.FromSeconds(10); //检测是否需要修正 RenZiJiaPeriod if (mLimitList.Count == 0) { dt_start = DefaultTime; } else { dt_start = mLimitList[index].dt_begin; } rotationCnt = LimitIndexToRotationCnt(index); if (dt > (dt_start + RenZiJiaPeriod)) { if (modifyparam) { //修正!!!! RenZiJiaPeriod = dt - dt_start + TimeSpan.FromSeconds(10); } else { dt = dt_start + RenZiJiaPeriod;//只是为了看,随便了。 时间停止下来 } } dt_end = dt_start + RenZiJiaPeriod; LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle); } break; case ListFindIndexResult.BetweenCells: { dt_start = mLimitList[index].dt_begin; dt_end = mLimitList[index + 1].dt_begin; rotationCnt = LimitIndexToRotationCnt(index); LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle); } break; //case ListFindIndexResult.Past://过去 default: { dt_start = DefaultTime; dt_end = mLimitList[0].dt_begin; rotationCnt = LimitIndexToRotationCnt(-1); LimitListCal_ConstantVelocity(dt_end, dt_start, dt, out angle); } break; } if (Direction == DIRECTION.BACKWARD) angle = 360 - angle; } public double GetAngleByGlobalRPos(int globalRPos) { //OrgRPos 为 -RAngle/2 int rPos = globalRPos - OrgRPos; //360°对应的旋转脉冲 int rPosOf360 = RPosOfR; while (rPos < 0) rPos += rPosOf360; while (rPos > rPosOf360) rPos -= rPosOf360; double angle = 360 * rPos / RPosOfR; //角度范围是 0°~ 360° return angle; } #region RollList 查找 /// <summary> /// 获取 [idx,idx+1] 之间 的 位置, 单位m /// </summary> /// <param name="idx"></param> /// <param name="dt"></param> /// <returns></returns> 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; } /// <summary> /// 获取 [idx,idx+1] 之间 的 速度 m/min /// </summary> /// <param name="idx"></param> /// <returns></returns> double RollList_GetVelocity(int idx) { DateTime dt1 = mRollList[idx + 1].SysTime; DateTime dt2 = mRollList[idx].SysTime; return MofR / dt1.Subtract(dt2).TotalMinutes; } /// <summary> /// 获取 [idx,idx+1] 之间 的 时间 /// </summary> /// <param name="idx"></param> /// <param name="filmPosition">纵向位置m</param> /// <returns></returns> 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)))); } /// <summary> /// 通过 filmPosition 查找时间 /// 返回 -1,这个过去的时间; /// 返回 0,在列表中; /// 返回 1,发生在未来的时间; /// </summary> /// <param name="filmPosition">单位m</param> /// <returns></returns> int RollList_GetDateTime(double filmPosition, out int idx, out DateTime dt) { idx = 0; dt = DateTime.MinValue; if (filmPosition < 0) return -1; if (mRollList.Count == 0) return 1; idx = (int)(filmPosition / MofR); if (idx >= mRollList.Count - 1) { return 1; } dt = RollList_GetDateTime(idx, filmPosition); return 0; } /// <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].Time) { //找到了 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 int rotationCnt) { //不修改旋转速度!!!!!!!!!!!! GetAngle( dt, false, out angle, out rotationCnt); double a = 0; a = GetValidAngle(a + angle); angle = a;; return true; } /// <summary> /// 限制角度在 [0~360) /// </summary> /// <param name="a"></param> /// <returns></returns> double GetValidAngle(double a) { if (a >= 360) a -= 360; else if (a < 0) a += 360; return a; } } }