Преглед изворни кода

提升ct 目前带mes还未验证

KWD пре 3 месеци
родитељ
комит
a35f0acd0d

+ 1 - 0
TeamAAS-VM/Core/ImageHelper.cs

@@ -1,5 +1,6 @@
 using Cognex.VisionPro;
 using OpenCvSharp;
+using OpenCvSharp.Extensions;
 using System;
 using System.Collections.Generic;
 using System.Drawing;

+ 314 - 307
TeamAAS-VM/Core/Management.cs

@@ -16,6 +16,7 @@ using Prism.Regions;
 using SqlSugar;
 using SqlSugar.SplitTableExtensions;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Drawing;
@@ -108,9 +109,9 @@ namespace TeamAAS_VP.Core
         // 扫码完成事件
         private readonly ManualResetEventSlim _scanDoneEvent = new ManualResetEventSlim(false);
         //键盘数据
-        public Dictionary<string, double> keyData = new Dictionary<string, double> { };
+        public ConcurrentDictionary<string, double> keyData = new ConcurrentDictionary<string, double> { };
         //键盘单独数据
-        public Dictionary<string, double> keyData1 = new Dictionary<string, double> { };
+        public ConcurrentDictionary<string, double> keyData1 = new ConcurrentDictionary<string, double> { };
         //键盘差值
         public bool isValueOk;
 
@@ -699,15 +700,15 @@ namespace TeamAAS_VP.Core
                             Task.Factory.StartNew(() =>
                             {
                                 ScriptHelper.ExecuteScriptingAsync(script, _regionManager, _eventAggregator, _container, rst =>
-                            {
-                                if (!rst.Item1)
                                 {
-                                    foreach (var err in rst.Item2)
+                                    if (!rst.Item1)
                                     {
-                                        SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error);
+                                        foreach (var err in rst.Item2)
+                                        {
+                                            SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error);
+                                        }
                                     }
-                                }
-                            });
+                                });
                             });
                         }
                         catch (Exception ex)
@@ -1513,7 +1514,7 @@ namespace TeamAAS_VP.Core
         Int16 _LastPickINC = 0;
         DateTime[] _LastMesCheckTime = new DateTime[10];
         List<Task<(bool IsSucceed, bool[] Result, string Message)>> _PrcTaskList = new List<Task<(bool IsSucceed, bool[] Result, string Message)>>();
-        List<Task<(bool IsSucceed, ICogImage[] Images, string Message, int cameraIndex, Guid procedureId)>> _ImageTaskList = new List<Task<(bool IsSucceed, ICogImage[] Images, string Message, int cameraIndex, Guid procedureId)>>();
+        //List<Task<(bool IsSucceed, ICogImage[] Images, string Message, int cameraIndex, Guid procedureId)>> _ImageTaskList = new List<Task<(bool IsSucceed, ICogImage[] Images, string Message, int cameraIndex, Guid procedureId)>>();
 
         private readonly object _stateLock = new object();
 
@@ -1623,7 +1624,6 @@ namespace TeamAAS_VP.Core
                         }
                     }
                 }
-
                 // 固定相机检测拍照
                 else if (addressConfig.In_FixedCameracheckExe.Address.Contains(tuple.nodeId))
                 {
@@ -1660,44 +1660,24 @@ namespace TeamAAS_VP.Core
                             try
                             {
                                 if (targetPoint.CameraProcedureId1 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId1)) procIds.Add(targetPoint.CameraProcedureId1);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId2 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId2)) procIds.Add(targetPoint.CameraProcedureId2);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId3 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId3)) procIds.Add(targetPoint.CameraProcedureId3);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId4 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId4)) procIds.Add(targetPoint.CameraProcedureId4);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId5 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId5)) procIds.Add(targetPoint.CameraProcedureId5);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId6 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId6)) procIds.Add(targetPoint.CameraProcedureId6);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId7 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId7)) procIds.Add(targetPoint.CameraProcedureId7);
-                            }
-                            catch { }
-                            try
-                            {
+
                                 if (targetPoint.CameraProcedureId8 != Guid.Empty && !procIds.Contains(targetPoint.CameraProcedureId8)) procIds.Add(targetPoint.CameraProcedureId8);
                             }
                             catch { }
                             var sysConfig = _configService.GetSystemConfiguration();
+
                             if (sysConfig.ScannerUse == true && cameraIndex == 1 && sysConfig.CodeUse2 == true)
                             {
                                 ScanValue = "";
@@ -1974,7 +1954,6 @@ namespace TeamAAS_VP.Core
                                 return;
                             }
 
-
                             if (procIds.Count == 0 && cameraIndex != currentProduct.PhotoCountStop)
                             {
                                 SendTaskMessage($"Point#{cameraIndex} 未配置任何视觉流程 (CameraProcedureId1/2/3)", MessageLevel.Alarm);
@@ -2002,6 +1981,7 @@ namespace TeamAAS_VP.Core
                                 {
                                     lockResultsAll.Clear();
                                 }
+
                                 App.Current.Dispatcher.Invoke(() =>
                                 {
                                     _eventAggregator.GetEvent<LockResetNotification>().Publish();
@@ -2344,7 +2324,7 @@ namespace TeamAAS_VP.Core
                                 return;
                             }
 
-                            _ImageTaskList.Clear();
+                            var _ImageTaskList = new List<Task<(bool IsSucceed, ICogImage[] Images, string Message, int cameraIndex, Guid procedureId)>>();
 
                             foreach (var prcId in procIds)
                             {
@@ -2371,18 +2351,18 @@ namespace TeamAAS_VP.Core
                                 if (sysConfig.PhotosUse == true)
                                 {
                                     bool Is3dPhoto = Is3D(prcId);
-                                    var tt = await GrabImageEx(cameraIndex, prcId, Is3dPhoto);
+                                    var tt = await GrabImageEx(cameraIndex, prcId, Is3dPhoto, currentProduct, sysConfig);
                                     if (tt.IsSucceed == false)
                                     {
                                         SendTaskMessage($"图像采集失败!{cameraIndex}: {prcId}", MessageLevel.Alarm);
                                         return;
                                     }
-                                    await ExecuteProcedureAsync(cameraIndex, prcId, tt.imges, Is3dPhoto);
+                                    await ExecuteProcedureAsync(cameraIndex, prcId, tt.imges, Is3dPhoto, currentProduct);
                                 }
                                 else
                                 {
                                     bool Is3dPhoto = Is3D(prcId);
-                                    _ImageTaskList.Add(GrabImageEx(cameraIndex, prcId, Is3dPhoto));
+                                    _ImageTaskList.Add(GrabImageEx(cameraIndex, prcId, Is3dPhoto, currentProduct, sysConfig));
                                 }
 
                                 if (sysConfig.TurnOnAllLightUse == false)
@@ -2390,11 +2370,13 @@ namespace TeamAAS_VP.Core
                                     await _remoteCommandService.TurnOffLightAfterPhoto(selectedProcedure);
                                 }
                             }
+
                             if (cameraIndex != currentProduct.PhotoCountStop)
                             {
                                 //直接放行,执行下一个检测点
                                 await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
                             }
+
                             if (_ImageTaskList.Count > 0)//如果2D取图有队列
                             {
                                 var ImageTaskResults = await Task.WhenAll(_ImageTaskList);
@@ -2403,7 +2385,7 @@ namespace TeamAAS_VP.Core
                                     if (item.IsSucceed)
                                     {
                                         bool Is3d = Is3D(item.procedureId);
-                                        _PrcTaskList.Add(ExecuteProcedureAsync(item.cameraIndex, item.procedureId, item.Images, Is3d));
+                                        _PrcTaskList.Add(ExecuteProcedureAsync(item.cameraIndex, item.procedureId, item.Images, Is3d, currentProduct));
                                         //_PrcTaskList.Add(ExecuteProcedureAsync(cameraIndex, prcId, imageData.imges));
                                     }
                                     else
@@ -2412,6 +2394,7 @@ namespace TeamAAS_VP.Core
                                     }
                                 }
                             }
+
                             //如果最后一个拍照点,则需要等待前面所有的拍照处理完成
                             if (cameraIndex == currentProduct.PhotoCountStop)
                             {
@@ -2434,7 +2417,6 @@ namespace TeamAAS_VP.Core
                                     //等待所有的图像处理流程处理完成
                                     var results = await Task.WhenAll(_PrcTaskList);
 
-
                                     foreach (var item in results)
                                     {
                                         if (!item.IsSucceed || item.Result == null)
@@ -2455,8 +2437,6 @@ namespace TeamAAS_VP.Core
                                         }
                                     }
                                 }
-
-
                                 //全部OK
                                 if (resultFlog)
                                 {
@@ -2470,6 +2450,7 @@ namespace TeamAAS_VP.Core
                                     await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)2);
                                 }
                             }
+                            return;
                         }
                         else
                         {
@@ -2478,7 +2459,6 @@ namespace TeamAAS_VP.Core
                         }
                     }
                 }
-
                 // 翻转相机检测拍照
                 else if (addressConfig.In_FzCameraPixExe.Address.Contains(tuple.nodeId))
                 {
@@ -2702,6 +2682,7 @@ namespace TeamAAS_VP.Core
                                 {
                                     //这里缺少从MES获取产品配方的内容,需要补充
                                     _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(code, 2)).Now;
+                                    ProductMainSN = "";
                                     var stationConfig2 = _configService.GetDeviceInfo(0);
                                     if (homeview.IsNoCode == false)
                                     {
@@ -2802,7 +2783,7 @@ namespace TeamAAS_VP.Core
                                                                 break;
                                                             }
                                                         }
-                                                        //if(kb_sn==)
+                                                        //if (kb_sn ==)
                                                         if (currentProduct.ID != productId || productWithMes.ProductKBSN != kb_sn)
                                                         {
                                                             //切换产品
@@ -2817,16 +2798,23 @@ namespace TeamAAS_VP.Core
                                             await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
                                             LogHelper.WriteLogMes($"【mes结束QUERY】");
                                             SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
-                                           // return;
+                                            // return;
                                         }
                                         else
                                         {
                                             await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
 
-                                            LogHelper.WriteLogMes($"询问mes失败");
-                                            SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
+                                            LogHelper.WriteLogMes($"二次询问mes失败");
+                                            SendTaskMessage($"二次询问mes失败:{response}", MessageLevel.Error);
                                         }
-                                    }                                   
+                                    }
+                                    else
+                                    {
+                                        await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+
+                                        LogHelper.WriteLogMes($"询问mes失败");
+                                        SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
+                                    }
                                 }
                                 // 上传本站数据
                                 else if (state == 2)
@@ -2837,6 +2825,8 @@ namespace TeamAAS_VP.Core
                                     List<DataFormCtq> DFC = new List<DataFormCtq>();
                                     SendTaskMessage($"收到{i}上传本站数据请求...", MessageLevel.Debug);
 
+                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+
                                     //这里缺少上传MES的本站数据结果及开始和结束时间,需要补充
                                     //从服务器中获取当前时间
                                     var serverTimeResult = await _mesService.GetServerTimeAsync(ProductMainSN, 2);
@@ -2923,7 +2913,7 @@ namespace TeamAAS_VP.Core
 
                                         if (keyData != null)
                                         {
-                                            ok = CheckKeyBoardValue(keyData,name, out string msg);
+                                            ok = CheckKeyBoardValue(keyData, name, out string msg);
                                         }
 
                                         if (ok && resultIndex == 1 ? true : false && isValueOk)
@@ -2933,7 +2923,7 @@ namespace TeamAAS_VP.Core
                                         else
                                         {
                                             ShowMes_Result = false;
-                                        }                                       
+                                        }
                                     }
 
                                     ShowMes_Result = resultIndex == 1 ? true : false;
@@ -2983,7 +2973,7 @@ namespace TeamAAS_VP.Core
                                         LogHelper.WriteLogMes($"上传mes失败");
                                         SendTaskMessage($"上传mes失败", MessageLevel.Error);
                                     }
-                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                    //await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
 
 
                                     //是否保存当前的产品记录至CSV?
@@ -2992,7 +2982,7 @@ namespace TeamAAS_VP.Core
                                         string saveppid = "NA";
                                         string names = currentProduct.Name;
                                         if (stationConfig.SaveCsvPPID) saveppid = ppid;
-                                        
+
                                         // Dictionary<string, double> adjacentDiffDict1 = GetAdjacentDiffDict(keyData1);
                                         //判断锁付路径字符串是否为空,如果不为空则保存,否则不保存
                                         if (!string.IsNullOrEmpty(stationConfig.SaveCsvPath))
@@ -3014,6 +3004,45 @@ namespace TeamAAS_VP.Core
                                                     //    item.PhotoName = "_" + k.ToString();
                                                     //}
                                                 }
+
+                                                string temp_COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE;
+
+                                                if (item.PhotoName.Contains("A1b"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware LCD CVR";
+                                                }
+                                                if (item.PhotoName.Contains("A1c") || item.PhotoName.Contains("A1d"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_LCD BZL";
+                                                }
+                                                if (item.PhotoName.Contains("A2"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "CAMERA";
+                                                }
+                                                if (item.PhotoName.Contains("A4"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_HNG CVR";
+                                                }
+                                                if (item.PhotoName.Contains("C4"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_PLMRST";
+                                                }
+                                                if (item.PhotoName.Contains("D5"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_DOOR";
+                                                }
+                                                if (item.PhotoName.Contains("B1"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_KB BKT";
+                                                }
+                                                if (item.PhotoName.Contains("B2"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_TP BKT";
+                                                }
+                                                if (item.PhotoName.Contains("D1"))
+                                                {
+                                                    temp_COMMODITY_TYPE = "Mechanical Hardware_BTM CVR";
+                                                }
                                                 DFC.Add(new DataFormCtq
                                                 {
                                                     PPID = saveppid,
@@ -3026,7 +3055,7 @@ namespace TeamAAS_VP.Core
                                                     CTQ_USL = item.UpLimit,
                                                     PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
                                                     SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
-                                                    COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
+                                                    COMMODITY_TYPE = temp_COMMODITY_TYPE,
                                                     REVISION = stationConfig.DataInfo.REVISION + "_" + stage,//
                                                     WO = wo,//
                                                     MFG_LOT = stationConfig.DataInfo.MFG_LOT,//
@@ -3109,11 +3138,11 @@ namespace TeamAAS_VP.Core
                                                 }
                                             }
 
-                                        //测试使用,强制上传mes为pass
-                                        //if (sysConfig.IsMesResult)
-                                        //{
-                                        //    resultToMes.Clear();
-                                        //}
+                                            //测试使用,强制上传mes为pass
+                                            //if (sysConfig.IsMesResult)
+                                            //{
+                                            //    resultToMes.Clear();
+                                            //}
                                             bool recordResult = RecordStationResult(i, DFC, ProductMainSN, serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath);
                                             // bool recordResult1 = WriteProductLog( ProductMainSN, serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath, DFC);
 
@@ -3231,18 +3260,10 @@ namespace TeamAAS_VP.Core
                                     {
                                         SendTaskMessage($"未启用保存键盘格JMP数据", MessageLevel.Info);
                                     }
-                                    lock (_stateLock)
-                                    {
-                                        lockResultsAll.Clear();
-                                        keyData.Clear();
-                                        keyData1.Clear();
-                                    }
 
-                                    ProductMainSN = "";
                                     LogHelper.WriteLogMes($"【mes结束ADD】");
                                     SendTaskMessage($"mes结束ADD", MessageLevel.Info);
                                 }
-
                                 else
                                 {
                                     SendTaskMessage($"{i}过站检查信号:state={state},处理为默认状态", MessageLevel.Alarm);
@@ -3252,10 +3273,6 @@ namespace TeamAAS_VP.Core
                         }
                     }
                 }
-
-
-
-
             }
             catch (Exception ex)
             {
@@ -3429,279 +3446,269 @@ namespace TeamAAS_VP.Core
         /// <param name="pointIndex"></param>
         /// <param name="procedureId"></param>
         /// <returns></returns>
-        private Task<(bool IsSucceed, bool[] Result, string Message)> ExecuteProcedureAsync(int pointIndex, Guid procedureId, ICogImage[] images, bool is3dmodel)
+        private async Task<(bool IsSucceed, bool[] Result, string Message)> ExecuteProcedureAsync(int pointIndex, Guid procedureId, ICogImage[] images, bool is3dmodel, ProductModel currentProduct)
         {
-            return Task.Run(async () =>
-            {
-                List<bool> results = new List<bool>();
-                // 获取当前产品
-                var currentProduct = _productService.GetCurrentProduct();
-                if (currentProduct == null)
-                {
-                    return (false, null, "当前产品为空,未加载产品!");
-                }
 
-                var cameraProcedures = currentProduct.CameraProcedures;
-                if (cameraProcedures == null || cameraProcedures.Count == 0)
-                {
-                    SendTaskMessage($"当前产品未配置 CameraProcedures,无法执行视觉流程", MessageLevel.Alarm);
-                    return (false, null, $"当前产品未配置 CameraProcedures,无法执行视觉流程");
-                }
+            List<bool> results = new List<bool>();
 
+            //Guid start = currentProduct.FixedUpCamera1ProcedureId;
+            //Guid stop = currentProduct.FixedUpCamera2ProcedureId;
+            //var startProcedures = _productService.GetCurrentProductProcedureModelById(start);
+            //var stopProcedures = _productService.GetCurrentProductProcedureModelById(stop);
 
-                var ctsAll = new CancellationTokenSource();
+            var cameraProcedures = currentProduct.CameraProcedures;
+            if (cameraProcedures == null || cameraProcedures.Count == 0)
+            {
+                SendTaskMessage($"当前产品未配置 CameraProcedures,无法执行视觉流程", MessageLevel.Alarm);
+                return (false, null, $"当前产品未配置 CameraProcedures,无法执行视觉流程");
+            }
 
+
+            var ctsAll = new CancellationTokenSource();
+
+            try
+            {
+                ProcedureModel selectedProcedure = null;
                 try
                 {
-                    ProcedureModel selectedProcedure = null;
-                    try
+                    foreach (var cp in cameraProcedures)
                     {
-                        foreach (var cp in cameraProcedures)
-                        {
-                            if (cp?.ProcedureModels == null) continue;
-                            selectedProcedure = cp.ProcedureModels.FirstOrDefault(pm => pm.Id == procedureId);
+                        if (cp?.ProcedureModels == null) continue;
+                        selectedProcedure = cp.ProcedureModels.FirstOrDefault(pm => pm.Id == procedureId);
 
-                            if (selectedProcedure != null) break;
-                        }
+                        if (selectedProcedure != null) break;
                     }
-                    catch (Exception ex)
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.WriteLogError("查找 AOI 点位对应视觉流程时出错", ex);
+                }
+
+                if (selectedProcedure == null)
+                {
+                    SendTaskMessage($"未找到 AOI 点位对应的视觉流程,Point#{pointIndex} ProcedureId={pointIndex}", MessageLevel.Alarm);
+                    return (false, null, $"未找到 AOI 点位对应的视觉流程,Point#{pointIndex} ProcedureId={procedureId}");
+                }
+
+                SendTaskMessage($"开始执行 AOI 流程 (Point#{pointIndex}) 流程Id={pointIndex}", MessageLevel.Debug);
+                try
+                {
+                    var sysConfig = _configService.GetSystemConfiguration();
+                    if (selectedProcedure.Id == currentProduct.MoveDownCameraLockPhotoProcedureId && sysConfig.TurnOffAllLightUse == true)
                     {
-                        LogHelper.WriteLogError("查找 AOI 点位对应视觉流程时出错", ex);
+                        await _remoteCommandService.TurnOffLightAfterPhoto(selectedProcedure);
                     }
-
-                    if (selectedProcedure == null)
+                    PhotoPath = selectedProcedure.SavePath;
+                    //1. 执行拍照,图像处理
+                    var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint1(selectedProcedure, images, null, null, ctsAll.Token, is3dmodel);
+                    if (visionResult.Count == 0)
                     {
-                        SendTaskMessage($"未找到 AOI 点位对应的视觉流程,Point#{pointIndex} ProcedureId={pointIndex}", MessageLevel.Alarm);
+                        SendTaskMessage("Count值为0", MessageLevel.Alarm);
                         return (false, null, $"未找到 AOI 点位对应的视觉流程,Point#{pointIndex} ProcedureId={procedureId}");
                     }
 
-                    SendTaskMessage($"开始执行 AOI 流程 (Point#{pointIndex}) 流程Id={pointIndex}", MessageLevel.Debug);
-                    try
+                    //2. 结果分析
+                    //lock (_stateLock)
+                    //{
+
+                    //if (currentProduct.Name==startProcedures.Name)
+                    //{
+                    //    lockResultsAll.Clear();
+                    //}
+
+                    var stationConfig = _configService.GetDeviceInfo(0);
+                    for (int i = 1; i <= visionResult.Count; i++)
                     {
-                        var sysConfig = _configService.GetSystemConfiguration();
-                        if (selectedProcedure.Id == currentProduct.MoveDownCameraLockPhotoProcedureId && sysConfig.TurnOffAllLightUse == true)
-                        {
-                            await _remoteCommandService.TurnOffLightAfterPhoto(selectedProcedure);
-                        }
-                        PhotoPath = selectedProcedure.SavePath;
-                        //1. 执行拍照,图像处理
-                        var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint1(selectedProcedure, images, null, null, ctsAll.Token, is3dmodel);
-                        if (visionResult.Count == 0)
-                        {
-                            SendTaskMessage("Count值为0", MessageLevel.Alarm);
-                            return (false, null, $"未找到 AOI 点位对应的视觉流程,Point#{pointIndex} ProcedureId={procedureId}");
-                        }
 
-                        //2. 结果分析
-                        lock (_stateLock)
+                        LockResult lockResult1 = new LockResult();
+
+                        try
                         {
-                            var stationConfig = _configService.GetDeviceInfo(0);
-                            for (int i = 1; i <= visionResult.Count; i++)
-                            {
 
-                                LockResult lockResult1 = new LockResult();
-                                //if (pointIndex == currentProduct.PhotoCount && selectedProcedure.Id == currentProduct.FixedUpCamera1ProcedureId && i == 1)
-                                //{
-                                //    lockResult1.Number = 1;
-                                //    lock (_stateLock)
-                                //    {
-                                //        lockResultsAll.Clear();
-                                //    }
+                            string[] strpoints1 = visionResult.Items[i].Split(',');
+                            string[] body1 = strpoints1[1].Split(";");
+
+                            if (body1.Length > 1)
+                            {
 
-                                //}
-                                try
+                                for (int j = 1; j <= body1.Length; j++)
                                 {
+                                    lockResult1.PhotoName = strpoints1[0] + "_" + j.ToString();
+                                    lockResult1.ResultData = body1[j - 1];
+                                    lockResult1.PhotoCode = ProductMainSN;
 
-                                    string[] strpoints1 = visionResult.Items[i].Split(',');
-                                    string[] body1 = strpoints1[1].Split(";");
-
-                                    if (body1.Length > 1)
+                                    var globalParam = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Name == strpoints1[0]);
+                                    if (globalParam != null)
                                     {
+                                        lockResult1.UpLimit = globalParam.Param1;
+                                        lockResult1.DownLimit = globalParam.Param2;
+                                        lockResult1.MidLimit = globalParam.Param5;
+                                        string ResData = body1[j - 1];
+                                        if (ResData.Contains("NA"))
+                                        {
+                                            ResData = "-999";
+                                        }
 
-                                        for (int j = 1; j <= body1.Length; j++)
+                                        bool resultflog = true;
+                                        if (Convert.ToDouble(ResData) >= Convert.ToDouble(globalParam.Param2) && Convert.ToDouble(ResData) <= Convert.ToDouble(globalParam.Param1))
                                         {
-                                            lockResult1.PhotoName = strpoints1[0] + "_" + j.ToString();
-                                            lockResult1.ResultData = body1[j - 1];
-                                            lockResult1.PhotoCode = ProductMainSN;
-                                            //if (lockResult1.PhotoName.Contains("gap"))
-                                            //{
-                                            //    lockResult1.PhotoPassed = "OK";
-                                            //    results.Add(true);
-                                            //}
-                                            var globalParam = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Name == strpoints1[0]);
-                                            if (globalParam != null)
-                                            {
-                                                lockResult1.UpLimit = globalParam.Param1;
-                                                lockResult1.DownLimit = globalParam.Param2;
-                                                lockResult1.MidLimit = globalParam.Param5;
-                                                string ResData = body1[j - 1];
-                                                if (ResData.Contains("NA"))
-                                                {
-                                                    ResData = "-999";
-                                                }
 
-                                                bool resultflog = true;
-                                                if (Convert.ToDouble(ResData) >= Convert.ToDouble(globalParam.Param2) && Convert.ToDouble(ResData) <= Convert.ToDouble(globalParam.Param1))
-                                                {
+                                        }
+                                        else
+                                        {
+                                            SendTaskMessage($"检测项{lockResult1.PhotoName}结果{ResData}超出上下限!", MessageLevel.Error);
+                                            resultflog = false;
+                                        }
+                                        if (resultflog)
+                                        {
+                                            lockResult1.PhotoPassed = "OK";
+                                            results.Add(true);
+                                        }
+                                        else
+                                        {
+                                            lockResult1.PhotoPassed = "NG";
+                                            results.Add(false);
+                                        }
 
-                                                }
-                                                else
-                                                {
-                                                    SendTaskMessage($"检测项{lockResult1.PhotoName}结果{ResData}超出上下限!", MessageLevel.Error);
-                                                    resultflog = false;
-                                                }
-                                                if (resultflog )
-                                                {
-                                                    lockResult1.PhotoPassed = "OK";
-                                                    results.Add(true);
-                                                }
-                                                else
-                                                {
-                                                    lockResult1.PhotoPassed = "NG";
-                                                    results.Add(false);
-                                                }
+                                    }
+                                    lockResultsAll.Add(lockResult1.Copy());
+                                }
+                            }
+                            else
+                            {
+                                lockResult1.PhotoName = strpoints1[0];
+                                lockResult1.ResultData = strpoints1[1];
+                                lockResult1.PhotoCode = ProductMainSN;
 
-                                            }
-                                            lockResultsAll.Add(lockResult1.Copy());
-                                        }
+                                var globalParam = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Name == strpoints1[0]);
+                                if (globalParam != null)
+                                {
+                                    lockResult1.UpLimit = globalParam.Param1;
+                                    lockResult1.DownLimit = globalParam.Param2;
+                                    lockResult1.MidLimit = globalParam.Param5;
+                                    string ResData = strpoints1[1];
+                                    if (ResData.Contains("NA"))
+                                    {
+                                        ResData = "-1000";
                                     }
-                                    else
+
+                                    bool resultflog = true;
+                                    if (Convert.ToDouble(ResData) >= Convert.ToDouble(globalParam.Param2) && Convert.ToDouble(ResData) <= Convert.ToDouble(globalParam.Param1))
                                     {
-                                        // if(lockResult1.PhotoName)
-                                        lockResult1.PhotoName = strpoints1[0];
-                                        lockResult1.ResultData = strpoints1[1];
-                                        lockResult1.PhotoCode = ProductMainSN;
-                                        //if (lockResult1.PhotoName.Contains("gap"))
-                                        //{
-                                        //    lockResult1.PhotoPassed = "OK";
-                                        //    results.Add(true);
-                                        //}
-                                        var globalParam = currentProduct.GlobalParamListCollection.FirstOrDefault(p => p.Name == strpoints1[0]);
-                                        if (globalParam != null)
-                                        {
-                                            lockResult1.UpLimit = globalParam.Param1;
-                                            lockResult1.DownLimit = globalParam.Param2;
-                                            lockResult1.MidLimit = globalParam.Param5;
-                                            string ResData = strpoints1[1];
-                                            if (ResData.Contains("NA"))
-                                            {
-                                                ResData = "-1000";
-                                            }
 
-                                            bool resultflog = true;
-                                            if (Convert.ToDouble(ResData) >= Convert.ToDouble(globalParam.Param2) && Convert.ToDouble(ResData) <= Convert.ToDouble(globalParam.Param1))
-                                            {
+                                    }
+                                    else
+                                    {
+                                        SendTaskMessage($"检测项{lockResult1.PhotoName}结果{ResData}超出上下限!", MessageLevel.Error);
+                                        resultflog = false;
+                                    }
+                                    if (resultflog)
+                                    {
+                                        lockResult1.PhotoPassed = "OK";
+                                        results.Add(true);
+                                    }
+                                    else
+                                    {
+                                        lockResult1.PhotoPassed = "NG";
+                                        results.Add(false);
+                                    }
+                                }
 
-                                            }
-                                            else
-                                            {
-                                                SendTaskMessage($"检测项{lockResult1.PhotoName}结果{ResData}超出上下限!",MessageLevel.Error);
-                                                resultflog = false;
-                                            }
-                                            if (resultflog )
-                                            {
-                                                lockResult1.PhotoPassed = "OK";
-                                                results.Add(true);
-                                            }
-                                            else
-                                            {
-                                                lockResult1.PhotoPassed = "NG";
-                                                results.Add(false);
-                                            }
-                                        }
+                                lock (_stateLock)
+                                {
+                                    lockResultsAll.Add(lockResult1.Copy());
+                                }
+                            }
 
-                                        lockResultsAll.Add(lockResult1);
-                                    }
+                            if (stationConfig.SaveCsvkeyData)
+                            {
+                                if (body1.Length >= 5)
+                                {
+                                    string keyName = strpoints1[0].Split('-').Last().Trim();
+                                    List<double> list = new List<double>();
 
-                                    if (stationConfig.SaveCsvkeyData)
+                                    foreach (string v in body1)
                                     {
-                                        if (body1.Length >= 5)
+                                        if (double.TryParse(v.Trim(), out double num))
                                         {
-                                            string keyName = strpoints1[0].Split('-').Last().Trim();
-                                            List<double> list = new List<double>();
-
-                                            foreach (string v in body1)
-                                            {
-                                                if (double.TryParse(v.Trim(), out double num))
-                                                {
-                                                    if (num == 999)
-                                                    {
-                                                        continue;
-                                                    }
-                                                    list.Add(num);
-                                                }
-                                            }
-                                            if (list.Count == 0)
+                                            if (num == 999)
                                             {
                                                 continue;
                                             }
+                                            list.Add(num);
+                                        }
+                                    }
+                                    if (list.Count == 0)
+                                    {
+                                        continue;
+                                    }
 
-                                            double max = list.Max();
-                                            double min = list.Min();
-                                            double chazhi = max - min;
-                                            double average = list.Average();
-                                            if (max - min > 0.3)
-                                            {
-                                                isValueOk = false;
-                                                SendTaskMessage($"键盘格{keyName}最大值{max}与最小值{min}相差大于0.3!", MessageLevel.Error);
-                                            }
+                                    double max = list.Max();
+                                    double min = list.Min();
+                                    double chazhi = max - min;
+                                    double average = list.Average();
+                                    if (max - min > 0.3)
+                                    {
+                                        isValueOk = false;
+                                        SendTaskMessage($"键盘格{keyName}最大值{max}与最小值{min}相差大于0.3!", MessageLevel.Error);
+                                    }
 
-                                            if (keyData != null)
-                                            {
-                                                if (!keyData.ContainsKey(keyName))
-                                                {
-                                                    keyData.Add(keyName, average);
+                                    if (keyData != null)
+                                    {
+                                        if (!keyData.ContainsKey(keyName))
+                                        {
+                                            keyData.TryAdd(keyName, average);
 
-                                                }
-                                                if (!keyData1.ContainsKey(keyName))
-                                                {
-                                                    keyData1.Add(keyName, chazhi);
-                                                }
-                                            }
-                                            else
-                                            {
-                                                keyData.Add(keyName, average);
-                                                keyData1.Add(keyName, chazhi);
-                                            }
+                                        }
+                                        if (!keyData1.ContainsKey(keyName))
+                                        {
+                                            keyData1.TryAdd(keyName, chazhi);
                                         }
                                     }
-                                    App.Current.Dispatcher.Invoke(() =>
+                                    else
                                     {
-                                        _eventAggregator.GetEvent<LockFinishNotification>().Publish(lockResult1);
-                                    });
-
-                                }
-                                catch (Exception ex)
-                                {
-                                    SendTaskMessage($"拆分视觉结果出错:{ex}", MessageLevel.Error);
-
+                                        keyData.TryAdd(keyName, average);
+                                        keyData1.TryAdd(keyName, chazhi);
+                                    }
                                 }
-
                             }
+                            _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
+                            {
+                                _eventAggregator
+                                    .GetEvent<LockFinishNotification>()
+                                    .Publish(lockResult1);
+                            }));
+
                         }
+                        catch (Exception ex)
+                        {
+                            SendTaskMessage($"拆分视觉结果出错:{ex}", MessageLevel.Error);
 
+                        }
 
-                        ////排序
-                        //lock (_stateLock)
-                        //{
-                        //    var bf1 = lockResultsAll.OrderBy(p => int.Parse(p.PhotoName));
-                        //    lockResultsAll = new List<LockResult>(bf1);
-                        //}
-                    }
-                    catch (Exception ex)
-                    {
-                        LogHelper.WriteLogError("执行 AOI 视觉流程时出错", ex);
-                        SendTaskMessage($"执行 AOI 视觉流程异常: {ex.Message}", MessageLevel.Alarm);
                     }
-
-                    return (true, results.ToArray(), $"成功");
+                    // }
+
+                    ////排序
+                    //lock (_stateLock)
+                    //{
+                    //    var bf1 = lockResultsAll.OrderBy(p => int.Parse(p.PhotoName));
+                    //    lockResultsAll = new List<LockResult>(bf1);
+                    //}
                 }
                 catch (Exception ex)
                 {
-                    LogHelper.WriteLogError("处理固定相机检测拍照时出错", ex);
-                    return (false, null, $"处理固定相机检测拍照时出错");
+                    LogHelper.WriteLogError("执行 AOI 视觉流程时出错", ex);
+                    SendTaskMessage($"执行 AOI 视觉流程异常: {ex.Message}", MessageLevel.Alarm);
                 }
-            });
+
+                return (true, results.ToArray(), $"成功");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("处理固定相机检测拍照时出错", ex);
+                return (false, null, $"处理固定相机检测拍照时出错");
+            }
         }
 
         /// <summary>
@@ -3985,7 +3992,7 @@ namespace TeamAAS_VP.Core
         /// <summary>
         /// 校验键盘所有按键值,相邻差值不能大于 0.3
         /// </summary>
-        public bool CheckKeyBoardValue(Dictionary<string, double> keyDic, string name, out string errorMsg)
+        public bool CheckKeyBoardValue(ConcurrentDictionary<string, double> keyDic, string name, out string errorMsg)
         {
             errorMsg = "";
             try
@@ -4416,7 +4423,7 @@ namespace TeamAAS_VP.Core
                 ["F12"] = new List<string> { "F11", "Insert", "=" },
                 ["Insert"] = new List<string> { "F12", "Backspace", "Delete" },
                 ["Delete"] = new List<string> { "Insert", "Power", "Backspace" },
-                ["Power"] = new List<string> { "Insert" , "Delete" , "Backspace" },
+                ["Power"] = new List<string> { "Insert", "Delete", "Backspace" },
                 // ==============================================
                 // 第二行:` 1 2 3 4 5 6 7 8 9 0 _ = Backspace
                 // ==============================================
@@ -4503,7 +4510,7 @@ namespace TeamAAS_VP.Core
         }
 
         #endregion
-        private Dictionary<string, double> GetAdjacentDiffDict(Dictionary<string, double> keyData, string name)
+        private Dictionary<string, double> GetAdjacentDiffDict(ConcurrentDictionary<string, double> keyData, string name)
         {
             var currentProduct = _productService.GetCurrentProduct();
 
@@ -4611,18 +4618,18 @@ namespace TeamAAS_VP.Core
         #endregion
 
         #region 保存CSV
-        public bool  WriteProductLogKB(IEnumerable<DataFormCtq> lockResults, string rfid)
+        public bool WriteProductLogKB(IEnumerable<DataFormCtq> lockResults, string rfid)
         {
             try
             {
                 DateTime dt = DateTime.Now;
-                if (rfid == null||rfid.Count()==0)
+                if (rfid == null || rfid.Count() == 0)
                 {
                     rfid = dt.ToString("MM-dd-mm");
                 }
-                if (lockResults.Count()==0 || lockResults == null)
+                if (lockResults.Count() == 0 || lockResults == null)
                 {
-                    SendTaskMessage("保存KB数据为空",MessageLevel.Error);
+                    SendTaskMessage("保存KB数据为空", MessageLevel.Error);
                     return false;
 
                 }
@@ -4634,11 +4641,11 @@ namespace TeamAAS_VP.Core
                 foreach (var item in lockResults)
                 {
                     try
-                    { 
+                    {
                         //!item.CTQ_NAME.Contains("Left")|| !item.CTQ_NAME.Contains("Right")||!item.CTQ_NAME.Contains("Updown")|| !item.CTQ_NAME.Contains("Down") 
                         if (item.CTQ_NAME.Contains("Left") || item.CTQ_NAME.Contains("Down") || item.CTQ_NAME.Contains("Right") || item.CTQ_NAME.Contains("Updown"))
                         {
-                            
+
                         }
                         else
                         {
@@ -4649,7 +4656,7 @@ namespace TeamAAS_VP.Core
                     catch (Exception)
                     {
                         sb.Append($",,,,,,,,,,,,,,");
-                        
+
                         return false;
                     }
                 }
@@ -4686,7 +4693,7 @@ namespace TeamAAS_VP.Core
             try
             {
                 DateTime dt = DateTime.Now;
-                if (rfid==null||rfid.Count()==0)
+                if (rfid == null || rfid.Count() == 0)
                 {
                     rfid = dt.ToString("MM-dd-mm");
                 }
@@ -4907,10 +4914,10 @@ namespace TeamAAS_VP.Core
         /// </summary>
         /// <param name="procedureId"></param>
         /// <returns></returns>
-        private async Task<(bool IsSucceed, ICogImage[] imges, string Message, int pointIndex, Guid procedureId)> GrabImageEx(int pointIndex, Guid procedureId, bool Is3dPhoto)
+        private async Task<(bool IsSucceed, ICogImage[] imges, string Message, int pointIndex, Guid procedureId)> GrabImageEx(int pointIndex, Guid procedureId, bool Is3dPhoto, ProductModel currentProduct, SystemConfiguration sysConfig)
         {
             // 获取当前产品
-            var currentProduct = _productService.GetCurrentProduct();
+            //var currentProduct = _productService.GetCurrentProduct();
             if (currentProduct == null)
             {
                 return (false, null, "当前产品为空,未加载产品!", pointIndex, procedureId);
@@ -4950,13 +4957,13 @@ namespace TeamAAS_VP.Core
                 SendTaskMessage($"开始执行 AOI 流程 (Point#{pointIndex}) 流程Id={pointIndex}", MessageLevel.Debug);
                 try
                 {
-                    var sysConfig = _configService.GetSystemConfiguration();
+                    //var sysConfig = _configService.GetSystemConfiguration();
                     if (selectedProcedure.Id == currentProduct.FixedDownCameraProcedureId && sysConfig.TurnOnAllLightUse == true)
                     {
                         await _remoteCommandService.SetLightBeforePhoto(selectedProcedure);
                     }
                     //1. 执行拍照
-                    var res = await _remoteCommandService.ExecuteGrabImageAsync(selectedProcedure, Is3dPhoto);
+                    var res = await _remoteCommandService.ExecuteGrabImageAsync(selectedProcedure, Is3dPhoto, sysConfig);
                     return (res.IsSucceed, res.Image, res.Msg, pointIndex, procedureId);
                 }
                 catch (Exception ex)

+ 1 - 1
TeamAAS-VM/Interfaces/IRemoteCommandService.cs

@@ -273,7 +273,7 @@ namespace TeamAAS_VP.Interfaces
         /// </summary>
         /// <param name="procedure"></param>
         /// <returns></returns>
-        Task<(bool IsSucceed, ICogImage[] Image, string Msg)> ExecuteGrabImageAsync(ProcedureModel procedure,bool Is3dPhoto);
+        Task<(bool IsSucceed, ICogImage[] Image, string Msg)> ExecuteGrabImageAsync(ProcedureModel procedure,bool Is3dPhoto, SystemConfiguration sysConfig);
 
         /// <summary>
         /// 执行相机拍照获取单个点位结果

+ 47 - 21
TeamAAS-VM/Models/Product/ProcedureModel.cs

@@ -40,6 +40,31 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _CameraNo, value); }
         }
 
+        /// <summary>
+        /// AI Train 文件夹名称
+        /// </summary>
+        private string _CameraTrainFolder;
+
+        public string CameraTrainFolder
+        {
+            get { return _CameraTrainFolder; }
+            set
+            {
+                if (value == null)
+                {
+                    return;
+                }
+                if (value=="Null")
+                {
+                    SetProperty(ref _CameraTrainFolder, null);
+                }
+                else
+                {
+                    SetProperty(ref _CameraTrainFolder, value);
+                }
+            }
+        }
+
         /// <summary>
         /// 视觉图像处理模式
         /// </summary>
@@ -48,7 +73,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// 触发相机命令
         /// </summary>
-        public string TriggerCommand {  get; set; }
+        public string TriggerCommand { get; set; }
 
         /// <summary>
         /// 机器人
@@ -80,7 +105,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// 主页图像显示窗口的名称
         /// </summary>
-        public string DisplayName {  get; set; }
+        public string DisplayName { get; set; }
 
         /// <summary>
         /// 视觉流程输出集合
@@ -88,7 +113,7 @@ namespace TeamAAS_VP.Models
         public ObservableCollection<ProcedureOutput> ProcedureOutputs
         {
             get { return _ProcedureOutputs; }
-            set { SetProperty(ref _ProcedureOutputs,value); }
+            set { SetProperty(ref _ProcedureOutputs, value); }
         }
 
         private string _OutputSeparator = ";";
@@ -109,7 +134,7 @@ namespace TeamAAS_VP.Models
         public FeederWorks FeederWorks
         {
             get { return _FeederWorks; }
-            set { SetProperty(ref _FeederWorks,value); }
+            set { SetProperty(ref _FeederWorks, value); }
         }
 
         private FeederStockWorks _FeederStockWorks;
@@ -150,7 +175,7 @@ namespace TeamAAS_VP.Models
         public float ExposureTime
         {
             get { return _ExposureTime; }
-            set { SetProperty(ref _ExposureTime, value);}
+            set { SetProperty(ref _ExposureTime, value); }
         }
 
         private float _ExposureTime2;
@@ -236,7 +261,7 @@ namespace TeamAAS_VP.Models
         /// <summary>
         /// 第二次拍照光源3亮度
         /// </summary>
-        public int Photo2Light3   
+        public int Photo2Light3
         {
             get { return _Photo2Light3; }
             set { SetProperty(ref _Photo2Light3, value); }
@@ -259,7 +284,7 @@ namespace TeamAAS_VP.Models
         public CogToolBlock ToolBlock
         {
             get { return _ToolBlock; }
-            set { SetProperty(ref _ToolBlock,value); }
+            set { SetProperty(ref _ToolBlock, value); }
         }
 
         private ObservableCollection<ToolInfo> _ToolInfo = new ObservableCollection<ToolInfo>();
@@ -322,7 +347,7 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _OutputPointMode, value); }
         }
 
-        private int _OutputPointCountMax=100;
+        private int _OutputPointCountMax = 100;
         /// <summary>
         /// 每次发送给机器人的点数最大值
         /// </summary>
@@ -337,12 +362,12 @@ namespace TeamAAS_VP.Models
         /// 是否剔除本次识别到的物料与上一次识别到的物料位置过近的点
         /// </summary>
         public bool IsExcludeNearPoint
-            {
+        {
             get { return _IsExcludeNearPoint; }
             set { SetProperty(ref _IsExcludeNearPoint, value); }
         }
 
-        private double _ExcludeNearPointThreshol=1;
+        private double _ExcludeNearPointThreshol = 1;
         /// <summary>
         /// 物料位置过近阈值
         /// </summary>
@@ -473,15 +498,15 @@ namespace TeamAAS_VP.Models
         {
             Id = Guid.NewGuid();
             ProcedureOutputs = new ObservableCollection<ProcedureOutput>();
-            ToolInfo= new ObservableCollection<ToolInfo>();
-            FeederFindFrequency= new FeederFrequencyModel();
-            UserDefineParameters= new ObservableCollection<ProcedureUserDefineParam>();
+            ToolInfo = new ObservableCollection<ToolInfo>();
+            FeederFindFrequency = new FeederFrequencyModel();
+            UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>();
             FeederWorks = new FeederWorks();
-            FeederStockWorks= new FeederStockWorks();
+            FeederStockWorks = new FeederStockWorks();
             LightChannels = new ObservableCollection<ChannelConfig>();
         }
 
-        public ProcedureModel(string name, string cameraName, Guid cameraId,int camerano)
+        public ProcedureModel(string name, string cameraName, Guid cameraId, int camerano)
         {
             Id = Guid.NewGuid();
             Name = name;
@@ -503,10 +528,11 @@ namespace TeamAAS_VP.Models
             {
                 Id = this.Id,
                 Name = this.Name,
-                CameraName=this.CameraName,
-                CameraNo=this.CameraNo,
+                CameraName = this.CameraName,
+                CameraNo = this.CameraNo,
                 VisionModel = this.VisionModel,
-                TriggerCommand=this.TriggerCommand,
+                CameraTrainFolder = this.CameraTrainFolder,
+                TriggerCommand = this.TriggerCommand,
                 RobotId = this.RobotId,
                 FeederId = this.FeederId,
                 CameraId = this.CameraId,
@@ -534,10 +560,10 @@ namespace TeamAAS_VP.Models
                 ImageFileNameFormat = this.ImageFileNameFormat,
                 IsCompress = this.IsCompress,
                 FeederFindFrequency = this.FeederFindFrequency?.Copy(),
-                IsExcludeNearPoint=this.IsExcludeNearPoint,
+                IsExcludeNearPoint = this.IsExcludeNearPoint,
                 ExcludeNearPointThreshol = this.ExcludeNearPointThreshol,
-                OutputPointMode=this.OutputPointMode,
-                OutputSeparator=this.OutputSeparator,
+                OutputPointMode = this.OutputPointMode,
+                OutputSeparator = this.OutputSeparator,
                 UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>(this.UserDefineParameters.Select(udp => udp.Copy())),
                 LightChannels = this.LightChannels == null ? new ObservableCollection<ChannelConfig>() : new ObservableCollection<ChannelConfig>(this.LightChannels.Select(ch => new ChannelConfig
                 {

+ 118 - 79
TeamAAS-VM/Services/RemoteCommandService.cs

@@ -106,6 +106,9 @@ namespace TeamAAS_VP.Services
         /// </summary>
         public event EventHandler<TimeSpan> CycleTimingTicked;
 
+        public float TempExposureTime = 0;
+        public float TempGain = 0;
+
         /// <summary>
         /// 构造函数,注入必要的系统服务。
         /// </summary>
@@ -1715,7 +1718,7 @@ namespace TeamAAS_VP.Services
                         IsCompress = procedure.IsCompress,
                         ProceductName = procedure.Name,
                         ImageFileName = imgName,
-                        Is3DModel=true
+                        Is3DModel = true
                     });
                     return true;
                 }
@@ -1777,7 +1780,7 @@ namespace TeamAAS_VP.Services
         /// 执行相机拍照并运行 ToolBlock,返回是否成功并通过 out 参数返回 Outputs。
         /// 该方法会发布 RenderUpdateNotification 以更新 UI。
         /// </summary>
-        public  bool  ExecutePhoto2(ProcedureModel procedure, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection)
+        public bool ExecutePhoto2(ProcedureModel procedure, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection)
         {
             try
             {
@@ -1811,7 +1814,7 @@ namespace TeamAAS_VP.Services
                 // 2. 为ToolBlock传入其他输入终端
                 if (InputTerminal != null)
                 {
-                    
+
                     LogHelper.WriteLogInfo($"为ToolBlock传入输入终端");
                     foreach (var item in InputTerminal)
                     {
@@ -1880,7 +1883,7 @@ namespace TeamAAS_VP.Services
                         IsCompress = procedure.IsCompress,
                         ProceductName = procedure.Name,
                         ImageFileName = imgName,
-                        Is3DModel=false
+                        Is3DModel = false
                     });
                     return true;
                 }
@@ -1922,7 +1925,7 @@ namespace TeamAAS_VP.Services
                         SavePath = procedure.SavePath,
                         ProceductName = procedure.Name,
                         ImageFileName = imgName,
-                        Is3DModel=false
+                        Is3DModel = false
                     });
                     return false;
                 }
@@ -1939,17 +1942,17 @@ namespace TeamAAS_VP.Services
         /// 执行相机拍照并运行 ToolBlock,返回是否成功并通过 out 参数返回 Outputs。
         /// 该方法会发布 RenderUpdateNotification 以更新 UI。
         /// </summary>
-        public bool ExecutePhoto(ProcedureModel procedure,ICogImage[] imges,Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection,bool is3dmodel, float confThreshold = 0.25f, float iouThreshold = 0.45f)
+        public bool ExecutePhoto(ProcedureModel procedure, ICogImage[] imges, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection, bool is3dmodel, float confThreshold = 0.25f, float iouThreshold = 0.45f)
         {
             try
             {
-                
+
                 procedure.ToolBlock.Inputs["InputImage"].Value = imges[0];
-                if(is3dmodel==true || imges.Length > 1)
+                if (is3dmodel == true || imges.Length > 1)
                 {
                     procedure.ToolBlock.Inputs["InputImage2"].Value = imges[1];
                 }
-                
+
                 // 2. 为ToolBlock传入其他输入终端
                 if (InputTerminal != null)
                 {
@@ -1974,11 +1977,14 @@ namespace TeamAAS_VP.Services
                 // photoAiValue 可能为 null/空,建议给个默认值
                 bool enablePhotoAi = photoAiValue == "1";
                 if (enablePhotoAi == true)
-                {                    
+                {
                     string exeDir = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
 
-                    string modelPath = System.IO.Path.Combine(exeDir, "TrainPhoto", "weights", "best_openvino_model", "best.xml");
-                    string metadataPath = System.IO.Path.Combine(exeDir, "TrainPhoto", "weights", "best_openvino_model", "metadata.yaml");
+                    string trainFolderName = string.IsNullOrWhiteSpace(procedure.CameraTrainFolder) ? "TrainPhoto" : procedure.CameraTrainFolder;
+                    string trainRoot = System.IO.Path.Combine(exeDir, "Train", trainFolderName);
+
+                    string modelPath = System.IO.Path.Combine(exeDir, trainRoot, "weights", "best_openvino_model", "best.xml");
+                    string metadataPath = System.IO.Path.Combine(exeDir, trainRoot, "weights", "best_openvino_model", "metadata.yaml");
 
                     // (可选)如果你希望缺文件时给出更明确提示:
                     if (!File.Exists(modelPath))
@@ -1997,7 +2003,7 @@ namespace TeamAAS_VP.Services
                         {
                             var results = yolo.Infer(bitmap);
                             procedure.ToolBlock.Inputs["PhotoData"].Value = "";
-                            procedure.ToolBlock.Inputs["Confidence"].Value ="";
+                            procedure.ToolBlock.Inputs["Confidence"].Value = "";
                             // 输出结果
                             SendTaskMessage($"识别的个数:{results.Count()}", MessageLevel.Alarm);
                             string ClassName = "";
@@ -2014,6 +2020,9 @@ namespace TeamAAS_VP.Services
                             // 绘制结果 - 使用 OpenCvSharp
                             using (var image2 = ImageHelper.ICogImageConvertMat(imges[0]))
                             {
+
+                                var OriginyoloColorCogImage = ImageHelper.MatConvertICogImage(image2, true);
+
                                 // 定义颜色映射
                                 var colors = new[]
                                 {
@@ -2078,14 +2087,17 @@ namespace TeamAAS_VP.Services
                                         HersheyFonts.HersheySimplex, 0.6, Scalar.White, 2);
                                 }
 
-                                // 保存结果图像
-                                //string outputPath = @"G:\TEST\文件\左上插线\result.jpg";
-                                //Cv2.ImWrite(outputPath, image2);
-                                //Console.WriteLine($"结果已保存到: {outputPath}");
-                                var cogImage = ImageHelper.MatConvertICogImage(image2);
-                                procedure.ToolBlock.Inputs["InputImage"].Value = cogImage;
+                                // 关键:这里转成彩色 ICogImage
+                                var yoloColorCogImage = ImageHelper.MatConvertICogImage(image2);
+
+                                // 模板检测如果需要原图,可以用 imges[0]
+                                // 如果你希望模板也拿 YOLO 画框图,就用 yoloColorCogImage
+                                procedure.ToolBlock.Inputs["InputImage"].Value = yoloColorCogImage;
+
                                 procedure.ToolBlock.Run();
-                                outputCollection = procedure.ToolBlock.Outputs;     //获取输出终端集合
+
+                                outputCollection = procedure.ToolBlock.Outputs;
+
                                 ICogRecord record = null;
                                 foreach (CogToolBlockTerminal item in outputCollection)
                                 {
@@ -2094,6 +2106,7 @@ namespace TeamAAS_VP.Services
                                 }
 
                                 bool Found = (bool)(outputCollection["Found"].Value);
+
                                 //获取图片存储的图片文件名
                                 string imgName = string.Empty;
                                 if (procedure.IsSaveImage && !string.IsNullOrEmpty(procedure.ImageFileNameFormat))
@@ -2111,11 +2124,36 @@ namespace TeamAAS_VP.Services
                                     // 生成文件名
                                     imgName = template.Generate();
                                 }
+
+                                _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
+                                {
+                                    Id = procedure.DisplayId,
+                                    DisplayName = procedure.DisplayName,
+
+                                    Image = OriginyoloColorCogImage,
+
+                                    Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
+                                    Record = record,
+                                    Result = Found,
+                                    IsShow = false,
+                                    IsSaveImage = procedure.IsSaveImage,
+                                    SaveImageModel = procedure.SaveImageModel,
+                                    SaveImagePathModel = procedure.SaveImagePathModel,
+                                    SavePath = procedure.SavePath,
+                                    IsCompress = procedure.IsCompress,
+                                    ProceductName = procedure.Name,
+                                    ImageFileName = imgName,
+                                    Is3DModel = is3dmodel,
+                                });
+
                                 _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
                                 {
                                     Id = procedure.DisplayId,
                                     DisplayName = procedure.DisplayName,
-                                    Image = cogImage,
+
+                                    // 显示和保存用 YOLO 彩色图
+                                    Image = yoloColorCogImage,
+
                                     Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
                                     Record = record,
                                     Result = Found,
@@ -2128,12 +2166,8 @@ namespace TeamAAS_VP.Services
                                     ProceductName = procedure.Name,
                                     ImageFileName = imgName,
                                     Is3DModel = is3dmodel,
-                                    //RangeImage = (CogImage16Range)procedure.ToolBlock.Outputs["RangeImage"].Value,
-                                    //GreyImage = (CogImage16Grey)procedure.ToolBlock.Outputs["GreyImage"].Value
                                 });
-                                // 可选:显示图像
-                                //Cv2.ImShow("Detection Result", image2);
-                                //Cv2.WaitKey(0);
+     
                             }
 
                         }
@@ -2199,8 +2233,8 @@ namespace TeamAAS_VP.Services
                                         SavePath = procedure.SavePath,
                                         IsCompress = procedure.IsCompress,
                                         ProceductName = procedure.Name,
-                                        ImageFileName = imgName+"image2",
-                                        Is3DModel = is3dmodel,
+                                        ImageFileName = imgName + "image2",
+                                        Is3DModel = false,
                                     });
                                 }
                                 _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
@@ -2246,7 +2280,7 @@ namespace TeamAAS_VP.Services
                                         IsCompress = procedure.IsCompress,
                                         ProceductName = procedure.Name,
                                         ImageFileName = imgName + "image2",
-                                        Is3DModel = is3dmodel,
+                                        Is3DModel = false,
                                     });
                                 }
                                 _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
@@ -2293,7 +2327,7 @@ namespace TeamAAS_VP.Services
                                     IsCompress = procedure.IsCompress,
                                     ProceductName = procedure.Name,
                                     ImageFileName = imgName + "image2",
-                                    Is3DModel = is3dmodel,
+                                    Is3DModel = false,
                                 });
                             }
                             _eventAggregator.GetEvent<RenderUpdateNotification>().Publish(new ShowRender()
@@ -2429,7 +2463,7 @@ namespace TeamAAS_VP.Services
                         return false;
                     }
                 }
-                
+
             }
             catch (Exception ex)
             {
@@ -2445,12 +2479,12 @@ namespace TeamAAS_VP.Services
         /// </summary>
         /// <param name="procedure"></param>
         /// <returns></returns>
-        public async Task <(bool IsSucceed, ICogImage[] Image, string Msg)> ExecuteGrabImageAsync(ProcedureModel procedure,bool Is3dPhoto)
+        public async Task<(bool IsSucceed, ICogImage[] Image, string Msg)> ExecuteGrabImageAsync(ProcedureModel procedure, bool Is3dPhoto, SystemConfiguration sysConfig)
         {
             try
             {
-                var sysConfig = _configService.GetSystemConfiguration();
-                
+                //var sysConfig = _configService.GetSystemConfiguration();
+
                 if (sysConfig.PhotosUse == true)
                 {
                     // 1. 采集图像
@@ -2562,6 +2596,7 @@ namespace TeamAAS_VP.Services
                     {
                         image = camera.Grab();
                         image2 = camera.Grab2();
+
                         LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms");
                         if (image == null || image2 == null)
                         {
@@ -2573,15 +2608,15 @@ namespace TeamAAS_VP.Services
                     }
                     else
                     {
-                        
+
                         LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms");
-                        if (image == null )
+                        if (image == null)
                         {
 
                             SendTaskMessage(Lang.图像采集失败, MessageLevel.Error);
                             return (false, null, "图像采集失败");
                         }
-                        if(procedure.PhotoCount>1)
+                        if (procedure.PhotoCount > 1)
                         {
                             if (image2 == null)
                             {
@@ -2591,7 +2626,7 @@ namespace TeamAAS_VP.Services
                             }
                             return (true, new ICogImage[] { image, image2 }, "成功");
                         }
-                        return (true, new ICogImage[] { image}, "成功");
+                        return (true, new ICogImage[] { image }, "成功");
 
                     }
                 }
@@ -2599,16 +2634,39 @@ namespace TeamAAS_VP.Services
                 {
                     // 1. 采集图像
                     var camera = _cameraService.GetCamera(procedure.CameraId);
-                    bool succed = camera.SetExposureTime(procedure.ExposureTime);
-                    if (!succed)
+                    if (TempExposureTime != procedure.ExposureTime)
                     {
-                        SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error);
+                        bool succed1 = camera.SetExposureTime(procedure.ExposureTime);
+                        if (!succed1)
+                        {
+                            SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error);
+                        }
+                        else
+                        {
+                            TempExposureTime = procedure.ExposureTime;
+                        }
                     }
-                    succed = camera.SetGain(procedure.Gain);
-                    if (!succed)
+                    //else
+                    //{
+                    //    SendTaskMessage($"相机曝光重复", MessageLevel.Error);
+                    //}
+                    if (TempGain != procedure.Gain)
                     {
-                        SendTaskMessage($"相机增益设置失败!", MessageLevel.Error);
+                        bool succed2 = camera.SetGain(procedure.Gain);
+                        if (!succed2)
+                        {
+                            SendTaskMessage($"相机增益设置失败!", MessageLevel.Error);
+                        }
+                        else
+                        {
+                            TempGain = procedure.Gain;
+                        }
                     }
+                    //else
+                    //{
+                    //    SendTaskMessage($"相机增益重复", MessageLevel.Error);
+                    //}
+
                     DateTime nowtime = DateTime.Now;
                     LogHelper.WriteLogInfo("开始采集图像");
                     //var image = camera.Grab();
@@ -2631,19 +2689,18 @@ namespace TeamAAS_VP.Services
                         LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms");
                         if (image == null)
                         {
-
                             SendTaskMessage(Lang.图像采集失败, MessageLevel.Error);
                             return (false, null, "图像采集失败");
                         }
                         return (true, new ICogImage[] { image }, "成功");
                     }
                 }
-                
-                
+
+
             }
             catch (Exception ex)
             {
-                LogHelper.WriteLogError("采集图像失败!",ex);
+                LogHelper.WriteLogError("采集图像失败!", ex);
                 return (false, null, "图像采集失败");
             }
         }
@@ -2656,7 +2713,7 @@ namespace TeamAAS_VP.Services
         /// <param name="robotCoord"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        public async Task<(bool IsSucceed, string[] Items, double Count)> ExecutePhotoGetSinglePoint1(ProcedureModel procedure, ICogImage[] imges, Dictionary<string, string> InputTerminal, double[] robotCoord, CancellationToken cancellationToken,bool is3dmodel, float confThreshold = 0.25f, float iouThreshold = 0.45f)
+        public async Task<(bool IsSucceed, string[] Items, double Count)> ExecutePhotoGetSinglePoint1(ProcedureModel procedure, ICogImage[] imges, Dictionary<string, string> InputTerminal, double[] robotCoord, CancellationToken cancellationToken, bool is3dmodel, float confThreshold = 0.25f, float iouThreshold = 0.45f)
         {
             DateTime nowtime = DateTime.Now;
             try
@@ -2689,37 +2746,19 @@ namespace TeamAAS_VP.Services
                     //    continue;
                     //}
 
-                    SendTaskMessage(Lang.拍照OK, MessageLevel.Debug);
+                    //SendTaskMessage(Lang.拍照OK, MessageLevel.Debug);
 
                     int Count = (int)(outputCollection["Count"].Value);
                     string[] PhotoRes = new string[100];
                     for (int k = 1; k <= Count; k++)
                     {
-                       
+
                         PhotoRes[k] = (string)(outputCollection[$"dis{k}"].Value);
                         //PhotoRes[k] = "222";
                     }
                     // 3. 获取视觉输出结果
                     LogHelper.WriteLogInfo("开始获取视觉输出结果");
-                    /*
-                    //获取相机校准
-                    var calib = _calibrationService.GetCalibration(procedure.CalibrationId);
-                    string points = (string)(outputCollection["Point"].Value);
-                    string[] strpoints = points.Split(';');
-                    //先将pos按照逗号进行分隔,拿到数组
-                    var posParts = strpoints[0].Split(',');
-                    double _pixel_x = double.Parse(posParts[0]);
-                    double _pixel_y = double.Parse(posParts[1]);
-                    double _pixel_u = double.Parse(posParts[2]);
-                    //转换点位-像素转换成机器人绝对坐标
-                    var calibResult = _calibrationService.ConvertPixelToPosition((_pixel_x, _pixel_y, _pixel_u), robotCoord, calib, RobotBrand.XYZ_Platform);
-                    if (!calibResult.IsSucceed)
-                    {
-                        return (false, 0, 0, 0);
-                    }
-
-                    return (true, calibResult.X, calibResult.Y, calibResult.U);
-                    */
+               
                     return (true, PhotoRes, Count);
                 }
                 SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
@@ -2757,7 +2796,7 @@ namespace TeamAAS_VP.Services
             {
                 await SetLightBeforePhoto(procedure);
             }
-            
+
             try
             {
                 //拍照失败时,需要多次拍照
@@ -2838,13 +2877,13 @@ namespace TeamAAS_VP.Services
         /// <param name="outImage"></param>
         /// <param name="outGraphicCollection"></param>
         /// <returns></returns>
-        public bool ExecutePhotoEx(ProcedureModel procedure, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection,out ICogImage outImage,out CogGraphicCollection outGraphicCollection)
+        public bool ExecutePhotoEx(ProcedureModel procedure, Dictionary<string, string> InputTerminal, out CogToolBlockTerminalCollection outputCollection, out ICogImage outImage, out CogGraphicCollection outGraphicCollection)
         {
             outImage = null;
             outGraphicCollection = null;
             try
             {
-                
+
                 // 1. 采集图像
                 var camera = _cameraService.GetCamera(procedure.CameraId);
                 bool succed = camera.SetExposureTime(procedure.ExposureTime);
@@ -2867,7 +2906,7 @@ namespace TeamAAS_VP.Services
                     outputCollection = null;
                     return false;
                 }
-                outImage= image;
+                outImage = image;
                 procedure.ToolBlock.Inputs["InputImage"].Value = image;
                 LogHelper.WriteLogInfo($"采集图像完成;用时:{(DateTime.Now - nowtime).Milliseconds}ms");
 
@@ -2905,7 +2944,7 @@ namespace TeamAAS_VP.Services
                         if (item.Value is ICogRecord)
                             record = item.Value as ICogRecord;
                     }
-                    outGraphicCollection= outputCollection["Graphic"].Value as CogGraphicCollection;
+                    outGraphicCollection = outputCollection["Graphic"].Value as CogGraphicCollection;
                     return true;
                 }
                 else
@@ -2987,7 +3026,7 @@ namespace TeamAAS_VP.Services
                     //{
                     //    return (false, 0, 0, 0);
                     //}
-                    return (true, 0,0, 0);
+                    return (true, 0, 0, 0);
                 }
                 SendTaskMessage(Lang.多次拍照失败, MessageLevel.Alarm);
                 return (false, 0, 0, 0);
@@ -3112,8 +3151,8 @@ namespace TeamAAS_VP.Services
         {
             DateTime nowtime = DateTime.Now;
             await SetLightBeforePhoto(procedure);
-            ICogImage outImage=null;
-            CogGraphicCollection outGraphicCollection=null;
+            ICogImage outImage = null;
+            CogGraphicCollection outGraphicCollection = null;
             try
             {
                 //拍照失败时,需要多次拍照
@@ -3601,7 +3640,7 @@ namespace TeamAAS_VP.Services
             {
                 return;
             }
-               
+
             //判断当前流程是否启用光源控制
             if (procedure.IsControlLightChannels)
             {

+ 29 - 1
TeamAAS-VM/ViewModels/Product/ProcessBasicsViewModel.cs

@@ -13,6 +13,7 @@ using System.ComponentModel;
 using System.Diagnostics.Eventing.Reader;
 using System.IO;
 using System.Linq;
+using System.Reflection;
 using TeamAAS_VP;
 using TeamAAS_VP.Core;
 using TeamAAS_VP.Events;
@@ -38,6 +39,14 @@ namespace TeamAAS_VP.ViewModels.Product
 
         #region 属性
 
+
+        private ObservableCollection<string> _CameraTrainFolderOptions;
+        public ObservableCollection<string> CameraTrainFolderOptions
+        {
+            get { return _CameraTrainFolderOptions; }
+            set { SetProperty(ref _CameraTrainFolderOptions, value); }
+        }
+
         private ProductModel _SelectProduct;
 
         /// <summary>
@@ -68,6 +77,7 @@ namespace TeamAAS_VP.ViewModels.Product
                 }
                 else
                 {
+
                     SelectRobot = _configService.GetAllRobots().FirstOrDefault(p => p.Id == value.RobotId);
                     SelectFeeder = _configService.GetAllFeeders().FirstOrDefault(p => p.Id == value.FeederId);
                     SelectCalibration = _calibrationService.GetAllCalibrations().FirstOrDefault(p => p.Id == value.CalibrationId);
@@ -85,6 +95,7 @@ namespace TeamAAS_VP.ViewModels.Product
             }
         }
 
+
         private ObservableCollection<ProcedureModel> _ProcedureModels;
 
         /// <summary>
@@ -512,11 +523,28 @@ namespace TeamAAS_VP.ViewModels.Product
             FeederList = new ObservableCollection<FeederInfo>(_configService.GetAllFeeders());
             CalibrationList = new ObservableCollection<CalibrationInfo>(_calibrationService.GetAllCalibrations());
             DisplatConfigs = new ObservableCollection<DisplayConfig>();
-            
+
             if (navigationContext.Parameters.ContainsKey("SelectedProcedure"))
             {
                 SelectProcedure = navigationContext.Parameters.GetValue<ProcedureModel>("SelectedProcedure");
             }
+
+            string exeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+            var trainRoot = Path.Combine(exeDir, "Train");
+            if (!Directory.Exists(trainRoot))
+            {
+                return;
+            }
+            CameraTrainFolderOptions = new ObservableCollection<string>();
+            foreach (var directory in Directory.GetDirectories(trainRoot).OrderBy(p => p))
+            {
+                var folderName = Path.GetFileName(directory);
+                if (!string.IsNullOrWhiteSpace(folderName))
+                {
+                    CameraTrainFolderOptions.Add(folderName);
+                }
+            }
+            CameraTrainFolderOptions.Add("Null");
         }
 
         /// <summary>

+ 44 - 0
TeamAAS-VM/Views/AddScrewView.xaml

@@ -0,0 +1,44 @@
+<Window x:Class="TeamAAS_VP.Views.AddScrewView"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:local="clr-namespace:TeamAAS_VP.Views"
+        mc:Ignorable="d"
+        Title="AddScrewView" Height="450" Width="800">
+    <Grid Margin="20">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+
+        <TextBlock Grid.Row="0" 
+                   Text="请扫描螺丝条码:" 
+                   FontSize="14" 
+                   Margin="0,0,0,10"/>
+
+        <!-- 输入框会自动接收并显示扫码枪的结果 -->
+        <TextBox x:Name="BarcodeTextBox" 
+                 Grid.Row="1" 
+                 Height="30" 
+                 FontSize="14"
+                 Margin="0,0,0,20"/>
+
+        <StackPanel Grid.Row="2" 
+                    Orientation="Horizontal" 
+                    HorizontalAlignment="Right">
+            <Button x:Name="OkButton" 
+                    Content="确定" 
+                    Width="80" 
+                    Height="30" 
+                    Margin="0,0,10,0"
+                    Click="OkButton_Click"/>
+            <Button x:Name="CancelButton" 
+                    Content="取消" 
+                    Width="80" 
+                    Height="30"
+                    Click="CancelButton_Click"/>
+        </StackPanel>
+    </Grid>
+</Window>

+ 77 - 0
TeamAAS-VM/Views/AddScrewView.xaml.cs

@@ -0,0 +1,77 @@
+using Prism.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+using TeamAAS_VP.Enums;
+using TeamAAS_VP.Events;
+using TeamAAS_VP.Interfaces;
+
+namespace TeamAAS_VP.Views
+{
+    /// <summary>
+    /// AddScrewView.xaml 的交互逻辑
+    /// </summary>
+    public partial class AddScrewView : Window
+    {
+        IConfigService _configService;
+        IEventAggregator _eventAggregator;
+        public string ScannedBarcode { get; private set; }
+        public AddScrewView()
+        {
+            InitializeComponent();
+            // 窗口加载完成后,自动让输入框获得焦点
+            this.Loaded += (s, e) =>
+            {
+                BarcodeTextBox.Focus();
+            };
+        }
+        private void OkButton_Click(object sender, RoutedEventArgs e)
+        {
+            var stationConfig = _configService.GetDeviceInfo(0);
+            ScannedBarcode = BarcodeTextBox.Text;
+
+            if (string.IsNullOrWhiteSpace(ScannedBarcode))
+            {
+                MessageBox.Show("请先扫描螺丝条码", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+                BarcodeTextBox.Focus(); // 重新聚焦
+                return;
+            }
+            if (ScannedBarcode == stationConfig.DataInfo.Screwsize1 || ScannedBarcode == stationConfig.DataInfo.Screwsize2)
+            {
+                MessageBox.Show("螺丝型号匹配成功", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+                SendTaskMessage($"螺丝型号匹配成功", MessageLevel.Info);
+            }
+            else
+            {
+                MessageBox.Show("螺丝型号匹配失败", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
+                SendTaskMessage($"螺丝型号匹配失败", MessageLevel.Error);
+            }
+            DialogResult = true;
+            Close();
+        }
+
+        private void CancelButton_Click(object sender, RoutedEventArgs e)
+        {
+            DialogResult = false;
+            Close();
+        }
+
+        public void SendTaskMessage(string msg, MessageLevel level)
+        {
+            App.Current.Dispatcher.Invoke(() =>
+            {
+                _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = msg, level = level });
+            });
+        }
+    }
+}

+ 32 - 7
TeamAAS-VM/Views/Product/ProcessBasics.xaml

@@ -131,6 +131,11 @@
                                    MinHeight="35" />
                     <RowDefinition Height="auto"
                                    MinHeight="35" />
+                    <RowDefinition Height="auto"
+               MinHeight="35" />
+                    <RowDefinition Height="auto"
+               MinHeight="35" />
+
                 </Grid.RowDefinitions>
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="auto" />
@@ -224,16 +229,16 @@
                 </ComboBox>
 
                 <TextBlock Text="{lex:Loc 是否控制光源通道亮灭,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
-                           Grid.Row="8"
+                           Grid.Row="9"
                            Grid.Column="0" />
-                <ToggleButton Grid.Row="8"
+                <ToggleButton Grid.Row="9"
                               Grid.Column="1"
                               Width="60"
                               Height="30"
                               HorizontalAlignment="Left"
                               IsChecked="{Binding SelectProcedure.IsControlLightChannels,Mode=TwoWay}" />
 
-                <Button Grid.Row="8"
+                <Button Grid.Row="9"
                         Grid.Column="1"
                         HorizontalAlignment="Right"
                         materialDesign:HintAssist.Hint="{lex:Loc 选择光源通道}"
@@ -246,10 +251,10 @@
 
                 <!-- 光源通道配置(已改为弹窗选择) -->
                 <TextBlock Text="{lex:Loc 光源通道,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
-                           Grid.Row="9"
+                           Grid.Row="10"
                            Grid.RowSpan="1" />
 
-                <ListBox Grid.Row="9"
+                <ListBox Grid.Row="10"
                          Grid.Column="1"
                          ItemsSource="{Binding SelectProcedure.LightChannels}"
                          SelectedItem="{Binding SelectedLightChannel}"
@@ -311,10 +316,30 @@
                     </ComboBox.ItemTemplate>
                 </ComboBox>
 
+                <TextBlock Text="AI Train: "
+                      Grid.Row="7"
+                      Grid.Column="0" />
+                           <ComboBox Grid.Row="7"
+                     Grid.Column="1"
+                     materialDesign:HintAssist.Hint="选择AI Train"
+                     materialDesign:TextFieldAssist.HasClearButton="True"
+                     ItemsSource="{Binding CameraTrainFolderOptions}"
+                     SelectedItem="{Binding CameraTrainFolder,Mode=TwoWay}">
+                    <ComboBox.ItemTemplate>
+                        <DataTemplate>
+                            <StackPanel Orientation="Horizontal">
+                                <materialDesign:PackIcon Kind="Dais" />
+                                <TextBlock Text="{Binding}"
+                           Margin="6,0" />
+                            </StackPanel>
+                        </DataTemplate>
+                    </ComboBox.ItemTemplate>
+                </ComboBox>
+
                 <TextBlock Text="{lex:Loc 触发命令,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
-                           Grid.Row="7"
+                           Grid.Row="8"
                            Grid.Column="0" />
-                <TextBox Grid.Row="7"
+                <TextBox Grid.Row="8"
                          Grid.Column="1"
                          materialDesign:HintAssist.Hint="{lex:Loc 触发命令}"
                          Text="{Binding SelectProcedure.TriggerCommand}" />