using Cognex.VisionPro; using Cognex.VisionPro.ToolBlock; using MaterialDesignThemes.Wpf; using Prism.Commands; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using Prism.Services.Dialogs; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Team.FFFeederService; using Team.FFFeederService.Interfaces; using TeamAAS_VP.Core; using TeamAAS_VP.Data; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Models.Calibration; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.Services; using TeamAAS_VP.Views.Setting; using static TeamAAS_VP.Core.StabilityAnalyzer; namespace TeamAAS_VP.ViewModels.Product { public class VisionStaticAccuracyAnalyzerViewModel : BindableBase, IDialogAware { IRegionManager _regionManager; IEventAggregator _eventAggregator; IContainerProvider _container; IDialogService _dialogService; IFeederService _feederService; IRobotService _robotService; ICameraService _cameraService; ISystemDatabaseService _systemDatabaseService; ICalibrationService _calibrationService; IConfigService _configService; //测试过程中控制取消的CTS CancellationTokenSource _cts; #region 属性 private ICogImage _Image; public ICogImage Image { get { return _Image; } set { SetProperty(ref _Image, value); } } private Cognex.VisionPro.CogGraphicCollection _Graphic; public Cognex.VisionPro.CogGraphicCollection Graphic { get { return _Graphic; } set { SetProperty(ref _Graphic, value); } } private ProcedureModel _SelectProcedure; /// /// 选中的流程 /// public ProcedureModel SelectProcedure { get { return _SelectProcedure; } set { SetProperty(ref _SelectProcedure, value); } } private ICamera _Camera; public ICamera Camera { get { return _Camera; } set { SetProperty(ref _Camera, value); } } private string _Message = "等待开始..."; public string Message { get { return _Message; } set { SetProperty(ref _Message, value); } } private bool _IsRunning; public bool IsRunning { get { return _IsRunning; } set { SetProperty(ref _IsRunning, value); } } private CogToolBlock _VisionTool; public CogToolBlock VisionTool { get { return _VisionTool; } set { SetProperty(ref _VisionTool, value); } } private int _RepeatCount = 10; /// /// 重复次数 /// public int RepeatCount { get { return _RepeatCount; } set { SetProperty(ref _RepeatCount, value); } } private int _CurrentProgress; /// /// 当前进度 /// public int CurrentProgress { get { return _CurrentProgress; } set { SetProperty(ref _CurrentProgress, value); } } private int _SuccessCount; /// /// 成功次数 /// public int SuccessCount { get { return _SuccessCount; } set { SetProperty(ref _SuccessCount, value); } } private int _FailCount; /// /// 失败次数 /// public int FailCount { get { return _FailCount; } set { SetProperty(ref _FailCount, value); } } private double _SuccessRate; /// /// 成功率 /// public double SuccessRate { get { return _SuccessRate; } set { SetProperty(ref _SuccessRate, value); } } private DataTable _TestResult; /// /// 测试结果列表DataTable /// public DataTable TestResult { get { return _TestResult; } set { SetProperty(ref _TestResult, value); } } private ObservableCollection _ResultDataColumns; /// /// DataTable的列集合 /// public ObservableCollection ResultDataColumns { get { return _ResultDataColumns; } set { SetProperty(ref _ResultDataColumns, value); } } // private DataColumnInfo _SelectedDataColumn; /// /// 选中的列 /// public DataColumnInfo SelectedDataColumn { get { return _SelectedDataColumn; } set { SetProperty(ref _SelectedDataColumn, value); } } private SnackbarMessageQueue _MessageQueue; public SnackbarMessageQueue MessageQueue { get { return _MessageQueue; } set { SetProperty(ref _MessageQueue, value); } } //选中列分析结果 private StabilityMetrics _StabilityMetrics; public StabilityMetrics StabilityMetrics { get { return _StabilityMetrics; } set { SetProperty(ref _StabilityMetrics, value); } } private int _DelayTime = 200; /// /// 延时时间 /// public int DelayTime { get { return _DelayTime; } set { SetProperty(ref _DelayTime, value); } } #endregion #region 命令 private DelegateCommand _ConfirmCommand; public DelegateCommand ConfirmCommand => _ConfirmCommand ?? (_ConfirmCommand = new DelegateCommand(ExecuteConfirmCommand, () => !IsRunning).ObservesProperty(() => IsRunning)); private DelegateCommand _CancelCommand; public DelegateCommand CancelCommand => _CancelCommand ?? (_CancelCommand = new DelegateCommand(ExecuteCancelCommand, () => !IsRunning).ObservesProperty(() => IsRunning)); private DelegateCommand _StartTestCommand; public DelegateCommand StartTestCommand => _StartTestCommand ?? (_StartTestCommand = new DelegateCommand(ExecuteStartTestCommand, CanExecuteStartTestCommand).ObservesProperty(() => IsRunning)); private DelegateCommand _StopTestCommand; public DelegateCommand StopTestCommand => _StopTestCommand ?? (_StopTestCommand = new DelegateCommand(() => { _cts?.Cancel(); }, () => IsRunning).ObservesProperty(() => IsRunning)); //暂停测试命令 private DelegateCommand _PauseTestCommand; public DelegateCommand PauseTestCommand => _PauseTestCommand ?? (_PauseTestCommand = new DelegateCommand(() => { _cts?.Cancel(); }, () => IsRunning).ObservesProperty(() => IsRunning)); private DelegateCommand _ExportCSVCommand; public DelegateCommand ExportCSVCommand => _ExportCSVCommand ?? (_ExportCSVCommand = new DelegateCommand(ExecuteExportCSVCommand, () => !IsRunning).ObservesProperty(() => IsRunning)); private DelegateCommand _ExportReportCommand; public DelegateCommand ExportReportCommand => _ExportReportCommand ?? (_ExportReportCommand = new DelegateCommand(ExecuteExportReportCommand, () => !IsRunning).ObservesProperty(() => IsRunning)); private DelegateCommand _ClearDataCommand; public DelegateCommand ClearDataCommand => _ClearDataCommand ?? (_ClearDataCommand = new DelegateCommand(ExecuteClearDataCommand, () => !IsRunning).ObservesProperty(() => IsRunning)); private DelegateCommand _SelectColumnCommand; public DelegateCommand SelectColumnCommand => _SelectColumnCommand ?? (_SelectColumnCommand = new DelegateCommand(ExecuteSelectColumnCommand)); #endregion #region 事件 #endregion public VisionStaticAccuracyAnalyzerViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService, IFeederService feederService, IRobotService robotService, ICameraService cameraService, ISystemDatabaseService systemDatabaseService, ICalibrationService calibrationService, IConfigService configService) { _regionManager = regionManager; _eventAggregator = ea; _container = container; _dialogService = dialogService; _feederService = feederService; _robotService = robotService; _cameraService = cameraService; _systemDatabaseService = systemDatabaseService; MessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(1)); _calibrationService = calibrationService; _configService = configService; } #region 方法 /// /// 确定 /// void ExecuteConfirmCommand() { _cts?.Cancel(); IDialogParameters parameters = new DialogParameters(); //parameters.Add("Tool", Tool); //parameters.Add("RobotCameraRotationMatrix", RobotCameraRotationMatrix); //parameters.Add("Angle", Angle); RequestClose?.Invoke(new DialogResult(ButtonResult.OK, parameters)); } /// /// 取消 /// void ExecuteCancelCommand() { _cts?.Cancel(); IDialogParameters parameters = new DialogParameters(); //parameters.Add("Tool", Tool); RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel, parameters)); } /// /// 开始测试 /// async void ExecuteStartTestCommand() { IsRunning = true; _cts = new CancellationTokenSource(); CurrentProgress = 0; SuccessCount = 0; SuccessRate = 0; FailCount = 0; Message = Lang.测试进行中; try { while (CurrentProgress < RepeatCount) { if (_cts.Token.IsCancellationRequested) { Message = Lang.测试已取消; break; } (bool isSuccess, object[] Result) = await ExecutePhotoEx(CurrentProgress); if (isSuccess) { SuccessCount++; } else { FailCount++; } TestResult.Rows.Add(Result); ExecuteSelectColumnCommand(); await Task.Delay(DelayTime); CurrentProgress++; SuccessRate = (double)SuccessCount / CurrentProgress * 100; SendTaskMessage($"测试进行中... {CurrentProgress}/{RepeatCount}"); } if (CurrentProgress >= RepeatCount) { Message = Lang.测试完成; } } catch (Exception ex) { LogHelper.WriteLogError("视觉静态重复测试时出错!", ex); } finally { IsRunning = false; } } bool CanExecuteStartTestCommand() { return !IsRunning; } /// /// 清除数据 /// void ExecuteClearDataCommand() { if(TestResult!=null) { TestResult.Rows.Clear(); } } /// /// 导出测试报告 /// 使用 iTextSharp 生成 PDF,包含:标题、测试摘要、数据表格、每列稳定性分析结果 /// 详细伪代码: /// 1. 校验 TestResult 是否存在数据,若无则提示并返回。 /// 2. 弹出保存对话框,获取保存路径,若取消返回。 /// 3. 创建 iTextSharp Document 与 PdfWriter,打开文档。 /// 4. 创建支持中文的 BaseFont(例如 STSongStd-Light + UniGB-UCS2-H)。 /// 5. 写入标题(居中、大号字体)。 /// 6. 写入测试摘要信息(测试时间、重复次数、成功/失败/成功率等),每项为单独段落或表格。 /// 7. 构建 PdfPTable:列数 = TestResult.Columns.Count,写入表头(加粗),逐行写入数据: /// - 对于数值使用 InvariantCulture 格式化,空值写空字符串。 /// 8. 写入分析结果标题。 /// 9. 对于 ResultDataColumns 中的每列: /// - 从 TestResult 读取该列所有数值(尝试转换为 double,忽略无法转换的项)。 /// - 如果样本数为 0,写入“样本数为0”提示并跳过。 /// - 调用 StabilityAnalyzer.AnalyzeStability(values) 获取指标对象。 /// - 遍历指标对象的公开属性,将属性名与值写入一个两列的 PdfPTable(或段落)。 /// 10. 关闭文档并释放资源。 /// 11. 捕获异常并记录日志,提示用户失败信息。 /// void ExecuteExportReportCommand() { try { if (TestResult == null || TestResult.Columns.Count == 0 || TestResult.Rows.Count == 0) { SendTaskMessage(Lang.无测试数据无法导出报告); return; } var dlg = new Microsoft.Win32.SaveFileDialog { DefaultExt = "pdf", Filter = "PDF 文件 (*.pdf)|*.pdf|所有文件 (*.*)|*.*", FileName = "TestReport.pdf", Title = Lang.保存测试报告为PDF }; bool? dlgResult = dlg.ShowDialog(); if (dlgResult != true) return; string path = dlg.FileName; // 创建文档(A4, 边距) var doc = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 36, 36, 54, 54); using (var fs = System.IO.File.Create(path)) { var writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, fs); doc.Open(); // --- 字体加载:优先载入系统中文字体并以 IDENTITY_H 编码嵌入,保证中文显示 --- iTextSharp.text.Font titleFont; iTextSharp.text.Font headerFont; iTextSharp.text.Font normalFont; iTextSharp.text.pdf.BaseFont baseFont = null; try { var fontsFolder = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); var candidates = new[] { "msyh.ttf", "msyhbd.ttf", "simsun.ttc,0", // 添加 ",0" 指定第一个字体 "simsun.ttc,1", // 第二个字体 "Microsoft YaHei.ttf", "msyh.ttc,0", // 添加 ",0" "msyh.ttc,1", // 添加 ",1" "simhei.ttf" }; foreach (var f in candidates) { var path1 = Path.Combine(fontsFolder, f); try { baseFont = iTextSharp.text.pdf.BaseFont.CreateFont( path1, iTextSharp.text.pdf.BaseFont.IDENTITY_H, iTextSharp.text.pdf.BaseFont.EMBEDDED ); if (baseFont != null) { Console.WriteLine($"成功加载字体: {f}"); break; } } catch (Exception ex) { Console.WriteLine($"字体 {f} 加载失败: {ex.Message}"); continue; } } } catch { baseFont = null; } if (baseFont != null) { titleFont = new iTextSharp.text.Font(baseFont, 16, iTextSharp.text.Font.BOLD, iTextSharp.text.BaseColor.BLACK); headerFont = new iTextSharp.text.Font(baseFont, 10, iTextSharp.text.Font.BOLD, iTextSharp.text.BaseColor.BLACK); normalFont = new iTextSharp.text.Font(baseFont, 10, iTextSharp.text.Font.NORMAL, iTextSharp.text.BaseColor.BLACK); } else { // 回退:如果未找到系统中文字体,使用内置 Helvetica(注意:可能无法正确显示中文) titleFont = iTextSharp.text.FontFactory.GetFont(iTextSharp.text.FontFactory.HELVETICA, 16, iTextSharp.text.Font.BOLD, iTextSharp.text.BaseColor.BLACK); headerFont = iTextSharp.text.FontFactory.GetFont(iTextSharp.text.FontFactory.HELVETICA, 10, iTextSharp.text.Font.BOLD, iTextSharp.text.BaseColor.BLACK); normalFont = iTextSharp.text.FontFactory.GetFont(iTextSharp.text.FontFactory.HELVETICA, 10, iTextSharp.text.Font.NORMAL, iTextSharp.text.BaseColor.BLACK); } // 标题 var title = new iTextSharp.text.Paragraph(Lang.视觉静态精度测试报告, titleFont) { Alignment = iTextSharp.text.Element.ALIGN_CENTER, SpacingAfter = 12f }; doc.Add(title); // 测试摘要 var metaTable = new iTextSharp.text.pdf.PdfPTable(2) { WidthPercentage = 100f }; metaTable.SetWidths(new float[] { 1f, 2f }); void AddMeta(string name, string value) { var cellName = new iTextSharp.text.pdf.PdfPCell(new iTextSharp.text.Phrase(name, headerFont)) { Border = 0, PaddingBottom = 6f }; var cellVal = new iTextSharp.text.pdf.PdfPCell(new iTextSharp.text.Phrase(value, normalFont)) { Border = 0, PaddingBottom = 6f }; metaTable.AddCell(cellName); metaTable.AddCell(cellVal); } // include device info (line, station, fixture id) but exclude MES flags/urls var device = _configService?.GetDeviceInfo() ?? _configService?.GetDeviceInfo(); if (device != null) { AddMeta("线别", device.Line ?? string.Empty); AddMeta("站别", device.Station ?? string.Empty); AddMeta("机台号", device.FixtureId ?? string.Empty); } AddMeta("导出时间", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); AddMeta("测试次数(目标)", RepeatCount.ToString()); AddMeta("当前进度", $"{CurrentProgress} / {RepeatCount}"); AddMeta("成功次数", SuccessCount.ToString()); AddMeta("失败次数", FailCount.ToString()); AddMeta("成功率(%)", SuccessRate.ToString("F2", System.Globalization.CultureInfo.InvariantCulture)); doc.Add(metaTable); doc.Add(new iTextSharp.text.Paragraph(" ")); // 空行 // 数据表格 int colCount = TestResult.Columns.Count; var table = new iTextSharp.text.pdf.PdfPTable(colCount) { WidthPercentage = 100f }; // 表头 foreach (System.Data.DataColumn col in TestResult.Columns) { var cell = new iTextSharp.text.pdf.PdfPCell(new iTextSharp.text.Phrase(col.ColumnName, headerFont)) { HorizontalAlignment = iTextSharp.text.Element.ALIGN_CENTER, BackgroundColor = new iTextSharp.text.BaseColor(230, 230, 230), Padding = 4f }; table.AddCell(cell); } // 数据行 foreach (System.Data.DataRow row in TestResult.Rows) { for (int c = 0; c < colCount; c++) { object val = row[c]; string s; if (val == null || val == DBNull.Value) s = ""; else if (val is double || val is float || val is decimal) s = Convert.ToDouble(val).ToString("G", System.Globalization.CultureInfo.InvariantCulture); else s = val.ToString(); var cell = new iTextSharp.text.pdf.PdfPCell(new iTextSharp.text.Phrase(s, normalFont)) { Padding = 4f }; table.AddCell(cell); } } doc.Add(table); doc.NewPage(); // 分析结果 var analysisTitle = new iTextSharp.text.Paragraph("分析结果", titleFont) { SpacingAfter = 8f }; doc.Add(analysisTitle); if (ResultDataColumns != null && ResultDataColumns.Count > 0) { foreach (var colInfo in ResultDataColumns) { try { // 收集数值 var values = new System.Collections.Generic.List(); foreach (System.Data.DataRow row in TestResult.Rows) { object v = row[colInfo.ColumnIndex]; if (v == null || v == DBNull.Value) continue; double d; if (v is double) d = (double)v; else if (v is float) d = Convert.ToDouble((float)v); else if (v is decimal) d = Convert.ToDouble((decimal)v); else if (v is int) d = Convert.ToDouble((int)v); else if (v is long) d = Convert.ToDouble((long)v); else { if (!double.TryParse(v.ToString(), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out d)) continue; } values.Add(d); } var colHeader = new iTextSharp.text.Paragraph(colInfo.ColumnName, headerFont) { SpacingBefore = 6f, SpacingAfter = 4f }; doc.Add(colHeader); if (values.Count == 0) { doc.Add(new iTextSharp.text.Paragraph("样本数为 0,无法计算。", normalFont)); continue; } // 计算稳定性指标 StabilityMetrics metrics = StabilityAnalyzer.AnalyzeStability(values.ToArray()); // 将指标写成两列表(属性名 / 值) var metricsTable = new iTextSharp.text.pdf.PdfPTable(2) { WidthPercentage = 60f, SpacingAfter = 6f }; metricsTable.SetWidths(new float[] { 1f, 1f }); var props = metrics.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); foreach (var p in props) { object pv = p.GetValue(metrics); string pvStr; if (pv == null) pvStr = ""; else if (pv is double) pvStr = ((double)pv).ToString("G", System.Globalization.CultureInfo.InvariantCulture); else pvStr = pv.ToString(); var pc = new iTextSharp.text.pdf.PdfPCell(new iTextSharp.text.Phrase(p.Name, normalFont)) { Padding = 4f }; var vc = new iTextSharp.text.pdf.PdfPCell(new iTextSharp.text.Phrase(pvStr, normalFont)) { Padding = 4f }; metricsTable.AddCell(pc); metricsTable.AddCell(vc); } doc.Add(metricsTable); } catch (Exception exCol) { LogHelper.WriteLogError($"导出报告时处理列[{colInfo.ColumnName}]出错", exCol); doc.Add(new iTextSharp.text.Paragraph($"处理列 {colInfo.ColumnName} 时出错: {exCol.Message}", normalFont)); } } } else { doc.Add(new iTextSharp.text.Paragraph("无用于分析的数值列。", normalFont)); } doc.Close(); writer.Close(); } SendTaskMessage($"已导出测试报告到:{path}"); } catch (Exception ex) { LogHelper.WriteLogError("导出测试报告时出错", ex); SendTaskMessage($"导出测试报告失败:{ex.Message}"); } } /// /// 导出测试数据为CSV文件 /// void ExecuteExportCSVCommand() { try { if (TestResult == null || TestResult.Columns.Count == 0 || TestResult.Rows.Count == 0) { SendTaskMessage("无测试数据,无法导出。"); return; } var dlg = new Microsoft.Win32.SaveFileDialog { DefaultExt = "csv", Filter = "CSV 文件 (*.csv)|*.csv|所有文件 (*.*)|*.*", FileName = "TestResult.csv", Title = "保存测试结果为 CSV" }; bool? dlgResult = dlg.ShowDialog(); if (dlgResult != true) return; string path = dlg.FileName; var sb = new System.Text.StringBuilder(); // 辅助:CSV 字段转义 Func EscapeCsv = (s) => { if (s == null) return ""; bool mustQuote = s.Contains(",") || s.Contains("\"") || s.Contains("\r") || s.Contains("\n"); string esc = s.Replace("\"", "\"\""); return mustQuote ? $"\"{esc}\"" : esc; }; // include device info at top of CSV var device = _configService?.GetDeviceInfo(); if (device != null) { sb.AppendLine("设备信息"); sb.AppendLine($"线别,{EscapeCsv(device.Line)}"); sb.AppendLine($"站别,{EscapeCsv(device.Station)}"); sb.AppendLine($"机台号,{EscapeCsv(device.FixtureId)}"); sb.AppendLine(); } // 1. 写入表头 for (int c = 0; c < TestResult.Columns.Count; c++) { if (c > 0) sb.Append(","); sb.Append(EscapeCsv(TestResult.Columns[c].ColumnName)); } sb.AppendLine(); // 2. 写入数据行 foreach (System.Data.DataRow row in TestResult.Rows) { for (int c = 0; c < TestResult.Columns.Count; c++) { if (c > 0) sb.Append(","); object val = row[c]; if (val == DBNull.Value || val == null) { sb.Append(""); } else { // 保持数值格式,其他转为字符串 string outStr; if (val is double || val is float || val is decimal) outStr = Convert.ToString(val, System.Globalization.CultureInfo.InvariantCulture); else if (val is int || val is long || val is short || val is byte) outStr = val.ToString(); else if (val is bool) outStr = (bool)val ? "True" : "False"; else outStr = val.ToString(); sb.Append(EscapeCsv(outStr)); } } sb.AppendLine(); } // 3. 在末尾追加分析结果 sb.AppendLine(); // 空行 sb.AppendLine("分析结果"); sb.AppendLine("列名,指标,值"); // CSV 表头:列名,指标,值 if (ResultDataColumns != null) { foreach (var colInfo in ResultDataColumns) { try { List values = new List(); foreach (System.Data.DataRow row in TestResult.Rows) { object v = row[colInfo.ColumnIndex]; if (v != DBNull.Value && v != null) { double d; // 支持不同数字类型 if (v is double) d = (double)v; else if (v is float) d = Convert.ToDouble((float)v); else if (v is decimal) d = Convert.ToDouble((decimal)v); else if (v is int) d = Convert.ToDouble((int)v); else if (v is long) d = Convert.ToDouble((long)v); else { if (!double.TryParse(v.ToString(), System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out d)) continue; } values.Add(d); } } if (values.Count == 0) { sb.AppendLine($"{EscapeCsv(colInfo.ColumnName)},{EscapeCsv("样本数")},{0}"); continue; } // 调用稳定性分析 StabilityMetrics metrics = StabilityAnalyzer.AnalyzeStability(values.ToArray()); // 使用反射枚举 metrics 的公开属性并写入 CSV var props = metrics.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); foreach (var p in props) { object pv = p.GetValue(metrics); string pvStr = pv == null ? "" : (pv is double ? Convert.ToString((double)pv, System.Globalization.CultureInfo.InvariantCulture) : pv.ToString()); sb.AppendLine($"{EscapeCsv(colInfo.ColumnName)},{EscapeCsv(p.Name)},{EscapeCsv(pvStr)}"); } } catch (Exception exCol) { LogHelper.WriteLogError($"导出分析结果时处理列[{colInfo.ColumnName}]出错", exCol); sb.AppendLine($"{EscapeCsv(colInfo.ColumnName)},{EscapeCsv("Error")},{EscapeCsv(exCol.Message)}"); } } } // 写入文件(UTF8,无 BOM,若需 BOM 可使用 new UTF8Encoding(true)) System.IO.File.WriteAllText(path, sb.ToString(), System.Text.Encoding.UTF8); SendTaskMessage($"已导出测试结果到:{path}"); } catch (Exception ex) { LogHelper.WriteLogError("导出CSV时出错", ex); SendTaskMessage($"导出CSV失败:{ex.Message}"); } } /// /// 选中列时触发 /// void ExecuteSelectColumnCommand() { try { //如果选中列为空,则直接返回 if (SelectedDataColumn == null) return; //获取表的指定列的所有数据集合 List dataList = new List(); foreach (DataRow row in TestResult.Rows) { if (row[SelectedDataColumn.ColumnIndex] != DBNull.Value) { dataList.Add(Convert.ToDouble(row[SelectedDataColumn.ColumnIndex])); } } //如果数据量小于10,则不进行分析 if (dataList.Count < 10) { //MessageQueue.Enqueue("选中列的数据量小于10,无法进行统计分析!"); return; } //进行稳定性分析 StabilityMetrics metrics = StabilityAnalyzer.AnalyzeStability(dataList.ToArray()); App.Current.Dispatcher.Invoke(() => { StabilityMetrics = metrics; }); } catch (Exception ex) { LogHelper.WriteLogError("选中列的数据分析数据稳定性时出错!", ex); } } /// /// 执行相机拍照并运行 ToolBlock,返回是否成功并通过 out 参数返回 Outputs、图像和图形集合。 /// /// /// public Task<(bool isSuccess, object[] Result)> ExecutePhotoEx(int index) { return Task.Run(() => { CogToolBlockTerminalCollection outputCollection; List result = new List(); result.Add(index); try { // 1. 采集图像 bool succed = Camera.SetExposureTime(SelectProcedure.ExposureTime); if (!succed) { SendTaskMessage("相机曝光设置失败"); } succed = Camera.SetGain(SelectProcedure.Gain); if (!succed) { SendTaskMessage("相机增益设置失败"); } DateTime nowtime = DateTime.Now; LogHelper.WriteLogInfo("开始采集图像"); var image = Camera.Grab(); if (image == null) { SendTaskMessage(Lang.图像采集失败); outputCollection = null; return (false, result.ToArray()); } //Image = image; VisionTool.Inputs["InputImage"].Value = image; LogHelper.WriteLogInfo($"采集图像完成用时{(DateTime.Now - nowtime).Milliseconds}ms"); // 2. 为ToolBlock传入其他输入终端 //if (InputTerminal != null) //{ // LogHelper.WriteLogInfo(Lang.为ToolBlock传入输入终端); // foreach (var item in InputTerminal) // { // if (VisionTool.Inputs.Contains(item.Key)) // { // LogHelper.WriteLogInfo($"传入[{item.Key}]={item.Value}"); // VisionTool.Inputs[item.Key].Value = item.Value; // } // else // { // LogHelper.WriteLogInfo($"创建并传入[{item.Key}]={item.Value}"); // VisionTool.Inputs.Add(new CogToolBlockTerminal(item.Key, item.Value)); // } // } //} Image = VisionTool.Inputs["InputImage"].Value as ICogImage; // 3. 运行视觉工具 LogHelper.WriteLogInfo("开始运行视觉工具"); VisionTool.Run(); //运行ToolBlock if (VisionTool.RunStatus.Result == CogToolResultConstants.Accept) { SendTaskMessage(Lang.视觉流程执行耗时.Replace("{0}", SelectProcedure.Name).Replace("{1}", $"{VisionTool.RunStatus.ProcessingTime:F1}")); outputCollection = VisionTool.Outputs; //获取输出终端集合 foreach (CogToolBlockTerminal terminal in outputCollection) { //如果类型是常见的字符串、数字、布尔类型,则创建对应的列 if (terminal.ValueType == typeof(string)) { //如果输出的名称是"Point",则要判断是否是点位格式,点位格式为"x1,y1,u1;x2,y2,u2;...." if (terminal.Name == "Point") { //是否需要转换为绝对坐标 needConvertToAbsolute = false; //如果当前流程的校准为空,则不需要转换为绝对坐标 if (SelectProcedure.CalibrationId != Guid.Empty) { var calibration = _calibrationService.GetCalibration(SelectProcedure.CalibrationId); if (calibration != null) { needConvertToAbsolute = true; } } //判断是否是点位格式 string strpoints = terminal.Value.ToString(); string[] items = strpoints.Split(';'); bool isPointFormat = true; foreach (string item in items) { string[] subitems = item.Split(','); if (subitems.Length < 3) { isPointFormat = false; break; } double x, y, u; if (!double.TryParse(subitems[0], out x) || !double.TryParse(subitems[1], out y) || !double.TryParse(subitems[2], out u)) { isPointFormat = false; break; } } if (isPointFormat) { //是点位格式,则创建多列 for (int i = 0; i < items.Length; i++) { string[] subitems = items[i].Split(','); //先分别创建像素X、像素Y、角度U三列 result.Add(double.Parse(subitems[0])); result.Add(double.Parse(subitems[1])); result.Add(double.Parse(subitems[2])); //如果需要转换为绝对坐标,则再创建绝对X、绝对Y两列 if (needConvertToAbsolute) { double[] point = new double[3]; var calibration = _calibrationService.GetCalibration(SelectProcedure.CalibrationId); var Robot = _robotService.GetRobot(SelectProcedure.RobotId); if (Robot != null) { //获取机器人当前坐标系 RPoint rPoint = Robot.GetRobotPos(); point[0] = rPoint.X; point[1] = rPoint.Y; point[2] = rPoint.U; if (calibration.CameraMount == CameraMount.FixedUp || calibration.CameraMount == CameraMount.FixedDown) { point = null; } } (bool IsSucceed, double X, double Y, double U) = _calibrationService.ConvertPixelToPosition((double.Parse(subitems[0]), double.Parse(subitems[1]), double.Parse(subitems[2])), point, calibration); result.Add(Math.Round(X, 5)); result.Add(Math.Round(Y, 5)); } } } else { result.Add(terminal.Value.ToString()); } } else { result.Add(terminal.Value.ToString()); } } else if (terminal.ValueType == typeof(int)) result.Add((int)terminal.Value); else if (terminal.ValueType == typeof(double)) result.Add((double)terminal.Value); else if (terminal.ValueType == typeof(bool)) result.Add((bool)terminal.Value); } Graphic = outputCollection["Graphic"].Value as CogGraphicCollection; return (true, result.ToArray()); } else { SendTaskMessage(Lang.视觉流程执行出错耗时.Replace("{0}", SelectProcedure.Name).Replace("{1}", $"{VisionTool.RunStatus.ProcessingTime:F1}")); SendTaskMessage(VisionTool.RunStatus.Message); outputCollection = null; Graphic = outputCollection["Graphic"].Value as CogGraphicCollection; return (false, result.ToArray()); } } catch (Exception ex) { SendTaskMessage(ex.Message); LogHelper.WriteLogError("执行相机取图并执行视觉工具组时出错", ex); outputCollection = null; return (false, result.ToArray()); } }); } public void SendTaskMessage(string msg) { App.Current.Dispatcher.Invoke(() => { MessageQueue.Clear(); MessageQueue.Enqueue(msg); }); } bool needConvertToAbsolute = false; /// /// 根据VisionTool的输出终端,创建DataTable的列 /// /// /// private DataTable CreateResultDataTable(CogToolBlockTerminalCollection outputCollection) { DataTable dt = new DataTable(); ResultDataColumns = new ObservableCollection(); //第一列添加序号 dt.Columns.Add("序号", typeof(int)); //列索引 foreach (CogToolBlockTerminal terminal in outputCollection) { //如果类型是常见的字符串、数字、布尔类型,则创建对应的列 if (terminal.ValueType == typeof(string)) { //如果输出的名称是"Point",则要判断是否是点位格式,点位格式为"x1,y1,u1;x2,y2,u2;...." if (terminal.Name == "Point") { //是否需要转换为绝对坐标 needConvertToAbsolute = false; //如果当前流程的校准为空,则不需要转换为绝对坐标 if (SelectProcedure.CalibrationId != Guid.Empty) { var calibration = _calibrationService.GetCalibration(SelectProcedure.CalibrationId); if (calibration != null) { needConvertToAbsolute = true; } } //判断是否是点位格式 string strpoints = terminal.Value.ToString(); string[] items = strpoints.Split(';'); bool isPointFormat = true; foreach (string item in items) { string[] subitems = item.Split(','); if (subitems.Length < 3) { isPointFormat = false; break; } double x, y, u; if (!double.TryParse(subitems[0], out x) || !double.TryParse(subitems[1], out y) || !double.TryParse(subitems[2], out u)) { isPointFormat = false; break; } } if (isPointFormat) { //是点位格式,则创建多列 for (int i = 0; i < items.Length; i++) { //先分别创建像素X、像素Y、角度U三列 dt.Columns.Add($"{terminal.Name}_Pixel_X{i + 1}", typeof(double)); ResultDataColumns.Add(new DataColumnInfo($"{terminal.Name}_Pixel_X{i + 1}", dt.Columns.Count - 1)); dt.Columns.Add($"{terminal.Name}_Pixel_Y{i + 1}", typeof(double)); ResultDataColumns.Add(new DataColumnInfo($"{terminal.Name}_Pixel_Y{i + 1}", dt.Columns.Count - 1)); dt.Columns.Add($"{terminal.Name}_Angle_U{i + 1}", typeof(double)); ResultDataColumns.Add(new DataColumnInfo($"{terminal.Name}_Angle_U{i + 1}", dt.Columns.Count - 1)); //如果需要转换为绝对坐标,则再创建绝对X、绝对Y两列 if (needConvertToAbsolute) { dt.Columns.Add($"{terminal.Name}_Absolute_X{i + 1}", typeof(double)); ResultDataColumns.Add(new DataColumnInfo($"{terminal.Name}_Absolute_X{i + 1}", dt.Columns.Count - 1)); dt.Columns.Add($"{terminal.Name}_Absolute_Y{i + 1}", typeof(double)); ResultDataColumns.Add(new DataColumnInfo($"{terminal.Name}_Absolute_Y{i + 1}", dt.Columns.Count - 1)); } } } else { dt.Columns.Add(terminal.Name, typeof(string)); } } else { dt.Columns.Add(terminal.Name, typeof(string)); } } else if (terminal.ValueType == typeof(int)) { dt.Columns.Add(terminal.Name, typeof(int)); ResultDataColumns.Add(new DataColumnInfo(terminal.Name, dt.Columns.Count - 1)); } else if (terminal.ValueType == typeof(double)) { dt.Columns.Add(terminal.Name, typeof(double)); ResultDataColumns.Add(new DataColumnInfo(terminal.Name, dt.Columns.Count - 1)); } else if (terminal.ValueType == typeof(bool)) { dt.Columns.Add(terminal.Name, typeof(bool)); } //else // dt.Columns.Add(terminal.Name, typeof(string)); } return dt; } #endregion #region 继承 public string Title { get; set; } = "视觉静态精度分析仪"; public event Action RequestClose; public bool CanCloseDialog() { return true; } public void OnDialogClosed() { //关闭时,停止测试 _cts?.Cancel(); } public void OnDialogOpened(IDialogParameters parameters) { SelectProcedure = parameters.GetValue("SelectedProcedure"); Camera = _cameraService.GetCamera(SelectProcedure.CameraId); VisionTool = parameters.GetValue("ToolBlock"); VisionTool.Run(); TestResult = CreateResultDataTable(VisionTool.Outputs); } #endregion } //辅助类,用于定义DataTable的列,仅用来对于数值列进行统计分析 public class DataColumnInfo { public string ColumnName { get; set; } //列索引 public int ColumnIndex { get; set; } public DataColumnInfo(string columnName, int columnIndex) { ColumnName = columnName; ColumnIndex = columnIndex; } } }