Bladeren bron

新增视觉静态精度分析功能及相关界面

本次提交实现了“视觉静态精度分析”完整功能模块,包括新增分析对话框及视图模型,支持批量测试、结果统计、图像显示和数据导出。集成了界面按钮与命令,完善了本地化资源,并在项目文件和依赖注入中完成注册。同步优化了相关界面细节,提升了视觉算法调试与验证的效率。
孝锋 徐 8 maanden geleden
bovenliggende
commit
a2311b4fae

+ 1 - 0
TeamAAS-VM/App.xaml.cs

@@ -183,6 +183,7 @@ namespace TeamAAS_VP
             containerRegistry.RegisterDialog<ImageDisplay>();
             containerRegistry.RegisterDialog<TorqueCheck>();
             containerRegistry.RegisterDialog<SetAlarmValue>();
+            containerRegistry.RegisterDialog<VisionStaticAccuracyAnalyzer>();
             //**************************************************************************************
 
             // 注册 SQLSugar 客户端(单例模式)

+ 9 - 0
TeamAAS-VM/Resources/Languages/Lang.Designer.cs

@@ -9817,6 +9817,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 视觉静态精度分析 的本地化字符串。
+        /// </summary>
+        public static string 视觉静态精度分析 {
+            get {
+                return ResourceManager.GetString("视觉静态精度分析", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 角度 (°) 的本地化字符串。
         /// </summary>

+ 3 - 0
TeamAAS-VM/Resources/Languages/Lang.resx

@@ -4058,4 +4058,7 @@
   <data name="设置吸嘴报警值" xml:space="preserve">
     <value>设置吸嘴报警值</value>
   </data>
+  <data name="视觉静态精度分析" xml:space="preserve">
+    <value>视觉静态精度分析</value>
+  </data>
 </root>

+ 8 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -541,6 +541,7 @@
     <Compile Include="ViewModels\Product\ImageDisplayViewModel.cs" />
     <Compile Include="ViewModels\Setting\AddScrewFeederViewModel.cs" />
     <Compile Include="ViewModels\Statistics\LockResultRecoredQueryViewModel.cs" />
+    <Compile Include="ViewModels\Product\VisionStaticAccuracyAnalyzerViewModel.cs" />
     <Compile Include="Views\DebugMod\FocusAnalyzerl.xaml.cs">
       <DependentUpon>FocusAnalyzerl.xaml</DependentUpon>
     </Compile>
@@ -1012,6 +1013,9 @@
     <Compile Include="Views\Product\VisionScriptPage.xaml.cs">
       <DependentUpon>VisionScriptPage.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\Product\VisionStaticAccuracyAnalyzer.xaml.cs">
+      <DependentUpon>VisionStaticAccuracyAnalyzer.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\SettingView.xaml.cs">
       <DependentUpon>SettingView.xaml</DependentUpon>
     </Compile>
@@ -1376,6 +1380,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\Product\VisionStaticAccuracyAnalyzer.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\SettingView.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 47 - 9
TeamAAS-VM/ViewModels/Product/AdvancedProcedureViewModel.cs

@@ -1,4 +1,6 @@
-using Prism.Commands;
+using Cognex.VisionPro.ToolBlock;
+using MathNet.Numerics.LinearAlgebra;
+using Prism.Commands;
 using Prism.Events;
 using Prism.Ioc;
 using Prism.Mvvm;
@@ -8,18 +10,17 @@ using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Linq;
-using TeamAAS_VP.Events;
+using System.Web.Hosting;
+using System.Windows;
+using System.Windows.Media.Media3D;
+using Team.FFFeederService;
+using Team.FFFeederService.Interfaces;
+using TeamAAS_VP;
 using TeamAAS_VP.Core;
+using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
-using TeamAAS_VP;
-using System.Windows.Media.Media3D;
-using Cognex.VisionPro.ToolBlock;
-using System.Web.Hosting;
-using Team.FFFeederService;
-using System.Windows;
 using TeamAAS_VP.Resources.Languages;
-using Team.FFFeederService.Interfaces;
 
 namespace TeamAAS_VP.ViewModels.Product
 {
@@ -102,6 +103,11 @@ namespace TeamAAS_VP.ViewModels.Product
         public DelegateCommand RunVisionProcessCommand =>
             _RunVisionProcessCommand ?? (_RunVisionProcessCommand = new DelegateCommand(ExecuteRunVisionProcessCommand));
 
+        private DelegateCommand _VisionStaticAccuracyAnalyzerCommand;
+        public DelegateCommand VisionStaticAccuracyAnalyzerCommand =>
+            _VisionStaticAccuracyAnalyzerCommand ?? (_VisionStaticAccuracyAnalyzerCommand = new DelegateCommand(ExecuteVisionStaticAccuracyAnalyzerCommand));
+
+        
         #endregion
 
         #region 事件
@@ -216,6 +222,38 @@ namespace TeamAAS_VP.ViewModels.Product
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
             }
         }
+
+        /// <summary>
+        /// 执行视觉静态精度分析仪命令
+        /// </summary>
+        void ExecuteVisionStaticAccuracyAnalyzerCommand()
+        {
+            try
+            {
+                IDialogParameters parameters = new DialogParameters();
+                parameters.Add("ToolBlock", ToolBlockEdit.Subject);
+                parameters.Add("SelectedProcedure", SelectProcedure);
+                _dialogService.Show("VisionStaticAccuracyAnalyzer", parameters, rst =>
+                {
+                    //对话框关闭之后的回调函数,可以在这解析结果。
+                    ButtonResult result1 = rst.Result;
+
+                    if (result1 == ButtonResult.OK)
+                    {
+                        //var param = rst.Parameters.GetValue<RobotTool>("Tool");
+                        //var param1 = rst.Parameters.GetValue<Matrix<double>>("RobotCameraRotationMatrix");
+                        //var param2 = rst.Parameters.GetValue<double>("Angle");
+                        _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
+                    }
+
+                });
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("执行视觉静态精度分析仪命令时出错", ex);
+                MessageBox.Show(ex.Message);
+            }
+        }
         #endregion
 
         #region 继承

+ 496 - 0
TeamAAS-VM/ViewModels/Product/VisionStaticAccuracyAnalyzerViewModel.cs

@@ -0,0 +1,496 @@
+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.Data;
+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;
+
+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;
+
+        //测试过程中控制取消的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;
+
+        /// <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); }
+        }
+
+        //测试结果列表DataTable
+        private DataTable _TestResult;
+        public DataTable TestResult
+        {
+            get { return _TestResult; }
+            set { SetProperty(ref _TestResult, value); }
+        }
+
+        private SnackbarMessageQueue _MessageQueue;
+        public SnackbarMessageQueue MessageQueue
+        {
+            get { return _MessageQueue; }
+            set { SetProperty(ref _MessageQueue, value); }
+        }
+        #endregion
+
+        #region 命令
+        private DelegateCommand _ConfirmCommand;
+        public DelegateCommand ConfirmCommand =>
+            _ConfirmCommand ?? (_ConfirmCommand = new DelegateCommand(ExecuteConfirmCommand).ObservesCanExecute(() => IsRunning));
+
+        private DelegateCommand _CancelCommand;
+        public DelegateCommand CancelCommand =>
+            _CancelCommand ?? (_CancelCommand = new DelegateCommand(ExecuteCancelCommand).ObservesCanExecute(() => 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));
+
+        private DelegateCommand _ExportReportCommand;
+        public DelegateCommand ExportReportCommand =>
+            _ExportReportCommand ?? (_ExportReportCommand = new DelegateCommand(ExecuteExportReportCommand));
+
+        private DelegateCommand _ClearDataCommand;
+        public DelegateCommand ClearDataCommand =>
+            _ClearDataCommand ?? (_ClearDataCommand = new DelegateCommand(ExecuteClearDataCommand));
+
+
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        public VisionStaticAccuracyAnalyzerViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
+            IFeederService feederService, IRobotService robotService, ICameraService cameraService, ISystemDatabaseService systemDatabaseService)
+        {
+            _regionManager = regionManager;
+            _eventAggregator = ea;
+            _container = container;
+            _dialogService = dialogService;
+            _feederService = feederService;
+            _robotService = robotService;
+            _cameraService = cameraService;
+            _systemDatabaseService = systemDatabaseService;
+            MessageQueue= new SnackbarMessageQueue(TimeSpan.FromSeconds(1));
+        }
+
+        #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;
+                    }
+                    await Task.Delay(1000);
+                    CurrentProgress++;
+                    SuccessCount++;
+                    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()
+        {
+
+        }
+
+        /// <summary>
+        /// 导出测试报告
+        /// </summary>
+        void ExecuteExportReportCommand()
+        {
+
+        }
+
+        /// <summary>
+        /// 导出测试数据为CSV文件
+        /// </summary>
+        void ExecuteExportCSVCommand()
+        {
+
+        }
+
+        /// <summary>
+        /// 执行相机拍照并运行 ToolBlock,返回是否成功并通过 out 参数返回 Outputs、图像和图形集合。
+        /// </summary>
+        /// <param name="outputCollection"></param>
+        /// <param name="outImage"></param>
+        /// <param name="outGraphicCollection"></param>
+        /// <returns></returns>
+        public bool ExecutePhotoEx(out CogToolBlockTerminalCollection outputCollection, out ICogImage outImage, out CogGraphicCollection outGraphicCollection)
+        {
+            outImage = null;
+            outGraphicCollection = null;
+            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;
+                }
+                outImage = 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));
+                //        }
+                //    }
+                //}
+
+                // 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;     //获取输出终端集合
+                    ICogRecord record = null;
+                    foreach (CogToolBlockTerminal item in outputCollection)
+                    {
+                        if (item.Value is ICogRecord)
+                            record = item.Value as ICogRecord;
+                    }
+                    outGraphicCollection = outputCollection["Graphic"].Value as CogGraphicCollection;
+                    return true;
+                }
+                else
+                {
+                    SendTaskMessage(Lang.视觉流程执行出错耗时.Replace("{0}", SelectProcedure.Name).Replace("{1}", $"{VisionTool.RunStatus.ProcessingTime:F1}"));
+                    SendTaskMessage(VisionTool.RunStatus.Message);
+                    outputCollection = null;
+                    outGraphicCollection = outputCollection["Graphic"].Value as CogGraphicCollection;
+                    return false;
+                }
+            }
+            catch (Exception ex)
+            {
+                SendTaskMessage(ex.Message);
+                LogHelper.WriteLogError(Lang.执行相机取图并执行视觉工具组时出错, ex);
+                outputCollection = null;
+                return false;
+            }
+        }
+
+        public void SendTaskMessage(string msg)
+        {
+            App.Current.Dispatcher.Invoke(() =>
+            {
+                MessageQueue.Clear();
+                MessageQueue.Enqueue(msg);
+            });
+        }
+
+        /// <summary>
+        /// 相机拍照
+        /// </summary>
+        /// <returns></returns>
+        //private async Task<(bool IsSuccess, double X, double Y, double U, int ImageWidth, int ImageHeight)> CaptureAndProcess()
+        //{
+        //    if (Feeder != null)
+        //    {
+        //        Feeder.OpenLight();
+        //        Feeder.SetLightTimeOut((ushort)SelectCalibration.Brightness);
+        //    }
+        //    await Task.Delay(200);
+        //    //采集图像
+        //    if (Camera != null)
+        //    {
+        //        SelectCalibration.ToolBlock.Inputs["InputImage"].Value = Camera.Grab();
+
+        //    }
+        //    App.Current.Dispatcher.Invoke(() =>
+        //    {
+        //        Graphic = null;
+        //        Image = (ICogImage)SelectCalibration.ToolBlock.Inputs["InputImage"].Value;
+        //    });
+
+        //    SelectCalibration.ToolBlock.Run();
+        //    CogToolBlockTerminalCollection outputCollection = SelectCalibration.ToolBlock.Outputs;
+        //    App.Current.Dispatcher.Invoke(() =>
+        //    {
+        //        Graphic = outputCollection["Graphic"].Value as CogGraphicCollection;
+        //    });
+
+        //    bool Found = (bool)(outputCollection["Found"].Value);
+        //    if (Found)
+        //    {
+        //        var strpoints = outputCollection["Point"].Value.ToString();
+        //        string[] items = strpoints.Split(',');
+        //        double x = double.Parse(items[0]);
+        //        double y = double.Parse(items[1]);
+        //        return (true, x, y, 0, Image.Width, Image.Height);
+        //    }
+        //    else
+        //    {
+        //        return (false, 0, 0, 0, Image.Width, Image.Height);
+        //    }
+        //}
+        #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)
+        {
+            SelectProcedure = parameters.GetValue<ProcedureModel>("SelectedProcedure");
+            Camera = _cameraService.GetCamera(SelectProcedure.CameraId);
+            VisionTool = parameters.GetValue<CogToolBlock>("ToolBlock");
+        }
+        #endregion
+    }
+}

+ 2 - 2
TeamAAS-VM/Views/Calibration/CalibTool.xaml

@@ -20,8 +20,8 @@
              d:Height="600"
              d:Width="1024"
              d:Background="White"
-             MinHeight="500"
-             MinWidth="500"
+             MinHeight="700"
+             MinWidth="700"
              FontFamily="{DynamicResource DefaultFont}">
 
     <prism:Dialog.WindowStyle>

+ 8 - 1
TeamAAS-VM/Views/Product/AdvancedProcedure.xaml

@@ -131,7 +131,14 @@
                                    VerticalAlignment="Center"
                                    Foreground="Gray" />
                     </StackPanel>
-                    
+                    <Button Grid.Row="1"
+                            Margin="5,3"
+                            HorizontalAlignment="Right"
+                            VerticalAlignment="Center"
+                            Content="{lex:Loc 视觉静态精度分析}"
+                            MinWidth="100"
+                            Command="{Binding VisionStaticAccuracyAnalyzerCommand}" />
+
                     <ToggleButton materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=ArrowRight}"
                                   Content="{materialDesign:PackIcon Kind=RobotIndustrial}"
                                   Style="{StaticResource MaterialDesignActionToggleButton}"

+ 424 - 0
TeamAAS-VM/Views/Product/VisionStaticAccuracyAnalyzer.xaml

@@ -0,0 +1,424 @@
+<UserControl x:Class="TeamAAS_VP.Views.Product.VisionStaticAccuracyAnalyzer"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:prism="http://prismlibrary.com/"
+             xmlns:wf="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
+             xmlns:vp="clr-namespace:Cognex.VisionPro;assembly=Cognex.VisionPro.Controls"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:sys="clr-namespace:System;assembly=mscorlib"
+             xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.Product"
+             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             prism:ViewModelLocator.AutoWireViewModel="True"
+             mc:Ignorable="d"
+             d:DataContext="{d:DesignInstance Type=vm:VisionStaticAccuracyAnalyzerViewModel}"
+             d:Height="600"
+             d:Width="1024"
+             MinHeight="700"
+             MinWidth="900"
+             d:Background="White"
+             FontFamily="{DynamicResource DefaultFont}">
+
+    <prism:Dialog.WindowStyle>
+        <Style TargetType="Window">
+            <Setter Property="prism:Dialog.WindowStartupLocation"
+                    Value="CenterScreen" />
+            <Setter Property="AllowDrop"
+                    Value="True" />
+            <Setter Property="ShowInTaskbar"
+                    Value="True" />
+            <!--<Setter Property="SizeToContent"
+                 Value="WidthAndHeight" />-->
+            <!--<Setter Property="WindowState"
+                 Value="Maximized" />-->
+            <Setter Property="Topmost"
+                    Value="True" />
+        </Style>
+    </prism:Dialog.WindowStyle>
+
+    <UserControl.Resources>
+        <!-- 样式定义 -->
+        <Style TargetType="Button"
+               BasedOn="{StaticResource MaterialDesignFlatDarkBgButton}">
+            <Setter Property="Padding"
+                    Value="10 5" />
+            <Setter Property="Margin"
+                    Value="5" />
+            <Setter Property="FontSize"
+                    Value="14" />
+        </Style>
+
+        <Style TargetType="TextBlock">
+            <Setter Property="VerticalAlignment"
+                    Value="Center" />
+            <Setter Property="Margin"
+                    Value="5" />
+        </Style>
+
+        <Style TargetType="TextBox"
+               BasedOn="{StaticResource MaterialDesignTextBox}">
+            <Setter Property="Margin"
+                    Value="5" />
+            <Setter Property="VerticalContentAlignment"
+                    Value="Center" />
+        </Style>
+
+        <Style x:Key="DataGridStyle"
+               TargetType="DataGrid"
+               BasedOn="{StaticResource MaterialDesignDataGrid}">
+            <Setter Property="Margin"
+                    Value="5" />
+            <Setter Property="AutoGenerateColumns"
+                    Value="False" />
+            <Setter Property="CanUserAddRows"
+                    Value="False" />
+            <Setter Property="CanUserDeleteRows"
+                    Value="False" />
+            <Setter Property="IsReadOnly"
+                    Value="True" />
+            <Setter Property="AlternatingRowBackground"
+                    Value="#F5F5F5" />
+        </Style>
+
+        <Style x:Key="TitleStyle"
+               TargetType="TextBlock">
+            <Setter Property="FontSize"
+                    Value="16" />
+            <Setter Property="FontWeight"
+                    Value="Bold" />
+            <Setter Property="Margin"
+                    Value="0 10 0 5" />
+            <Setter Property="Foreground"
+                    Value="#2C3E50" />
+        </Style>
+    </UserControl.Resources>
+
+    <Grid>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="2*" />
+            <ColumnDefinition Width="3*" />
+        </Grid.ColumnDefinitions>
+
+        <!-- 左侧控制面板 -->
+        <Border Grid.Column="0"
+                BorderBrush="#BDC3C7"
+                BorderThickness="0 0 1 0">
+            <Grid>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="Auto" />
+                    <RowDefinition Height="*" />
+                    <RowDefinition Height="Auto" />
+                </Grid.RowDefinitions>
+
+                <!-- 测试设置区域 -->
+                <Border Grid.Row="0"
+                        Background="#ECF0F1"
+                        Padding="10">
+                    <StackPanel>
+                        <TextBlock Style="{StaticResource TitleStyle}"
+                                   Text="测试设置" />
+
+                        <Grid Margin="0 10 0 0">
+                            <Grid.ColumnDefinitions>
+                                <ColumnDefinition Width="Auto" />
+                                <ColumnDefinition Width="*" />
+                                <ColumnDefinition Width="Auto" />
+                            </Grid.ColumnDefinitions>
+
+                            <TextBlock Text="重复次数:"
+                                       Grid.Column="0" />
+                            <mah:NumericUpDown Grid.Column="1"
+                                               Value="{Binding RepeatCount, Mode=TwoWay}"
+                                               Minimum="1"
+                                               d:Value="10"
+                                               TextAlignment="Center"
+                                               HorizontalContentAlignment="Center"
+                                               HorizontalAlignment="Stretch"
+                                               IsEnabled="{Binding IsRunning,Converter={StaticResource InvertBooleanConverter}}" />
+                            <!--<Button x:Name="btnSetCount"
+                                    Grid.Column="2"
+                                    Content="设置"
+                                    Width="60" />-->
+                        </Grid>
+
+                        <Separator Margin="0 15 0 10" />
+
+                        <!-- 控制按钮 -->
+                        <WrapPanel HorizontalAlignment="Center">
+                            <Button x:Name="btnStart"
+                                    Content="开始测试"
+                                    Background="#27AE60"
+                                    Foreground="White"
+                                    MinWidth="100"
+                                    Height="35"
+                                    FontWeight="Bold"
+                                    Command="{Binding StartTestCommand}" />
+
+                            <Button x:Name="btnStop"
+                                    Content="停止"
+                                    Background="#E74C3C"
+                                    Foreground="White"
+                                    MinWidth="100"
+                                    Height="35"
+                                    d:IsEnabled="False"
+                                    Command="{Binding StopTestCommand}" />
+
+                            <Button x:Name="btnPause"
+                                    Content="暂停"
+                                    Background="#F39C12"
+                                    Foreground="White"
+                                    MinWidth="100"
+                                    Height="35"
+                                    d:IsEnabled="False"
+                                    Command="{Binding PauseTestCommand}" />
+                        </WrapPanel>
+                    </StackPanel>
+                </Border>
+
+                <!-- 进度显示 -->
+                <Border Grid.Row="1"
+                        Background="#F8F9FA"
+                        Padding="10">
+                    <StackPanel>
+                        <TextBlock Style="{StaticResource TitleStyle}"
+                                   Text="测试进度" />
+
+                        <Grid Margin="0 10 0 0">
+                            <Grid.RowDefinitions>
+                                <RowDefinition Height="Auto" />
+                                <RowDefinition Height="Auto" />
+                            </Grid.RowDefinitions>
+
+                            <ProgressBar Grid.Row="0"
+                                         Height="25"
+                                         Margin="5"
+                                         Minimum="0"
+                                         Maximum="{Binding RepeatCount,Mode=TwoWay}"
+                                         Value="{Binding CurrentProgress,Mode=TwoWay}"
+                                         IsEnabled="False"
+                                         d:Maximum="100"
+                                         d:Value="50" />
+
+                            <TextBlock Grid.Row="1"
+                                       HorizontalAlignment="Center"
+                                       FontSize="12"
+                                       Foreground="#7F8C8D"
+                                       d:Text="等待开始..."
+                                       Text="{Binding Message}" />
+                        </Grid>
+
+                        <!-- 统计信息 -->
+                        <WrapPanel Margin="0 15 0 0"
+                                   HorizontalAlignment="Center">
+                            <Border Background="#3498DB"
+                                    Padding="10 5"
+                                    Margin="5"
+                                    CornerRadius="3">
+                                <StackPanel>
+                                    <TextBlock Text="已完成"
+                                               Foreground="White"
+                                               FontSize="11" />
+                                    <TextBlock x:Name="txtCompleted"
+                                               Text="{Binding CurrentProgress}"
+                                               d:Text="0"
+                                               Foreground="White"
+                                               FontSize="16"
+                                               FontWeight="Bold"
+                                               HorizontalAlignment="Center" />
+                                </StackPanel>
+                            </Border>
+
+                            <Border Background="#2ECC71"
+                                    Padding="10 5"
+                                    Margin="5"
+                                    CornerRadius="3">
+                                <StackPanel>
+                                    <TextBlock Text="成功率"
+                                               Foreground="White"
+                                               FontSize="11" />
+                                    <TextBlock x:Name="txtSuccessRate"
+                                               d:Text="0%"
+                                               Text="{Binding SuccessRate, StringFormat={}{0:F2}%}"
+                                               Foreground="White"
+                                               FontSize="16"
+                                               FontWeight="Bold"
+                                               HorizontalAlignment="Center" />
+                                </StackPanel>
+                            </Border>
+
+                            <Border Background="#E74C3C"
+                                    Padding="10 5"
+                                    Margin="5"
+                                    CornerRadius="3">
+                                <StackPanel>
+                                    <TextBlock Text="失败数"
+                                               Foreground="White"
+                                               FontSize="11" />
+                                    <TextBlock x:Name="txtFailedCount"
+                                               d:Text="0"
+                                               Text="{Binding FailCount}"
+                                               Foreground="White"
+                                               FontSize="16"
+                                               FontWeight="Bold"
+                                               HorizontalAlignment="Center" />
+                                </StackPanel>
+                            </Border>
+                        </WrapPanel>
+                    </StackPanel>
+                </Border>
+
+                <!-- 测试结果列表 -->
+                <Border Grid.Row="2"
+                        Background="White"
+                        Padding="10">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="Auto" />
+                            <RowDefinition Height="*" />
+                        </Grid.RowDefinitions>
+
+                        <TextBlock Style="{StaticResource TitleStyle}"
+                                   Text="测试结果列表" />
+
+                        <DataGrid x:Name="dgResults"
+                                  Grid.Row="1"
+                                  Style="{StaticResource DataGridStyle}"
+                                  MaxHeight="300"
+                                  AutoGenerateColumns="True"
+                                  CanUserAddRows="False"
+                                  IsReadOnly="True"
+                                  ItemsSource="{Binding TestResult.DefaultView}"
+                                  AutoGeneratingColumn="DataGrid_AutoGeneratingColumn" />
+                    </Grid>
+                </Border>
+
+                <!-- 导出按钮 -->
+                <Border Grid.Row="3"
+                        Background="#ECF0F1"
+                        Padding="10">
+                    <StackPanel>
+                        <WrapPanel HorizontalAlignment="Right">
+                            <Button x:Name="btnExportCSV"
+                                    Content="导出CSV"
+                                    Background="#3498DB"
+                                    Foreground="White"
+                                    MinWidth="100"
+                                    Command="{Binding ExportCSVCommand}" />
+
+                            <Button x:Name="btnExportReport"
+                                    Content="生成报告"
+                                    Background="#9B59B6"
+                                    Foreground="White"
+                                    MinWidth="100"
+                                    Command="{Binding ExportReportCommand}" />
+
+                            <Button x:Name="btnClearData"
+                                    Content="清空数据"
+                                    Background="#95A5A6"
+                                    Foreground="White"
+                                    MinWidth="100"
+                                    Command="{Binding ClearDataCommand}" />
+                        </WrapPanel>
+
+                        <!-- 精度统计 -->
+                        <Border Margin="0 10 0 0"
+                                Background="#F8F9FA"
+                                Padding="10"
+                                CornerRadius="5">
+                            <StackPanel>
+                                <TextBlock Text="精度统计"
+                                           FontWeight="Bold"
+                                           Margin="0 0 0 5" />
+                                <Grid>
+                                    <Grid.ColumnDefinitions>
+                                        <ColumnDefinition Width="*" />
+                                        <ColumnDefinition Width="*" />
+                                    </Grid.ColumnDefinitions>
+
+                                    <StackPanel Grid.Column="0">
+                                        <TextBlock Text="X标准差:">
+                                            <Run x:Name="runStdX"
+                                                 Text="0.000"
+                                                 Foreground="#2C3E50"
+                                                 FontWeight="Bold" />
+                                        </TextBlock>
+                                        <TextBlock Text="Y标准差:">
+                                            <Run x:Name="runStdY"
+                                                 Text="0.000"
+                                                 Foreground="#2C3E50"
+                                                 FontWeight="Bold" />
+                                        </TextBlock>
+                                    </StackPanel>
+
+                                    <StackPanel Grid.Column="1">
+                                        <TextBlock Text="平均偏差:">
+                                            <Run x:Name="runAvgDeviation"
+                                                 Text="0.000"
+                                                 Foreground="#2C3E50"
+                                                 FontWeight="Bold" />
+                                        </TextBlock>
+                                        <TextBlock Text="最大偏差:">
+                                            <Run x:Name="runMaxDeviation"
+                                                 Text="0.000"
+                                                 Foreground="#2C3E50"
+                                                 FontWeight="Bold" />
+                                        </TextBlock>
+                                    </StackPanel>
+                                </Grid>
+                            </StackPanel>
+                        </Border>
+                    </StackPanel>
+                </Border>
+            </Grid>
+        </Border>
+
+        <Grid Grid.Column="1">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="*" />
+                <RowDefinition Height="Auto" />
+            </Grid.RowDefinitions>
+            <!-- 右侧图像显示区域 -->
+            <Border Grid.Column="1"
+                    Background="#2C3E50">
+                <!-- 图像显示画布 -->
+                <wf:WindowsFormsHost>
+                    <vp:CogRecordDisplay x:Name="display" />
+                </wf:WindowsFormsHost>
+            </Border>
+            <StackPanel Grid.Row="3"
+                        Orientation="Horizontal"
+                        VerticalAlignment="Center"
+                        HorizontalAlignment="Right">
+                <Button Height="30"
+                        MinWidth="100"
+                        Content="{lex:Loc 确定}"
+                        materialDesign:ButtonAssist.CornerRadius="10"
+                        ToolTip="{lex:Loc 确定}"
+                        Command="{Binding ConfirmCommand}" />
+                <Button Height="30"
+                        Margin="10,0"
+                        MinWidth="100"
+                        materialDesign:ButtonAssist.CornerRadius="10"
+                        ToolTip="{lex:Loc 取消}"
+                        Content="{lex:Loc 取消}"
+                        Command="{Binding CancelCommand}" />
+            </StackPanel>
+        </Grid>
+
+        <materialDesign:Snackbar Grid.ColumnSpan="2"
+                                 Opacity="0.8"
+                                 HorizontalContentAlignment="Center"
+                                 FontSize="14"
+                                 MessageQueue="{Binding MessageQueue}"
+                                 VerticalAlignment="Bottom" />
+
+    </Grid>
+</UserControl>

+ 93 - 0
TeamAAS-VM/Views/Product/VisionStaticAccuracyAnalyzer.xaml.cs

@@ -0,0 +1,93 @@
+using Cognex.VisionPro;
+using Cognex.VisionPro.Dimensioning;
+using System;
+using System.ComponentModel;
+using System.Data;
+using System.Windows.Controls;
+using TeamAAS_VP.ViewModels.Calibration;
+using TeamAAS_VP.ViewModels.Product;
+
+namespace TeamAAS_VP.Views.Product
+{
+    /// <summary>
+    /// Interaction logic for VisionStaticAccuracyAnalyzer
+    /// </summary>
+    public partial class VisionStaticAccuracyAnalyzer : UserControl
+    {
+        VisionStaticAccuracyAnalyzerViewModel VM;
+        public VisionStaticAccuracyAnalyzer()
+        {
+            InitializeComponent();
+            VM = DataContext as VisionStaticAccuracyAnalyzerViewModel;
+            VM.PropertyChanged += VM_PropertyChanged;
+            this.display.VerticalScrollBar = false;
+            this.display.HorizontalScrollBar = false;
+            this.display.AutoFit = true;
+            this.display.BackColor = System.Drawing.SystemColors.ActiveCaption;
+        }
+
+        private int _imageWidth;
+        private int _imageHeight;
+        private void VM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+        {
+            try
+            {
+                if (e.PropertyName == "Image")
+                {
+                    this.display.Image = VM.Image;
+                    //图像中心点坐标
+                    _imageWidth = this.display.Image.Width;
+                    _imageHeight = this.display.Image.Height;
+                }
+                else if (e.PropertyName == "Graphic")
+                {
+                    this.display.StaticGraphics.Clear();
+                    CogGraphicCollection graphic = new CogGraphicCollection();
+                    CogCreateLineTool line1 = new CogCreateLineTool();
+                    CogCreateLineTool line2 = new CogCreateLineTool();
+                    line1.InputImage = this.display.Image;
+                    line2.InputImage = this.display.Image;
+                    line1.Line.X = _imageWidth / 2;
+                    line1.Line.Y = _imageHeight / 2;
+                    line1.Line.Rotation = 0;
+                    line2.Line.X = _imageWidth / 2;
+                    line2.Line.Y = _imageHeight / 2;
+                    line2.Line.Rotation = Math.PI / 180 * 90;
+                    line1.Run();
+                    line2.Run();
+                    graphic.Add(line1.GetOutputLine());
+                    graphic.Add(line2.GetOutputLine());
+                    this.display.StaticGraphics.Clear();
+                    this.display.StaticGraphics.AddList(graphic, "");
+                    if (VM.Graphic != null)
+                    {
+                        this.display.StaticGraphics.AddList(VM.Graphic, "");
+                    }
+
+                }
+            }
+            catch (System.Exception)
+            {
+
+            }
+        }
+
+        private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
+        {
+            // If the source is a DataTable, we can read the DataColumn's Caption
+            if (e.PropertyDescriptor is PropertyDescriptor pd)
+            {
+                var columnName = pd.Name;
+                var dv = dgResults.ItemsSource as DataView;
+                if (dv != null && dv.Table != null && dv.Table.Columns.Contains(columnName))
+                {
+                    var dataCol = dv.Table.Columns[columnName];
+                    if (!string.IsNullOrEmpty(dataCol.Caption))
+                    {
+                        e.Column.Header = dataCol.Caption;
+                    }
+                }
+            }
+        }
+    }
+}