using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Collections.ObjectModel; namespace FLY.Simulation.Blowing { /// /// 吹膜模拟 /// 1.加载数据 /// public class Blowing:INotifyPropertyChanged { #region 参数 #region 旋转架 /// /// 两个限位间的角度范围 != 旋转的总角度,限位值 ° /// public double AngleRange; /// /// 旋转的速度, min/R 1R = 360° /// public double AngleVelocity { get; set; } /// /// 加速时间 单位 s /// public double AngleAccTime; /// /// 减速时间 单位 s /// public double AngleDecTime; /// /// 换方向冷却时间 单位s /// public double AngleCoolTime; #endregion #region 膜 private double filmwidth; /// /// 膜泡压扁后总宽度,单位m /// public double FilmWidth { get; set; } /// /// 膜走带速度, m/min /// public double FilmVelocity { get; set; } /// /// 人字架到测厚仪的距离 23m /// public double FilmDistance; /// /// 辊轴承,单位mm /// public double RollPerimeter; /// /// 测辊信号,信号长,单位mm /// public double RollSignLength; #endregion //#region 风环 ///// ///// 加热棒数量 ///// //public int ChannelCnt; ///// ///// 加热功率, 1~100 ///// //public int[] Heats; ///// ///// 第1根加热棒对应角度 ///// //public int Channel1stAngle; ///// ///// 温度影响厚度变化比例, 1厚度,对应多少加热 ///// //public int Factor;// //#endregion public bool IsShieldI9 { get; set; } #endregion #region 动态数据 /// /// 当前人字架角度 ° /// public double CurrAngle { get; set; } /// /// 当前速度,单位 °/s /// public double CurrAngleVelocity; /// /// 当前加速度, 单位 °/s^2 /// public double CurrAngleAccV; /// /// 当前已经走到长度, 通过编码器获取 单位 m /// public double CurrFilmLength; #endregion public HMI mHMI; private int avg; public int Avg { get; set; } /// /// 原始数据 /// public ObservableCollection BeforeDatas = new ObservableCollection(); /// /// 系统扰动后 数据 /// public ObservableCollection BeforeDatas2 = new ObservableCollection(); /// /// 系统扰动后 再 加热数据 /// public ObservableCollection AfterDatas = new ObservableCollection(); class PositionAngleInfo { public DateTime dt; public double position; public double angle; } /// /// 10ms 1个记录点 /// List mPositionAngleList = new List(); int ListCap = 30000;//300s public Blowing() { //撞了限位后,限位信号一直在。。。。 AngleRange = 350;//两个限位间的角度范围350° AngleVelocity = 6;//出限位 到 撞另一个限位的时间 8min/ 360° AngleAccTime = 1;//加速时间1s AngleDecTime = 5;//减速时间5s AngleCoolTime = 5;//正反转延迟时间5s FilmWidth = 2.3;//膜宽2.3m FilmVelocity = 20;//线速度m/min FilmDistance = 26;//膜距离m RollPerimeter = 314;//辊周长mm RollSignLength = 30;//辊信号长度mm //整个膜片1000个数据 int[] datas = OrgData.GetData(1000); for(int i=0;i<1000;i++) { BeforeDatas.Add(datas[i]);//原始数据 BeforeDatas2.Add(datas[i]);//系统扰动后数据 AfterDatas.Add(datas[i]);//系统扰动后 再 加热数据 } mAirRing = new AirRing(88);//44支加热棒 mAirRing.Init(BeforeDatas, BeforeDatas2, 0); mAirRing2 = new AirRing(88);//44支加热棒; mAirRing2.Init(BeforeDatas2, AfterDatas, 400);//加热偏移了 400个数据, 总数是1000 mAirRing2.AfterDatasUpdateEvent += new Action>(mAirRing2_AfterDatasUpdateEvent); Avg = (int)(BeforeDatas.Average());//原始平均值 TestPos = 500; UpdateTestHeat(); //AfterDatas.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(AfterDatas_CollectionChanged); PropertyChanged += new PropertyChangedEventHandler(Blowing_PropertyChanged); mHMI = new HMI(); mHMI.mAirRing = mAirRing2; } void mAirRing2_AfterDatasUpdateEvent(ObservableCollection obj) { NotifyPropertyChanged("AfterDatas"); } public int TestPos { get; set; } public int TestHeat { get; set; } public void Test() { int idx = mAirRing.ChannelCnt * TestPos / BeforeDatas.Count(); int offset = TestHeat - mAirRing.Heats[idx]; mAirRing.Test(idx, offset); mAirRing2.HeatApply(); } public class AirRing { /// /// 加热通过 /// public int ChannelCnt; /// /// 加热量, 数据量为 ChannelCnt /// public int[] Heats; /// /// 第1个加热棒,对应的 数据序号, 数据量为 1000, BeforeData.Count /// public int Channel1stIndex; /// /// 加热 对 厚度的影响 /// public double Factor; //需要别人赋值 /// /// 原始数据!! 数据量为1000 /// public ObservableCollection BeforeDatas; /// /// 加热后的数据!!!! 数据量为BeforeDatas.Count /// public ObservableCollection AfterDatas; double[] p;// 一个凸起来的数组 0 1 4 9 16 9 4 1 0 void p_init() { int size = ChannelCnt / 10; p = new double[size]; for (int i = 0; i < size; i++) { int index1 = i; int index2 = size - 1 - i; double d = i * i; p[index1] = d; p[index2] = d; if (Math.Abs(index2-index1)<=1) break; } double sum_p = p.Max(); for (int i = 0; i < size; i++) { p[i] = p[i] / sum_p; } } public AirRing(int channelcnt) { SetChannelCnt(channelcnt); p_init(); Factor = 5; } public void Init(ObservableCollection before_datas, ObservableCollection after_datas, int channel1stIndex) { BeforeDatas = before_datas; AfterDatas = after_datas; Channel1stIndex = channel1stIndex; } public void SetChannelCnt(int channelcnt) { ChannelCnt = channelcnt; Heats = new int[ChannelCnt]; Array.Clear(Heats, 0, ChannelCnt); } public event Action> AfterDatasUpdateEvent; public void HeatApply() { int boltcnt = BeforeDatas.Count(); double b_c = (double)(boltcnt)/ChannelCnt; int b = Channel1stIndex; double beforeavg = BeforeDatas.Average(); int[] datas = new int[boltcnt]; for (int i = 0; i < ChannelCnt; i++) { int heat = Heats[i]; int e = (int)((i+1) * b_c)+ Channel1stIndex; if (e >= boltcnt) e -= boltcnt; else if (e < 0) e += boltcnt; int j=b; while (j != e) { datas[j] = (int)(BeforeDatas[j] - beforeavg * heat / Factor/100); j++; if (j >= boltcnt) j -= boltcnt; } b = e; } double afteravg = datas.Average(); for (int j = 0; j < boltcnt; j++) { AfterDatas[j] = (int)(beforeavg * datas[j] / afteravg); } if (AfterDatasUpdateEvent != null) { AfterDatasUpdateEvent(AfterDatas); } } public void Test(int idx,int offset) { idx -= p.Count() / 2; if (idx < 0) idx += ChannelCnt; else if (idx >= ChannelCnt) idx -= ChannelCnt; for (int i = 0; i < p.Count(); i++) { int index = idx+i; if (index < 0) index += ChannelCnt; else if (index>= ChannelCnt) index -= ChannelCnt; int heat = Heats[index] + (int)(offset * p[i]); if (heat > 100) heat = 100; else if (heat < 0) heat = 0; Heats[index] = heat; } HeatApply(); } } /// /// 1级加热, 模拟系统扰动 /// private AirRing mAirRing; /// /// 2级加热, 通过HMI 控制的加热 /// public AirRing mAirRing2; void Blowing_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Avg") { int avg = (int)(BeforeDatas.Average()); int offset = Avg - avg; if (offset != 0) { for (int i = 0; i < BeforeDatas.Count(); i++) { BeforeDatas[i] += offset; } } } else if (e.PropertyName == "TestPos") { UpdateTestHeat(); } } void UpdateTestHeat() { int idx = mAirRing.ChannelCnt * TestPos / BeforeDatas.Count(); TestHeat = mAirRing.Heats[idx]; } bool SearchAngleFromPositionList(double position, out double angle) { for (int i = 1; i < mPositionAngleList.Count; i++) { int idx1 = i; int idx2 = i - 1; if ((mPositionAngleList[idx1].position >= position)) { double percent = (position - mPositionAngleList[idx2].position) / (mPositionAngleList[idx1].position - mPositionAngleList[idx2].position); if (percent < 0) { angle = 0; return false; } //找到了 angle = (mPositionAngleList[idx1].angle - mPositionAngleList[idx2].angle) * percent + mPositionAngleList[idx2].angle; return true; } } angle = 0; return false; } public FilmData GetData(double position) { double ago_filmlength = CurrFilmLength - FilmDistance; double angle; //从膜位置列表找时间 if (!SearchAngleFromPositionList(ago_filmlength, out angle)) return null; return GetData(position, angle); } /// /// 获取膜泡数据 /// /// 相对于膜泡位置,横向 /// 人字架角度 /// FilmData GetData(double position, double angle) { if ((position < 0) || (position > FilmWidth)) { return null; } //与0°的偏移 double a = position / FilmWidth * 180; //压扁对面的角度 double a_other = 360 - a; 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; int dat_idx1 = (int)(a / 360 * AfterDatas.Count()); int dat_idx2 = (int)(a_other / 360 * AfterDatas.Count()); FilmData fd = new FilmData() { angle1 = a, angle2 = a_other, data1 = AfterDatas[dat_idx1], data2 = AfterDatas[dat_idx2] }; return fd; } /// /// 1.28ms 1次 /// public void OnPoll(DateTime now) { UpdatePositionAndAngle(now); } enum STATE { /// /// 加速 /// Acc, /// /// 减速 /// Dec, /// /// 恒速 /// Constant, /// /// 方向交换,冷却期 /// Wrap } STATE mState; DateTime mDTLast = DateTime.MinValue; /// /// 停下来的时间 /// DateTime mDTWrap = DateTime.MinValue; void UpdateAngleV(DateTime now, TimeSpan ts) { double angle = CurrAngle + CurrAngleVelocity * ts.TotalSeconds; //旋转 switch (mState) { case STATE.Constant: if (((CurrAngleVelocity > 0) && (angle >= AngleRange / 2)) || ((CurrAngleVelocity < 0) && (angle <= (-AngleRange / 2)))) { mState = STATE.Dec; double dv = 0 - CurrAngleVelocity; CurrAngleAccV = dv / AngleDecTime; } break; case STATE.Dec: { bool isForw = false; if (CurrAngleVelocity > 0) isForw = true; CurrAngleVelocity += CurrAngleAccV * ts.TotalSeconds; if (((isForw) && (CurrAngleVelocity < 0)) || ((!isForw) && (CurrAngleVelocity > 0))) { CurrAngleVelocity = 0; mState = STATE.Wrap; mDTWrap = now; } } break; case STATE.Wrap: { if ((now - mDTWrap).TotalSeconds >= AngleCoolTime) { //冷静完 mState = STATE.Acc; double anglev = 360.0 / (AngleVelocity * 60); if (angle >= AngleRange / 2) { //反转 anglev = -anglev; } double dv = anglev - CurrAngleVelocity; CurrAngleAccV = dv / AngleAccTime; } } break; case STATE.Acc: { CurrAngleVelocity += CurrAngleAccV * ts.TotalSeconds; double anglev = 360.0 / (AngleVelocity * 60); if (CurrAngleAccV > 0) { if (CurrAngleVelocity >= anglev) { CurrAngleVelocity = anglev; mState = STATE.Constant; } } else { if (CurrAngleVelocity <= -anglev) { CurrAngleVelocity = -anglev; mState = STATE.Constant; } } } break; } CurrAngle = angle; } void UpdatePositionAndAngle(DateTime now) { if (mDTLast == DateTime.MinValue) { mDTLast = now; CurrFilmLength = 0; CurrAngle = 0;//在中间 mState = STATE.Constant; double anglev = 360.0 / (AngleVelocity * 60); CurrAngleVelocity = anglev; mPositionAngleList.Add( new PositionAngleInfo() { dt = mDTLast, position = CurrFilmLength, angle = CurrAngle }); return; } DateTime dt = now; TimeSpan ts = dt.Subtract(mDTLast); if (ts.TotalMilliseconds < 10) return; mDTLast = dt; //线速度。。。。 CurrFilmLength += FilmVelocity * ts.TotalMinutes; UpdateAngleV(dt, ts); mPositionAngleList.Add( new PositionAngleInfo() { dt = mDTLast, angle = CurrAngle, position = CurrFilmLength }); if (mPositionAngleList.Count > ListCap) mPositionAngleList.RemoveAt(0); } public bool CheckLimit1() { if (CurrAngle >= AngleRange / 2) return true; else return false; } public bool CheckLimit0() { if (CurrAngle <= (-AngleRange / 2)) return true; else return false; } public bool CheckRoll() { int rollcnt = (int)(CurrFilmLength*1000 / RollPerimeter); double p = CurrFilmLength*1000 - rollcnt * RollPerimeter; if (p <= RollSignLength) return true; else return false; } protected void NotifyPropertyChanged(string propertyname) { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname)); } } public event PropertyChangedEventHandler PropertyChanged; } public class FilmData { public double angle1; public double angle2; public int data1; public int data2; } }