using Cognex.VisionPro; using Cognex.VisionPro.ToolBlock; using MaterialDesignThemes.Wpf; using OpenCvSharp; using Prism.Commands; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using Prism.Services.Dialogs; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Web.Hosting; using System.Windows; using System.Windows.Media.Media3D; using Team.FFFeederService; using Team.FFFeederService.Interfaces; using TeamAAS_VP; using TeamAAS_VP.Controls; using TeamAAS_VP.Core; using TeamAAS_VP.Core.Lights; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.Services; using WpfTest.core; using Point = OpenCvSharp.Point; namespace TeamAAS_VP.ViewModels.Product { public class AdvancedProcedureViewModel : BindableBase, IConfirmNavigationRequest { IRegionManager _regionManager; IEventAggregator _eventAggregator; IContainerProvider _container; IDialogService _dialogService; IFeederService _feederService; IRobotService _robotService; ICameraService _cameraService; ISystemDatabaseService _systemDatabaseService; IConfigService _configService; ILightManagerService _lightManagerService; #region 属性 private ProductModel _SelectProduct; /// /// 选中的产品 /// public ProductModel SelectProduct { get { return _SelectProduct; } set { SetProperty(ref _SelectProduct, value); } } private ProcedureModel _SelectProcedure; /// /// 选中的流程 /// public ProcedureModel SelectProcedure { get { return _SelectProcedure; } set { SetProperty(ref _SelectProcedure, value); } } public CogToolBlockEditV2 ToolBlockEdit { get; set; } private IRobot _Robot; public IRobot Robot { get { return _Robot; } set { SetProperty(ref _Robot, value); } } private ICamera _Camera; public ICamera Camera { get { return _Camera; } set { SetProperty(ref _Camera, value); } } private string _Message; public string Message { get { return _Message; } set { SetProperty(ref _Message, value); } } /// /// 模板 ToolBlock 的默认路径,用于在产品不存在视觉工具文件时进行初始化。 /// public const string TemplateToolBlock = "..//Vision Template//TemplateToolBlock.vpp"; /// /// 默认产品路径的相对子目录(相对于应用运行目录或配置指定的根)。 /// public const string DefaultProductsSubPath = "..//Products"; #endregion #region 命令 private DelegateCommand _NextCommand; public DelegateCommand NextCommand => _NextCommand ?? (_NextCommand = new DelegateCommand(ExecuteNextCommand)); private DelegateCommand _BackCommand; public DelegateCommand BackCommand => _BackCommand ?? (_BackCommand = new DelegateCommand(ExecuteBackCommand)); private DelegateCommand _LoadedCommand; public DelegateCommand LoadedCommand => _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand)); private DelegateCommand _RunVisionProcessCommand; public DelegateCommand RunVisionProcessCommand => _RunVisionProcessCommand ?? (_RunVisionProcessCommand = new DelegateCommand(async () => await ExecuteRunVisionProcessCommand())); private DelegateCommand _SaveRunVisionVPPCommand; public DelegateCommand SaveRunVisionVPPCommand => _SaveRunVisionVPPCommand ?? (_SaveRunVisionVPPCommand = new DelegateCommand(async () => await ExecuteSaveRunVisionVPPCommand())); private DelegateCommand _VisionStaticAccuracyAnalyzerCommand; public DelegateCommand VisionStaticAccuracyAnalyzerCommand => _VisionStaticAccuracyAnalyzerCommand ?? (_VisionStaticAccuracyAnalyzerCommand = new DelegateCommand(ExecuteVisionStaticAccuracyAnalyzerCommand)); #endregion #region 事件 #endregion public AdvancedProcedureViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService, IFeederService feederService, IRobotService robotService, ICameraService cameraService, ISystemDatabaseService systemDatabaseService, IConfigService configService, ILightManagerService lightManagerService) { _regionManager = regionManager; _eventAggregator = ea; _container = container; _dialogService = dialogService; _lightManagerService = lightManagerService; _feederService = feederService; _robotService = robotService; _cameraService = cameraService; _configService = configService; _systemDatabaseService = systemDatabaseService; //订阅权限登录事件 _eventAggregator.GetEvent().Subscribe(LoginChange); _configService = configService; } #region 方法 /// /// 下一步 /// void ExecuteNextCommand() { try { NavigationParameters param = new NavigationParameters(); param.Add("SelectProduct", SelectProduct); param.Add("SelectedProcedure", SelectProcedure); SelectProcedure.ToolBlock = ToolBlockEdit.Subject; if (SelectProcedure.FeederId == Guid.Empty) { _regionManager.RequestNavigate("ProductRegionContext", "ProcedureVisionOutput", param); } else { var management = _container.Resolve(); var Feeder = _feederService.GetFeeder(SelectProcedure.FeederId); if (Feeder.FeederBrand == FeederBrand.AFAG) { _regionManager.RequestNavigate("ProductRegionContext", "AfagFeederParams", param); } else { _regionManager.RequestNavigate("ProductRegionContext", "FeederParams", param); } } } catch (Exception ex) { LogHelper.WriteLogError(ex.Message, ex); } } /// /// 上一步 /// void ExecuteBackCommand() { try { NavigationParameters param = new NavigationParameters(); param.Add("SelectProduct", SelectProduct); param.Add("SelectedProcedure", SelectProcedure); _regionManager.RequestNavigate("ProductRegionContext", "CameraParams", param); } catch (Exception ex) { LogHelper.WriteLogError(ex.Message, ex); } } void ExecuteLoadedCommand() { if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator) { IsAllowEdit = false; } else { IsAllowEdit = true; } } /// /// 保存Vpp /// /// async Task ExecuteSaveRunVisionVPPCommand() { try { if (MessageBox.Show(Lang.是否保存产品.Replace("[{0}]", ""), Lang.保存产品, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { var waiting = new WaitingControl(); var task = DialogHost.Show(waiting, "RootDialog", null, null, null); string prcPath = DefaultProductsSubPath + "//" + SelectProduct.Name + "//" + SelectProcedure.CameraName + "//" + SelectProcedure.Name + ".vpp"; var prcDir = Path.GetDirectoryName(prcPath); if (!Directory.Exists(prcDir)) { Directory.CreateDirectory(prcDir); } // 【关键修复】如果目标 vpp 已存在(例如 CopyProduct 刚复制过来),则不再用模板/ToolBlock 覆盖它 if (File.Exists(prcPath)) { if (SelectProcedure.ToolBlock == null) { SelectProcedure.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } } if (SelectProcedure.ToolBlock != null) { CogSerializer.SaveObjectToFile(SelectProcedure.ToolBlock, prcPath, typeof(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter), CogSerializationOptionsConstants.Minimum); } else if (File.Exists(TemplateToolBlock)) { SelectProcedure.ToolBlock = CogSerializer.LoadObjectFromFile(TemplateToolBlock) as CogToolBlock; CogSerializer.SaveObjectToFile(SelectProcedure.ToolBlock, prcPath, typeof(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter), CogSerializationOptionsConstants.Minimum); } if (DialogHost.IsDialogOpen("RootDialog")) { DialogHost.Close("RootDialog"); } await task; MessageBox.Show($"保存{SelectProcedure.Name}VPP成功"); } } catch (Exception ex) { MessageBox.Show($"保存VPP异常{ex.Message}"); } } async Task ExecuteRunVisionProcessCommand() { try { var sysConfig = _configService.GetSystemConfiguration(); if (sysConfig.PhotosUse == true) { SelectProcedure.ToolBlock = ToolBlockEdit.Subject; var camera = _cameraService.GetCamera(SelectProcedure.CameraId); for (int i = 1; i <= SelectProcedure.PhotoCount; i++) { if (i == 1) { bool isSucceed = false; isSucceed = await _lightManagerService.SetGlobalChannelBrightnessAsync(0, SelectProcedure.Photo1Light1); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道1亮度{SelectProcedure.Photo1Light1}失败!"); } isSucceed = await _lightManagerService.SetGlobalChannelBrightnessAsync(1, SelectProcedure.Photo1Light2); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道2亮度{SelectProcedure.Photo1Light2}失败!"); } isSucceed = await _lightManagerService.SetGlobalChannelBrightnessAsync(2, SelectProcedure.Photo1Light3); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道3亮度{SelectProcedure.Photo1Light3}失败!"); } bool succed = camera.SetExposureTime(SelectProcedure.ExposureTime); if (!succed) { SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error); } succed = camera.SetGain(SelectProcedure.Gain); if (!succed) { SendTaskMessage($"相机增益设置失败!", MessageLevel.Error); } var image = camera.Grab(); if (image != null) { ToolBlockEdit.Subject.Inputs["InputImage"].Value = image; } else { Message = Lang.图像采集失败耗时.Replace("{0}", Camera.TotalTime.TotalMilliseconds.ToString("F2")).Replace("{1}", Camera.ErrorMessage); } isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(0); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道0关闭失败!"); } isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(1); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道1关闭失败!"); } isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(2); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道2关闭失败!"); } } else if (i == 2) { bool isSucceed = false; isSucceed = await _lightManagerService.SetGlobalChannelBrightnessAsync(0, SelectProcedure.Photo2Light1); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道1亮度{SelectProcedure.Photo2Light1}失败!"); } isSucceed = await _lightManagerService.SetGlobalChannelBrightnessAsync(1, SelectProcedure.Photo2Light2); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道2亮度{SelectProcedure.Photo2Light2}失败!"); } isSucceed = await _lightManagerService.SetGlobalChannelBrightnessAsync(2, SelectProcedure.Photo2Light3); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道3亮度{SelectProcedure.Photo2Light3}失败!"); } bool succed = camera.SetExposureTime(SelectProcedure.ExposureTime2); if (!succed) { SendTaskMessage($"相机曝光设置失败!", MessageLevel.Error); } //succed = camera.SetGain(SelectProcedure.Gain2); //if (!succed) //{ //SendTaskMessage($"相机增益设置失败!", MessageLevel.Error); //} var image2 = camera.Grab(); if (image2 != null) { ToolBlockEdit.Subject.Inputs["InputImage2"].Value = image2; } else { Message = Lang.图像采集失败耗时.Replace("{0}", Camera.TotalTime.TotalMilliseconds.ToString("F2")).Replace("{1}", Camera.ErrorMessage); } isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(0); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道0关闭失败!"); } isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(1); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道1关闭失败!"); } isSucceed = await _lightManagerService.TurnOffGlobalChannelAsync(2); if (!isSucceed) { LogHelper.WriteLogInfo($"设置光源通道2关闭失败!"); } } } ToolBlockEdit.Subject.Run(); Message = $"{Lang.图像采集耗时}:{Camera.TotalTime.TotalMilliseconds.ToString("F2")} ms {Lang.工具运行耗时}:{ToolBlockEdit.Subject.RunStatus.TotalTime.ToString("F2")} ms"; } else { SelectProcedure.ToolBlock = ToolBlockEdit.Subject; var image = Camera.Grab(); if (image != null) { string photoAiValue = SelectProcedure.UserDefineParameters?.FirstOrDefault(p => string.Equals(p.ParamName, "Photo_AI", StringComparison.OrdinalIgnoreCase))?.ParamValue; // photoAiValue 可能为 null/空,建议给个默认值 bool enablePhotoAi = photoAiValue == "1"; if (sysConfig.PhotoAI == true && enablePhotoAi == true) { string exeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); string modelPath = Path.Combine(exeDir, "TrainPhoto", "weights", "best_openvino_model", "best.xml"); string metadataPath = Path.Combine(exeDir, "TrainPhoto", "weights", "best_openvino_model", "metadata.yaml"); // (可选)如果你希望缺文件时给出更明确提示: if (!File.Exists(modelPath)) throw new FileNotFoundException("未找到模型文件 best.xml", modelPath); if (!File.Exists(metadataPath)) throw new FileNotFoundException("未找到模型文件 metadata.yaml", metadataPath); //string modelPath = @"D:\TEST\train17\weights\best_openvino_model\best.xml"; //string metadataPath = @"D:\TEST\train17\weights\best_openvino_model\metadata.yaml"; string imagePath = "G:\\TEST\\文件\\左上插线\\1.bmp"; using (var yolo = new YoloOpenVINOInference(modelPath, metadataPath)) { using (var bitmap = image.ToBitmap()) { var results = yolo.Infer(bitmap); // 输出结果 //Console.WriteLine($"检测到 {results.Count} 个目标:"); SendTaskMessage($"识别的个数:{results.Count()}", MessageLevel.Alarm); string PhotoData = ""; foreach (var det in results) { //Console.WriteLine($" - {det.ClassName}: 置信度={det.Confidence:F4}, " + // $"边界框=[{det.Box[0]:F0}, {det.Box[1]:F0}, {det.Box[2]:F0}, {det.Box[3]:F0}]"); SendTaskMessage($" - {det.ClassName}: 置信度={det.Confidence:F4}, " + $"边界框=[{det.Box[0]:F0}, {det.Box[1]:F0}, {det.Box[2]:F0}, {det.Box[3]:F0}]", MessageLevel.Alarm); PhotoData = PhotoData + $"{det.ClassName},"; } // 绘制结果 - 使用 OpenCvSharp using (var image2 = ImageHelper.ICogImageConvertMat(image)) { // 定义颜色映射 var colors = new[] { Scalar.Red, // NG1 - 红色 Scalar.Green, // NG2 - 绿色 Scalar.Blue, // NG3 - 蓝色 Scalar.Yellow // NG4 - 黄色 }; foreach (var det in results) { // 获取边界框坐标 int x1 = (int)det.Box[0]; int y1 = (int)det.Box[1]; int x2 = (int)det.Box[2]; int y2 = (int)det.Box[3]; // 添加边界检查:确保坐标有效 if (x1 >= x2 || y1 >= y2) { SendTaskMessage($"警告: 无效的边界框 [{x1}, {y1}, {x2}, {y2}]", MessageLevel.Error); continue; } // 确保坐标在图像范围内 x1 = Math.Max(0, Math.Min(image2.Width, x1)); y1 = Math.Max(0, Math.Min(image2.Height, y1)); x2 = Math.Max(0, Math.Min(image2.Width, x2)); y2 = Math.Max(0, Math.Min(image2.Height, y2)); // 选择颜色(根据类别ID) Scalar color = colors[det.ClassId % colors.Length]; // 绘制矩形框 Cv2.Rectangle(image2, new Point(x1, y1), new Point(x2, y2), color, 2); // 准备标签文本 string label = $"{det.ClassName} {det.Confidence:P0}"; // 计算文本大小 var textSize = Cv2.GetTextSize(label, HersheyFonts.HersheySimplex, 0.6, 1, out int baseline); // 确保文本位置不超出图像边界 int textX = x1; int textY = y1 - 5; // 如果文本超出图像顶部,显示在框内底部 if (textY - textSize.Height < 0) { textY = y2 + baseline + 5; } //绘制文本背景 Cv2.Rectangle(image2, new OpenCvSharp.Rect(textX, textY - textSize.Height - baseline - 10, textSize.Width + 10, textSize.Height + baseline + 10), color, -1); // 绘制文本 Cv2.PutText(image2, label, new Point(textX + 5, textY - baseline - 5), HersheyFonts.HersheySimplex, 0.6, Scalar.White, 2); } // 保存结果图像 //string outputPath = @"D:\TEST\result.jpg"; //Cv2.ImWrite(outputPath, image2); //Console.WriteLine($"结果已保存到: {outputPath}"); var matForCog = image2; //if (matForCog.Channels() == 3) //{ //matForCog = new Mat(); //Cv2.CvtColor(image2, matForCog, ColorConversionCodes.BGR2GRAY); //} var cogImage = ImageHelper.MatConvertICogImage(matForCog, true); ToolBlockEdit.Subject.Inputs["InputImage"].Value = cogImage; ToolBlockEdit.Subject.Inputs["PhotoData"].Value = PhotoData; ToolBlockEdit.Subject.Run(); // 可选:显示图像 //Cv2.ImShow("Detection Result", image2); //Cv2.WaitKey(0); } } } } else { ToolBlockEdit.Subject.Inputs["InputImage"].Value = image; ToolBlockEdit.Subject.Run(); } Message = $"{Lang.图像采集耗时}:{Camera.TotalTime.TotalMilliseconds.ToString("F2")} ms {Lang.工具运行耗时}:{ToolBlockEdit.Subject.RunStatus.TotalTime.ToString("F2")} ms"; } else { Message = Lang.图像采集失败耗时.Replace("{0}", Camera.TotalTime.TotalMilliseconds.ToString("F2")).Replace("{1}", Camera.ErrorMessage); } } } catch (Exception ex) { LogHelper.WriteLogError("高级模式-运行流程时出错时出错", ex); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 }); } } private void SendTaskMessage(string msg, MessageLevel level) { App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = msg, level = level }); }); } /// /// 执行视觉静态精度分析仪命令 /// void ExecuteVisionStaticAccuracyAnalyzerCommand() { try { IDialogParameters parameters = new DialogParameters(); parameters.Add("ToolBlock", ToolBlockEdit.Subject); parameters.Add("SelectedProcedure", SelectProcedure); _dialogService.Show("VisionStaticAccuracyAnalyzer", parameters, rst => { //对话框关闭之后的回调函数,可以在这解析结果。 ButtonResult result1 = rst.Result; if (result1 == ButtonResult.OK) { //var param = rst.Parameters.GetValue("Tool"); //var param1 = rst.Parameters.GetValue>("RobotCameraRotationMatrix"); //var param2 = rst.Parameters.GetValue("Angle"); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 }); } }); } catch (Exception ex) { LogHelper.WriteLogError("执行视觉静态精度分析仪命令时出错", ex); MessageBox.Show(ex.Message); } } #endregion #region 继承 /// /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。 /// /// /// public void ConfirmNavigationRequest(NavigationContext navigationContext, Action continuationCallback) { continuationCallback(true); } /// /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。 /// /// /// public async void OnNavigatedTo(NavigationContext navigationContext) { try { SelectProduct = navigationContext.Parameters.GetValue("SelectProduct"); SelectProcedure = navigationContext.Parameters.GetValue("SelectedProcedure"); if (SelectProcedure.RobotId != Guid.Empty) { Robot = _robotService.GetRobot(SelectProcedure.RobotId); if (Robot != null) { Robot.EnterDebugMode = true; } } Camera = _cameraService.GetCamera(SelectProcedure.CameraId); ToolBlockEdit.Subject = SelectProcedure.ToolBlock; } catch (Exception ex) { LogHelper.WriteLogError("高级视觉界面进入时出错!", ex); } } /// /// 是否允许导航到此视图模型。 /// /// /// public bool IsNavigationTarget(NavigationContext navigationContext) { return true; } /// /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。 /// /// public void OnNavigatedFrom(NavigationContext navigationContext) { if (Robot != null) { Robot.EnterDebugMode = false; } } #endregion private bool _IsAllowEdit = false; public bool IsAllowEdit { get { return _IsAllowEdit; } set { SetProperty(ref _IsAllowEdit, value); } } private void LoginChange(Models.User user) { if (user.userPart == Enums.UserPart.Operator) { IsAllowEdit = false; } else { IsAllowEdit = true; } } } }