using FLY.Thick.BlowingScan.Common;
using FLY.Thick.BlowingScan.IService;
using FLY.Thick.BlowingScan.Server;
using MathNet.Numerics.LinearAlgebra.Double;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using ThickTcpUiInWindow;
using FObjBase;
using System.Collections.ObjectModel;
using System.Diagnostics;
namespace FLY.Thick.BlowingScan.UI.Client
{
public class BlowingScanAnaylze : INotifyPropertyChanged
{
#region 缓存区
///
/// 从BlowingScanService 接收过来的数据;
/// 缓冲区第一个记录序号
///
public int FirstBM { get; set; } = -1;
///
/// 从BlowingScanService 接收过来的数据;
/// 缓冲区最后一个记录序号
///
public int LastBM { get; set; } = -1;
///
/// 从BlowingScanService 接收过来的数据;
/// 缓冲区
///
public List mBufList;
public ObservableCollection ScanInfoList { get; } = new ObservableCollection();
#endregion
#region 试调整参数
private int solvecnt = 4;
///
/// 多少次扫描解一次方程
///
public int SolveCnt
{
get { return solvecnt; }
set
{
if (value < 4)
value = 4;
if (solvecnt != value)
{
solvecnt = value;
}
}
}
private int smooth = 1;
///
/// 平滑
///
public int Smooth
{
get { return smooth; }
set
{
if (value < 0)
value = 0;
if (smooth != value)
{
smooth = value;
}
}
}
///
/// 旋转架转动角度
///
public double RAngle { get; set; } = 350;
///
/// 人字架到测厚仪膜的长度 单位m
///
public double FilmLength { get; set; } = 23;
#endregion
#region 常量参数
public int N2 { get; set; } = 400;
public int Sensor { get; set; } = 240;
public int OrgBoltNo { get; set; } = 1;
public int NBolts { get; set; } = 88;
#endregion
#region 状态
public bool IsStep2Ing { get; set; }
#endregion
#region 机架校正
public GageData mGagedata = new GageData();
#endregion
BlowingAngleDetect mAngleDetect = new BlowingAngleDetect();
public BlowingScanAnaylze()
{
MarkNo1.PropertyChanged += MarkNo_PropertyChanged;
MarkNo2.PropertyChanged += MarkNo_PropertyChanged;
}
public int GetMarkNoFromRList(int idx)
{
if (idx >= ScanInfoList.Count())
return LastBM;
else if (idx < 0)
return (LastBM - (ScanInfoList.Count() - 1));
int markno = idx+ (LastBM - (ScanInfoList.Count() - 1));
return markno;
}
int GetIdxFromRList(int markno)
{
int idx = markno - (LastBM - (ScanInfoList.Count() - 1));
if (idx >= ScanInfoList.Count())
return -1;
else if (idx < 0)
return -1;
return idx;
}
private void MarkNo_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MarkData markdata = sender as MarkData;
if (e.PropertyName == "Number")
{
int idx = GetIdxFromRList(markdata.Number);
if (idx >= 0)
{
markdata.IsValid = true;
if (!ScanInfoList[idx].HasCast)//解方程
ToThicks_SolveEquation(idx);
markdata.StartTime = ScanInfoList[idx].StartTime;
markdata.ThicksDT = ScanInfoList[idx].ThicksDT;
markdata.Thicks = ScanInfoList[idx].thicks;
markdata.Direction = ScanInfoList[idx].Direction;
markdata.Border = mBufList[idx].FilmRange;
markdata.PosOfGrid = mBufList[idx].PosOfGrid;
markdata.Time = mBufList[idx].Time;
markdata.OrgThicks = mBufList[idx].Thicks;
markdata.Power = ScanInfoList[idx].power;
markdata.AvgThicks = ScanInfoList[idx].thicks_avg;
}
else
{
markdata.IsValid = false;
}
UpdateCurrR();
UpdateIsFocus();
}
}
///
/// 能进入第2步解方程
///
///
public bool CanStep2()
{
if (mBufList == null)
return false;
else if (mBufList.Count() == 0)
return false;
else if (IsStep2Ing)
return false;
return true;
}
///
/// 更新 BufList
///
///
///
///
public void Step1(int firstbm, int lastbm, List datas)
{
FirstBM = firstbm;
LastBM = lastbm;
mBufList = datas;
if (mBufList == null)
return;
BufList2AngleInfo();
BufList2ScanInfoList();
BufList2GageData();
//当MarkNo1.Number 不在范围内, 限制它们
if (FirstBM < LastBM)
{
MarkNo1.Number = KeepInRange(MarkNo1.Number, FirstBM + SolveCnt - 1, LastBM);
MarkNo2.Number = KeepInRange(MarkNo2.Number, FirstBM + SolveCnt - 1, LastBM);
if (MarkNo1.Number == MarkNo2.Number)
{
MarkNo2.Number = MarkNo1.Number + SolveCnt;
}
}
else if (FirstBM > LastBM)
{
//转了一个圈,很异常,不处理
MarkNo1.Number = FirstBM + SolveCnt - 1;
MarkNo2.Number = MarkNo1.Number + SolveCnt;
}
else
{
//没有数据
}
}
int KeepInRange(int number, int begin, int end)
{
if (number < begin)
number = begin;
else if (number > end)
number = end;
return number;
}
///
/// 解方程
///
public void Step2()
{
IsStep2Ing = true;
if (mBufList == null)
{
IsStep2Ing = false;
return;
}
GetSolveEquation();
ClearAllResult();
MarkNo1.Refresh();
MarkNo2.Refresh();
//ToThicks_SolveEquation();
IsStep2Ing = false;
}
///
/// 清除全部计算结果
///
public void ClearAllResult()
{
for (int i = 0; i < mBufList.Count(); i++)
{
ScanInfoCell scaninfo = ScanInfoList[i];
scaninfo.HasCast = false;
}
}
///
/// 转为 旋转架信息
///
void BufList2AngleInfo()
{
mAngleDetect.mLimitList.Clear();
BlowingAngleDetect.LimitCell c_last = null;
for (int i=0; i < mBufList.Count(); i++)
{
FlyData_BlowingScan f = mBufList[i];
if (f.SignNo == -1)
continue;
if (c_last == null)
{
c_last = new BlowingAngleDetect.LimitCell();
c_last.no = f.SignNo;
c_last.dt_begin = f.Time;
c_last.dt_end = f.LeaveTime;
mAngleDetect.mLimitList.Add(c_last);
}
else if (c_last.no != f.SignNo)
{
c_last = new BlowingAngleDetect.LimitCell();
c_last.no = f.SignNo;
c_last.dt_begin = f.Time;
c_last.dt_end = f.LeaveTime;
mAngleDetect.mLimitList.Add(c_last);
}
else
{
c_last.no = f.SignNo;
c_last.dt_begin = f.Time;
c_last.dt_end = f.LeaveTime;
}
}
mAngleDetect.RotationCnt = mBufList.Last().RotateCnt;
mAngleDetect.LimitSignTime = mBufList.Last().LimitSignTime;
mAngleDetect.RenZiJiaPeriod = mBufList.Last().RotatePeriod;
//更新默认时间
if (mAngleDetect.mLimitList.Count() > 0)
{
if(mAngleDetect.mLimitList[0].dt_begin != DateTime.MinValue)
mAngleDetect.mDefaultTime = mAngleDetect.mLimitList[0].dt_begin - mAngleDetect.RenZiJiaPeriod;
else
mAngleDetect.mDefaultTime = mAngleDetect.mLimitList[0].dt_end - mAngleDetect.LimitSignTime - mAngleDetect.RenZiJiaPeriod;
}
}
///
/// 转为 位置与厚度信息
///
void BufList2ScanInfoList()
{
ScanInfoList.Clear();
for (int i = 0; i < mBufList.Count(); i++)
{
FlyData_BlowingScan f = mBufList[i];
int posOfGrid = f.PosOfGrid;
Range boltrange = new Range() { Begin = f.FilmRange.Begin + Sensor / 2 + N2, End = f.FilmRange.End - (Sensor / 2 + N2) };
Range border = f.FilmRange;
ScanInfoCell scaninfocell = new ScanInfoCell(NBolts, OrgBoltNo);
//对应的grid号
int bp_idx = boltrange.Begin / posOfGrid;
int ep_idx = boltrange.End / posOfGrid;
if (bp_idx < 0)
bp_idx = 0;
if (ep_idx > (f.Thicks.Length - 1))
ep_idx = f.Thicks.Length - 1;
for (int j = bp_idx; j <= ep_idx; j++)
{
int idx = j;
int pos = j * posOfGrid + posOfGrid / 2;//一个grid的中间脉冲,也就是大概而已
int thick = f.Thicks[idx];
pos -= border.Begin;//膜上面的位置
ScanPosCell scanpos = new ScanPosCell() { pos = pos, thick = thick, dt = f.ThicksDt[idx] };
//scaninfocell.StartTime 为整幅数据最早的时间
if (j == bp_idx)
{
scaninfocell.StartTime = scanpos.dt;
}
else
{
if (scaninfocell.StartTime > scanpos.dt)
scaninfocell.StartTime = scanpos.dt;
}
scaninfocell.frame.Add(scanpos);
}
ScanInfoList.Add(scaninfocell);
}
}
///
/// 计算平均数据
///
void BufList2GageData()
{
int[] gage_sum = null;
int[] gage_cnt = null;
Range border = new Range();
int posOfGrid = 10;
for (int i = 0; i < mBufList.Count(); i++)
{
FlyData_BlowingScan f = mBufList[i];
posOfGrid = f.PosOfGrid;
if (gage_sum == null)
{
gage_sum = f.Thicks.Clone() as int[];
gage_cnt = new int[f.Thicks.Count()];
border.Copy(f.FilmRange);
}
for (int j = 0; j < gage_sum.Count() && j < f.Thicks.Count(); j++)
{
if (Misc.MyBase.ISVALIDATA(f.Thicks[j]))
{
if (gage_cnt[j] > 0)
gage_sum[j] += f.Thicks[j];
else
gage_sum[j] = f.Thicks[j];
gage_cnt[j]++;
}
}
if (border.Begin < f.FilmRange.Begin)
border.Begin = f.FilmRange.Begin;
if (border.End > f.FilmRange.End)
border.End = f.FilmRange.End;
}
mGagedata.PosOfGrid = posOfGrid;
mGagedata.Border = border;
for (int i = 0; i < gage_sum.Count(); i++)
{
if (gage_cnt[i] > 0)
{
gage_sum[i] = gage_sum[i] / gage_cnt[i];
}
}
mGagedata.OrgThicks = gage_sum;
}
///
/// 第2步,修改 旋转角度,膜距离 执行, 耗时很短
///
void GetSolveEquation()
{
mAngleDetect.RAngle = RAngle;
//mAngleDetect.FilmLength = FilmLength;
//获取角度信息
for (int i = 0; i < mBufList.Count(); i++)
{
FlyData_BlowingScan f = mBufList[i];
ScanInfoCell scaninfocell = ScanInfoList[i];
scaninfocell.equationList.Clear();
scaninfocell.State = ScanInfoCell.STATE.OK;
for (int j = 0; j < scaninfocell.frame.Count(); j++)
{
ScanPosCell scanpos = scaninfocell.frame[j];
BlowingAngleDetect.FilmInfo filminfo;
int ret = mAngleDetect.GetFilmInfo(
out filminfo,
scanpos.dt, f.FilmRange.Width, scanpos.pos, FilmLength, f.Velocity);
//探头直径转为角度范围
double sensor_angle = 180.0 * Sensor / f.FilmRange.Width;
if (ret == 0)
{
scanpos.angle1 = filminfo.angle1;
scanpos.angle2 = filminfo.angle2;
scanpos.direction = filminfo.direction;
//TODO
scanpos.rotateAngle = 180 * scanpos.pos / f.FilmRange.Width;
}
else
{
scaninfocell.State = ScanInfoCell.STATE.Err_GetAngle;
break;
}
if (j == 0)
scaninfocell.Direction = scanpos.direction;
else
{
if (scaninfocell.Direction != scanpos.direction)
{
scaninfocell.Direction = DIRECTION.FIX;
scaninfocell.State = ScanInfoCell.STATE.Err_Solve;
break;
}
}
scaninfocell.AddEquation(j,sensor_angle);
}
}
}
///
/// 最后1步,解方程
///
void ToThicks_SolveEquation()
{
for (int i = 0; i < mBufList.Count(); i++)
{
ToThicks_SolveEquation(i);
}
MarkNo1.Refresh();
MarkNo2.Refresh();
}
bool ToThicks_SolveEquation(int scaninfo_idx)
{
ScanInfoCell scaninfo = ScanInfoList[scaninfo_idx];
scaninfo.HasCast = true;
if (scaninfo.State != ScanInfoCell.STATE.OK)
return false;
if (scaninfo.Direction == DIRECTION.FIX)//当前是Fix 不解方程
{
scaninfo.State = ScanInfoCell.STATE.Err_Solve;
return false;
}
//全部方向都必须一致,当然也不能是Fix
//for (int i = 0; i < (SolveCnt - 1); i++)
//{
// if (scaninfo.Direction != ScanInfoList[scaninfo_idx - 1 - i].Direction)
// {
// scaninfo.State = ScanInfoCell.STATE.ERR_Solve_Maybe;
// break;
// }
//}
//平均值图
{
int[] _sum = new int[scaninfo.nbolts];
int[] _cnt = new int[scaninfo.nbolts];
foreach (EquationCell e in scaninfo.equationList)
{
int avg = e.GetAvg();
foreach (KeyValuePair kv in e.boltPower)
{
_sum[kv.Key] += avg;
_cnt[kv.Key] += 1;
scaninfo.thicks_avg[kv.Key] = _sum[kv.Key] / _cnt[kv.Key];
}
}
}
List equationlist = new List();
scaninfo.solveMember.Clear();
int cnt = 0;
for (int i = scaninfo_idx; i >= 0; i--)
{
if (ScanInfoList[i].Direction == DIRECTION.FIX)
continue;
cnt++;
equationlist.AddRange(ScanInfoList[i].equationList);
//用到的扫描副
scaninfo.solveMember.Add(i);
if (cnt >= SolveCnt)
break;
}
//计算 每个分区的权
for (int i = 0; i < scaninfo.power.Count(); i++)
{
scaninfo.power[i] = 0;
}
foreach (EquationCell e in equationlist)
{
foreach (KeyValuePair kv in e.boltPower)
{
//if(e.mainBolt == kv.Key)
scaninfo.power[kv.Key] += kv.Value;
//else
// scaninfo.power[kv.Key+scaninfo.nbolts] += kv.Value;
}
}
if (cnt < SolveCnt)//方程不够!!!!
{
scaninfo.State = ScanInfoCell.STATE.Err_Solve;
return false;
}
if (equationlist.Count < 100)//方程数太少
{
scaninfo.State = ScanInfoCell.STATE.Err_Solve;
return false;
}
SolveEquation(equationlist, ScanInfoList[scaninfo_idx].thicks);
//2105 条方程,耗时 0.3s
//SolveEquation(equationlist, ScanInfoList[scaninfo_idx].thicks);
//不能有任何一个NULL_VALUE
for (int i = 0; i < scaninfo.thicks.Count(); i++)
if (!Misc.MyBase.ISVALIDATA(scaninfo.thicks[i]))
return false;
if (Smooth > 0)
{
int[] data = new int[scaninfo.thicks.Count()];
for (int i = 0; i < scaninfo.thicks.Count(); i++)
{
int sum = 0;
for (int j = 0; j < (Smooth * 2 + 1); j++)
{
int index = i + j - Smooth;
if (index < 0)
index += scaninfo.thicks.Count();
else if (index >= scaninfo.thicks.Count())
index -= scaninfo.thicks.Count();
sum += scaninfo.thicks[index];
}
data[i] = (int)(sum / (Smooth * 2 + 1));
}
Array.Copy(data, scaninfo.thicks, data.Count());
}
scaninfo.ThicksDT = ScanInfoList[scaninfo_idx - (SolveCnt - 1)].StartTime;
return true;
}
///
/// 解方程
///
/// N条方程
/// 输出的结果
void SolveEquation(List equationList, int[] frame_out)
{
int nrow = equationList.Count();
//A~AX = A~B
//转换为 DenseMatrix 与 DenseVector
DenseMatrix matrixA = new DenseMatrix(nrow, NBolts);
DenseMatrix matrixAT = new DenseMatrix(NBolts, nrow);
DenseVector vectorB = new DenseVector(nrow);
DenseVector vectorATB = new DenseVector(NBolts);
int rowindex = 0;
foreach (EquationCell ec in equationList)
{
double[] datas = ec.GetPower(NBolts);
matrixA.SetRow(rowindex, datas);
matrixAT.SetColumn(rowindex, datas);
vectorB[rowindex] = ec.thick;
rowindex++;
}
DenseMatrix ATA = (DenseMatrix)matrixA.TransposeThisAndMultiply(matrixA);
DenseVector ATB = (DenseVector)matrixA.TransposeThisAndMultiply(vectorB);
DenseVector X = (DenseVector)ATA.Solve(ATB);
int j;
for (int i = 0; i < NBolts; i++)
{
frame_out[i] = (int)X[i];
}
}
#region INotifyPropertyChanged 接口
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
#endregion
#region 两组数据
private double currR = -1;
public double CurrR
{
get { return currR; }
set {
if (currR != value)
{
currR = value;
NotifyPropertyChanged("CurrR");
}
}
}
public MarkData MarkNo1 = new MarkData();
public MarkData MarkNo2 = new MarkData();
void UpdateCurrR()
{
if (MarkNo1.IsValid && MarkNo2.IsValid)
{
CurrR = Misc.MyMath.Correl(MarkNo1.Thicks, MarkNo2.Thicks);
}
else
{
CurrR = -1;
}
}
void UpdateIsFocus()
{
int idx1 = GetIdxFromRList(MarkNo1.Number);
int idx2 = GetIdxFromRList(MarkNo2.Number);
for (int i = 0; i < ScanInfoList.Count(); i++)
{
ScanInfoList[i].IsFocus = false;
ScanInfoList[i].IsFocus2 = false;
}
if (idx1 > 0)
{
ScanInfoList[idx1].IsFocus = true;
foreach (int i in ScanInfoList[idx1].solveMember)
{
ScanInfoList[i].IsFocus = true;
}
}
if (idx2 > 0)
{
ScanInfoList[idx2].IsFocus2 = true;
foreach (int i in ScanInfoList[idx2].solveMember)
{
ScanInfoList[i].IsFocus2 = true;
}
}
}
#endregion
#region 调试
public bool Save(string path)
{
try
{
using (StreamWriter sw = new StreamWriter(path, false, Encoding.GetEncoding("GB2312")))
{
bool hasHeader = false;
foreach (FlyData_BlowingScan f in mBufList)
{
if (!hasHeader)
{
hasHeader = true;
sw.WriteLine(f.GetHeader());
}
sw.WriteLine(f.ToString());
}
sw.Flush();
sw.Close();
}
return true;
}
catch (Exception e)
{
return false;
}
}
public bool Load(string path)
{
if (mBufList != null)
mBufList.Clear();
else
mBufList = new List();
using (StreamReader sr = new StreamReader(path, Encoding.GetEncoding("GB2312")))
{
string header = sr.ReadLine();
while (!sr.EndOfStream)
{
string s = sr.ReadLine();
FlyData_BlowingScan f = new FlyData_BlowingScan();
if (f.TryParse(header, s))
{
mBufList.Add(f);
}
else
{
return false;
}
}
}
return true;
}
#endregion
}
public class GageData : INotifyPropertyChanged
{
private Range border = new Range();
///
/// 膜边界,单位脉冲
///
[PropertyChanged.DoNotCheckEquality]
public Range Border { get; set; }
///
/// pos / grid
///
public int PosOfGrid { get; set; } = 10;
///
/// 厚度数据, 大小 grid总数
///
[PropertyChanged.DoNotCheckEquality]
public int[] OrgThicks { get; set; }
#region INotifyPropertyChanged 接口
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
#endregion
}
public class MarkData : INotifyPropertyChanged
{
public int Number { get; set; } = -1;
///
/// 获取了焦点
///
public bool IsFocus { get; set; }
///
/// 有效
///
public bool IsValid { get; set; }
///
/// 扫描时间
///
public DateTime StartTime { get; set; } = DateTime.MinValue;
///
/// 旋转架旋转的方向,当同一次扫描中,有两个方向,Direction= Fix;
///
public Misc.DIRECTION Direction { get; set; } = DIRECTION.FORWARD;
///
/// 下面解方程得到的一幅数据对应的数据,与 StartTime 不一样
///
public DateTime ThicksDT { get; set; } = DateTime.MinValue;
///
/// 一幅数据
///
[PropertyChanged.DoNotCheckEquality]
public int[] Thicks { get; set; }
///
/// 膜边界,单位脉冲
///
[PropertyChanged.DoNotCheckEquality]
public Range Border { get; set; }
///
/// pos / grid
///
public int PosOfGrid { get; set; } = 10;
///
/// orgthick时间
///
public DateTime Time { get; set; } = DateTime.MinValue;
///
/// 厚度数据, 大小 grid总数
///
[PropertyChanged.DoNotCheckEquality]
public int[] OrgThicks { get; set; }
///
/// 权重图
///
[PropertyChanged.DoNotCheckEquality]
public int[] Power { get; set; }
///
/// 均值图
///
[PropertyChanged.DoNotCheckEquality]
public int[] AvgThicks { get; set; }
public MarkData()
{
}
public void Refresh()
{
NotifyPropertyChanged("Number");
}
#region INotifyPropertyChanged 接口
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
#endregion
}
public class BlowingAngleDetect : INotifyPropertyChanged
{
#region 自定义类
public class LimitCell
{
public LimitCell Clone()
{
LimitCell l = new LimitCell();
l.dt_begin = dt_begin;
l.dt_end = dt_end;
l.no = no;
return l;
}
public DateTime dt_begin = DateTime.MinValue;
public DateTime dt_end = DateTime.MinValue;
public int no;
public override string ToString()
{
return no.ToString() + " (" + dt_begin.ToString() + "-" + dt_end.ToString() + ")";
}
}
#endregion
#region 成员变量
#region 参数
///
/// 离开限位 到 撞下一个限位 的 旋转架转动总角度 单位°
///
public double RAngle { get; set; } = 350;
///
/// 离开限位 到 撞下一个限位 的 旋转架转动时间, 需要初始值,以后测量出来的
///
public TimeSpan RenZiJiaPeriod { get; set; }
///
/// 加速减速时间, 加速减速时间+正反转冷却时间 = 撞限位 到 离开限位 的时间
///
public TimeSpan AccDecTime { get; set; }
///
/// 撞限位 到 离开限位 的时间, 需要初始值,以后测量出来的
///
public TimeSpan LimitSignTime { get; set; }
///
/// 人字架到测厚仪膜长 单位m
///
public double FilmLength { get; set; }
#endregion
#region 状态
///
/// 正反转冷却时间, 默认1s, 一定要小于撞限位 到 离开限位 的时间
///
public TimeSpan SwapCoolTime { get; protected set; }
#endregion
public List mLimitList = new List();
///
/// 累计旋转次数
///
public int RotationCnt { get; set; }
int LimitIndexToRotationCnt(int index)
{
if (RotationCnt == 0)
return 0;
else
return RotationCnt - mLimitList.Count + index + 1;
}
#endregion
public DateTime mDefaultTime;
public BlowingAngleDetect()
{
RAngle = 337;//°
RenZiJiaPeriod = TimeSpan.FromMinutes(8);
AccDecTime = TimeSpan.FromSeconds(1);
LimitSignTime = TimeSpan.FromSeconds(4);
FilmLength = 25;//m
SwapCoolTime = LimitSignTime - AccDecTime - AccDecTime;
mDefaultTime = DateTime.Now;
}
public class FilmInfo
{
///
/// 上层 位于膜泡的角度 0~360°
///
public double angle1;
///
/// 下层 位于膜泡的角度 0~360°
///
public double angle2;
///
/// 膜泡旋转方向
///
public Misc.DIRECTION direction;
///
/// 线速度
///
public double filmVelocity;
///
/// 累计旋转次数
///
public int rotationCnt;
///
/// 在匀速阶段; 在限位信号 开始~结束 期间 inCV = false
///
public bool inCV;
}
public enum GetLimitTimeResult
{
///
/// 获取成功
///
OK,
///
/// 不存在该点
///
NOEXIST,
///
/// 限位已经撞了,但膜还没走完,等吧
///
ISFURTURE
}
///
/// 获取当前在膜上的测量点,对应于膜泡的角度信息, 当时间点,比LimitList.Last.dt+RenZiJiaPeriod还要后,更新RenZiJiaPeriod
/// 返回 -1, 当前的时间点在列表以前;
/// 返回 0, 当前的时间点在列表中;
/// 返回 1, 当前的时间点在列表的未来;
///
/// 输出资料
/// 测量的时间点
/// 被压扁后的膜宽度
/// 探头所在膜的横向位置
/// 线速度
///
public int GetFilmInfo(out FilmInfo filminfo, DateTime dt, double filmWidth, double filmPosH, double filmLen, double velocity)
{
filminfo = null;
//与0°的偏移
double a = filmPosH / filmWidth * 180;
if ((a < 0) || (a > 180))
{
return -1;//不在膜的范围内
}
DateTime ago_dt;
double angle0;
double angle1;
Misc.DIRECTION direction;
int rotationcnt;
bool inCV;
if (dt == DateTime.MinValue)
{
//TODO, BUG
return -1;
}
ago_dt = GetAgo(dt, filmLen, velocity);
bool r = GetAngle(a, ago_dt, out angle0, out angle1, out direction, out rotationcnt, out inCV);
if (r == false)//太久之前了
return -1;
filminfo = new FilmInfo()
{
angle1 = angle0,
angle2 = angle1,
filmVelocity = velocity,
direction = direction,
rotationCnt = rotationcnt,
inCV = inCV
};
return 0;
}
#region 人字架时间点<-->24m后膜时间点
///
/// 输入当前时间点,找到 刚出人字架时的时间点
///
/// 当前时间
/// 24米真实长度
/// 线速度
/// 24米以前的时间
DateTime GetAgo(DateTime dt, double filmlength, double velocity)
{
TimeSpan ts = TimeSpan.FromMinutes(filmlength / velocity);
return dt - ts;
}
#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;
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 变速阶段
///
///
///
/// dtStart的信号
/// dt 在 [dtStart , dtEnd]
/// 上一次两个限位的间距时间
/// 角度
/// 方向, 只有正向 与 反向
void LimitListCal_SpeedChange(DateTime dtEnd, DateTime dtStart, int noStart, DateTime dt, TimeSpan renZiJiaPeriod, out double angle, out Misc.DIRECTION direction)
{
//默认 dt 在 dtStart <= dt <= dtEnd, 出错不处理!!!
if ((dt < dtStart) || (dt > dtEnd))
{
throw new Exception("(dtdtEnd) dt=" + dt.ToString() + " dtStart=" + dtStart.ToString() + " dtEnd=" + dtEnd.ToString());
}
TimeSpan accDecTime = AccDecTime;
if ((dtEnd - dtStart) < (accDecTime + accDecTime))
accDecTime = TimeSpan.FromTicks((dtEnd - dtStart).Ticks / 2);
DateTime dtMid = dtStart + TimeSpan.FromTicks((dtEnd - dtStart).Ticks / 2);
if (noStart == 0)
{
if (dt < dtMid)
direction = Misc.DIRECTION.BACKWARD;
else
direction = Misc.DIRECTION.FORWARD;
}
else
{
if (dt < dtMid)
direction = Misc.DIRECTION.FORWARD;
else
direction = Misc.DIRECTION.BACKWARD;
}
double dAngle;
if (dt < (dtStart + accDecTime))//减速
{
TimeSpan ts = dt - dtStart;//已经过去时间
double vStart = RAngle / renZiJiaPeriod.TotalSeconds;//开始速度 °/s
double vEnd = 0;//结束速度 °/s
double a = (vEnd - vStart) / accDecTime.TotalSeconds;//加速度 °/s^2
double vCurr = vStart + a * ts.TotalSeconds;//当前速度 //开始速度 °/s
dAngle = (vStart + vCurr) / 2 * ts.TotalSeconds;//总角度 °
}
else if (dt < (dtEnd - accDecTime))//停止
{
double vStart = RAngle / renZiJiaPeriod.TotalSeconds;//开始速度 °/s
double vEnd = 0;//结束速度 °/s
double a = (vEnd - vStart) / accDecTime.TotalSeconds;//加速度 °/s^2
dAngle = (vStart + vEnd) / 2 * accDecTime.TotalSeconds;//总角度 °
}
else //加速
{
TimeSpan ts = dtEnd - dt;
double vStart = 0; //开始速度 °/s
double vEnd = RAngle / renZiJiaPeriod.TotalSeconds;//结束速度 °/s
double a = (vEnd - vStart) / accDecTime.TotalSeconds;//加速度 °/s^2
double vCurr = vEnd - a * ts.TotalSeconds;//当前速度 //开始速度 °/s
dAngle = (vEnd + vCurr) / 2 * ts.TotalSeconds;//总角度 °
}
if (noStart == 0)
angle = -RAngle / 2 - dAngle;
else
angle = RAngle / 2 + dAngle;
}
///
/// 时间点查找旋转角度 -180~180
///
/// 时间
/// 当dt 发生在限位列表的未来,是否修改参数,如RenZiJiaPeriod
///
///
///
///
bool LimitListSearch(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;
//TODO,rotationCnt 还没处理!!!!!
switch (ret)
{
case ListFindIndexResult.Future://发生在未来
{
if (dt < mDefaultTime)
mDefaultTime = dt - TimeSpan.FromSeconds(10);
//检测是否需要修正 RenZiJiaPeriod
if (mLimitList.Count == 0)
{
dt_start = mDefaultTime;
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 (mLimitList[0].no == 0)
no = 1;
else
no = 0;
dt_start = mDefaultTime;
dt_end = mLimitList[0].dt_begin;
if (dt < dt_start)
{
//数据发生在太遥远的过去,删除!!!!!
angle = 0;
direction = DIRECTION.FIX;
rotationCnt = -1;
inCV = false;
return false;
}
rotationCnt = LimitIndexToRotationCnt(-1);
inCV = true;
LimitListCal_ConstantVelocity(dt_end, dt_start, no, dt, out angle, out direction);
}
break;
case ListFindIndexResult.InCell://TODO
{
dt_start = mLimitList[index].dt_begin;
dt_end = mLimitList[index].dt_end;
no = mLimitList[index].no;
rotationCnt = LimitIndexToRotationCnt(index);
DateTime dtMid = dt_start + TimeSpan.FromTicks((dt_end - dt_start).Ticks / 2);
if (dt < dtMid)//以限位的中间时间为分界
{
rotationCnt--;
}
TimeSpan renZiJiaPeriod;
if (index >= 1)
{
renZiJiaPeriod = mLimitList[index].dt_begin - mLimitList[index - 1].dt_end;
}
else
{
renZiJiaPeriod = mLimitList[index].dt_begin - mDefaultTime;
}
inCV = false;
LimitListCal_SpeedChange(dt_end, dt_start, no, dt, renZiJiaPeriod, out angle, out direction);
}
break;
default:
//case ListFindIndexResult.MaybeInCell:
{
dt_start = mLimitList[index].dt_begin;
dt_end = dt_start + LimitSignTime;
if (dt > dt_end)
{
if (modifyparam) //基本不会发生!!!
{
LimitSignTime = dt - dt_start + TimeSpan.FromSeconds(5);
dt_end = dt_start + LimitSignTime;
}
else
{
dt_end = dt;
}
}
no = mLimitList[index].no;
rotationCnt = LimitIndexToRotationCnt(index);
DateTime dtMid = dt_start + TimeSpan.FromTicks((dt_end - dt_start).Ticks / 2);
if (dt < dtMid)//以限位的中间时间为分界
{
rotationCnt--;
}
TimeSpan renZiJiaPeriod;
if (index >= 1)
{
renZiJiaPeriod = mLimitList[index].dt_begin - mLimitList[index - 1].dt_end;
}
else
{
renZiJiaPeriod = mLimitList[index].dt_begin - mDefaultTime;
}
inCV = false;
LimitListCal_SpeedChange(dt_end, dt_start, no, dt, renZiJiaPeriod, out angle, out direction);
}
break;
}
return true;
}
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
}
///
///
///
///
///
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;
}
///
/// 获取膜角度, TODO
///
/// 与0°的偏移, 0~180
/// 时间点
/// 上面的膜对应的膜角度
/// 下面的膜对应的膜角度
/// 人字架正转?反转? 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;
double angle;
bool r = LimitListSearch(dt, false, out angle, out direction, out rotationCnt, out inCV);
if (r == false)
return false;
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;
angle0 = a;
angle1 = a_other;
return true;
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
#endregion
}
public class ScanPosCell
{
public DateTime dt = DateTime.MinValue;
public int pos;//位于膜的位置,不是机架
public int thick;//两层的厚度
//由pos转换!!
public double angle1;//上层 位于膜泡的角度 0~360°
public double angle2;//下层 位于膜泡的角度 0~360°
public Misc.DIRECTION direction;//只有一整副数据都是同一个方向,才能解方程
public double rotateAngle;//0~180° 可以认为是夹扁后膜的变形
public ScanPosCell Clone()
{
ScanPosCell scanpos = new ScanPosCell();
scanpos.dt = dt;
scanpos.pos = pos;
scanpos.thick = thick;
scanpos.angle1 = angle1;
scanpos.angle2 = angle2;
scanpos.direction = direction;
scanpos.rotateAngle = rotateAngle;
return scanpos;
}
}
public class EquationCell
{
public Dictionary boltPower = new Dictionary();//key=boltindex, value=power
public Dictionary heatRotatePower = new Dictionary();//key=boltindex, value=power
public int thick = 0;
public int mainBolt;//主分区号
public void Add(int boltindex1, int boltindex2, int thick)
{
if (boltPower.ContainsKey(boltindex1))
{
boltPower[boltindex1]++;
}
else
{
boltPower.Add(boltindex1, 1);
}
if (boltPower.ContainsKey(boltindex2))
{
boltPower[boltindex2]++;
}
else
{
boltPower.Add(boltindex2, 1);
}
this.thick += thick;
}
public override string ToString()
{
string str = "";
foreach (var b in boltPower)
{
str += "[" + b.Key.ToString() + "," + b.Value.ToString() + "] ";
}
return str;
}
public double[] GetPower(int nbolts)
{
double[] power = new double[nbolts];
for (int i = 0; i < power.Count(); i++)
power[i] = 0;
foreach (var kv in boltPower)
{
power[kv.Key] = kv.Value;
}
return power;
}
public int GetAvg()
{
int cnt = 0;
for (int i = 0; i < boltPower.Count(); i++)
{
cnt += boltPower.Values.ElementAt(i);
}
if (cnt > 0)
return thick / cnt;
else
return Misc.MyBase.NULL_VALUE;
}
}
public class ScanInfoCell : INotifyPropertyChanged
{
///
/// 已经解完方程
///
public bool HasCast { get; set; }
#region 数据
public enum STATE
{
///
/// 获取角度出错
///
Err_GetAngle,
///
/// 解方程出错
///
Err_Solve,
///
/// 解方程可能出错
///
ERR_Solve_Maybe,
///
/// 没问题
///
OK
}
public STATE State { get; set; } = STATE.OK;
///
/// 扫描时间
///
public DateTime StartTime { get; set; } = DateTime.MinValue;
///
/// 旋转架旋转的方向,当同一次扫描中,有两个方向,Direction= Fix;
///
public Misc.DIRECTION Direction { get; set; } = DIRECTION.FIX;
///
/// 当前在观察
///
public bool IsFocus { get; set; }
///
/// 当前在观察
///
public bool IsFocus2 { get; set; }
///
/// 原始数据(时间,位置,总厚度)
///
public List frame = new List();
///
/// 方程列表
///
public List equationList = new List();
///
/// 下面解方程得到的一幅数据对应的数据,与 StartTime 不一样
///
public DateTime ThicksDT { get; set; } = DateTime.MinValue;
///
/// 一幅数据
///
public int[] thicks;
///
/// 分区数
///
public int nbolts;
///
/// 复位区号
///
public int orgboltno;
#endregion
#region 分析观察
///
/// 解方程 用的scaninfo
///
public List solveMember = new List();
///
/// 每个分区出现次数, 上下分区各不相同
///
public int[] power;
///
/// 只用当前 scaninfo 的方程。 算出 厚度。
///
public int[] thicks_avg;
#endregion
public ScanInfoCell(int nbolts, int orgboltno)
{
this.nbolts = nbolts;
this.orgboltno = orgboltno;
thicks = new int[nbolts];
power = new int[nbolts];
thicks_avg = new int[nbolts];
for (int i = 0; i < nbolts; i++)
{
thicks[i] = Misc.MyBase.NULL_VALUE;
thicks_avg[i] = Misc.MyBase.NULL_VALUE;
}
}
int GetBoltIndexFromAngle(double a)
{
int boltno1st = 1;
int nbolts = thicks.Count();
int boltindex = (int)(a / 360 * nbolts);
boltindex += orgboltno - boltno1st;
if (boltindex >= nbolts)
boltindex -= nbolts;
else if (boltindex < 0)
boltindex += nbolts;
return boltindex;
}
///
/// 完成了一些方程,返回true
///
///
///
public bool AddEquation(int scanpos_idx,double sensor_angle)
{
ScanPosCell scanpos = frame[scanpos_idx];
EquationCell equation = new EquationCell();
equationList.Add(equation);
//TOD0
equation.Add(
GetBoltIndexFromAngle(scanpos.angle1),
GetBoltIndexFromAngle(scanpos.angle2),
scanpos.thick);
for (int i = 1; i < sensor_angle / 2; i++)
{
equation.Add(
GetBoltIndexFromAngle(scanpos.angle1 + i),
GetBoltIndexFromAngle(scanpos.angle2 + i),
scanpos.thick);
equation.Add(
GetBoltIndexFromAngle(scanpos.angle1 - i),
GetBoltIndexFromAngle(scanpos.angle2 - i),
scanpos.thick);
}
return true;
}
#region INotifyPropertyChanged 接口
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
#endregion
}
}