wanghan 7 meses atrás
pai
commit
339fc07cbc

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

@@ -212,6 +212,7 @@ namespace TeamAAS_VP
             containerRegistry.RegisterDialog<AddScrew>();
             containerRegistry.RegisterDialog<ChangeHeader>();
             containerRegistry.RegisterDialog<ChangeNozzle>();
+            containerRegistry.RegisterDialog<PlcPointOffserParams>();
             //**************************************************************************************
 
             // 注册 SQLSugar 客户端(单例模式)

+ 191 - 3
TeamAAS-VM/Core/Management.cs

@@ -2,6 +2,7 @@
 using Cognex.VisionPro;
 using Cognex.VisionPro.ToolBlock;
 using CSScripting;
+using CSScriptLib;
 using MathNet.Numerics.Distributions;
 using MathNet.Numerics.LinearAlgebra;
 using MathNet.Numerics.RootFinding;
@@ -11,6 +12,7 @@ using NPOI.Util;
 using Opc.Ua;
 using OpenCvSharp;
 using OpenCvSharp.Flann;
+using Org.BouncyCastle.Crypto;
 using Prism.Events;
 using Prism.Ioc;
 using Prism.Mvvm;
@@ -55,6 +57,7 @@ using TeamAAS_VP.Models.Product;
 using TeamAAS_VP.Resources.Languages;
 using TeamAAS_VP.Services;
 using TeamAAS_VP.ViewModels.Home;
+using TeamAAS_VP.Views.Product;
 using TouchSocket.Core;
 using TouchSocket.SerialPorts;
 using TouchSocket.Sockets;
@@ -86,6 +89,31 @@ namespace TeamAAS_VP.Core
         Timer yieldtimer;
         private DateTime StartTime = DateTime.Now;
         SerialPortClient client;
+        // Pressure collection
+        /// <summary>
+        /// 压力采集信号控制CTS
+        /// </summary>
+        private CancellationTokenSource _pressureCts;
+        /// <summary>
+        /// 压力采集超时CTS
+        /// </summary>
+        private CancellationTokenSource _pressureTimeoutCts;
+        /// <summary>
+        /// 压力采集CTS
+        /// </summary>
+        private CancellationTokenSource _pressureLinkedCts;
+        /// <summary>
+        /// 压力数据集合
+        /// </summary>
+        private List<(DateTime Timestamp, float Value)> _pressureSamples = new List<(DateTime, float)>();
+        /// <summary>
+        /// 采样间隔毫秒
+        /// </summary>
+        private int _pressureSampleIntervalMs = 50; //采样间隔毫秒
+        /// <summary>
+        /// 超时自动停止秒数
+        /// </summary>
+        private int _pressureMaxDurationSeconds = 30; //超时自动停止秒数
         Stopwatch swCT = new Stopwatch();
         List<LockResult> lockResultsAll = new List<LockResult>();//一个产品的总结果
         #endregion
@@ -1434,11 +1462,21 @@ namespace TeamAAS_VP.Core
                             Int16 cameraIndex = plc.ReadNode<Int16>(addressConfig.In_UpCameraPutNum.Address);
                             SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
 
+                            //视觉流程ID
+                            Guid procedureId = currentProduct.MoveDownCameraLockPhotoProcedureId;
+
+                            //获取锁付拍照的点位
+                            var pos = currentProduct.ScrewCameraPoints.FirstOrDefault(p => p.Number == cameraIndex);
+                            if (pos != null)
+                            {
+                                procedureId = pos.VisionProcessID;
+                            }
+
                             // 获取流程
-                            var process = _productService.GetCurrentProductProcedureModelById(currentProduct.MoveDownCameraLockPhotoProcedureId);
+                            var process = _productService.GetCurrentProductProcedureModelById(procedureId);
                             if (process == null)
                             {
-                                SendTaskMessage(string.Format(Lang.未找到上相机锁附组装拍照对应的视觉流程流程ID0, currentProduct.MoveDownCameraLockPhotoProcedureId), MessageLevel.Alarm);
+                                SendTaskMessage(string.Format(Lang.未找到上相机锁附组装拍照对应的视觉流程流程ID0, procedureId), MessageLevel.Alarm);
                                 await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
                                 return;
                             }
@@ -1613,6 +1651,8 @@ namespace TeamAAS_VP.Core
                             //位置编号
                             Int16 positionIndex = plc.ReadNode<Int16>(addressConfig.In_ProductNum.Address);
                             SendTaskMessage(string.Format(Lang.编号0, positionIndex), MessageLevel.Info);
+                            // 开始采集压力值
+                            StartPressureCollection(currentProduct.Name, _productService.CurrentProductCode, positionIndex);
                         }
                         else
                         {
@@ -1724,6 +1764,8 @@ namespace TeamAAS_VP.Core
                             NumStatistics.NozzleNum = await _systemDatabaseService.IncrementLatestNozzleLockCountAsync(currentUserName);
 
                             await plc.WriteNodeAsync(addressConfig.In_WorkDone.Address, (Int16)0);
+                            // 停止采集压力并保存
+                            await StopAndSavePressureCollectionAsync(currentProduct.Name, _productService.CurrentProductCode, positionIndex);
                         }
                         else
                         {
@@ -1768,9 +1810,14 @@ namespace TeamAAS_VP.Core
 
                                 for (int i = 0; i < currentProduct.ScrewPoints.Count; i++)
                                 {
-                                    var localPoint = new PointF(currentProduct.ScrewPoints[i].X_Position, currentProduct.ScrewPoints[i].Y_Position);
+                                    var localPoint = new PointF(currentProduct.ScrewPoints[i].X_Position + currentProduct.ScrewPoints[i].X_Offset,
+                                        currentProduct.ScrewPoints[i].Y_Position + currentProduct.ScrewPoints[i].Y_Offset);
                                     var worldPoint = local.ToOldCoord(localPoint.X, localPoint.Y);
                                     var point = currentProduct.ScrewPoints[i].Clone();
+
+                                    //锁付点位在Local中已经加了偏移,这里写回PLC时需要将偏移清零,否则写入PLC端时会重复计算偏移
+                                    point.X_Offset = 0;
+                                    point.Y_Offset = 0;
                                     point.X_Position = (float)worldPoint[0];
                                     point.Y_Position = (float)worldPoint[1];
                                     SendTaskMessage($"点:{i},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
@@ -1912,6 +1959,147 @@ namespace TeamAAS_VP.Core
             });
         }
 
+        #region Pressure collection helpers
+        /// <summary>
+        /// 开始采集压力值
+        /// </summary>
+        /// <param name="productName">配方名称</param>
+        /// <param name="productCode">产品编号</param>
+        /// <param name="positionIndex">锁付点编号</param>
+        private void StartPressureCollection(string productName,string productCode, int positionIndex)
+        {
+            try
+            {
+                // cancel existing if any
+                _pressureCts?.Cancel();
+                _pressureTimeoutCts?.Cancel();
+                _pressureLinkedCts?.Dispose();
+                _pressureSamples.Clear();
+
+                _pressureCts = new CancellationTokenSource();
+                _pressureTimeoutCts = new CancellationTokenSource();
+
+                // timeout after configured seconds
+                _pressureTimeoutCts.CancelAfter(TimeSpan.FromSeconds(_pressureMaxDurationSeconds));
+
+                // link tokens so either stop or timeout cancels
+                _pressureLinkedCts = CancellationTokenSource.CreateLinkedTokenSource(_pressureCts.Token, _pressureTimeoutCts.Token);
+
+                var token = _pressureLinkedCts.Token;
+                var addressConfig = _configService.GetPlcAddresses();
+                var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
+                if (plc == null || !plc.IsConnected) return;
+                Task.Run(async () =>
+                {
+                    SendTaskMessage($"Pressure collection started for position {positionIndex}", MessageLevel.Debug);
+                    while (!token.IsCancellationRequested)
+                    {
+                        try
+                        {
+                            float pressure = plc.ReadNode<float>(addressConfig.In_PressureValue.Address);
+                            lock (_pressureSamples)
+                            {
+                                _pressureSamples.Add((DateTime.Now, pressure));
+                            }
+                        }
+                        catch (Exception ex)
+                        {
+                            LogHelper.WriteLogError("采集压力值时出错", ex);
+                        }
+                        await Task.Delay(_pressureSampleIntervalMs);
+                    }
+                    // if cancelled due to timeout
+                    if (_pressureTimeoutCts.IsCancellationRequested && !_pressureCts.IsCancellationRequested)
+                    {
+                        SendTaskMessage("Pressure collection timed out, saving collected data.", MessageLevel.Alarm);
+                        await StopAndSavePressureCollectionAsync(productName, productCode,positionIndex);
+                    }
+                }, token);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("StartPressureCollection error", ex);
+            }
+        }
+
+        /// <summary>
+        /// 停止采集并保存压力数据
+        /// </summary>
+        /// <param name="productName"></param>
+        /// <param name="productCode"></param>
+        /// <param name="positionIndex"></param>
+        /// <returns></returns>
+        private async Task StopAndSavePressureCollectionAsync(string productName, string productCode, int positionIndex)
+        {
+            try
+            {
+                // cancel sampling
+                _pressureCts?.Cancel();
+                // allow worker to observe cancel
+                await Task.Delay(50);
+
+                List<(DateTime Timestamp, float Value)> snapshot;
+                lock (_pressureSamples)
+                {
+                    snapshot = new List<(DateTime, float)>(_pressureSamples);
+                }
+
+                if (snapshot.Count == 0)
+                {
+                    SendTaskMessage("No pressure samples collected.", MessageLevel.Info);
+                    return;
+                }
+
+                // save to file under configured path
+                try
+                {
+                    //获取系统参数
+                    var systemConfig = _configService.GetSystemConfiguration();
+                    //判断是否保存锁付曲线
+                    if (systemConfig.IsSaveScrewCurve)
+                    {
+                        string basePath = systemConfig?.ScrewCurvePath ?? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+                        string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
+                        string dirPath = Path.Combine(basePath, datePath, productName ?? "UnknownProduct", productCode ?? DateTime.Now.ToString("yyyyMMddHHmmssfff"));
+                        if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
+                        string fileName = $"pressure-{productCode}-{positionIndex}-{DateTime.Now.ToString("HHmmss")}.csv";
+                        string fullPath = Path.Combine(dirPath, fileName);
+                        var sb = new StringBuilder();
+                        sb.AppendLine("Timestamp,Value");
+                        foreach (var s in snapshot)
+                        {
+                            sb.AppendLine($"{s.Timestamp:O},{s.Value}");
+                        }
+
+                        File.WriteAllText(fullPath, sb.ToString());
+                        SendTaskMessage($"Pressure samples saved to {fullPath}", MessageLevel.Debug);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError("保存压力采样文件时出错", ex);
+                    SendTaskMessage("保存压力采样文件时出错: " + ex.Message, MessageLevel.Error);
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("StopAndSavePressureCollectionAsync error", ex);
+            }
+            finally
+            {
+                try
+                {
+                    _pressureLinkedCts?.Cancel();
+                    _pressureLinkedCts?.Dispose();
+                    _pressureTimeoutCts?.Dispose();
+                    _pressureCts?.Dispose();
+                    _pressureSamples.Clear();
+                }
+                catch { }
+            }
+        }
+        #endregion
+
         private async void DoYieldTime(object state)
         {
             try

+ 157 - 7
TeamAAS-VM/Models/PLC/PlcPoint.cs

@@ -13,6 +13,9 @@ namespace TeamAAS_VP.Models.PLC
     public class PlcPoint : BindableBase
     {
         private int _Number;
+        /// <summary>
+        /// 点位编号
+        /// </summary>
         public int Number
         {
             get { return _Number; }
@@ -20,6 +23,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private string _Label;
+        /// <summary>
+        /// 点位标签
+        /// </summary>
         public string Label
         {
             get { return _Label; }
@@ -27,6 +33,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _X_Position;
+        /// <summary>
+        /// X轴位置
+        /// </summary>
         public float X_Position
         {
             get { return _X_Position; }
@@ -34,6 +43,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _X_Velocity;
+        /// <summary>
+        /// X轴速度
+        /// </summary>
         public float X_Velocity
         {
             get { return _X_Velocity; }
@@ -41,6 +53,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Y_Position;
+        /// <summary>
+        /// Y轴位置
+        /// </summary>
         public float Y_Position
         {
             get { return _Y_Position; }
@@ -48,6 +63,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Y_Velocity;
+        /// <summary>
+        /// Y轴速度
+        /// </summary>
         public float Y_Velocity
         {
             get { return _Y_Velocity; }
@@ -55,6 +73,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Z_Position_Start;
+        /// <summary>
+        /// Z轴起点位置
+        /// </summary>
         public float Z_Position_Start
         {
             get { return _Z_Position_Start; }
@@ -62,6 +83,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Z_Velocity_Start;
+        /// <summary>
+        /// Z轴起点速度
+        /// </summary>
         public float Z_Velocity_Start
         {
             get { return _Z_Velocity_Start; }
@@ -69,6 +93,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Z_Position_Stop;
+        /// <summary>
+        /// Z轴终点位置
+        /// </summary>
         public float Z_Position_Stop
         {
             get { return _Z_Position_Stop; }
@@ -76,6 +103,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Z_Velocity_Stop;
+        /// <summary>
+        /// Z轴终点速度
+        /// </summary>
         public float Z_Velocity_Stop
         {
             get { return _Z_Velocity_Stop; }
@@ -83,6 +113,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _U_Position;
+        /// <summary>
+        /// U轴位置
+        /// </summary>
         public float U_Position
         {
             get { return _U_Position; }
@@ -90,6 +123,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _U_Velocity;
+        /// <summary>
+        /// U轴速度
+        /// </summary>
         public float U_Velocity
         {
             get { return _U_Velocity; }
@@ -97,6 +133,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _R_Position;
+        /// <summary>
+        /// R轴位置
+        /// </summary>
         public float R_Position
         {
             get { return _R_Position; }
@@ -104,6 +143,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _R_Velocity;
+        /// <summary>
+        /// R轴速度
+        /// </summary>
         public float R_Velocity
         {
             get { return _R_Velocity; }
@@ -111,6 +153,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private float _Torque;
+        /// <summary>
+        /// 扭矩值
+        /// </summary>
         public float Torque
         {
             get { return _Torque; }
@@ -118,6 +163,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private Int16 _Feeder;
+        /// <summary>
+        /// Feeder编号
+        /// </summary>
         public Int16 Feeder
         {
             get { return _Feeder; }
@@ -125,6 +173,9 @@ namespace TeamAAS_VP.Models.PLC
         }
 
         private Int16 _ScrewProNum;
+        /// <summary>
+        /// 电批程序号
+        /// </summary>
         public Int16 ScrewProNum
         {
             get { return _ScrewProNum; }
@@ -133,12 +184,95 @@ namespace TeamAAS_VP.Models.PLC
 
         //描述
         private string _Description;
+        /// <summary>
+        /// 点位描述
+        /// </summary>
         public string Description
         {
             get { return _Description; }
             set { SetProperty(ref _Description, value); }
         }
 
+        private Guid _VisionProcessID;
+        /// <summary>
+        /// 视觉流程ID
+        /// </summary>
+        public Guid VisionProcessID
+        {
+            get { return _VisionProcessID; }
+            set { SetProperty(ref _VisionProcessID, value); }
+        }
+
+        private string _VisionProcessName;
+        /// <summary>
+        /// 视觉流程名称
+        /// </summary>
+        public string VisionProcessName
+        {
+            get { return _VisionProcessName; }
+            set { SetProperty(ref _VisionProcessName, value); }
+        }
+
+        private float _X_Offset;
+        /// <summary>
+        /// X轴补偿值
+        /// </summary>
+        public float X_Offset
+        {
+            get { return _X_Offset; }
+            set { SetProperty(ref _X_Offset, value); }
+        }
+
+        private float _Y_Offset;
+        /// <summary>
+        /// Y轴补偿值
+        /// </summary>
+        public float Y_Offset
+        {
+            get { return _Y_Offset; }
+            set { SetProperty(ref _Y_Offset, value); }
+        }
+
+        private float _Z_Start_Offset;
+        /// <summary>
+        /// Z轴起点补偿值
+        /// </summary>
+        public float Z_Start_Offset
+        {
+            get { return _Z_Start_Offset; }
+            set { SetProperty(ref _Z_Start_Offset, value); }
+        }
+
+        private float _Z_Stop_Offset;
+        /// <summary>
+        /// Z轴终点补偿值
+        /// </summary>
+        public float Z_Stop_Offset
+        {
+            get { return _Z_Stop_Offset; }
+            set { SetProperty(ref _Z_Stop_Offset, value); }
+        }
+
+        private float _U_Offset;
+        /// <summary>
+        /// U轴补偿值
+        /// </summary>
+        public float U_Offset
+        {
+            get { return _U_Offset; }
+            set { SetProperty(ref _U_Offset, value); }
+        }
+
+        private float _R_Offset;
+        /// <summary>
+        /// R轴补偿值
+        /// </summary>
+        public float R_Offset
+        {
+            get { return _R_Offset; }
+            set { SetProperty(ref _R_Offset, value); }
+        }
+
         public PlcPoint()
         {
             Number = 0;
@@ -157,6 +291,14 @@ namespace TeamAAS_VP.Models.PLC
             Torque = 0;
             Feeder = 0;
             ScrewProNum = 0;
+            Description = string.Empty;
+            X_Offset = 0;
+            Y_Offset = 0;
+            Z_Start_Offset = 0;
+            Z_Stop_Offset = 0;
+            U_Offset = 0;
+            R_Offset = 0;
+
         }
 
         public PlcPoint Clone()
@@ -180,7 +322,15 @@ namespace TeamAAS_VP.Models.PLC
                 Torque = this.Torque,
                 Feeder = this.Feeder,
                 ScrewProNum = this.ScrewProNum,
-                Description = this.Description
+                Description = this.Description,
+                VisionProcessID = this.VisionProcessID,
+                VisionProcessName = this.VisionProcessName,
+                X_Offset = this.X_Offset,
+                Y_Offset = this.Y_Offset,
+                Z_Start_Offset = this.Z_Start_Offset,
+                Z_Stop_Offset = this.Z_Stop_Offset,
+                U_Offset = this.U_Offset,
+                R_Offset = this.R_Offset
             };
         }
 
@@ -193,17 +343,17 @@ namespace TeamAAS_VP.Models.PLC
         public async Task<bool> WriteToPlcAddress(PlcPointAddress plcAddress, OpcUaClientPLC pLC)
         {
             Dictionary<string, object> nodeValues = new Dictionary<string, object>();
-            nodeValues.Add(string.Format(plcAddress.X_Position.Address, Number), X_Position);
+            nodeValues.Add(string.Format(plcAddress.X_Position.Address, Number), X_Position + X_Offset);
             nodeValues.Add(string.Format(plcAddress.X_Velocity.Address, Number), X_Velocity);
-            nodeValues.Add(string.Format(plcAddress.Y_Position.Address, Number), Y_Position);
+            nodeValues.Add(string.Format(plcAddress.Y_Position.Address, Number), Y_Position + Y_Offset);
             nodeValues.Add(string.Format(plcAddress.Y_Velocity.Address, Number), Y_Velocity);
-            nodeValues.Add(string.Format(plcAddress.Z_Position_Start.Address, Number), Z_Position_Start);
+            nodeValues.Add(string.Format(plcAddress.Z_Position_Start.Address, Number), Z_Position_Start + Z_Start_Offset);
             nodeValues.Add(string.Format(plcAddress.Z_Velocity_Start.Address, Number), Z_Velocity_Start);
-            nodeValues.Add(string.Format(plcAddress.Z_Position_Stop.Address, Number), Z_Position_Stop);
+            nodeValues.Add(string.Format(plcAddress.Z_Position_Stop.Address, Number), Z_Position_Stop + Z_Stop_Offset);
             nodeValues.Add(string.Format(plcAddress.Z_Velocity_Stop.Address, Number), Z_Velocity_Stop);
-            nodeValues.Add(string.Format(plcAddress.U_Position.Address, Number), U_Position);
+            nodeValues.Add(string.Format(plcAddress.U_Position.Address, Number), U_Position + U_Offset);
             nodeValues.Add(string.Format(plcAddress.U_Velocity.Address, Number), U_Velocity);
-            nodeValues.Add(string.Format(plcAddress.R_Position.Address, Number), R_Position);
+            nodeValues.Add(string.Format(plcAddress.R_Position.Address, Number), R_Position + R_Offset);
             nodeValues.Add(string.Format(plcAddress.R_Velocity.Address, Number), R_Velocity);
             nodeValues.Add(string.Format(plcAddress.Torque.Address, Number), Torque);
             nodeValues.Add(string.Format(plcAddress.Feeder.Address, Number), Feeder);

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

@@ -546,6 +546,7 @@
       <DependentUpon>Lang.resx</DependentUpon>
     </Compile>
     <Compile Include="Services\MesService.cs" />
+    <Compile Include="ViewModels\Product\PlcPointOffserParamsViewModel.cs" />
     <Compile Include="ViewModels\User\CardLoginWindowViewModel.cs" />
     <Compile Include="ViewModels\Home\ChangeNozzleViewModel.cs" />
     <Compile Include="ViewModels\Home\ChangeHeaderViewModel.cs" />
@@ -997,6 +998,9 @@
     <Compile Include="Views\Product\PalletManage.xaml.cs">
       <DependentUpon>PalletManage.xaml</DependentUpon>
     </Compile>
+    <Compile Include="Views\Product\PlcPointOffserParams.xaml.cs">
+      <DependentUpon>PlcPointOffserParams.xaml</DependentUpon>
+    </Compile>
     <Compile Include="Views\Product\PlcPointParams.xaml.cs">
       <DependentUpon>PlcPointParams.xaml</DependentUpon>
     </Compile>
@@ -1366,6 +1370,10 @@
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
     </Page>
+    <Page Include="Views\Product\PlcPointOffserParams.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="Views\Product\PlcPointParams.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>

+ 124 - 0
TeamAAS-VM/ViewModels/Product/PlcPointOffserParamsViewModel.cs

@@ -0,0 +1,124 @@
+using Prism.Commands;
+using Prism.Mvvm;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Models;
+using TeamAAS_VP.Models.PLC;
+using TeamAAS_VP.Resources.Languages;
+using TeamAAS_VP.Views.Setting;
+
+namespace TeamAAS_VP.ViewModels.Product
+{
+    public class PlcPointOffserParamsViewModel : BindableBase, IDialogAware
+    {
+
+        #region 属性
+        private ObservableCollection<PlcPoint> _Points = new ObservableCollection<PlcPoint>();
+        /// <summary>
+        /// 点位集合
+        /// </summary>
+        public ObservableCollection<PlcPoint> Points
+        {
+            get { return _Points; }
+            set { SetProperty(ref _Points, value); }
+        }
+        #endregion
+
+        #region 命令
+        private DelegateCommand _ConfirmCommand;
+        public DelegateCommand ConfirmCommand =>
+            _ConfirmCommand ?? (_ConfirmCommand = new DelegateCommand(ExecuteConfirmCommand, CanExecuteConfirmCommand));
+
+        private DelegateCommand _CancelCommand;
+        public DelegateCommand CancelCommand =>
+            _CancelCommand ?? (_CancelCommand = new DelegateCommand(ExecuteCancelCommand));
+
+        private DelegateCommand _ClearAllOffsetCommand;
+        public DelegateCommand ClearAllOffsetCommand =>
+            _ClearAllOffsetCommand ?? (_ClearAllOffsetCommand = new DelegateCommand(ExecuteClearAllOffsetCommand));
+
+
+        #endregion
+
+        public PlcPointOffserParamsViewModel()
+        {
+
+        }
+
+        /// <summary>
+        /// 确认按钮
+        /// </summary>
+        /// <returns></returns>
+        bool CanExecuteConfirmCommand()
+        {
+            return true;
+        }
+
+        void ExecuteConfirmCommand()
+        {
+            IDialogParameters parameters = new DialogParameters();
+            parameters.Add("Points", Points.ToArray());
+            RequestClose?.Invoke(new DialogResult(ButtonResult.OK, parameters));
+        }
+
+        /// <summary>
+        /// 取消按钮
+        /// </summary>
+        void ExecuteCancelCommand()
+        {
+            IDialogParameters parameters = new DialogParameters();
+            RequestClose?.Invoke(new DialogResult(ButtonResult.Cancel, parameters));
+        }
+
+        /// <summary>
+        /// 清除所有补偿值
+        /// </summary>
+        void ExecuteClearAllOffsetCommand()
+        {
+            foreach (var point in Points)
+            {
+                point.X_Offset = 0;
+                point.Y_Offset = 0;
+                point.Z_Start_Offset = 0;
+                point.Z_Stop_Offset = 0;
+                point.U_Offset = 0;
+                point.R_Offset = 0;
+            }
+
+        }
+
+        private string _Title;
+        public string Title
+        {
+            get { return _Title; }
+            set { SetProperty(ref _Title, value); }
+        }
+
+        public event Action<IDialogResult> RequestClose;
+
+        public bool CanCloseDialog()
+        {
+            return true;
+        }
+
+        public void OnDialogClosed()
+        {
+
+        }
+
+        public void OnDialogOpened(IDialogParameters parameters)
+        {
+            Title = parameters.GetValue<string>("Title");
+            var pts = parameters.GetValue<PlcPoint[]>("Points");
+            if (pts != null)
+            {
+                Points = new ObservableCollection<PlcPoint>(pts);
+            }
+        }
+    }
+}

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

@@ -45,7 +45,7 @@ namespace TeamAAS_VP.ViewModels.Product
         IRemoteCommandService _remoteCommandService;
         ICalibrationService _calibrationService;
         ICameraService _cameraService;
-        CoordinateTransformer local=null;
+        CoordinateTransformer local = null;
 
         #region 属性
 
@@ -292,10 +292,12 @@ namespace TeamAAS_VP.ViewModels.Product
         public CameraInfo SelectedCamera
         {
             get { return _SelectedCamera; }
-            set { SetProperty(ref _SelectedCamera, value);
-                if (value!=null)
+            set
+            {
+                SetProperty(ref _SelectedCamera, value);
+                if (value != null)
                 {
-                    if(Camera!=null)
+                    if (Camera != null)
                     {
                         if (Camera.IsGrabbing)
                         {
@@ -335,7 +337,9 @@ namespace TeamAAS_VP.ViewModels.Product
         public bool IsLeftDrawerOpen
         {
             get { return _IsLeftDrawerOpen; }
-            set { SetProperty(ref _IsLeftDrawerOpen, value);
+            set
+            {
+                SetProperty(ref _IsLeftDrawerOpen, value);
                 if (!value)
                 {
                     if (IsGrap)
@@ -438,7 +442,7 @@ namespace TeamAAS_VP.ViewModels.Product
 
         private DelegateCommand _StartGrabbingCommand;
         public DelegateCommand StartGrabbingCommand =>
-            _StartGrabbingCommand ?? (_StartGrabbingCommand = new DelegateCommand(ExecuteStartGrabbingCommand, CanExecuteStartGrabbingCommand).ObservesProperty(() => IsGrap).ObservesProperty(()=> SelectedCamera));
+            _StartGrabbingCommand ?? (_StartGrabbingCommand = new DelegateCommand(ExecuteStartGrabbingCommand, CanExecuteStartGrabbingCommand).ObservesProperty(() => IsGrap).ObservesProperty(() => SelectedCamera));
 
         private DelegateCommand _StopGrabbingCommand;
         public DelegateCommand StopGrabbingCommand =>
@@ -448,7 +452,11 @@ namespace TeamAAS_VP.ViewModels.Product
         public DelegateCommand DynamicTestAnalyzerCommand =>
             _DynamicTestAnalyzerCommand ?? (_DynamicTestAnalyzerCommand = new DelegateCommand(ExecuteDynamicTestAnalyzerCommand));
 
-        
+        private DelegateCommand _SetOffsetCommand;
+        public DelegateCommand SetOffsetCommand =>
+            _SetOffsetCommand ?? (_SetOffsetCommand = new DelegateCommand(ExecuteSetOffsetCommand));
+
+
         #endregion
 
         #region 事件
@@ -625,7 +633,7 @@ namespace TeamAAS_VP.ViewModels.Product
                             break;
                     }
                     //Robot.Timeout = 5000;
-                }    
+                }
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}!", Duration = 0.4 });
             }
             catch (Exception ex)
@@ -657,7 +665,7 @@ namespace TeamAAS_VP.ViewModels.Product
                     }
 
                     //弹窗用户是否确认要示教该点位
-                    if (MessageBox.Show(string.Format(Lang.确认要示教点位0吗,SelectedPoint.Number), Lang.示教点位, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                    if (MessageBox.Show(string.Format(Lang.确认要示教点位0吗, SelectedPoint.Number), Lang.示教点位, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
                     {
                         return;
                     }
@@ -976,10 +984,17 @@ namespace TeamAAS_VP.ViewModels.Product
                         int startNumber = view.StartIndex;
 
                         //弹窗提示用户是否确认要导入从该编号开始的点位,点位数量为cadPoints.Count
-                        if (MessageBox.Show(string.Format(Lang.确认要从点位编号0开始导入1个点位吗, startNumber,cadPoints.Count), Lang.导入点位, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        if (MessageBox.Show(string.Format(Lang.确认要从点位编号0开始导入1个点位吗, startNumber, cadPoints.Count), Lang.导入点位, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
                         {
                             return;
                         }
+                        //是否对原有点位的XY补偿值进行清零?
+                        bool clearCompensate = false;
+                        if (MessageBox.Show("是否对原有点位的XY补偿值进行清零?", Lang.确认, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
+                        {
+                            clearCompensate = true;
+                        }
+
                         //导入点位
                         for (int i = 0; i < cadPoints.Count; i++)
                         {
@@ -990,7 +1005,11 @@ namespace TeamAAS_VP.ViewModels.Product
                             {
                                 existingPoint.X_Position = point.X;
                                 existingPoint.Y_Position = point.Y;
-
+                                if (clearCompensate)
+                                {
+                                    existingPoint.X_Offset = 0;
+                                    existingPoint.Y_Offset = 0;
+                                }
                             }
                             else
                             {
@@ -1171,7 +1190,7 @@ namespace TeamAAS_VP.ViewModels.Product
             {
                 if (point.Z_Position_Stop >= point.Z_Position_Start)
                 {
-                    MessageBox.Show(string.Format(Lang.点位编号0的终点坐标大于等于起始坐标请调整终点坐标, point.Number ), Lang.警告, MessageBoxButton.OK, MessageBoxImage.Warning);
+                    MessageBox.Show(string.Format(Lang.点位编号0的终点坐标大于等于起始坐标请调整终点坐标, point.Number), Lang.警告, MessageBoxButton.OK, MessageBoxImage.Warning);
                 }
             }
 
@@ -1246,8 +1265,8 @@ namespace TeamAAS_VP.ViewModels.Product
                     if (result1 == ButtonResult.OK)
                     {
                         var param = rst.Parameters.GetValue<ObservableCollection<PointEx>>("CorrectPoints");
-                        var local1= rst.Parameters.GetValue<CoordinateTransformer>("Local");
-                        if (local1!=null)
+                        var local1 = rst.Parameters.GetValue<CoordinateTransformer>("Local");
+                        if (local1 != null)
                         {
                             local = local1;
                         }
@@ -1255,6 +1274,9 @@ namespace TeamAAS_VP.ViewModels.Product
                         {
                             SelectProduct.ScrewPoints[i].X_Position = param[i].PostX;
                             SelectProduct.ScrewPoints[i].Y_Position = param[i].PostY;
+                            //XY的补偿值清零
+                            SelectProduct.ScrewPoints[i].X_Offset = 0;
+                            SelectProduct.ScrewPoints[i].Y_Offset = 0;
                         }
 
                         _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
@@ -1309,7 +1331,7 @@ namespace TeamAAS_VP.ViewModels.Product
                 }
 
 
-                
+
                 //如果当前Local为空,则自动进行锁付点位的Local计算
                 if (local == null)
                 {
@@ -1545,7 +1567,7 @@ namespace TeamAAS_VP.ViewModels.Product
         /// </summary>
         void ExecuteStopGrabbingCommand()
         {
-            
+
             if (Camera != null)
                 Camera.StopGrabbing();
             IsGrap = false;
@@ -1586,6 +1608,74 @@ namespace TeamAAS_VP.ViewModels.Product
                      }
                  }));
         }
+
+        /// <summary>
+        /// 设置偏移
+        /// </summary>
+        void ExecuteSetOffsetCommand()
+        {
+            if (SelectedIndex<0) return;
+            IDialogParameters parameters1 = new DialogParameters();
+            if (SelectedIndex == 0)
+            {
+                parameters1.Add("Title", "取料拍照点位-补偿值设置");
+            }
+            else if (SelectedIndex == 1)
+            {
+                parameters1.Add("Title", "取料点位-补偿值设置");
+            }
+            else if (SelectedIndex == 2)
+            {
+                parameters1.Add("Title", "下相机二次定位点位-补偿值设置");
+            }
+            else if (SelectedIndex == 3)
+            {
+                parameters1.Add("Title", "锁附拍照点位-补偿值设置");
+            }
+            else if (SelectedIndex == 4)
+            {
+                parameters1.Add("Title", "锁附点位-补偿值设置");
+            }
+            else if (SelectedIndex == 5)
+            {
+                parameters1.Add("Title", "复检点位-补偿值设置");
+            }
+            else
+            {
+                parameters1.Add("Title", "点位-补偿值设置");
+            }
+
+            List<PlcPoint> _points = new List<PlcPoint>();
+            foreach (var item in Points)
+            {
+                _points.Add(item.Clone());
+            }
+            parameters1.Add("Points", _points.ToArray());
+            _dialogService.ShowDialog("PlcPointOffserParams", parameters1, rst =>
+            {
+                //对话框关闭之后的回调函数,可以在这解析结果。
+                ButtonResult result1 = rst.Result;
+
+                if (result1 == ButtonResult.OK)
+                {
+                    var param = rst.Parameters.GetValue<PlcPoint[]>("Points");
+
+                    //更新点位
+                    for (int i = 0; i < Points.Count; i++)
+                    {
+                        Points[i].X_Offset = param[i].X_Offset;
+                        Points[i].Y_Offset = param[i].Y_Offset;
+                        Points[i].Z_Start_Offset = param[i].Z_Start_Offset;
+                        Points[i].Z_Stop_Offset = param[i].Z_Stop_Offset;
+                        Points[i].U_Offset = param[i].U_Offset;
+                        Points[i].R_Offset = param[i].R_Offset;
+                    }
+
+                    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.完成}", Duration = 1 });
+                }
+
+            });
+        }
         #endregion
 
         #region 继承
@@ -1649,7 +1739,7 @@ namespace TeamAAS_VP.ViewModels.Product
                 LockScrewProgramNumber = SelectProduct.ScrewPoints.FirstOrDefault()?.ScrewProNum ?? 1;
 
                 CameraList = new ObservableCollection<CameraInfo>(_configService.GetAllCameras());
-               
+
             }
             catch (Exception ex)
             {
@@ -1687,7 +1777,7 @@ namespace TeamAAS_VP.ViewModels.Product
                         Camera.StopGrabbing();
                     }
                     Camera.ImageCallbackEvent -= Camera_ImageCallbackEvent;
-                    
+
                     Camera = null;
                 }
                 SelectedCamera = null;

+ 17 - 1
TeamAAS-VM/ViewModels/Product/VisionStaticAccuracyAnalyzerViewModel.cs

@@ -967,8 +967,24 @@ namespace TeamAAS_VP.ViewModels.Product
                                             //如果需要转换为绝对坐标,则再创建绝对X、绝对Y两列
                                             if (needConvertToAbsolute)
                                             {
+                                                double[] point = new double[3];
                                                 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);
+
+                                                var Robot = _robotService.GetRobot(SelectProcedure.RobotId);
+                                                if (Robot!=null)
+                                                {
+                                                    //获取机器人当前坐标系
+                                                    RPoint rPoint = Robot.GetRobotPos();
+
+                                                    point[0] = rPoint.X;
+                                                    point[1] = rPoint.Y;
+                                                    point[2] = rPoint.U;
+                                                    if (calibration.CameraMount == CameraMount.FixedUp || calibration.CameraMount == CameraMount.FixedDown)
+                                                    {
+                                                        point = null;
+                                                    }
+                                                }
+                                                (bool IsSucceed, double X, double Y, double U) = _calibrationService.ConvertPixelToPosition((double.Parse(subitems[0]), double.Parse(subitems[1]), double.Parse(subitems[2])), point, calibration);
                                                 result.Add(Math.Round(X, 5));
                                                 result.Add(Math.Round(Y, 5));
                                             }

+ 12 - 4
TeamAAS-VM/ViewModels/User/CardLoginWindowViewModel.cs

@@ -33,6 +33,7 @@ namespace TeamAAS_VP.ViewModels.User
         #region 字段
         IConfigService _configService;
         private readonly ISystemDatabaseService _systemDatabaseService;
+        private readonly IEventAggregator _eventAggregator;
         private List<TeamAAS_VP.Models.User> Users;
         #endregion
 
@@ -115,10 +116,11 @@ namespace TeamAAS_VP.ViewModels.User
 
         #endregion
 
-        public CardLoginWindowViewModel(IConfigService configService, ISystemDatabaseService systemDatabaseService)
+        public CardLoginWindowViewModel(IConfigService configService, ISystemDatabaseService systemDatabaseService, IEventAggregator eventAggregator)
         {
             _configService = configService;
             _systemDatabaseService = systemDatabaseService;
+            _eventAggregator = eventAggregator;
         }
 
 
@@ -179,15 +181,20 @@ namespace TeamAAS_VP.ViewModels.User
                     return;
                 }
                 Models.User user = new Models.User();
-                user.UserName = code;
+                user.UserName = code.Trim().TrimStart('\0');
                 user.userPart = UserPart.Engineer;
-                user.UserPassword = code; //刷卡密码默认为卡号
+                user.UserPassword = code.Trim().TrimStart('\0'); //刷卡密码默认为卡号
                 user.CreateTime = DateTime.Now;
                 user.Id = -1;
 
                 await _systemDatabaseService.SetCurrentUserAsync(user);
 
-                View.DialogResult = true;
+                App.Current.Dispatcher.Invoke(() =>
+                {
+                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
+                    View.DialogResult = true;
+                });
+                
             }
         }
 
@@ -240,6 +247,7 @@ namespace TeamAAS_VP.ViewModels.User
                     // mark current user in system DB and record login
                     await _systemDatabaseService.SetCurrentUserAsync(user.Id);
                     await _systemDatabaseService.RecordUserLoginAsync(new UserLoginRecord { UserId = user.Id, Success = true, Time = DateTime.UtcNow, Message = "Login success" });
+                    _eventAggregator.GetEvent<UserLoginNotification>().Publish(user);
                     View.DialogResult = true;
                 }
 

+ 319 - 0
TeamAAS-VM/Views/Product/PlcPointOffserParams.xaml

@@ -0,0 +1,319 @@
+<UserControl x:Class="TeamAAS_VP.Views.Product.PlcPointOffserParams"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:prism="http://prismlibrary.com/"
+             prism:ViewModelLocator.AutoWireViewModel="True"
+             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:system="clr-namespace:System;assembly=mscorlib"
+             xmlns:local="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.Product"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             mc:Ignorable="d"
+             d:DesignHeight="768"
+             d:DesignWidth="1024"
+             d:Background="White"
+             MaxHeight="1000"
+             MaxWidth="1840"
+             d:DataContext="{d:DesignInstance Type=vm:PlcPointOffserParamsViewModel}"
+             FontFamily="{DynamicResource DefaultFont}">
+    <prism:Dialog.WindowStyle>
+        <Style TargetType="Window">
+            <Setter Property="prism:Dialog.WindowStartupLocation"
+                    Value="CenterScreen" />
+            <!--<Setter Property="WindowChrome.WindowChrome">
+            <Setter.Value>
+                <WindowChrome CaptionHeight="0"
+                              ResizeBorderThickness="1" />
+            </Setter.Value>
+        </Setter>-->
+            <Setter Property="WindowStyle"
+                    Value="None" />
+            <Setter Property="AllowDrop"
+                    Value="True" />
+            <Setter Property="BorderThickness"
+                    Value="0" />
+            <Setter Property="ShowInTaskbar"
+                    Value="False" />
+            <Setter Property="SizeToContent"
+                    Value="WidthAndHeight" />
+            <Setter Property="WindowState"
+                    Value="Normal" />
+            <Setter Property="Topmost"
+                    Value="True" />
+        </Style>
+    </prism:Dialog.WindowStyle>
+    <Grid Margin="16">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="auto" />
+        </Grid.RowDefinitions>
+        <TextBlock Text="{Binding Title}"
+                   FontSize="16"
+                   FontWeight="Bold"
+                   Margin="0,10"
+                   VerticalAlignment="Center"
+                   TextAlignment="Left"
+                   HorizontalAlignment="Left" />
+        <!--点位表-->
+        <Border Grid.Row="1"
+                BorderBrush="#D0D0D0"
+                BorderThickness="1"
+                Margin="0,5">
+            <DataGrid x:Name="dgPoints"
+                      AutoGenerateColumns="False"
+                      CanUserAddRows="False"
+                      CanUserDeleteRows="False"
+                      ColumnWidth="*"
+                      SelectionUnit="FullRow"
+                      SelectionMode="Single"
+                      ItemsSource="{Binding Points}"
+                      HeadersVisibility="Column"
+                      HorizontalScrollBarVisibility="Auto"
+                      ScrollViewer.CanContentScroll="True"
+                      ScrollViewer.HorizontalScrollBarVisibility="Auto"
+                      GridLinesVisibility="All"
+                      VerticalGridLinesBrush="#D0D0D0">
+                <DataGrid.Resources>
+                    <Style TargetType="DataGridCell">
+                        <Style.Resources>
+                            <SolidColorBrush  x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
+                                              Color="#0078D7" />
+                        </Style.Resources>
+                        <Setter Property="MinHeight"
+                                Value="25" />
+                        <Setter Property="VerticalContentAlignment"
+                                Value="Center" />
+                        <Setter Property="HorizontalContentAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                        <Setter Property="TextBlock.TextAlignment"
+                                Value="Center" />
+                        <Setter Property="Padding"
+                                Value="0" />
+                        <Style.Triggers>
+                            <Trigger Property="IsSelected"
+                                     Value="True">
+                                <Setter Property="Foreground"
+                                        Value="White" />
+                            </Trigger>
+                        </Style.Triggers>
+                    </Style>
+
+                    <!-- 为 TextBlock 添加默认居中样式 -->
+                    <Style TargetType="TextBlock">
+                        <Setter Property="TextAlignment"
+                                Value="Center" />
+                        <Setter Property="HorizontalAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                    </Style>
+                </DataGrid.Resources>
+                <DataGrid.Columns>
+                    <DataGridTextColumn Header="{lex:Loc 编号}"
+                                        IsReadOnly="True"
+                                        MinWidth="60"
+                                        Width="Auto"
+                                        Binding="{Binding Number, Mode=TwoWay, StringFormat={}{0:F0}}">
+                    </DataGridTextColumn>
+                    <DataGridTextColumn Header="{lex:Loc 标签}"
+                                        MinWidth="100"
+                                        Width="Auto"
+                                        Binding="{Binding Label, Mode=TwoWay}" />
+                    <!--X 坐标-->
+                    <DataGridTemplateColumn Header="X补偿"
+                                            MinWidth="60"
+                                            Width="Auto">
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding X_Offset, StringFormat={}{0:F3}}"
+                                           HorizontalAlignment="Center"
+                                           VerticalAlignment="Center" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                        <DataGridTemplateColumn.CellEditingTemplate>
+                            <DataTemplate>
+                                <mah:NumericUpDown Value="{Binding X_Offset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                   Minimum="-9999.000"
+                                                   Maximum="9999.000"
+                                                   Interval="0.1"
+                                                   StringFormat="F3"
+                                                   HorizontalAlignment="Stretch"
+                                                   VerticalAlignment="Center"
+                                                   Margin="0"
+                                                   Padding="2" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellEditingTemplate>
+                    </DataGridTemplateColumn>
+                    <!--Y 坐标-->
+                    <DataGridTemplateColumn Header="Y补偿"
+                                            MinWidth="60"
+                                            Width="Auto">
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding Y_Offset, StringFormat={}{0:F3}}"
+                                           HorizontalAlignment="Center"
+                                           VerticalAlignment="Center" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                        <DataGridTemplateColumn.CellEditingTemplate>
+                            <DataTemplate>
+                                <mah:NumericUpDown Value="{Binding Y_Offset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                   Minimum="-9999.000"
+                                                   Maximum="9999.000"
+                                                   Interval="0.1"
+                                                   StringFormat="F3"
+                                                   HorizontalAlignment="Stretch"
+                                                   VerticalAlignment="Center"
+                                                   Margin="0"
+                                                   Padding="2" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellEditingTemplate>
+                    </DataGridTemplateColumn>
+                    <!--Z 起点坐标-->
+                    <DataGridTemplateColumn Header="Z起点补偿"
+                                            MinWidth="60"
+                                            Width="Auto">
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding Z_Start_Offset, StringFormat={}{0:F3}}"
+                                           HorizontalAlignment="Center"
+                                           VerticalAlignment="Center" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                        <DataGridTemplateColumn.CellEditingTemplate>
+                            <DataTemplate>
+                                <mah:NumericUpDown Value="{Binding Z_Start_Offset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                   Minimum="-9999.000"
+                                                   Maximum="9999.000"
+                                                   Interval="0.1"
+                                                   StringFormat="F3"
+                                                   HorizontalAlignment="Stretch"
+                                                   VerticalAlignment="Center"
+                                                   Margin="0"
+                                                   Padding="2" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellEditingTemplate>
+                    </DataGridTemplateColumn>
+                    <!--Z 终点坐标-->
+                    <DataGridTemplateColumn Header="Z终点补偿"
+                                            MinWidth="60"
+                                            Width="Auto">
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding Z_Stop_Offset, StringFormat={}{0:F3}}"
+                                           HorizontalAlignment="Center"
+                                           VerticalAlignment="Center" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                        <DataGridTemplateColumn.CellEditingTemplate>
+                            <DataTemplate>
+                                <mah:NumericUpDown Value="{Binding Z_Stop_Offset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                   Minimum="-9999.000"
+                                                   Maximum="9999.000"
+                                                   Interval="0.1"
+                                                   StringFormat="F3"
+                                                   HorizontalAlignment="Stretch"
+                                                   VerticalAlignment="Center"
+                                                   Margin="0"
+                                                   Padding="2" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellEditingTemplate>
+                    </DataGridTemplateColumn>
+                    <!--U 坐标-->
+                    <DataGridTemplateColumn Header="U补偿"
+                                            MinWidth="60"
+                                            Width="Auto">
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding U_Offset, StringFormat={}{0:F3}}"
+                                           HorizontalAlignment="Center"
+                                           VerticalAlignment="Center" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                        <DataGridTemplateColumn.CellEditingTemplate>
+                            <DataTemplate>
+                                <mah:NumericUpDown Value="{Binding U_Offset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                   Minimum="-9999.000"
+                                                   Maximum="9999.000"
+                                                   Interval="0.1"
+                                                   StringFormat="F3"
+                                                   HorizontalAlignment="Stretch"
+                                                   VerticalAlignment="Center"
+                                                   Margin="0"
+                                                   Padding="2" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellEditingTemplate>
+                    </DataGridTemplateColumn>
+                    <!--R 坐标-->
+                    <DataGridTemplateColumn Header="R补偿"
+                                            MinWidth="60"
+                                            Width="Auto">
+                        <DataGridTemplateColumn.CellTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding R_Offset, StringFormat={}{0:F3}}"
+                                           HorizontalAlignment="Center"
+                                           VerticalAlignment="Center" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellTemplate>
+                        <DataGridTemplateColumn.CellEditingTemplate>
+                            <DataTemplate>
+                                <mah:NumericUpDown Value="{Binding R_Offset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                   Minimum="-9999.000"
+                                                   Maximum="9999.000"
+                                                   Interval="0.1"
+                                                   StringFormat="F3"
+                                                   HorizontalAlignment="Stretch"
+                                                   VerticalAlignment="Center"
+                                                   Margin="0"
+                                                   Padding="2" />
+                            </DataTemplate>
+                        </DataGridTemplateColumn.CellEditingTemplate>
+                    </DataGridTemplateColumn>
+
+                    <DataGridTextColumn Header="{lex:Loc 描述}"
+                                        MinWidth="100"
+                                        Width="200"
+                                        Binding="{Binding Description, Mode=TwoWay}" />
+                </DataGrid.Columns>
+            </DataGrid>
+        </Border>
+        <StackPanel Grid.Row="2"
+                    Orientation="Horizontal"
+                    HorizontalAlignment="Right">
+            <!--清除所有补偿-->
+            <Button Height="30"
+                    Margin="0,0,20,0"
+                    materialDesign:ButtonAssist.CornerRadius="10"
+                    Style="{StaticResource MaterialDesignRaisedButton}"
+                    ToolTip="清除所有补偿"
+                    Command="{Binding ClearAllOffsetCommand}">
+                <TextBlock Text="清除所有补偿" />
+            </Button>
+
+            <Button Height="30"
+                    materialDesign:ButtonAssist.CornerRadius="10"
+                    Style="{StaticResource MaterialDesignRaisedButton}"
+                    ToolTip="{lex:Loc 确定}"
+                    Command="{Binding ConfirmCommand}">
+                <TextBlock Text="{lex:Loc 确定}" />
+            </Button>
+            <Button Height="30"
+                    Margin="20,0"
+                    materialDesign:ButtonAssist.CornerRadius="10"
+                    Style="{StaticResource MaterialDesignRaisedButton}"
+                    ToolTip="{lex:Loc 取消}"
+                    Command="{Binding CancelCommand}">
+                <TextBlock Text="{lex:Loc 取消}" />
+            </Button>
+        </StackPanel>
+    </Grid>
+</UserControl>

+ 15 - 0
TeamAAS-VM/Views/Product/PlcPointOffserParams.xaml.cs

@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace TeamAAS_VP.Views.Product
+{
+    /// <summary>
+    /// Interaction logic for PlcPointOffserParams
+    /// </summary>
+    public partial class PlcPointOffserParams : UserControl
+    {
+        public PlcPointOffserParams()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 59 - 6
TeamAAS-VM/Views/Product/PlcPointParams.xaml

@@ -125,8 +125,7 @@
                                 Orientation="Horizontal">
                         <TextBlock Text="{lex:Loc 相机,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
                                    FontWeight="Bold"
-                                   VerticalAlignment="Center"
-                                   />
+                                   VerticalAlignment="Center" />
                         <ComboBox Margin="5,0,10,0"
                                   IsEnabled="{Binding IsGrap,Converter={StaticResource InvertBooleanConverter}}"
                                   ItemsSource="{Binding CameraList}"
@@ -162,7 +161,7 @@
                                    Foreground="Gray" />
                     </StackPanel>
                 </Grid>
-                
+
             </materialDesign:DrawerHost.LeftDrawerContent>
             <Grid>
                 <Grid.ColumnDefinitions>
@@ -330,6 +329,31 @@
                                     </Style>
                                 </Button.Style>
                             </Button>
+                            <!--设置补偿值-->
+                            <Button Content="设置补偿值"
+                                    Margin="5,2"
+                                    MinWidth="80"
+                                    d:Visibility="Visible"
+                                    materialDesign:ButtonAssist.CornerRadius="5"
+                                    Command="{Binding SetOffsetCommand}">
+                                <!--<Button.Style>
+                                    <Style TargetType="Button"
+                                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                                        <Setter Property="Visibility"
+                                                Value="Collapsed" />
+                                        <Style.Triggers>
+                                            -->
+                                <!-- 当 SelectedIndex == 0 时显示此面板 -->
+                                <!--
+                                            <DataTrigger Binding="{Binding SelectedIndex}"
+                                                         Value="1">
+                                                <Setter Property="Visibility"
+                                                        Value="Visible" />
+                                            </DataTrigger>
+                                        </Style.Triggers>
+                                    </Style>
+                                </Button.Style>-->
+                            </Button>
                         </StackPanel>
                         <Border BorderBrush="LightGray"
                                 BorderThickness="1"
@@ -1259,6 +1283,35 @@
                                             </DataTemplate>
                                         </DataGridTemplateColumn.CellEditingTemplate>
                                     </DataGridTemplateColumn>
+                                    <!--视觉流程-->
+                                    <DataGridTemplateColumn x:Name="colProcedure"
+                                                            Header="视觉流程"
+                                                            MinWidth="60"
+                                                            Width="Auto">
+                                        <DataGridTemplateColumn.CellTemplate>
+                                            <DataTemplate>
+                                                <ComboBox MinWidth="50"
+                                                          Margin="5,0,0,0"
+                                                          ItemsSource="{Binding DataContext.ProcedureModels,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
+                                                          SelectedValuePath="Id"
+                                                          SelectedValue="{Binding VisionProcessID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+                                                          SelectionChanged="ProcedureCombo_SelectionChanged">
+                                                    <ComboBox.ItemTemplate>
+                                                        <DataTemplate>
+                                                            <StackPanel Orientation="Horizontal">
+                                                                <materialDesign:PackIcon Kind="VideoOutline" />
+                                                                <TextBlock Text="{Binding CameraName}"
+                                                                           Margin="6,0" />
+                                                                <materialDesign:PackIcon Kind="ProgressWrench" />
+                                                                <TextBlock Text="{Binding Name}"
+                                                                           Margin="6,0" />
+                                                            </StackPanel>
+                                                        </DataTemplate>
+                                                    </ComboBox.ItemTemplate>
+                                                </ComboBox>
+                                            </DataTemplate>
+                                        </DataGridTemplateColumn.CellTemplate>
+                                    </DataGridTemplateColumn>
 
                                     <DataGridTextColumn Header="{lex:Loc 描述}"
                                                         MinWidth="100"
@@ -1286,7 +1339,7 @@
                             <ToggleButton Grid.ColumnSpan="2"
                                           HorizontalAlignment="Left"
                                           VerticalAlignment="Top"
-                                         materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=ArrowLeft}"
+                                          materialDesign:ToggleButtonAssist.OnContent="{materialDesign:PackIcon Kind=ArrowLeft}"
                                           Content="{materialDesign:PackIcon Kind=Camera}"
                                           Style="{StaticResource MaterialDesignActionToggleButton}"
                                           ToolTip="{lex:Loc 相机图像}"
@@ -1301,8 +1354,8 @@
                                     <RowDefinition Height="auto" />
                                     <RowDefinition Height="auto" />
                                 </Grid.RowDefinitions>
-                                
-                                
+
+
                                 <Button Content="{lex:Loc 上使能}"
                                         Grid.Row="0"
                                         materialDesign:ButtonAssist.CornerRadius="10"

+ 42 - 0
TeamAAS-VM/Views/Product/PlcPointParams.xaml.cs

@@ -19,12 +19,54 @@ namespace TeamAAS_VP.Views.Product
             InitializeComponent();
             VM = DataContext as PlcPointParamsViewModel;
             VM.PropertyChanged += VM_PropertyChanged;
+            VM.PropertyChanged += Vm_SelectedIndexChanged;
+            // initialize visibility
+            try
+            {
+                if (VM != null && colProcedure != null)
+                    colProcedure.Visibility = VM.SelectedIndex == 3 ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
+            }
+            catch { }
             this.display.HorizontalScrollBar = false;
             this.display.VerticalScrollBar = false;
             this.display.AutoFit = true;
             this.display.BackColor = System.Drawing.SystemColors.ActiveCaption;
         }
 
+        private void Vm_SelectedIndexChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+        {
+            if (e.PropertyName == nameof(PlcPointParamsViewModel.SelectedIndex))
+            {
+                try
+                {
+                    var visible = VM.SelectedIndex == 3 ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
+                    this.Dispatcher.BeginInvoke(new Action(() =>
+                    {
+                        if (colProcedure != null)
+                            colProcedure.Visibility = visible;
+                    }));
+                }
+                catch { }
+            }
+        }
+
+        private void ProcedureCombo_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
+        {
+            try
+            {
+                var combo = sender as System.Windows.Controls.ComboBox;
+                if (combo == null) return;
+                var proc = combo.SelectedItem as TeamAAS_VP.Models.ProcedureModel;
+                var point = combo.DataContext as TeamAAS_VP.Models.PLC.PlcPoint;
+                if (point != null && proc != null)
+                {
+                    // SelectedValue binding already sets VisionProcessID; sync name
+                    point.VisionProcessName = proc.Name;
+                }
+            }
+            catch { }
+        }
+
         private int _imageWidth;
         private int _imageHeight;
         private void VM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)