فهرست منبع

新增视觉图像弹窗及锁付点位自动矫正优化

新增 ImageDisplay 弹窗组件,支持视觉图像与图形显示,并在 App 注册弹窗。完善 PlcPointParamsViewModel,支持锁付点位自动视觉矫正,两点标定,区分执行逻辑,限制示教操作。修正拍照点位数量判断,完善依赖注入。更新 csproj 文件,提升视觉调试与点位矫正体验。
孝锋 徐 8 ماه پیش
والد
کامیت
a8f7679eb3

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

@@ -179,6 +179,7 @@ namespace TeamAAS_VP
             containerRegistry.RegisterDialog<AddLightController>();
             containerRegistry.RegisterDialog<LightChannelList>();
             containerRegistry.RegisterDialog<AutoCorrectLockPoint>();
+            containerRegistry.RegisterDialog<ImageDisplay>();
             //**************************************************************************************
 
             // 注册 SQLSugar 客户端(单例模式)

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

@@ -529,6 +529,7 @@
     <Compile Include="Events\MainTabSwitchNotification.cs" />
     <Compile Include="Models\Feeder\ScrewFeederInfo.cs" />
     <Compile Include="Models\ScrewFeederBatchRecord.cs" />
+    <Compile Include="ViewModels\Product\ImageDisplayViewModel.cs" />
     <Compile Include="ViewModels\Setting\AddScrewFeederViewModel.cs" />
     <Compile Include="ViewModels\Statistics\LockResultRecoredQueryViewModel.cs" />
     <Compile Include="Views\DebugMod\FocusAnalyzerl.xaml.cs">
@@ -956,6 +957,9 @@
     <Compile Include="Views\Product\FeederParams.xaml.cs">
       <DependentUpon>FeederParams.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\Product\ImageDisplay.xaml.cs">
+      <DependentUpon>ImageDisplay.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\Product\LightChannelList.xaml.cs">
       <DependentUpon>LightChannelList.xaml</DependentUpon>
     </Compile>
@@ -1294,6 +1298,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\Product\ImageDisplay.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\Product\LightChannelList.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 1 - 1
TeamAAS-VM/ViewModels/Product/AutoCorrectLockPointViewModel.cs

@@ -180,7 +180,7 @@ namespace TeamAAS_VP.ViewModels.Product
                 }
                 //获取锁付相机拍照点位
                 var screwCameraPoint = product.ScrewCameraPoints;
-                if (screwCameraPoint == null || screwCameraPoint.Count == 0)
+                if (screwCameraPoint == null || screwCameraPoint.Count < 2)
                 {
                     _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未获取到锁付相机拍照点位,无法执行取料拍照点位矫正!", Duration = 1 });
                     return;

+ 41 - 0
TeamAAS-VM/ViewModels/Product/ImageDisplayViewModel.cs

@@ -0,0 +1,41 @@
+using Prism.Commands;
+using Prism.Mvvm;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Models.PLC;
+
+namespace TeamAAS_VP.ViewModels.Product
+{
+    public class ImageDisplayViewModel : BindableBase, IDialogAware
+    {
+
+        public string Title => "视觉运行中...";
+
+        #region 事件
+        public event Action<IDialogResult> RequestClose;
+        #endregion
+
+        public ImageDisplayViewModel()
+        {
+
+        }
+
+        public bool CanCloseDialog()
+        {
+            return true;
+        }
+
+        public void OnDialogClosed()
+        {
+
+        }
+
+        public void OnDialogOpened(IDialogParameters parameters)
+        {
+            
+        }
+    }
+}

+ 272 - 18
TeamAAS-VM/ViewModels/Product/PlcPointParamsViewModel.cs

@@ -1,5 +1,6 @@
 using MaterialDesignThemes.Wpf;
 using MathNet.Numerics.LinearAlgebra;
+using NPOI.SS.Formula.Functions;
 using Prism.Commands;
 using Prism.Events;
 using Prism.Ioc;
@@ -9,6 +10,7 @@ using Prism.Services.Dialogs;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Drawing;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -40,6 +42,8 @@ namespace TeamAAS_VP.ViewModels.Product
         IRobotService _robotService;
         ISystemDatabaseService _systemDatabaseService;
         IRemoteCommandService _remoteCommandService;
+        ICalibrationService _calibrationService;
+        CoordinateTransformer local;
 
         #region 属性
 
@@ -365,7 +369,8 @@ namespace TeamAAS_VP.ViewModels.Product
         #endregion
 
         public PlcPointParamsViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
-            IConfigService configService, IRobotService robotService, ISystemDatabaseService systemDatabaseService, IRemoteCommandService remoteCommandService)
+            IConfigService configService, IRobotService robotService, ISystemDatabaseService systemDatabaseService, IRemoteCommandService remoteCommandService,
+            ICalibrationService calibrationService)
         {
             _regionManager = regionManager;
             _eventAggregator = ea;
@@ -378,6 +383,7 @@ namespace TeamAAS_VP.ViewModels.Product
             management = _container.Resolve<Management>();
             _robotService = robotService;
             _remoteCommandService = remoteCommandService;
+            _calibrationService = calibrationService;
         }
 
         #region 方法
@@ -505,26 +511,33 @@ namespace TeamAAS_VP.ViewModels.Product
             if (Robot == null || !Robot.IsConnected) return;
             try
             {
-                //确认要执行该点位的该运动指令吗
-                if (MessageBox.Show($"确认要执行点位 {SelectedPoint.Number} 的 {motionCmd} 指令吗?", "执行点位", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                if (SelectedIndex == 4)
                 {
-                    return;
+                    await ExecuteAutoCorrectLockPointAndExecuteCommand(SelectedPoint);
                 }
-
-                Robot.Timeout = 6000000;
-                switch (motionCmd)
+                else
                 {
-                    case MotionCommand.Go:
-                        await Robot.GoAsync(point);
-                        break;
-                    case MotionCommand.Jump:
-                        await Robot.JumpAsync(point, 0);
-                        break;
-                    case MotionCommand.Move:
-                        await Robot.MoveAsync(point);
-                        break;
-                }
-                Robot.Timeout = 5000;
+                    //确认要执行该点位的该运动指令吗
+                    if (MessageBox.Show($"确认要执行点位 {SelectedPoint.Number} 的 {motionCmd} 指令吗?", "执行点位", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                    {
+                        return;
+                    }
+
+                    //Robot.Timeout = 6000000;
+                    switch (motionCmd)
+                    {
+                        case MotionCommand.Go:
+                            await Robot.GoAsync(point);
+                            break;
+                        case MotionCommand.Jump:
+                            await Robot.JumpAsync(point, 0);
+                            break;
+                        case MotionCommand.Move:
+                            await Robot.MoveAsync(point);
+                            break;
+                    }
+                    //Robot.Timeout = 5000;
+                }    
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}!", Duration = 0.4 });
             }
             catch (Exception ex)
@@ -545,6 +558,13 @@ namespace TeamAAS_VP.ViewModels.Product
 
             try
             {
+                //如果当前的选项卡处于锁付点位,则提示用户当前模式不允许进行示教
+                if (SelectedIndex == 4)
+                {
+                    MessageBox.Show("当前处于锁付点位模式,不允许进行示教操作!", "示教点位", MessageBoxButton.OK, MessageBoxImage.Warning);
+                    return;
+                }
+
                 //弹窗用户是否确认要示教该点位
                 if (MessageBox.Show($"确认要示教点位 {SelectedPoint.Number} 吗?", "示教点位", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
                 {
@@ -1124,6 +1144,239 @@ namespace TeamAAS_VP.ViewModels.Product
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
             }
         }
+
+        /// <summary>
+        /// 通过相机先定位产品再执行锁付点位
+        /// </summary>
+        /// <param name="point"></param>
+        private async Task ExecuteAutoCorrectLockPointAndExecuteCommand(PlcPoint point)
+        {
+            var waiting = new WaitingControl();
+            Task<object> task;
+            try
+            {
+                if (Robot == null || !Robot.IsConnected) return;
+                //弹窗提示用户是否确认要通过相机自动矫正锁附点位并执行该点位
+                var resultConfirm = MessageBox.Show($"确认要通过相机自动矫正锁附点位并执行点位 {point.Number} 吗?", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
+                if (resultConfirm != MessageBoxResult.Yes)
+                {
+                    return;
+                }
+
+
+                if (Robot == null || !Robot.IsConnected)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "机器人未连接,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return;
+                }
+                //获取当前产品
+                var product = SelectProduct;
+                if (product == null)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未选择产品,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return;
+                }
+
+
+                
+                //如果当前Local为空,则自动进行锁付点位的Local计算
+                if (local == null)
+                {
+                    //show the dialog
+                    task = DialogHost.Show(waiting, "RootDialog", null, null, null);
+                    var isCalibSucceed = await AutoCalibLockPointLocal(product);
+                    if (!isCalibSucceed)
+                    {
+                        return;
+                    }
+                }
+                else
+                {
+                    //提示用户,当前系统检测到进入此界面后已经进行过相机产品定位并计算过Local,是否继续使用该Local进行锁付点位的转换计算
+                    var resultUseExistingLocal = MessageBox.Show("系统检测到进入此界面后已经进行过相机产品定位并计算过Local,是否继续使用该Local进行锁付点位的转换计算?点击“是”继续使用,点击“否”则重新进行锁付点位的Local计算。", "确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
+                    if (resultUseExistingLocal == MessageBoxResult.No)
+                    {
+                        //show the dialog
+                        task = DialogHost.Show(waiting, "RootDialog", null, null, null);
+                        var isCalibSucceed = await AutoCalibLockPointLocal(product);
+                        if (!isCalibSucceed)
+                        {
+                            return;
+                        }
+                    }
+                    else
+                    {
+                        //show the dialog
+                        task = DialogHost.Show(waiting, "RootDialog", null, null, null);
+                    }
+                }
+
+                //将锁付点位转换到世界坐标系
+                var localPoint = new PointF(point.X_Position, point.Y_Position);
+                var worldPoint = local.ToOldCoord(localPoint.X, localPoint.Y);
+                var point1 = point.Clone();
+                point1.X_Position = (float)worldPoint[0];
+                point1.Y_Position = (float)worldPoint[1];
+                //执行该点位
+                //移动机器人至拍照点位,拍照
+                var rpoint = new RPoint()
+                {
+                    X = point.X_Position,
+                    Y = point.Y_Position,
+                    Z = point.Z_Position_Start,
+                };
+                await Robot.CalibMotionAsync(rpoint, 0);
+
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "完成!", Duration = 1 });
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("通过相机自动矫正锁附点位并执行时出错!", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
+            }
+            finally
+            {
+                try
+                {
+                    if (DialogHost.IsDialogOpen("RootDialog"))
+                    {
+                        DialogHost.Close("RootDialog");
+                    }
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        /// <summary>
+        /// 移动相机拍产品两个Msrk点计算Local
+        /// </summary>
+        /// <param name="product"></param>
+        /// <returns></returns>
+        private async Task<bool> AutoCalibLockPointLocal(ProductModel product)
+        {
+            try
+            {
+                //获取锁付相机拍照点位
+                var screwCameraPoint = product.ScrewCameraPoints;
+                if (screwCameraPoint == null || screwCameraPoint.Count < 2)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未获取到锁付相机拍照点位,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+                //获取相机拍照点位对应的锁付点
+                var screwCameraLocalPoint1 = product.ScrewCameraLocalPos1;
+                if (screwCameraLocalPoint1 == null)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未获取到锁付相机拍照点位对应的锁付点,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+                var screwCameraLocalPoint2 = product.ScrewCameraLocalPos2;
+                if (screwCameraLocalPoint2 == null)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未获取到锁付相机拍照点位对应的锁付点,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+                var ProcedureModels = new ObservableCollection<ProcedureModel>();
+                foreach (var item in product.CameraProcedures)
+                {
+                    foreach (var item1 in item.ProcedureModels)
+                    {
+                        ProcedureModels.Add(item1);
+                    }
+                }
+                //获取拍照流程
+                if (product.MoveDownCameraLockPhotoProcedureId == Guid.Empty)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未获取到锁付相机拍照流程,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+                var procedure = ProcedureModels.FirstOrDefault(p => p.Id == product.MoveDownCameraLockPhotoProcedureId);
+                if (procedure == null)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "未获取到锁付相机拍照流程,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+
+                CancellationTokenSource _cts = new CancellationTokenSource();
+                //移动机器人至拍照点位,拍照
+                var rpoint = new RPoint()
+                {
+                    X = screwCameraPoint[0].X_Position,
+                    Y = screwCameraPoint[0].Y_Position,
+                    Z = screwCameraPoint[0].Z_Position_Start,
+                    U = screwCameraPoint[0].U_Position,
+                    V = screwCameraPoint[0].R_Position,
+                };
+                await Robot.CalibMotionAsync(rpoint, 0);
+                await Task.Delay(200);
+                // 执行拍照并获取识别结果
+                var recognitionResult = await _remoteCommandService.ExecutePhotoGetSinglePointWithImage(procedure, null, new double[] { rpoint.X, rpoint.Y, 0 }, _cts.Token);
+                //var Image = recognitionResult.Image;
+                //var Graphic = null;
+                //var Graphic = recognitionResult.Graphic;
+                if (!recognitionResult.IsSucceed)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "拍照获取识别结果失败,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+                //第一个点位的校正结果
+                PointF worldP1 = new PointF((float)recognitionResult.X, (float)recognitionResult.Y);
+                //移动机器人至拍照点位,拍照
+                rpoint = new RPoint()
+                {
+                    X = screwCameraPoint[1].X_Position,
+                    Y = screwCameraPoint[1].Y_Position,
+                    Z = screwCameraPoint[1].Z_Position_Start,
+                    U = screwCameraPoint[1].U_Position,
+                    V = screwCameraPoint[1].R_Position,
+                };
+                await Robot.CalibMotionAsync(rpoint, 0);
+                if (_cts.IsCancellationRequested)
+                    return false;
+                await Task.Delay(200);
+                // 执行拍照并获取识别结果
+                recognitionResult = await _remoteCommandService.ExecutePhotoGetSinglePointWithImage(procedure, null, new double[] { rpoint.X, rpoint.Y, 0 }, _cts.Token);
+                //Image = recognitionResult.Image;
+                //Graphic = null;
+                //Graphic = recognitionResult.Graphic;
+                if (!recognitionResult.IsSucceed)
+                {
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = "拍照获取识别结果失败,无法执行取料拍照点位矫正!", Duration = 1 });
+                    return false;
+                }
+                //第一个点位的校正结果
+                PointF worldP2 = new PointF((float)recognitionResult.X, (float)recognitionResult.Y);
+
+                //计算移动相机与拍照点之间的偏差
+                //获取校准
+                var calibration = _calibrationService.GetCalibration(procedure.CalibrationId);
+
+                //获取mark点
+                var mark = calibration.MarkPoint;
+                //获取中心点
+                var center = calibration.CenterPoint;
+                //计算相机中心与披头之间的坐标偏差
+                var cameraOffsetX = mark.X - center.X;
+                var cameraOffsetY = mark.Y - center.Y;
+
+
+                //根据UpCameraPutResults[]中的索引1、2,和拍照点对应的Local下的坐标,从创建Local坐标系
+                PointF localP1 = new PointF(screwCameraLocalPoint1.X_Position, screwCameraLocalPoint1.Y_Position);
+                PointF localP2 = new PointF(screwCameraLocalPoint2.X_Position, screwCameraLocalPoint2.Y_Position);
+                local = new CoordinateTransformer(worldP1, worldP2, localP1, localP2);
+                return true;
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("计算锁付点位产品坐标系坐标时出错!", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
+                return false;
+            }
+        }
+
         #endregion
 
         #region 继承
@@ -1146,6 +1399,7 @@ namespace TeamAAS_VP.ViewModels.Product
         {
             try
             {
+                local = null;
                 SelectProduct = navigationContext.Parameters.GetValue<ProductModel>("SelectProduct");
 
                 SelectedIndex = 0;

+ 51 - 0
TeamAAS-VM/Views/Product/ImageDisplay.xaml

@@ -0,0 +1,51 @@
+<UserControl x:Class="TeamAAS_VP.Views.Product.ImageDisplay"
+             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: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:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:sys="clr-namespace:System;assembly=mscorlib"
+             xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:localbehaviors="clr-namespace:TeamAAS_VP.Behaviors"
+             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:AutoCorrectLockPointViewModel}"
+             d:Height="768"
+             d:Width="1024"
+             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="Width"
+                    Value="500" />
+            <Setter Property="Height"
+                    Value="400" />
+            <!--<Setter Property="SizeToContent"
+            Value="WidthAndHeight" />-->
+            <!--<Setter Property="WindowState"
+            Value="Maximized" />-->
+            <Setter Property="Topmost"
+                    Value="True" />
+        </Style>
+    </prism:Dialog.WindowStyle>
+    <wf:WindowsFormsHost Grid.Column="0"
+                         Margin="5">
+        <vp:CogRecordDisplay x:Name="display" />
+    </wf:WindowsFormsHost>
+</UserControl>

+ 31 - 0
TeamAAS-VM/Views/Product/ImageDisplay.xaml.cs

@@ -0,0 +1,31 @@
+using Cognex.VisionPro;
+using System.Windows.Controls;
+using TeamAAS_VP.ViewModels.Product;
+
+namespace TeamAAS_VP.Views.Product
+{
+    /// <summary>
+    /// Interaction logic for ImageDisplay
+    /// </summary>
+    public partial class ImageDisplay : UserControl
+    {
+        public ImageDisplay()
+        {
+            InitializeComponent();
+            this.display.VerticalScrollBar = false;
+            this.display.HorizontalScrollBar = false;
+            this.display.AutoFit = true;
+            this.display.BackColor = System.Drawing.SystemColors.ActiveCaption;
+        }
+
+        public void SetImageDisplayViewModel(ICogImage image, Cognex.VisionPro.CogGraphicCollection graphic)
+        {
+            this.display.Image = image;
+            this.display.StaticGraphics.Clear();
+            if (graphic != null)
+            {
+                this.display.StaticGraphics.AddList(graphic, "");
+            }
+        }
+    }
+}