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