修改框架(未完全完成)实现单个相机分开绑定算法
This commit is contained in:
		| @@ -25,6 +25,8 @@ namespace Check.Main.Infer | ||||
|         /// </summary> | ||||
|         private static int _enabledCameraCount = 0; | ||||
|  | ||||
|         private static long _productCounter = 0; // 新增产品计数器10.22 | ||||
|  | ||||
|         public static event EventHandler<DetectionResultEventArgs> OnDetectionCompleted; | ||||
|         public static bool IsDetectionRunning { get; private set; } = false; | ||||
|  | ||||
| @@ -51,72 +53,220 @@ namespace Check.Main.Infer | ||||
|         public static void Initialize(List<CameraSettings> cameraSettings, List<ModelSettings> modelSettings) | ||||
|         { | ||||
|             Shutdown(); // 先关闭旧的 | ||||
|             YoloModelManager.Initialize(modelSettings); // 确保 YOLO 模型在初始化协调器时加载。10.22新增 | ||||
|  | ||||
|             var enabledCameras = cameraSettings.Where(c => c.IsEnabled).ToList(); | ||||
|             _enabledCameraCount = enabledCameras.Count; | ||||
|             if (_enabledCameraCount == 0) return; | ||||
|             //if (_enabledCameraCount == 0) return; | ||||
|             if (_enabledCameraCount == 0) | ||||
|             { | ||||
|                 ThreadSafeLogger.Log("没有启用的相机,检测协调器未初始化。"); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             foreach (var camSetting in enabledCameras) | ||||
|             { | ||||
|                 // 找到与相机编号匹配的模型 | ||||
|                 var model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID); | ||||
|                 ModelSettings model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID); | ||||
|                 if (model == null) | ||||
|                 { | ||||
|                     ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像。"); | ||||
|                     //ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像"); | ||||
|                     ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 匹配的模型 (ID: {camSetting.ModelID})。该相机将无法处理图像。"); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 var processor = new CameraProcessor(camSetting.CameraIndex,camSetting.ModelID); | ||||
|                 IDetector detector = null; | ||||
|                 object detectorSettings = null; // 用于传递特定检测器的设置 | ||||
|  | ||||
|                 // 根据相机的 CheckType 和模型的 AlgorithmType 决定使用哪个检测器 | ||||
|                 if (camSetting.CheckType == CheckType.Traditional && model.M_AType == AlgorithmType.Tradition) | ||||
|                 { | ||||
|                     detector = new HalconTemplateDetector(); | ||||
|                     detectorSettings = new HalconDetectionSettings { ScoreThreshold = model.HalconScoreThreshold }; | ||||
|                     ThreadSafeLogger.Log($"为相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 绑定 HALCON 传统算法。"); | ||||
|                 } | ||||
|                 else if (camSetting.CheckType == CheckType.DeepLearning && model.M_AType == AlgorithmType.DeepLearning) | ||||
|                 { | ||||
|                     detector = new YoloDetector(); | ||||
|                     detectorSettings = new YoloDetectionSettings | ||||
|                     { | ||||
|                         ConfidenceThreshold = model.YoloConfidenceThreshold, | ||||
|                         NmsThreshold = model.YoloNmsThreshold | ||||
|                     }; | ||||
|                     ThreadSafeLogger.Log($"为相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 绑定 YOLO 深度学习算法。"); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ThreadSafeLogger.Log($"[警告] 相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 的 CheckType ({camSetting.CheckType}) 与模型 (ID: {model.Id}, AlgorithmType: {model.M_AType}) 不匹配或不支持。跳过此相机。"); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 // 初始化检测器 | ||||
|                 try | ||||
|                 { | ||||
|                     // 对于YOLO,modelPath实际上传递的是ModelID | ||||
|                     // 对于HALCON,modelPath是实际的模板目录 | ||||
|                     string initPath = (detector is YoloDetector) ? model.Id.ToString() : model.Path; | ||||
|                     detector.Initialize(initPath, detectorSettings); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     ThreadSafeLogger.Log($"[错误] 初始化相机 #{camSetting.CameraIndex} 的检测器失败: {ex.Message}"); | ||||
|                     detector?.Dispose(); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 var processor = new CameraProcessor(camSetting.CameraIndex, detector, model); | ||||
|                 _processors.TryAdd(camSetting.CameraIndex, processor); | ||||
|                 processor.Start(); | ||||
|             } | ||||
|             ThreadSafeLogger.Log($"检测协调器已初始化,启动了 {_processors.Count} 个相机处理线程。"); | ||||
|         } | ||||
|  | ||||
|         //public static void EnqueueImage(int cameraIndex, Bitmap bmp) | ||||
|         //{ | ||||
|         //    if (_processors.TryGetValue(cameraIndex, out var processor)) | ||||
|         //    { | ||||
|         //        processor.EnqueueImage(bmp); | ||||
|         //    } | ||||
|         //    else | ||||
|         //    { | ||||
|         //        // 如果找不到处理器,必须释放Bitmap防止泄漏 | ||||
|         //        bmp?.Dispose(); | ||||
|         //    } | ||||
|         //} | ||||
|  | ||||
|         public static void EnqueueImage(int cameraIndex, Bitmap bmp) | ||||
|         { | ||||
|             // 在图像进入队列之前生成一个新的产品ID | ||||
|             long currentProductId; | ||||
|             lock (_productAssemblies) // 同步访问产品计数器 | ||||
|             { | ||||
|                 _productCounter++; | ||||
|                 currentProductId = _productCounter; | ||||
|             } | ||||
|  | ||||
|             if (_processors.TryGetValue(cameraIndex, out var processor)) | ||||
|             { | ||||
|                 processor.EnqueueImage(bmp); | ||||
|                 processor.EnqueueImage(bmp, currentProductId); // 传递产品ID | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // 如果找不到处理器,必须释放Bitmap防止泄漏 | ||||
|                 bmp?.Dispose(); | ||||
|                 bmp?.Dispose(); // 如果找不到处理器,必须释放Bitmap防止泄漏 | ||||
|                 ThreadSafeLogger.Log($"[警告] 未能为相机 {cameraIndex} 找到处理器,产品 {currentProductId} 的图像被丢弃。"); | ||||
|                 // 如果没有处理器,不需要在 _productAssemblies 中添加,因为不会有结果返回 | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // 供 CameraProcessor 回调,用以组装产品 | ||||
|         public static void AssembleProduct(ImageData data, string result) | ||||
|         { | ||||
|             var assembly = _productAssemblies.GetOrAdd(data.ProductId, (id) => new ProductAssembly(id, _enabledCameraCount)); | ||||
|         //// 供 CameraProcessor 回调,用以组装产品 | ||||
|         //public static void AssembleProduct(ImageData data, string result) | ||||
|         //{ | ||||
|         //    var assembly = _productAssemblies.GetOrAdd(data.ProductId, (id) => new ProductAssembly(id, _enabledCameraCount)); | ||||
|  | ||||
|             if (assembly.AddResult(data.CameraIndex, result)) | ||||
|         //    if (assembly.AddResult(data.CameraIndex, result)) | ||||
|         //    { | ||||
|         //        string finalResult = assembly.GetFinalResult(); | ||||
|         //        ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}"); | ||||
|  | ||||
|         //        // 只有在检测运行时,才触发事件 | ||||
|         //        if (IsDetectionRunning) | ||||
|         //        { | ||||
|         //            OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(finalResult == "OK")); | ||||
|         //        } | ||||
|  | ||||
|         //        if (_productAssemblies.TryRemove(assembly.ProductId, out var finishedAssembly)) | ||||
|         //        { | ||||
|         //            finishedAssembly.Dispose(); | ||||
|         //        } | ||||
|         //    } | ||||
|         //} | ||||
|  | ||||
|         // CameraProcessor 回调,用以组装产品 | ||||
|         public static void AssembleProduct(long productId, int cameraIndex, bool isOk, Bitmap resultImage) | ||||
|         { | ||||
|             // GetOrAdd 确保 ProductAssembly 只被创建一次 | ||||
|             var assembly = _productAssemblies.GetOrAdd(productId, (id) => new ProductAssembly(id, _enabledCameraCount)); | ||||
|  | ||||
|             assembly.AddResult(cameraIndex, isOk, resultImage); | ||||
|  | ||||
|             // 检查产品是否已完成所有相机的检测 | ||||
|             if (assembly.IsComplete()) | ||||
|             { | ||||
|                 string finalResult = assembly.GetFinalResult(); | ||||
|                 string finalResult = assembly.GetFinalResult() ? "OK" : "NG"; | ||||
|                 ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}"); | ||||
|  | ||||
|                 // 只有在检测运行时,才触发事件 | ||||
|                 // 触发事件 (例如更新主UI上的总OK/NG计数) | ||||
|                 if (IsDetectionRunning) | ||||
|                 { | ||||
|                     OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(finalResult == "OK")); | ||||
|                     OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(assembly.GetFinalResult())); | ||||
|                 } | ||||
|  | ||||
|                 if (_productAssemblies.TryRemove(assembly.ProductId, out var finishedAssembly)) | ||||
|                 // PLC 写入逻辑 | ||||
|                 if (FrmMain.PlcClient != null) // 假设 FrmMain.PlcClient 可访问 | ||||
|                 { | ||||
|                     finishedAssembly.Dispose(); | ||||
|                     try | ||||
|                     { | ||||
|                         if (assembly.GetFinalResult()) // 最终结果 OK | ||||
|                         { | ||||
|                             FrmMain.PlcClient.WriteBool("M90", true); // 写入M90为1 | ||||
|                             Thread.Sleep(50); // 短暂延时 | ||||
|                             FrmMain.PlcClient.WriteBool("M90", false); | ||||
|                         } | ||||
|                         else // 最终结果 NG | ||||
|                         { | ||||
|                             FrmMain.PlcClient.WriteBool("M91", true); // 写入M91为1 | ||||
|                             Thread.Sleep(50); // 短暂延时 | ||||
|                             FrmMain.PlcClient.WriteBool("M91", false); | ||||
|                         } | ||||
|                         ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 最终结果 {finalResult} 已写入PLC。"); | ||||
|                     } | ||||
|                     catch (Exception ex) | ||||
|                     { | ||||
|                         ThreadSafeLogger.Log($"[错误] 写入PLC失败:{ex.Message}"); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     ThreadSafeLogger.Log($"[警告] 产品 #{assembly.ProductId} 检测结果未能写入PLC:PLC客户端未连接。"); | ||||
|                 } | ||||
|  | ||||
|                 // 移除并释放 ProductAssembly | ||||
|                 if (_productAssemblies.TryRemove(productId, out var finishedAssembly)) | ||||
|                 { | ||||
|                     finishedAssembly.Dispose(); // 释放所有存储的 Bitmap | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// 命令所有活动的相机处理器重置它们的内部计数器。 | ||||
|         /// </summary> | ||||
|  | ||||
|         ///// <summary> | ||||
|         ///// 命令所有活动的相机处理器重置它们的内部计数器。 | ||||
|         ///// </summary> | ||||
|         //public static void ResetAllCounters() | ||||
|         //{ | ||||
|         //    foreach (var processor in _processors.Values) | ||||
|         //    { | ||||
|         //        processor.ResetCounter(); | ||||
|         //    } | ||||
|         //    ThreadSafeLogger.Log("所有相机处理器的产品计数器已重置。"); | ||||
|         //} | ||||
|  | ||||
|         public static void ResetAllCounters() | ||||
|         { | ||||
|             lock (_productAssemblies) | ||||
|             { | ||||
|                 _productCounter = 0; | ||||
|                 // 清空所有未完成的产品,并释放其资源 | ||||
|                 foreach (var assembly in _productAssemblies.Values) | ||||
|                 { | ||||
|                     assembly.Dispose(); | ||||
|                 } | ||||
|                 _productAssemblies.Clear(); | ||||
|             } | ||||
|             foreach (var processor in _processors.Values) | ||||
|             { | ||||
|                 processor.ResetCounter(); | ||||
|             } | ||||
|             ThreadSafeLogger.Log("所有相机处理器的产品计数器已重置。"); | ||||
|             ThreadSafeLogger.Log("所有相机处理器和产品计数器已重置。"); | ||||
|         } | ||||
|  | ||||
|         public static CameraProcessor GetProcessor(int cameraIndex) | ||||
| @@ -142,8 +292,88 @@ namespace Check.Main.Infer | ||||
|             { | ||||
|                 assembly.Dispose(); | ||||
|             } | ||||
|             _productAssemblies.Clear(); | ||||
|             YoloModelManager.Shutdown(); // 确保YOLO模型也关闭 | ||||
|             ThreadSafeLogger.Log("检测协调器已关闭。"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 新增 ProductAssembly 类,用于集中管理一个产品的检测结果和图像 | ||||
|     public class ProductAssembly : IDisposable | ||||
|     { | ||||
|         public long ProductId { get; } | ||||
|         private readonly int _expectedCameraCount; | ||||
|         private readonly ConcurrentDictionary<int, bool> _cameraResults = new ConcurrentDictionary<int, bool>(); | ||||
|         private readonly ConcurrentDictionary<int, Bitmap> _resultImages = new ConcurrentDictionary<int, Bitmap>(); // 存储每个相机的结果图像 | ||||
|         private readonly object _lock = new object(); | ||||
|  | ||||
|         public ProductAssembly(long productId, int expectedCameraCount) | ||||
|         { | ||||
|             ProductId = productId; | ||||
|             _expectedCameraCount = expectedCameraCount; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 添加单个相机的检测结果。 | ||||
|         /// </summary> | ||||
|         /// <param name="cameraIndex">相机编号。</param> | ||||
|         /// <param name="isOk">检测结果是否为OK。</param> | ||||
|         /// <param name="resultImage">带有检测结果的图像。</param> | ||||
|         public void AddResult(int cameraIndex, bool isOk, Bitmap resultImage) | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 _cameraResults.TryAdd(cameraIndex, isOk); | ||||
|                 if (resultImage != null) | ||||
|                 { | ||||
|                     // 克隆图像,确保 ProductAssembly 拥有其所有权 | ||||
|                     _resultImages.TryAdd(cameraIndex, (Bitmap)resultImage.Clone()); | ||||
|                     resultImage.Dispose(); // 释放传入的原始图像副本 | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 检查是否所有相机都已提交结果。 | ||||
|         /// </summary> | ||||
|         public bool IsComplete() | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 return _cameraResults.Count == _expectedCameraCount; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取最终产品检测结果(所有相机都OK才为OK)。 | ||||
|         /// </summary> | ||||
|         public bool GetFinalResult() | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 return _cameraResults.Values.All(r => r); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// 获取某个相机的结果图像。 | ||||
|         /// </summary> | ||||
|         public Bitmap GetResultImage(int cameraIndex) | ||||
|         { | ||||
|             _resultImages.TryGetValue(cameraIndex, out var bmp); | ||||
|             return bmp; | ||||
|         } | ||||
|  | ||||
|         public void Dispose() | ||||
|         { | ||||
|             lock (_lock) | ||||
|             { | ||||
|                 foreach (var bmp in _resultImages.Values) | ||||
|                 { | ||||
|                     bmp?.Dispose(); | ||||
|                 } | ||||
|                 _resultImages.Clear(); | ||||
|                 _cameraResults.Clear(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user