|
|
@@ -0,0 +1,1195 @@
|
|
|
+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.Interfaces;
|
|
|
+using TeamAAS_VP.Core;
|
|
|
+using TeamAAS_VP.Interfaces;
|
|
|
+using TeamAAS_VP.Models;
|
|
|
+using TeamAAS_VP.Resources.Languages;
|
|
|
+using static TeamAAS_VP.Core.StabilityAnalyzer;
|
|
|
+
|
|
|
+namespace TeamAAS_VP.ViewModels.Product
|
|
|
+{
|
|
|
+ public class VisionDynamicAccuracyAnalyzerViewModel : BindableBase, IDialogAware
|
|
|
+ {
|
|
|
+ IRegionManager _regionManager;
|
|
|
+ IEventAggregator _eventAggregator;
|
|
|
+ IContainerProvider _container;
|
|
|
+ IDialogService _dialogService;
|
|
|
+ IFeederService _feederService;
|
|
|
+ IRobotService _robotService;
|
|
|
+ ICameraService _cameraService;
|
|
|
+ ISystemDatabaseService _systemDatabaseService;
|
|
|
+ ICalibrationService _calibrationService;
|
|
|
+
|
|
|
+ //测试过程中控制取消的CTS
|
|
|
+ CancellationTokenSource _cts;
|
|
|
+
|
|
|
+ #region 属性
|
|
|
+
|
|
|
+ private ProductModel _SelectProduct;
|
|
|
+ public ProductModel SelectProduct
|
|
|
+ {
|
|
|
+ get { return _SelectProduct; }
|
|
|
+ set { SetProperty(ref _SelectProduct, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 选中的流程
|
|
|
+ /// </summary>
|
|
|
+ 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;
|
|
|
+ /// <summary>
|
|
|
+ /// 重复次数
|
|
|
+ /// </summary>
|
|
|
+ public int RepeatCount
|
|
|
+ {
|
|
|
+ get { return _RepeatCount; }
|
|
|
+ set { SetProperty(ref _RepeatCount, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int _CurrentProgress;
|
|
|
+ /// <summary>
|
|
|
+ /// 当前进度
|
|
|
+ /// </summary>
|
|
|
+ public int CurrentProgress
|
|
|
+ {
|
|
|
+ get { return _CurrentProgress; }
|
|
|
+ set { SetProperty(ref _CurrentProgress, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int _SuccessCount;
|
|
|
+ /// <summary>
|
|
|
+ /// 成功次数
|
|
|
+ /// </summary>
|
|
|
+ public int SuccessCount
|
|
|
+ {
|
|
|
+ get { return _SuccessCount; }
|
|
|
+ set { SetProperty(ref _SuccessCount, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int _FailCount;
|
|
|
+ /// <summary>
|
|
|
+ /// 失败次数
|
|
|
+ /// </summary>
|
|
|
+ public int FailCount
|
|
|
+ {
|
|
|
+ get { return _FailCount; }
|
|
|
+ set { SetProperty(ref _FailCount, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private double _SuccessRate;
|
|
|
+ /// <summary>
|
|
|
+ /// 成功率
|
|
|
+ /// </summary>
|
|
|
+ public double SuccessRate
|
|
|
+ {
|
|
|
+ get { return _SuccessRate; }
|
|
|
+ set { SetProperty(ref _SuccessRate, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private DataTable _TestResult;
|
|
|
+ /// <summary>
|
|
|
+ /// 测试结果列表DataTable
|
|
|
+ /// </summary>
|
|
|
+ public DataTable TestResult
|
|
|
+ {
|
|
|
+ get { return _TestResult; }
|
|
|
+ set { SetProperty(ref _TestResult, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private ObservableCollection<DataColumnInfo> _ResultDataColumns;
|
|
|
+ /// <summary>
|
|
|
+ /// DataTable的列集合
|
|
|
+ /// </summary>
|
|
|
+ public ObservableCollection<DataColumnInfo> ResultDataColumns
|
|
|
+ {
|
|
|
+ get { return _ResultDataColumns; }
|
|
|
+ set { SetProperty(ref _ResultDataColumns, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ private DataColumnInfo _SelectedDataColumn;
|
|
|
+ /// <summary>
|
|
|
+ /// 选中的列
|
|
|
+ /// </summary>
|
|
|
+ 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 RPoint _PointA;
|
|
|
+ public RPoint PointA
|
|
|
+ {
|
|
|
+ get { return _PointA; }
|
|
|
+ set { SetProperty(ref _PointA, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private RPoint _PointB;
|
|
|
+ public RPoint PointB
|
|
|
+ {
|
|
|
+ get { return _PointB; }
|
|
|
+ set { SetProperty(ref _PointB, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private ObservableCollection<ProcedureModel> _ProcedureModels;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 流程集合
|
|
|
+ /// </summary>
|
|
|
+ public ObservableCollection<ProcedureModel> ProcedureModels
|
|
|
+ {
|
|
|
+ get { return _ProcedureModels; }
|
|
|
+ set { SetProperty(ref _ProcedureModels, value); }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int _MoveSpeed;
|
|
|
+ /// <summary>
|
|
|
+ /// 移动速度
|
|
|
+ /// </summary>
|
|
|
+ public int MoveSpeed
|
|
|
+ {
|
|
|
+ get { return _MoveSpeed; }
|
|
|
+ set { SetProperty(ref _MoveSpeed, 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));
|
|
|
+
|
|
|
+ private DelegateCommand _TeachPointACommand;
|
|
|
+ public DelegateCommand TeachPointACommand =>
|
|
|
+ _TeachPointACommand ?? (_TeachPointACommand = new DelegateCommand(ExecuteTeachPointACommand, () => !IsRunning).ObservesProperty(() => IsRunning));
|
|
|
+
|
|
|
+ private DelegateCommand _TeachPointBCommand;
|
|
|
+ public DelegateCommand TeachPointBCommand =>
|
|
|
+ _TeachPointBCommand ?? (_TeachPointBCommand = new DelegateCommand(ExecuteTeachPointBCommand, () => !IsRunning).ObservesProperty(() => IsRunning));
|
|
|
+
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region 事件
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ public VisionDynamicAccuracyAnalyzerViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
|
|
|
+ IFeederService feederService, IRobotService robotService, ICameraService cameraService, ISystemDatabaseService systemDatabaseService, ICalibrationService calibrationService)
|
|
|
+ {
|
|
|
+ _regionManager = regionManager;
|
|
|
+ _eventAggregator = ea;
|
|
|
+ _container = container;
|
|
|
+ _dialogService = dialogService;
|
|
|
+ _feederService = feederService;
|
|
|
+ _robotService = robotService;
|
|
|
+ _cameraService = cameraService;
|
|
|
+ _systemDatabaseService = systemDatabaseService;
|
|
|
+ MessageQueue = new SnackbarMessageQueue(TimeSpan.FromSeconds(1));
|
|
|
+ _calibrationService = calibrationService;
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 方法
|
|
|
+ /// <summary>
|
|
|
+ /// 确定
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteConfirmCommand()
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ IDialogParameters parameters = new DialogParameters();
|
|
|
+ //parameters.Add("Tool", Tool);
|
|
|
+ //parameters.Add("RobotCameraRotationMatrix", RobotCameraRotationMatrix);
|
|
|
+ //parameters.Add("Angle", Angle);
|
|
|
+ RequestClose?.Invoke(new DialogResult(ButtonResult.OK, parameters));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 取消
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteCancelCommand()
|
|
|
+ {
|
|
|
+ IDialogParameters parameters = new DialogParameters();
|
|
|
+ //parameters.Add("Tool", Tool);
|
|
|
+ RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel, parameters));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 开始测试
|
|
|
+ /// </summary>
|
|
|
+ async void ExecuteStartTestCommand()
|
|
|
+ {
|
|
|
+ IsRunning = true;
|
|
|
+ _cts = new CancellationTokenSource();
|
|
|
+ CurrentProgress = 0;
|
|
|
+ SuccessCount = 0;
|
|
|
+ SuccessRate = 0;
|
|
|
+ FailCount = 0;
|
|
|
+ Message = "测试进行中...";
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ while (CurrentProgress < RepeatCount)
|
|
|
+ {
|
|
|
+ if (_cts.Token.IsCancellationRequested)
|
|
|
+ {
|
|
|
+ Message = "测试已取消!";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ (bool isSuccess, object[] Result) = await ExecutePhotoEx(CurrentProgress);
|
|
|
+ if (isSuccess)
|
|
|
+ {
|
|
|
+ SuccessCount++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ FailCount++;
|
|
|
+ }
|
|
|
+ TestResult.Rows.Add(Result);
|
|
|
+ ExecuteSelectColumnCommand();
|
|
|
+
|
|
|
+ CurrentProgress++;
|
|
|
+ SuccessRate = (double)SuccessCount / CurrentProgress * 100;
|
|
|
+ SendTaskMessage($"测试进行中... {CurrentProgress}/{RepeatCount}");
|
|
|
+ }
|
|
|
+ if (CurrentProgress >= RepeatCount)
|
|
|
+ {
|
|
|
+ Message = "测试完成!";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ LogHelper.WriteLogError("视觉静态重复测试时出错!", ex);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ IsRunning = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool CanExecuteStartTestCommand()
|
|
|
+ {
|
|
|
+ return !IsRunning;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 清除数据
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteClearDataCommand()
|
|
|
+ {
|
|
|
+ if (TestResult != null)
|
|
|
+ {
|
|
|
+ TestResult.Rows.Clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 导出测试报告
|
|
|
+ /// 使用 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. 捕获异常并记录日志,提示用户失败信息。
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteExportReportCommand()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ if (TestResult == null || TestResult.Columns.Count == 0 || TestResult.Rows.Count == 0)
|
|
|
+ {
|
|
|
+ SendTaskMessage("无测试数据,无法导出报告。");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var dlg = new Microsoft.Win32.SaveFileDialog
|
|
|
+ {
|
|
|
+ DefaultExt = "pdf",
|
|
|
+ Filter = "PDF 文件 (*.pdf)|*.pdf|所有文件 (*.*)|*.*",
|
|
|
+ FileName = "TestReport.pdf",
|
|
|
+ Title = "保存测试报告为 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("视觉静态精度测试报告", 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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<double>();
|
|
|
+ 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}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 导出测试数据为CSV文件
|
|
|
+ /// </summary>
|
|
|
+ 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<string, string> 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;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 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<double> values = new List<double>();
|
|
|
+ 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}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 选中列时触发
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteSelectColumnCommand()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ //如果选中列为空,则直接返回
|
|
|
+ if (SelectedDataColumn == null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ //获取表的指定列的所有数据集合
|
|
|
+ List<double> dataList = new List<double>();
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 示教点B
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteTeachPointBCommand()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 示教点A
|
|
|
+ /// </summary>
|
|
|
+ void ExecuteTeachPointACommand()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 执行相机拍照并运行 ToolBlock,返回是否成功并通过 out 参数返回 Outputs、图像和图形集合。
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="index"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public Task<(bool isSuccess, object[] Result)> ExecutePhotoEx(int index)
|
|
|
+ {
|
|
|
+ return Task.Run(() =>
|
|
|
+ {
|
|
|
+ CogToolBlockTerminalCollection outputCollection;
|
|
|
+ List<object> result = new List<object>();
|
|
|
+ result.Add(index);
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 1. 采集图像
|
|
|
+ //bool succed = Camera.SetExposureTime(SelectProcedure.ExposureTime);
|
|
|
+ //if (!succed)
|
|
|
+ //{
|
|
|
+ // SendTaskMessage(Lang.相机曝光设置失败);
|
|
|
+ //}
|
|
|
+ //succed = Camera.SetGain(SelectProcedure.Gain);
|
|
|
+ //if (!succed)
|
|
|
+ //{
|
|
|
+ // SendTaskMessage(Lang.相机增益设置失败);
|
|
|
+ //}
|
|
|
+ //DateTime nowtime = DateTime.Now;
|
|
|
+ //LogHelper.WriteLogInfo(Lang.开始采集图像);
|
|
|
+ //var image = Camera.Grab();
|
|
|
+ //if (image == null)
|
|
|
+ //{
|
|
|
+
|
|
|
+ // SendTaskMessage(Lang.图像采集失败);
|
|
|
+ // outputCollection = null;
|
|
|
+ // return (false, result.ToArray());
|
|
|
+ //}
|
|
|
+ //Image = image;
|
|
|
+ //VisionTool.Inputs["InputImage"].Value = image;
|
|
|
+ //LogHelper.WriteLogInfo(string.Format(Lang.采集图像完成用时0ms, (DateTime.Now - nowtime).Milliseconds));
|
|
|
+
|
|
|
+ // 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(Lang.开始运行视觉工具);
|
|
|
+ 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)
|
|
|
+ {
|
|
|
+ var calibration = _calibrationService.GetCalibration(SelectProcedure.CalibrationId);
|
|
|
+ (bool IsSucceed, double X, double Y, double U) = _calibrationService.ConvertPixelToPosition((double.Parse(subitems[0]), double.Parse(subitems[1]), double.Parse(subitems[2])), null, 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(Lang.执行相机取图并执行视觉工具组时出错, ex);
|
|
|
+ outputCollection = null;
|
|
|
+ return (false, result.ToArray());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ public void SendTaskMessage(string msg)
|
|
|
+ {
|
|
|
+ App.Current.Dispatcher.Invoke(() =>
|
|
|
+ {
|
|
|
+ MessageQueue.Clear();
|
|
|
+ MessageQueue.Enqueue(msg);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ bool needConvertToAbsolute = false;
|
|
|
+ /// <summary>
|
|
|
+ /// 根据VisionTool的输出终端,创建DataTable的列
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="outputCollection"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private DataTable CreateResultDataTable(CogToolBlockTerminalCollection outputCollection)
|
|
|
+ {
|
|
|
+ DataTable dt = new DataTable();
|
|
|
+ ResultDataColumns = new ObservableCollection<DataColumnInfo>();
|
|
|
+ //第一列添加序号
|
|
|
+ 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<IDialogResult> RequestClose;
|
|
|
+
|
|
|
+ public bool CanCloseDialog()
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void OnDialogClosed()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public void OnDialogOpened(IDialogParameters parameters)
|
|
|
+ {
|
|
|
+ SelectProduct = parameters.GetValue<ProductModel>("SelectProduct");
|
|
|
+ Camera = _cameraService.GetCamera(SelectProcedure.CameraId);
|
|
|
+ VisionTool = parameters.GetValue<CogToolBlock>("ToolBlock");
|
|
|
+ ProcedureModels = new ObservableCollection<ProcedureModel>();
|
|
|
+ foreach (var item in SelectProduct.CameraProcedures)
|
|
|
+ {
|
|
|
+ foreach (var item1 in item.ProcedureModels)
|
|
|
+ {
|
|
|
+ ProcedureModels.Add(item1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ VisionTool.Run();
|
|
|
+ TestResult = CreateResultDataTable(VisionTool.Outputs);
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+}
|