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