using CommunityToolkit.Mvvm.Input;
using FLY.Thick.Base.IService;
using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using MathNet.Numerics.LinearAlgebra.Double;
using Microsoft.Win32;
using Misc;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Unity;
namespace FLY.Thick.Base.UI
{
///
/// Page_ScanCorr.xaml 的交互逻辑
///
public partial class PgScanCorr : Page
{
PgScanCorrVm viewModel;
public PgScanCorr()
{
InitializeComponent();
if (System.ComponentModel.LicenseManager.UsageMode != System.ComponentModel.LicenseUsageMode.Runtime)
return;
this.Loaded += Page_Loaded;
this.Unloaded += Page_Unloaded;
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
viewModel.DisposeBinding();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
viewModel.SetBinding();
}
[InjectionMethod]
public void Init(IUnityContainer container,
IScanCorrService scanCorrService,
IInitParamService initParamService,
ITDGageService gageService
)
{
viewModel = new PgScanCorrVm();
viewModel.Init(scanCorrService, initParamService, gageService);
this.DataContext = viewModel;
container.BuildUp(mircoGage);
}
private void UIElement_OnMouseMove(object sender, MouseEventArgs e)
{
if (!isDown)
return;
var vm = viewModel;
var chart = (LiveCharts.Wpf.CartesianChart)sender;
//lets get where the mouse is at our chart
var mouseCoordinate = e.GetPosition(chart);
//now that we know where the mouse is, lets use
//ConverToChartValues extension
//it takes a point in pixes and scales it to our chart current scale/values
var p = chart.ConvertToChartValues(mouseCoordinate);
//var series = vm.Series[0];
//var closetsPoint = series.ClosestPointTo(p.X, AxisOrientation.X);
//if (closetsPoint == null)
// return;
//pEnd = closetsPoint;
pEnd = p;
vm.Select(PgScanCorrVm.SelectType.Move, pBegin, pEnd);
}
private System.Windows.Point pBegin;
private System.Windows.Point pEnd;
private bool isDown = false;
private void UIElement_MouseDown(object sender, MouseButtonEventArgs e)
{
isDown = true;
var vm = viewModel;
var chart = (LiveCharts.Wpf.CartesianChart)sender;
var mouseCoordinate = e.GetPosition(chart);
var p = chart.ConvertToChartValues(mouseCoordinate);
pBegin = p;
//var series = chart.Series[0];
//ChartPoint closetsPoint = series.ClosestPointTo(p.X, AxisOrientation.X);
//if (closetsPoint == null)
//{
// isDown = false;
// return;
//}
//cpBegin = closetsPoint;
vm.Select(PgScanCorrVm.SelectType.Click, pBegin, pBegin);
}
private void UIElement_MouseUp(object sender, MouseButtonEventArgs e)
{
isDown = false;
var vm = viewModel;
var series = vm.Series[0];
var chart = (LiveCharts.Wpf.CartesianChart)sender;
//lets get where the mouse is at our chart
var mouseCoordinate = e.GetPosition(chart);
//now that we know where the mouse is, lets use
//ConverToChartValues extension
//it takes a point in pixes and scales it to our chart current scale/values
var p = chart.ConvertToChartValues(mouseCoordinate);
//for X in this case we will only highlight the closest point.
//lets use the already defined ClosestPointTo extension
//it will return the closest ChartPoint to a value according to an axis.
//here we get the closest point to p.X according to the X axis
//var series = chart.Series[0];
//var closetsPoint = series.ClosestPointTo(p.X, AxisOrientation.X);
//if (closetsPoint == null)
// return;
pEnd = p;
vm.Select(PgScanCorrVm.SelectType.Cancel, pBegin, pEnd);
}
}
public class PgScanCorrVm : INotifyPropertyChanged
{
#region 轴调节
///
/// Y轴拖拉条 最大值
///
public double YRangeSliderMax { get; set; } = 65535;
///
/// Y轴拖拉条 最小值
///
public double YRangeSliderMin { get; set; } = 0;
#endregion
#region 曲线
public Func XFormatter { get; private set; }
public Func YFormatter { get; private set; }
public double XMin => 0;
public double XMax => PosLength / PosOfGrid;
private double ymax = 65535;
///
/// Y轴 最大值
///
public double YMax
{
get { return ymax; }
set
{
if (value <= YMin)
{
return;
}
if (value != ymax)
ymax = value;
}
}
private double ymin = 0;
///
/// Y轴 最小值
///
public double YMin
{
get { return ymin; }
set
{
if (value >= YMax)
{
return;
}
if (value != ymin)
ymin = value;
}
}
public SeriesCollection Series { get; } = new SeriesCollection();
public class SeriesInfo : INotifyPropertyChanged
{
public string Name { get; set; }
public Brush Color { get; set; }
public ChartValues Datas = new ChartValues();
public event PropertyChangedEventHandler PropertyChanged;
}
public SeriesInfo[] SeriesInfos { get; set; }
ChartValues KeyPointDatas = new ChartValues();
CartesianMapper Mapper { get; set; }
#endregion
///
/// 机架总长,脉冲
///
public int PosLength { get; set; } = 8000;
///
/// 1个grid = N个pos
///
public int PosOfGrid { get; set; } = 10;
///
/// 比例
///
public double Mmpp { get; set; } = 0.1;
#region
///
/// 使能修正
///
public bool Enable { set; get; }
///
/// 当前修正的组
///
public int CurrGroupIndex { get; set; }
///
/// 当前进度 0 - 100
///
public int Progress { get; set; }
///
/// 数据更新时间
///
public DateTime[] UpdateTimes { get; set; }
///
/// 正在机架修正中
///
public bool IsRunning { get; set; }
///
/// 来回次数
///
public int ScanCnt { get; set; }
#endregion
///
/// 当前被选择的组序号
///
public int SelectedGroupIndex { get; set; }
#region 平滑方式生成修正曲线
///
/// 平均滤波平滑数,单位grid
///
public int SmoothFactor { get; set; } = 50;
#endregion
#region 关键点方式生成修正曲线
public KeyPointsSelectMode KpSelectMode { get; set; } = KeyPointsSelectMode.Null;
#endregion
///
/// 数据好了!!!!
/// 当 flyad7 的poslen, posOfGrid 发生变化时,DataOK = false, 需要重新记录。
///
public bool IsDataOK { get; set; } = false;
public double AvgAd { get; set; }
#region CMD
public RelayCommand SelectedGroup0Cmd { get; }
public RelayCommand SelectedGroup1Cmd { get; }
public RelayCommand StopCmd { get; }
public RelayCommand StartCmd { get; }
public RelayCommand ClearCmd { get; }
public RelayCommand CreateCorrBySmoothCmd { get; }
public RelayCommand CreateCorrByKeyPointCmd { get; }
///
/// 添加关键点模式
///
public RelayCommand AddKeyPointModeCmd { get; }
public RelayCommand RemoveKeyPointModeCmd { get; }
public RelayCommand MoveKeyPointModeCmd { get; }
public RelayCommand CancelKeyPointModeCmd { get; }
public RelayCommand SetCorrDataCmd { get; }
public RelayCommand SaveXlsxCmd { get; }
public RelayCommand LoadXlsxCmd { get; }
#endregion
IScanCorrService scanCorrService;
IInitParamService initParamService;
ITDGageService gageService;
KeyPointLine keyPointLine;
Dictionary> bindingConexts = new Dictionary>();
///
/// 数据已经绑定了
///
bool isBinding;
public PgScanCorrVm()
{
#region 与数据无关界面参数
XFormatter = (x) =>
{
//x 是 grid
int pos = (int)(x * PosOfGrid);
int mm = (int)(pos * Mmpp);
return $"{pos}\n{mm}mm";
};
YFormatter = (y) =>
{
return $"{y:F0}";
};
Mapper = Mappers.Xy()
.X((value, index) => value.X)
.Y(value => value.Y);
string seriesName0 = (string)Application.Current.TryFindResource("str.PgScanCorr.ForwOrigData");
string seriesName1 = (string)Application.Current.TryFindResource("str.PgScanCorr.BackwOrigData");
string seriesName2 = (string)Application.Current.TryFindResource("str.PgScanCorr.ForwSmoothData");
string seriesName3 = (string)Application.Current.TryFindResource("str.PgScanCorr.BackwSmoothData");
string seriesName4 = (string)Application.Current.TryFindResource("str.PgScanCorr.KeyPoint");
SeriesInfos = new SeriesInfo[] {
new SeriesInfo(){Name = seriesName0, Color = FLY.ControlLibrary.Themes.Styles.GetForeground(0)},
new SeriesInfo(){Name = seriesName1, Color = FLY.ControlLibrary.Themes.Styles.GetForeground(1)},
new SeriesInfo(){Name = seriesName2, Color = FLY.ControlLibrary.Themes.Styles.GetForeground(0,true)},
new SeriesInfo(){Name = seriesName3, Color = FLY.ControlLibrary.Themes.Styles.GetForeground(1,true)},
};
for (int i = 0; i < SeriesInfos.Count(); i++)
{
var seriesInfo = SeriesInfos[i];
var lineSeries = new LineSeries
{
Values = seriesInfo.Datas,
StrokeThickness = 2,
Title = seriesInfo.Name,
Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
Stroke = seriesInfo.Color,
PointGeometry = null
};
Series.Add(lineSeries);
}
keyPointLine = new KeyPointLine();
Misc.BindingOperations.SetBinding(this, nameof(SmoothFactor), keyPointLine, nameof(keyPointLine.SmoothFactor));
{
var lineSeries = new LineSeries
{
Title = seriesName4,
Values = KeyPointDatas,
StrokeThickness = 6,
Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
Stroke = FLY.ControlLibrary.Themes.Styles.GetForeground(2),
Configuration = Mapper,
//PointGeometry = Geometry.
PointGeometrySize = 15,
//PointForeground = "#222E31"
};
Series.Add(lineSeries);
}
keyPointLine.KeyPoints.CollectionChanged += (s, e) =>
{
UpdateKeyPointDatas();
};
#endregion
StopCmd = new RelayCommand(Stop);
StartCmd = new RelayCommand(Start);
ClearCmd = new RelayCommand(Clear);
CreateCorrBySmoothCmd = new RelayCommand(CreateCorrBySmooth);
CreateCorrByKeyPointCmd = new RelayCommand(CreateCorrByKeyPoint);
AddKeyPointModeCmd = new RelayCommand(AddKeyPointMode);
RemoveKeyPointModeCmd = new RelayCommand(RemoveKeyPointMode);
MoveKeyPointModeCmd = new RelayCommand(MoveKeyPointMode);
CancelKeyPointModeCmd = new RelayCommand(CancelKeyPointMode);
SetCorrDataCmd = new RelayCommand(SetCorrData);
SelectedGroup0Cmd = new RelayCommand(SelectedGroup0);
SelectedGroup1Cmd = new RelayCommand(SelectedGroup1);
SaveXlsxCmd = new RelayCommand(SaveXlsx);
LoadXlsxCmd = new RelayCommand(LoadXlsx);
}
public void Init(
IScanCorrService scanCorrService,
IInitParamService initParamService,
ITDGageService gageService
)
{
this.scanCorrService = scanCorrService;
this.initParamService = initParamService;
this.gageService = gageService;
//参数绑定
SetBinding();
}
///
/// 参数绑定
///
public void SetBinding()
{
if (isBinding)//已经绑定了
return;
isBinding = true;
//下面全部event保存在bindingConexts
Misc.BindingOperations.StartMarkdownEvents(bindingConexts);
Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.Enable), this, nameof(Enable), BindingOperations.BindingMode.TwoWay);
Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.ScanCnt), this, nameof(ScanCnt));
Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.CurrGroupIndex), this, nameof(CurrGroupIndex));
Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.Progress), this, nameof(Progress));
Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.IsRunning), this, nameof(IsRunning));
Misc.BindingOperations.SetBinding(this.scanCorrService, nameof(scanCorrService.SmoothFactor), this, nameof(SmoothFactor));
Misc.BindingOperations.SetBinding(this.initParamService, nameof(initParamService.Encoder1_mmpp), this, nameof(Mmpp));
Misc.BindingOperations.SetBinding(this.initParamService, nameof(initParamService.PosLength), this, nameof(PosLength));
Misc.BindingOperations.SetBinding(this.initParamService, nameof(initParamService.PosOfGrid), this, nameof(PosOfGrid));
//备份event完毕, 必须关闭记录
Misc.BindingOperations.StopMarkdownEvents();
UpdateTimes = new DateTime[2];
downloadAll();
this.PropertyChanged += PgScanCorrVm_PropertyChanged;
this.scanCorrService.PropertyChanged += ScanCorrService_PropertyChanged;
}
///
/// 解除绑定
///
public void DisposeBinding()
{
if (!isBinding)//已经解除绑定了
return;
isBinding = false;
Misc.BindingOperations.DisposeEventTargetObject(bindingConexts);
this.PropertyChanged -= PgScanCorrVm_PropertyChanged;
this.scanCorrService.PropertyChanged -= ScanCorrService_PropertyChanged;
}
private void ScanCorrService_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(FObjBase.FObjServiceClient.IsConnected))
{
var client = scanCorrService as FObjBase.FObjServiceClient;
if (client.IsConnected)
{
downloadAll();
}
}
else if (e.PropertyName == nameof(scanCorrService.UpdateTimes))
{
if (UpdateTimes[SelectedGroupIndex] != scanCorrService.UpdateTimes[SelectedGroupIndex])
{
//当前显示的,发生变化了,下载
downloadAll();
}
}
}
private void Clear()
{
scanCorrService.Clear(SelectedGroupIndex);
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.ExecutedSuccessfully");
FLY.ControlLibrary.Window_Tip.Show(tit,null,TimeSpan.FromSeconds(2));
}
private void Start()
{
scanCorrService.Start(SelectedGroupIndex, ScanCnt, SmoothFactor);
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.StartOfExecution");
FLY.ControlLibrary.Window_Tip.Show(tit, null, TimeSpan.FromSeconds(2));
}
private void Stop()
{
gageService.StartP2(Base.Common.STARTP2_MODE.STOP);
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.StopOfExecution");
FLY.ControlLibrary.Window_Tip.Show(tit, null, TimeSpan.FromSeconds(2));
}
private void SelectedGroup1()
{
SelectedGroupIndex = 1;
}
private void SelectedGroup0()
{
SelectedGroupIndex = 0;
}
private void SetCorrData()
{
//TODO
//判断是否有数据
for (int i = 2; i <= 3; i++)
{
}
int[][] corrDatas = new int[2][];
for (int i = 0; i < 2; i++)
corrDatas[i] = SeriesInfos[2 + i].Datas.Select(d => double.IsNaN(d) ? Misc.MyBase.NULL_VALUE : (int)d).ToArray();
int[][] orgDatas = new int[2][];
for (int i = 0; i < 2; i++)
orgDatas[i] = SeriesInfos[i].Datas.Select(d => double.IsNaN(d) ? Misc.MyBase.NULL_VALUE : (int)d).ToArray();
//发送出去
scanCorrService.SetCorrData(SelectedGroupIndex, corrDatas, (int)AvgAd, orgDatas);
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.ApplySuccessfully");
FLY.ControlLibrary.Window_Tip.Show(tit, null, TimeSpan.FromSeconds(2));
}
private void MoveKeyPointMode()
{
KpSelectMode = KeyPointsSelectMode.Move;
}
private void RemoveKeyPointMode()
{
KpSelectMode = KeyPointsSelectMode.Remove;
}
private void AddKeyPointMode()
{
KpSelectMode = KeyPointsSelectMode.Add;
}
private void CancelKeyPointMode()
{
KpSelectMode = KeyPointsSelectMode.Null;
}
private void CreateCorrByKeyPoint()
{
CancelKeyPointMode();
if (keyPointLine.KeyPoints.Count() < 3)
{
//少于3个点
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.MakeFailed");
string msg = (string)Application.Current.TryFindResource("str.PgScanCorr.KeyPointMustMoreThan3");
FLY.ControlLibrary.Window_WarningTip.Show(tit, msg, TimeSpan.FromSeconds(2));
return;
}
for (int i = 0; i < 2; i++)
{
var filterDatas = keyPointLine.CalFilterDatas(SeriesInfos[i].Datas);
SeriesInfos[i + 2].Datas.Clear();
SeriesInfos[i + 2].Datas.AddRange(filterDatas);
}
//计算平均值
double avg0 = SeriesInfos[2].Datas.AverageNoNull();
double avg1 = SeriesInfos[3].Datas.AverageNoNull();
AvgAd = (int)((avg0 + avg1) / 2);
{
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.MakeSuccessfully");
FLY.ControlLibrary.Window_Tip.Show(tit,null,TimeSpan.FromSeconds(2));
}
}
void Smooth(ChartValues orgDatas, ChartValues filterDatas)
{
var filters = new double[orgDatas.Count()];
for (int i = 0; i < orgDatas.Count(); i++)
{
double sum = 0;
int cnt = 0;
for (int j = 0; j < SmoothFactor; j++)
{
int index = i - SmoothFactor / 2 + j;
if (index < 0)
continue;
else if (index >= orgDatas.Count())
break;
if (!double.IsNaN(orgDatas[index]))
{
sum += orgDatas[index];
cnt++;
}
}
if (cnt > 0)
filters[i] = sum / cnt;
else
filters[i] = double.NaN;
}
filterDatas.Clear();
filterDatas.AddRange(filters);
}
private void CreateCorrBySmooth()
{
for (int i = 0; i < 2; i++)
Smooth(SeriesInfos[i].Datas, SeriesInfos[2 + i].Datas);
//计算平均值
double avg0 = SeriesInfos[2].Datas.AverageNoNull();
double avg1 = SeriesInfos[3].Datas.AverageNoNull();
AvgAd = (int)((avg0 + avg1) / 2);
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.MakeSuccessfully");
FLY.ControlLibrary.Window_Tip.Show(tit, null, TimeSpan.FromSeconds(2));
}
void downloadAll()
{
var client = scanCorrService as FObjBase.FObjServiceClient;
if (!client.IsConnected)
return;
//下载数据
scanCorrService.GetScanCorrGroup(SelectedGroupIndex, scanCorrService_GetScanCorrGroup, null);
keyPointLine.Clear();
}
void scanCorrService_GetScanCorrGroup(object asyncContext, object retData)
{
var reponse = retData as GetScanCorrGroupResponse;
int groupIndex = reponse.GroupIndex;
UpdateTimes[groupIndex] = reponse.UpdateTime;
AvgAd = Misc.MyBase.ISVALIDATA(reponse.Avg) ? reponse.Avg : double.NaN;
IsDataOK = reponse.IsDataOK;
SeriesInfos[0].Datas.Clear();
SeriesInfos[1].Datas.Clear();
SeriesInfos[2].Datas.Clear();
SeriesInfos[3].Datas.Clear();
if (reponse.OrgDatas == null || reponse.OrgDatas.Count() != 2)
return;
if (reponse.OrgDatas[0] == null)
return;
SeriesInfos[0].Datas.AddRange(reponse.OrgDatas[0].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
var list = SeriesInfos[0].Datas.Where((d) =>
{
if (double.IsNaN(d))
return false;
else
return true;
});
//设置Y轴范围
int max = (int)list.Max();
int min = (int)list.Min();
//扩大3倍显示
int range = max - min;
if (range == 0)
{
//异常
}
else
{
ymax = max + range / 4;
ymin = min - range / 4;
YRangeSliderMin = min - range;
YRangeSliderMax = max + range;
}
NotifyPropertyChanged(nameof(YMax));
NotifyPropertyChanged(nameof(YMin));
if (reponse.OrgDatas[1] == null)
return;
SeriesInfos[1].Datas.AddRange(reponse.OrgDatas[1].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
if (reponse.CorrDatas == null || reponse.CorrDatas.Count() != 2 || reponse.CorrDatas[0] == null || reponse.CorrDatas[1] == null)
return;
SeriesInfos[2].Datas.AddRange(reponse.CorrDatas[0].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
SeriesInfos[3].Datas.AddRange(reponse.CorrDatas[1].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
}
private void PgScanCorrVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SelectedGroupIndex))
{
downloadAll();
}
}
public enum SelectType
{
Click,
Move,
Cancel
}
public void Select(SelectType selectType, System.Windows.Point cpBegin, System.Windows.Point cpEnd)
{
if (KpSelectMode == KeyPointsSelectMode.Null)
return;
switch (KpSelectMode)
{
case KeyPointsSelectMode.Add:
{
if (selectType != SelectType.Click)
return;
keyPointLine.Add(cpBegin.X, SeriesInfos[0].Datas);
return;
}
break;
case KeyPointsSelectMode.Remove:
{
if (selectType != SelectType.Click)
return;
keyPointLine.Remove(cpBegin.X);
return;
}
break;
}
}
void UpdateKeyPointDatas()
{
KeyPointDatas.Clear();
KeyPointDatas.AddRange(keyPointLine.KeyPoints);
}
private async void LoadXlsx()
{
//加载数据
string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
OpenFileDialog sfd = new OpenFileDialog();
sfd.Filter = "xlsx files (*.xlsx)|*.xlsx";
sfd.InitialDirectory = strDesktopPath;
if (sfd.ShowDialog() == true)
{
string filename = sfd.FileName;
Exception error = null;
var ret = await Task.Factory.StartNew(() =>
{
try
{
LoadXlsx(filename);
}
catch (Exception e)
{
error = e;
return false;
}
return true;
});
if (ret)
{
//画图
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.LoadSuccessfully");
FLY.ControlLibrary.Window_Tip.Show(tit, filename, TimeSpan.FromSeconds(2));
}
else
{
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.LoadFailed");
MessageBox.Show($"{error}", tit, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private void LoadXlsx(string filepath)
{
//检测标题
DataTable dataTable = new DataTable("table_scancorr");
dataTable.Columns.Add(new DataColumn() { ColumnName = "序号", DataType = typeof(double), Caption = "0" });
dataTable.Columns.Add(new DataColumn() { ColumnName = "脉冲", DataType = typeof(double), Caption = "0" });
dataTable.Columns.Add(new DataColumn() { ColumnName = "位置mm", DataType = typeof(double), Caption = "0" });
for (int j = 0; j < SeriesInfos.Count(); j++)
{
dataTable.Columns.Add(new DataColumn()
{
ColumnName = SeriesInfos[j].Name,
DataType = typeof(double),
Caption = "0"
});
}
using (ExcelPackage p = new ExcelPackage(new FileInfo(filepath)))
{
ExcelWorksheet sheet = p.Workbook.Worksheets["机架修正"];
FromSheet(sheet, dataTable);
}
App.Current.Dispatcher.Invoke(() =>
{
for (int j = 0; j < 4; j++)
{
List datas = new List();
for (int i = 0; i < dataTable.Rows.Count; i++)
{
var dataRow = dataTable.Rows[i];
double ad = System.Convert.ToDouble(dataRow[SeriesInfos[j].Name]);
datas.Add(ad);
}
SeriesInfos[j].Datas.Clear();
SeriesInfos[j].Datas.AddRange(datas);
datas.AverageNoNull();
}
var list = SeriesInfos[0].Datas.Where((d) =>
{
if (double.IsNaN(d))
return false;
else
return true;
});
//设置Y轴范围
int max = (int)list.Max();
int min = (int)list.Min();
//扩大3倍显示
int range = max - min;
if (range == 0)
{
//异常
}
else
{
ymax = max + range / 4;
ymin = min - range / 4;
YRangeSliderMin = min - range;
YRangeSliderMax = max + range;
}
NotifyPropertyChanged(nameof(YMax));
NotifyPropertyChanged(nameof(YMin));
double avg0 = SeriesInfos[2].Datas.AverageNoNull();
double avg1 = SeriesInfos[3].Datas.AverageNoNull();
if (!double.IsNaN(avg0) && !double.IsNaN(avg1))
{
//异常
AvgAd = (avg0 + avg1) / 2;
}
});
}
void FromSheet(ExcelWorksheet sheet, DataTable dataTable)
{
int from_row = 1;
int row = from_row;
for (int i = 0; i < dataTable.Columns.Count; i++)
{
int col = i + 1;
if ((string)(sheet.Cells[row, col].Value) != dataTable.Columns[i].ColumnName)
{
throw new Exception($"格式出错, 第{i}列 不是{dataTable.Columns[i].ColumnName}");
}
}
row++;
while (true)
{
var newRow = dataTable.NewRow();
//当一行都是空的,认为没有数据
int nullCnt = 0;
for (int j = 0; j < dataTable.Columns.Count; j++)
{
int col = j + 1;
object value = sheet.Cells[row, col].Value;
if (value is string || value == null)
{
if (string.IsNullOrEmpty((string)(value)))
{
//空的
nullCnt++;
//continue;
return;
}
}
try
{
newRow[j] = sheet.Cells[row, col].Value;
}
catch (Exception e)
{
throw e;
}
}
if (nullCnt == dataTable.Columns.Count)
{
//没有数据了
break;
}
dataTable.Rows.Add(newRow);
row++;
}
}
private void SaveXlsx()
{
//下载数据
string strDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string filename = $"机架修正_组{SelectedGroupIndex}_{DateTime.Now:yyyyMMddHHmmss}.xlsx";
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "xlsx files (*.xlsx)|*.xlsx";
sfd.InitialDirectory = strDesktopPath;
sfd.FileName = filename;
if (sfd.ShowDialog() == true)
{
filename = sfd.FileName;
Task.Factory.StartNew(() =>
{
SaveToXlsx(filename);
Application.Current.Dispatcher.Invoke(() =>
{
string tit = (string)Application.Current.TryFindResource("str.PgScanCorr.SaveSuccessfully");
FLY.ControlLibrary.Window_Tip.Show(tit, filename, TimeSpan.FromSeconds(2));
});
});
}
}
private void SaveToXlsx(string filepath)
{
if (File.Exists(filepath))
{
File.Delete(filepath);
}
using (ExcelPackage p = new ExcelPackage(new FileInfo(filepath)))
{
ExcelWorksheet sheet = p.Workbook.Worksheets.Add("机架修正");
DataTable dataTable = new DataTable("table_scancorr");
dataTable.Columns.Add(new DataColumn() { ColumnName = "序号", DataType = typeof(double), Caption = "0" });
dataTable.Columns.Add(new DataColumn() { ColumnName = "脉冲", DataType = typeof(double), Caption = "0" });
dataTable.Columns.Add(new DataColumn() { ColumnName = "位置mm", DataType = typeof(double), Caption = "0" });
for (int j = 0; j < SeriesInfos.Count(); j++)
{
dataTable.Columns.Add(new DataColumn()
{
ColumnName = SeriesInfos[j].Name,
DataType = typeof(double),
Caption = "0"
});
}
int count = SeriesInfos.Max(cv => cv.Datas.Count());
for (int i = 0; i < count; i++)
{
var dataRow = dataTable.NewRow();
dataRow["序号"] = i;
dataRow["脉冲"] = i * PosOfGrid;
dataRow["位置mm"] = (int)(i * PosOfGrid * Mmpp);
for (int j = 0; j < SeriesInfos.Count(); j++)
{
if (i < SeriesInfos[j].Datas.Count())
{
//if(!double.IsNaN(SeriesInfos[j].Datas[i]))
dataRow[SeriesInfos[j].Name] = SeriesInfos[j].Datas[i];
}
}
dataTable.Rows.Add(dataRow);
}
ToSheet(sheet, dataTable);
p.Save();
}
void ToSheet(ExcelWorksheet sheet, DataTable dataTable)
{
int from_row = 1;
int row = from_row;
//添加标题
for (int i = 0; i < dataTable.Columns.Count; i++)
{
int col = i + 1;
sheet.Cells[row, col].Value = dataTable.Columns[i].ColumnName;
//格式
sheet.Column(col).Style.Numberformat.Format = dataTable.Columns[i].Caption;
}
row++;
for (int i = 0; i < dataTable.Rows.Count; i++)
{
for (int j = 0; j < dataTable.Columns.Count; j++)
{
int col = j + 1;
sheet.Cells[row, col].Value = dataTable.Rows[i][j];
}
row++;
}
int colcnt = dataTable.Columns.Count;
int rowcnt = dataTable.Rows.Count;
var range = sheet.Cells[from_row, 1, from_row + rowcnt, colcnt];
var tbl = sheet.Tables.Add(range, dataTable.TableName);
sheet.Cells[sheet.Dimension.Address].AutoFitColumns();
}
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
///
/// 生成关键点曲线
///
public class KeyPointLine : INotifyPropertyChanged
{
public ObservableCollection KeyPoints { get; } = new ObservableCollection();
public int SmoothFactor { get; set; } = 20;
public event PropertyChangedEventHandler PropertyChanged;
public void Add(double x, IEnumerable orgDatas)
{
if (x < 0 || x >= orgDatas.Count())
return;//超出范围
int index = FindIndex(x);
if (index != -1)
return;
double y = CalY(x, orgDatas);
//没有,可以添加
Insert(x, y);
}
public void Remove(double x)
{
int index = FindIndex(x);
if (index == -1)
return;
KeyPoints.RemoveAt(index);
}
public void Clear()
{
KeyPoints.Clear();
}
///
/// 计算 位置为x 在 orgDatas中的y
///
///
///
///
double CalY(double x, IEnumerable orgDatas)
{
double sum = 0;
int cnt = 0;
for (int j = 0; j < SmoothFactor; j++)
{
int index = (int)(x - SmoothFactor / 2 + j);
if (index < 0)
continue;
else if (index >= orgDatas.Count())
break;
var value = orgDatas.ElementAt(index);
if (!double.IsNaN(value))
{
sum += value;
cnt++;
}
}
if (cnt > 0)
return sum / cnt;
else
return double.NaN;
}
int FindIndex(double x)
{
for (int i = 0; i < KeyPoints.Count(); i++)
{
if (Math.Abs(KeyPoints[i].X - x) < SmoothFactor / 2)
{
return i;
}
}
return -1;
}
int Insert(double x, double y)
{
for (int i = 0; i < KeyPoints.Count(); i++)
{
if (x < KeyPoints[i].X)
{
//就在它前面插入
KeyPoints.Insert(i, new XY() { X = x, Y = y });
return i;
}
}
//在最后添加
KeyPoints.Add(new XY() { X = x, Y = y });
return KeyPoints.Count() - 1;
}
List GetKeyValues(IEnumerable orgDatas)
{
List keyValues = new List();
for (int i = 0; i < KeyPoints.Count(); i++)
{
int x = (int)KeyPoints[i].X;
double sum = 0;
int cnt = 0;
for (int j = 0; j < SmoothFactor; j++)
{
int index = x - SmoothFactor / 2 + j;
if (index < 0)
continue;
else if (index >= orgDatas.Count())
break;
var value = orgDatas.ElementAt(index);
if (!double.IsNaN(value))
{
sum += value;
cnt++;
}
}
double y;
if (cnt > 0)
y = sum / cnt;
else
y = double.NaN;
keyValues.Add(new XY() { X = x, Y = y });
}
return keyValues;
}
DenseVector Solve(List keyValues, int i)
{
DenseMatrix X = new DenseMatrix(3, 3);
DenseVector Y = new DenseVector(3);
var kv0 = keyValues[i - 1];
var kv1 = keyValues[i];
var kv2 = keyValues[i + 1];
X.SetRow(0, new double[] { kv0.X * kv0.X, kv0.X, 1 });
X.SetRow(1, new double[] { kv1.X * kv1.X, kv1.X, 1 });
X.SetRow(2, new double[] { kv2.X * kv2.X, kv2.X, 1 });
Y.SetValues(new double[] { kv0.Y, kv1.Y, kv2.Y });
DenseVector abc = (DenseVector)X.Solve(Y);
return abc;
}
public double[] CalFilterDatas(IEnumerable orgDatas)
{
List keyValues = GetKeyValues(orgDatas);
//计算每个点的2次方程 a,b,c
List abcList = new List();
for (int i = 0; i < keyValues.Count(); i++)
{
if (i == 0)
{
DenseVector abc = Solve(keyValues, 1);
abcList.Add(abc);
}
else if (i == 1)
{
abcList.Add(abcList.Last());
}
else if (i == keyValues.Count() - 1)
{
abcList.Add(abcList.Last());
}
else
{
DenseVector abc = Solve(keyValues, i);
abcList.Add(abc);
}
}
double[] filterDatas = new double[orgDatas.Count()];
int begin = -1;
for (int i = 0; i < keyValues.Count(); i++)
{
int j = (int)keyValues[i].X;
filterDatas[j] = keyValues[i].Y;
for (j = j - 1; j > begin; j--)
{
double y = abcList[i][0] * j * j + abcList[i][1] * j + abcList[i][2];
if (i - 1 >= 0)
{
double y_pre = abcList[i - 1][0] * j * j + abcList[i - 1][1] * j + abcList[i - 1][2];
//权重
double t = (j - keyValues[i - 1].X) / (keyValues[i].X - keyValues[i - 1].X);
y = t * y + (1 - t) * y_pre;
}
filterDatas[j] = y;
}
begin = (int)keyValues[i].X;
}
//计算最后的
{
int i = keyValues.Count() - 1;
for (int j = begin + 1; j < filterDatas.Count(); j++)
{
double y = abcList[i][0] * j * j + abcList[i][1] * j + abcList[i][2];
filterDatas[j] = y;
}
}
return filterDatas;
}
}
public enum KeyPointsSelectMode
{
Null,
Add,
Remove,
Move
}
public class PgScanCorrVmUt : INotifyPropertyChanged
{
#region 轴调节
///
/// Y轴拖拉条 最大值
///
public double YRangeSliderMax { get; set; } = 65535;
///
/// Y轴拖拉条 最小值
///
public double YRangeSliderMin { get; set; } = 0;
#endregion
#region 曲线
public Func XFormatter { get; private set; }
public Func YFormatter { get; private set; }
public double XMin => 0;
public double XMax => PosLength / PosOfGrid;
///
/// Y轴 最大值
///
public double YMax { get; set; } = 65535;
///
/// Y轴 最小值
///
public double YMin { get; set; }
public SeriesCollection Series { get; } = new SeriesCollection();
public class SeriesInfo : INotifyPropertyChanged
{
public string Name { get; set; }
public Brush Color { get; set; }
public ChartValues Datas = new ChartValues();
public event PropertyChangedEventHandler PropertyChanged;
}
public SeriesInfo[] SeriesInfos { get; set; }
CartesianMapper Mapper { get; set; }
#endregion
///
/// 机架总长,脉冲
///
public int PosLength { get; set; } = 8000;
///
/// 1个grid = N个pos
///
public int PosOfGrid { get; set; } = 10;
///
/// 比例
///
public double Mmpp { get; set; } = 0.1;
#region
///
/// 使能修正
///
public bool Enable { set; get; }
///
/// 当前修正的组
///
public int CurrGroupIndex { get; set; }
///
/// 当前进度 0 - 100
///
public int Progress { get; set; }
///
/// 数据更新时间
///
public DateTime[] UpdateTimes { get; set; }
///
/// 正在机架修正中
///
public bool IsRunning { get; set; }
///
/// 来回次数
///
public int ScanCnt { get; set; }
#endregion
///
/// 当前被选择的组序号
///
public int SelectedGroupIndex { get; set; }
#region 平滑方式生成修正曲线
///
/// 平均滤波平滑数,单位grid
///
public int SmoothFactor { get; set; } = 20;
#endregion
#region 关键点方式生成修正曲线
public KeyPointsSelectMode KpSelectMode { get; set; } = KeyPointsSelectMode.Null;
#endregion
///
/// 数据好了!!!!
/// 当 flyad7 的poslen, posOfGrid 发生变化时,DataOK = false, 需要重新记录。
///
public bool IsDataOK { get; set; } = false;
public int AvgAd { get; set; }
public RelayCommand SelectedGroup0Cmd { get; }
public RelayCommand SelectedGroup1Cmd { get; }
public RelayCommand StopCmd { get; }
public RelayCommand StartCmd { get; }
public RelayCommand ClearCmd { get; }
public RelayCommand CreateCorrBySmoothCmd { get; }
public RelayCommand CreateCorrByKeyPointCmd { get; }
///
/// 添加关键点模式
///
public RelayCommand AddKeyPointModeCmd { get; }
public RelayCommand RemoveKeyPointModeCmd { get; }
public RelayCommand MoveKeyPointModeCmd { get; }
public RelayCommand CancelKeyPointModeCmd { get; }
public RelayCommand SetCorrDataCmd { get; }
public PgScanCorrVmUt()
{
#region 与数据无关界面参数
XFormatter = (x) =>
{
//x 是 grid
int pos = (int)(x * PosOfGrid);
int mm = (int)(pos * Mmpp);
return $"{pos}\n{mm}mm";
};
YFormatter = (y) =>
{
return $"{y:F0}";
};
//Mapper = Mappers.Xy()
// .X((value, index) => value.X)
// .Y(value => value.Y);
SeriesInfos = new SeriesInfo[] {
new SeriesInfo(){Name = "正向原始", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(0)},
new SeriesInfo(){Name = "反向原始", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(1)},
new SeriesInfo(){Name = "正向滤波", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(0, true)},
new SeriesInfo(){Name = "反向滤波", Color = FLY.ControlLibrary.Themes.Styles.GetForeground(1, true)},
};
for (int i = 0; i < SeriesInfos.Count(); i++)
{
var seriesInfo = SeriesInfos[i];
var lineSeries = new LineSeries
{
Values = seriesInfo.Datas,
StrokeThickness = 2,
Title = seriesInfo.Name,
Fill = new SolidColorBrush(System.Windows.Media.Colors.Transparent),
Stroke = seriesInfo.Color,
PointGeometry = null,
Configuration = Mapper
};
Series.Add(lineSeries);
}
#endregion
PosLength = 24000;
PosOfGrid = 24;
Mmpp = 0.1135;
int gridLen = PosLength / PosOfGrid;
Random random = new Random();
int[] orgDatas0 = new int[gridLen];
int[] orgDatas1 = new int[gridLen];
int avgAd = 50300;
int tolerance = 500;
for (int i = 0; i < gridLen; i++)
{
int ad = (int)(Math.Sin(1.0 * i / (gridLen / 3) * Math.PI) * tolerance) + avgAd;
orgDatas0[i] = ad + (random.Next(10) - 5);
orgDatas1[i] = ad + (random.Next(10) - 5);
}
int[] corrDatas0 = Smooth(orgDatas0);
int[] corrDatas1 = Smooth(orgDatas1);
int avg = avgAd;
GetScanCorrGroupResponse response = new GetScanCorrGroupResponse();
response.Avg = avg;
response.OrgDatas = new int[][] { orgDatas0, orgDatas1 };
response.CorrDatas = new int[][] { corrDatas0, corrDatas1 };
UpdateTimes = new DateTime[2];
scanCorrService_GetScanCorrGroup(0, response);
}
int[] Smooth(int[] orgDatas, int smoothFactor = 50)
{
var filters = new int[orgDatas.Count()];
for (int i = 0; i < orgDatas.Count(); i++)
{
int sum = 0;
int cnt = 0;
for (int j = 0; j < smoothFactor; j++)
{
int index = i - smoothFactor / 2;
if (index < 0)
continue;
else if (index >= orgDatas.Count())
break;
if (Misc.MyBase.ISVALIDATA(orgDatas[index]))
{
sum += orgDatas[index];
cnt++;
}
}
if (cnt > 0)
filters[i] = sum / cnt;
else
filters[i] = Misc.MyBase.NULL_VALUE;
}
return filters;
}
void scanCorrService_GetScanCorrGroup(object asyncContext, object retData)
{
var reponse = retData as GetScanCorrGroupResponse;
int groupIndex = reponse.GroupIndex;
UpdateTimes[groupIndex] = reponse.UpdateTime;
AvgAd = reponse.Avg;
IsDataOK = reponse.IsDataOK;
SeriesInfos[0].Datas.Clear();
SeriesInfos[1].Datas.Clear();
SeriesInfos[2].Datas.Clear();
SeriesInfos[3].Datas.Clear();
if (reponse.OrgDatas == null || reponse.OrgDatas.Count() != 2)
return;
SeriesInfos[0].Datas.AddRange(reponse.OrgDatas[0].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
SeriesInfos[1].Datas.AddRange(reponse.OrgDatas[1].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
int max = (int)SeriesInfos[0].Datas.Max();
int min = (int)SeriesInfos[0].Datas.Min();
//设置Y轴范围
YMax = max;
YMin = min;
if (reponse.CorrDatas == null || reponse.CorrDatas.Count() != 2)
return;
SeriesInfos[2].Datas.AddRange(reponse.CorrDatas[0].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
SeriesInfos[3].Datas.AddRange(reponse.CorrDatas[1].Select(ad => Misc.MyBase.ISVALIDATA(ad) ? ad : double.NaN));
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}