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; } } }