using Cognex.VisionPro; using Cognex.VisionPro.ImageFile; using Prism.Events; using Prism.Ioc; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Forms.Integration; using TeamAAS_VP.Core; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.ViewModels.Home; namespace TeamAAS_VP.Views.Home { /// /// Interaction logic for ShowVisionRender /// public partial class ShowVisionRender : UserControl { private ShowVisionRenderViewModel viewModel; // key=ShowRender.Id private readonly Dictionary vmRenders; private readonly Dictionary Titles; private readonly IEventAggregator _eventAggregator; private readonly ISystemDatabaseService _systemDatabaseService; private readonly IProductService _productService; public ShowVisionRender() { InitializeComponent(); vmRenders = new Dictionary(); Titles = new Dictionary(); viewModel = DataContext as ShowVisionRenderViewModel; _eventAggregator = viewModel._eventAggregator; _systemDatabaseService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve(); _productService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve(); viewModel.UpdateLayout += ViewModel_UpdateLayout; _eventAggregator.GetEvent().Subscribe(UpdateRenderModuleSource); _eventAggregator.GetEvent().Subscribe(ProductChanged); } /// /// 更新某个工位/模块的显示内容(图像/图形/Record/保存) /// private void UpdateRenderModuleSource(ShowRender render) { this.gridRender.Dispatcher.BeginInvoke(new Action(() => { foreach (var item in vmRenders) { if (item.Value?.Child is CogRecordDisplay disp) { // 通过 Tag(产品名) 匹配当前需要更新的窗口 if (disp.Tag != null && disp.Tag.ToString() == render.ProceductName) { disp.Record = null; disp.Image = render.Image; disp.StaticGraphics.Clear(); if (render.Graphic != null) disp.StaticGraphics.AddList(render.Graphic, ""); if (render.Record != null) disp.Record = render.Record; if (render.IsSaveImage) { // 注意:CreateContentBitmap 可能比较耗时,但你这里已经在 UI 线程里调用了; // 如果卡顿明显,建议把 bitmap 生成放到后台线程(需谨慎处理线程访问) App.Current.Dispatcher.BeginInvoke(new Action(() => { var bmp = (Bitmap)disp.CreateContentBitmap(Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom); SaveImage( render.SaveImageModel, render.SaveImagePathModel, render.SavePath, render.Image, bmp, render.Result, render.IsCompress ); })); } break; } } } })); } /// /// 【核心】根据 obj.Count 自动生成 1~16 个显示格子(标题+CogRecordDisplay) /// private async void ViewModel_UpdateLayout(System.Collections.ObjectModel.ObservableCollection obj) { System.Windows.Media.FontFamily font = Application.Current.Resources["DefaultFont"] as System.Windows.Media.FontFamily; // 1) 清理旧控件 if (vmRenders.Count > 0) { foreach (var render in vmRenders) { // WindowsFormsHost.Dispose() 会销毁 WinForms 句柄,防止资源泄漏 render.Value?.Dispose(); } } vmRenders.Clear(); Titles.Clear(); gridRender.Children.Clear(); gridRender.RowDefinitions.Clear(); gridRender.ColumnDefinitions.Clear(); // 2) 判空 if (obj == null || obj.Count == 0) return; // 3) 限制最大 16(防止越界) int count = Math.Min(obj.Count, 16); // 4) 计算行列(1~16自动铺满:尽量接近正方形;最大 4*4) // 1 -> 1*1 // 2 -> 1*2 // 3 -> 2*2 // 4 -> 2*2 // 5~6 -> 2*3 // 7~9 -> 3*3(9满) // 10~12 -> 3*4(12满) // 13~16 -> 4*4(16满) GetGridSize(count, out int rows, out int cols); // 5) 创建主Grid的行列 for (int r = 0; r < rows; r++) gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) }); for (int c = 0; c < cols; c++) gridRender.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }); gridRender.ShowGridLines = true; // 6) 逐个创建子格子(每个子格子两行:标题/画面) for (int i = 0; i < count; i++) { var sr = obj[i]; // (1) 创建显示控件(WindowsFormsHost + CogRecordDisplay) var host = new WindowsFormsHost() { Tag = sr.ProceductName, Child = new CogRecordDisplay() { Tag = sr.ProceductName, } }; vmRenders.Add(sr.Id, host); // (2) 创建标题 var title = new TextBlock() { Text = sr.ProceductName, HorizontalAlignment = System.Windows.HorizontalAlignment.Center, FontWeight = FontWeights.Bold, FontFamily = font, FontSize = 12, Foreground = System.Windows.Media.Brushes.Black }; Titles.Add(sr.Id, title); // (3) 子Grid(标题 + 显示) var cell = new Grid(); cell.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Auto) }); // 标题 cell.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) }); // 图像 cell.Children.Add(title); cell.Children.Add(host); Grid.SetRow(title, 0); Grid.SetColumn(title, 0); Grid.SetRow(host, 1); Grid.SetColumn(host, 0); // (4) 放到主 gridRender 中 int row = i / cols; int col = i % cols; gridRender.Children.Add(cell); Grid.SetRow(cell, row); Grid.SetColumn(cell, col); } } /// /// 根据数量返回最合适的 rows/cols(最大 4*4) /// private void GetGridSize(int count, out int rows, out int cols) { // 默认值 rows = 1; cols = 1; if (count <= 1) { rows = 1; cols = 1; return; } if (count <= 2) { rows = 1; cols = 2; return; } if (count <= 4) { rows = 2; cols = 2; return; } if (count <= 6) { rows = 2; cols = 3; return; } if (count <= 9) { rows = 3; cols = 3; return; } if (count <= 12) { rows = 3; cols = 4; return; } // 13~16 rows = 4; cols = 4; } /// /// 统一配置 VisionPro 显示属性 /// private void ConfigureCogDisplay(CogRecordDisplay disp) { disp.HorizontalScrollBar = false; disp.VerticalScrollBar = false; disp.AutoFit = true; disp.BackColor = System.Drawing.SystemColors.ActiveCaption; } /// /// 保存图像(保持你原有逻辑不变) /// private void SaveImage(ProcedureSaveImageModel saveImageModel, ProcedureSaveImagePathModel saveImagePathModel, string path, ICogImage image, Bitmap reimage, bool result, bool isCompress) { if (string.IsNullOrEmpty(path) || string.IsNullOrWhiteSpace(path) || !Path.IsPathRooted(path)) { _eventAggregator.GetEvent() .Publish(new Models.MessageStruct() { Message = "保存图像路径为无效的路径!", level = MessageLevel.Alarm }); return; } Task.Run(() => { try { DateTime now = DateTime.Now; if (saveImageModel == ProcedureSaveImageModel.Original) { string Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time) { Originalpath = result ? $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp" : $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time) { if (result) Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time) { if (!result) Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Formula_ProductSN_Time) { // 当前产品配方名(过滤非法字符) string formulaName = _productService.GetCurrentProduct().Name; foreach (char c in Path.GetInvalidFileNameChars()) formulaName = formulaName.Replace(c, '_'); // 产品SN(过滤非法字符) string productSN = _productService.CurrentProductCode; foreach (char c in Path.GetInvalidFileNameChars()) productSN = productSN.Replace(c, '_'); string resultStr = result ? "OK" : "NG"; Originalpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; } if (!Directory.Exists(Path.GetDirectoryName(Originalpath))) Directory.CreateDirectory(Path.GetDirectoryName(Originalpath)); using (CogImageFileBMP _cogImageFileTool = new CogImageFileBMP()) { _cogImageFileTool.Open(Originalpath, CogImageFileModeConstants.Write); _cogImageFileTool.Append(image); _cogImageFileTool.Close(); } } else if (saveImageModel == ProcedureSaveImageModel.Recorded) { string Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time) { Recordedpath = result ? $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp" : $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time) { if (result) Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time) { if (!result) Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; else return; } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Formula_ProductSN_Time) { string formulaName = _productService.GetCurrentProduct().Name; foreach (char c in Path.GetInvalidFileNameChars()) formulaName = formulaName.Replace(c, '_'); string productSN = _productService.CurrentProductCode; foreach (char c in Path.GetInvalidFileNameChars()) productSN = productSN.Replace(c, '_'); string resultStr = result ? "OK" : "NG"; Recordedpath = $"{path}\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; } if (!Directory.Exists(Path.GetDirectoryName(Recordedpath))) Directory.CreateDirectory(Path.GetDirectoryName(Recordedpath)); if (isCompress) { ImageHelper.CompressImage(reimage, Recordedpath, 100); } else { reimage.Save(Recordedpath); reimage.Dispose(); } } else if (saveImageModel == ProcedureSaveImageModel.OriginalAndRecorded) { string Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; string Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time) { if (result) { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time) { if (result) { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else { return; } } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time) { if (!result) { Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{now:yyyy-MM-dd HH_mm_ss}.bmp"; } else { return; } } else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Formula_ProductSN_Time) { string formulaName = _productService.GetCurrentProduct().Name; foreach (char c in Path.GetInvalidFileNameChars()) formulaName = formulaName.Replace(c, '_'); string productSN = _productService.CurrentProductCode; foreach (char c in Path.GetInvalidFileNameChars()) productSN = productSN.Replace(c, '_'); string resultStr = result ? "OK" : "NG"; Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{productSN}-{now:yyyy-MM-dd HH_mm_ss}-{resultStr}.bmp"; } if (!Directory.Exists(Path.GetDirectoryName(Originalpath))) Directory.CreateDirectory(Path.GetDirectoryName(Originalpath)); using (CogImageFileBMP _cogImageFileTool = new CogImageFileBMP()) { _cogImageFileTool.Open(Originalpath, CogImageFileModeConstants.Write); _cogImageFileTool.Append(image); _cogImageFileTool.Close(); } if (!Directory.Exists(Path.GetDirectoryName(Recordedpath))) Directory.CreateDirectory(Path.GetDirectoryName(Recordedpath)); if (isCompress) { ImageHelper.CompressImage(reimage, Recordedpath, 100); } else { reimage.Save(Recordedpath); reimage.Dispose(); } } } catch (Exception ex) { LogHelper.WriteLogError("保存流程运行结果的图片时出错!", ex); } }); } /// /// 切换产品时清空主页显示(保持你原有逻辑) /// private void ProductChanged(ProductModel product) { this.gridRender.Dispatcher.BeginInvoke(new Action(() => { foreach (var item in vmRenders) { try { if (item.Value?.Child is CogRecordDisplay disp) { if (disp.Tag != null) { disp.Image = null; disp.StaticGraphics.Clear(); // 你原来 break 只清一次;这里保留你的行为 break; } } } catch (Exception ex) { LogHelper.WriteLogError(Lang.切换产品清除主页图像时出错, ex); } } })); } } }