using Check.Main.Camera; using Check.Main.Common; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Check.Main.Infer { public static class DetectionCoordinator { /// /// 定义存储所有相机处理器的字典 /// 键是相机的唯一编号 (CameraIndex),值是对应的处理器实例。 /// private static ConcurrentDictionary _processors = new ConcurrentDictionary(); /// /// 用于在产品组装时进行同步,确保线程安全 /// private static ConcurrentDictionary _productAssemblies = new ConcurrentDictionary(); /// /// 可用的相机数量 /// private static int _enabledCameraCount = 0; private static long _productCounter = 0; // 新增产品计数器10.22 public static event EventHandler OnDetectionCompleted; public static bool IsDetectionRunning { get; private set; } = false; // OnDetectionCompleted 事件现在也属于这里 //public static event EventHandler OnDetectionCompleted; public static void StartDetection() { if (!IsDetectionRunning) { IsDetectionRunning = true; ThreadSafeLogger.Log("检测统计已启动。"); } } public static void StopDetection() { if (IsDetectionRunning) { IsDetectionRunning = false; ThreadSafeLogger.Log("检测统计已停止。"); } } public static void Initialize(List cameraSettings, List 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) { ThreadSafeLogger.Log("没有启用的相机,检测协调器未初始化。"); return; } foreach (var camSetting in enabledCameras) { // 找到与相机编号匹配的模型 ModelSettings model = modelSettings.FirstOrDefault(m => m.Id == camSetting.ModelID); if (model == null) { //ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} 匹配的模型,该相机将无法处理图像"); ThreadSafeLogger.Log($"[警告] 找不到与相机 #{camSetting.CameraIndex} (Name: {camSetting.Name}) 匹配的模型 (ID: {camSetting.ModelID})。该相机将无法处理图像。"); continue; } 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, currentProductId); // 传递产品ID } else { 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)); // 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() ? "OK" : "NG"; ThreadSafeLogger.Log($"产品 #{assembly.ProductId} 已检测完毕,最终结果: {finalResult}"); // 触发事件 (例如更新主UI上的总OK/NG计数) if (IsDetectionRunning) { OnDetectionCompleted?.Invoke(null, new DetectionResultEventArgs(assembly.GetFinalResult())); } // PLC 写入逻辑 if (FrmMain.PlcClient != null) // 假设 FrmMain.PlcClient 可访问 { 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 } } } ///// ///// 命令所有活动的相机处理器重置它们的内部计数器。 ///// //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("所有相机处理器和产品计数器已重置。"); } public static CameraProcessor GetProcessor(int cameraIndex) { _processors.TryGetValue(cameraIndex, out var p); return p; } public static IEnumerable GetAllProcessors() { return _processors.Values; } public static void Shutdown() { foreach (var processor in _processors.Values) { processor.Dispose(); } _processors.Clear(); foreach (var assembly in _productAssemblies.Values) { assembly.Dispose(); } YoloModelManager.Shutdown(); // 确保YOLO模型也关闭 ThreadSafeLogger.Log("检测协调器已关闭。"); } } // 新增 ProductAssembly 类,用于集中管理一个产品的检测结果和图像 public class ProductAssembly : IDisposable { public long ProductId { get; } private readonly int _expectedCameraCount; private readonly ConcurrentDictionary _cameraResults = new ConcurrentDictionary(); private readonly ConcurrentDictionary _resultImages = new ConcurrentDictionary(); // 存储每个相机的结果图像 private readonly object _lock = new object(); public ProductAssembly(long productId, int expectedCameraCount) { ProductId = productId; _expectedCameraCount = expectedCameraCount; } /// /// 添加单个相机的检测结果。 /// /// 相机编号。 /// 检测结果是否为OK。 /// 带有检测结果的图像。 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(); // 释放传入的原始图像副本 } } } /// /// 检查是否所有相机都已提交结果。 /// public bool IsComplete() { lock (_lock) { return _cameraResults.Count == _expectedCameraCount; } } /// /// 获取最终产品检测结果(所有相机都OK才为OK)。 /// public bool GetFinalResult() { lock (_lock) { return _cameraResults.Values.All(r => r); } } /// /// 获取某个相机的结果图像。 /// 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(); } } } }