| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249 |
- using Cognex.VisionPro;
- using Cognex.VisionPro.ImageFile;
- using Cognex.VisionPro3D;
- using log4net.Core;
- using MahApps.Metro.Controls;
- using Prism.Events;
- using Prism.Ioc;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Drawing;
- using System.IO;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Forms.Integration;
- using System.Windows.Interop;
- using TeamAAS_VP;
- 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;
- using TeamAAS_VP.ViewModels.Home;
- //using static MaterialDesignThemes.Wtf.ToolBar;
- namespace TeamAAS_VP.Views.Home
- {
- /// <summary>
- /// Interaction logic for ShowVisionRender
- /// </summary>
- public partial class ShowVisionRender : UserControl
- {
- private const string Render2DKeySuffix = ":2D";
- private const string Render3DKeySuffix = ":3D";
- private static string Get2DKey(Guid id) => id.ToString() + Render2DKeySuffix;
- private static string Get3DKey(Guid id) => id.ToString() + Render3DKeySuffix;
- ShowVisionRenderViewModel viewModel;
- Dictionary<string, WindowsFormsHost> vmRenders;
- Dictionary<Guid, TextBlock> Titles;
- IEventAggregator _eventAggregator;
- private readonly IProductService _productService;
- private readonly Prism.Services.Dialogs.IDialogService _dialogService;
- private CancellationTokenSource _saveImageCts = new CancellationTokenSource();
- private readonly object _saveImageCtsLock = new object();
- private readonly SemaphoreSlim _saveSemaphore = new SemaphoreSlim(5, 5);
- private readonly ConcurrentQueue<ShowRender> _pendingRenderUpdates = new ConcurrentQueue<ShowRender>();
- private int _renderDrainScheduled;
- private List<Models.DelaySaveImage> delaySaveImages = new List<Models.DelaySaveImage>();
- /// <summary>
- /// 安全获取有效的 CancellationToken。如果当前 CTS 已释放则自动创建新的。
- /// </summary>
- private CancellationToken GetSafeSaveToken()
- {
- lock (_saveImageCtsLock)
- {
- try
- {
- var token = _saveImageCts.Token;
- return token;
- }
- catch (ObjectDisposedException)
- {
- _saveImageCts = new CancellationTokenSource();
- return _saveImageCts.Token;
- }
- }
- }
- public ShowVisionRender()
- {
- InitializeComponent();
- vmRenders = new Dictionary<string, WindowsFormsHost>();
- Titles = new Dictionary<Guid, TextBlock>();
- viewModel = DataContext as ShowVisionRenderViewModel;
- _eventAggregator = viewModel._eventAggregator;
- _productService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve<IProductService>();
- // 新增:DialogService
- _dialogService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve<Prism.Services.Dialogs.IDialogService>();
- viewModel.UpdateLayout += ViewModel_UpdateLayout;
- // 后台线程仅负责准备渲染数据;真正的 UI 应用合并到单个 Background 优先级批处理里,
- // 避免每张图都以 Normal 优先级抢占用户输入。
- _eventAggregator.GetEvent<RenderUpdateNotification>().Subscribe(
- EnqueueRenderUpdate,
- ThreadOption.BackgroundThread);
- _eventAggregator.GetEvent<ProductChangedNotification>().Subscribe(ProductChanged);
- _eventAggregator.GetEvent<DelaySaveImageNotification>().Subscribe(UpdateDelaySaveImage);
- }
- private bool GetView3DUse()
- {
- try
- {
- var configService = ((Prism.PrismApplicationBase)App.Current).Container.Resolve<IConfigService>();
- if(configService.GetSystemConfiguration().PickPointNum==0)
- {
- return false;
- }
- else
- {
- return true;
- }
- //return true;
- }
- catch
- {
- return false;
- }
- }
- private void EnqueueRenderUpdate(ShowRender render)
- {
- if (render == null)
- return;
- ICogImage imageCopy = null;
- try
- {
- imageCopy = render.Image?.CopyBase(CogImageCopyModeConstants.CopyPixels);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("深拷贝 ICogImage 失败", ex);
- }
- _pendingRenderUpdates.Enqueue(new ShowRender
- {
- Id = render.Id,
- DisplayName = render.DisplayName,
- Image = imageCopy,
- Graphic = render.Graphic,
- Record = render.Record,
- Result = render.Result,
- IsShow = render.IsShow,
- IsSaveImage = render.IsSaveImage,
- SaveImageModel = render.SaveImageModel,
- SaveImagePathModel = render.SaveImagePathModel,
- SavePath = render.SavePath,
- IsCompress = render.IsCompress,
- ProceductName = render.ProceductName,
- ImageFileName = render.ImageFileName,
- Is3DModel = render.Is3DModel,
- RangeImage = render.RangeImage,
- GreyImage = render.GreyImage,
- IsDelaySaveImage = render.IsDelaySaveImage,
- });
- if (_pendingRenderUpdates.Count > 8)
- {
- LogHelper.WriteLogInfo($"[UI-RENDER] Pending render queue depth={_pendingRenderUpdates.Count}");
- }
- ScheduleRenderDrain();
- }
- private void ScheduleRenderDrain()
- {
- if (Interlocked.CompareExchange(ref _renderDrainScheduled, 1, 0) != 0)
- return;
- _ = this.gridRender.Dispatcher.InvokeAsync(async () =>
- {
- try
- {
- int processed = 0;
- while (_pendingRenderUpdates.TryDequeue(out var render))
- {
- ApplyRenderUpdate(render);
- processed++;
- if (processed % 2 == 0 && !_pendingRenderUpdates.IsEmpty)
- await System.Windows.Threading.Dispatcher.Yield(System.Windows.Threading.DispatcherPriority.Input);
- }
- }
- finally
- {
- Interlocked.Exchange(ref _renderDrainScheduled, 0);
- if (!_pendingRenderUpdates.IsEmpty)
- ScheduleRenderDrain();
- }
- }, System.Windows.Threading.DispatcherPriority.Background);
- }
- private void ApplyRenderUpdate(ShowRender render)
- {
- if (render.Is3DModel == true && GetView3DUse() == true)
- {
- if (!vmRenders.TryGetValue(Get3DKey(render.Id), out var host3D))
- return;
- var display3D = host3D.Child as Cognex.VisionPro3D.Cog3DDisplayV2WF;
- if (display3D == null)
- return;
- try
- {
- display3D.Clear();
- if (render.RangeImage == null || render.GreyImage == null)
- return;
- var rImgG = new Cog3DRangeImageGraphic(render.RangeImage, render.GreyImage);
- display3D.Add(rImgG, "__default");
- display3D.FitView();
- }
- catch (System.Windows.Forms.AxHost.InvalidActiveXStateException)
- {
- }
- }
- if (!vmRenders.TryGetValue(Get2DKey(render.Id), out var host2D))
- return;
- var recordDisplay = host2D.Child as Cognex.VisionPro.CogRecordDisplay;
- if (recordDisplay == null)
- return;
- try
- {
- recordDisplay.Record = null;
- recordDisplay.Image = render.Image;
- recordDisplay.StaticGraphics.Clear();
- if (render.Graphic != null)
- recordDisplay.StaticGraphics.AddList(render.Graphic, "");
- if (render.Record != null)
- recordDisplay.Record = render.Record;
- if (render.IsSaveImage)
- {
- RequestSaveImage(render, recordDisplay);
- }
- }
- catch (System.Windows.Forms.AxHost.InvalidActiveXStateException)
- {
- }
- }
- private void AddRenderPair(Guid id, string displayName)
- {
- // 2D
- var display2D = new Cognex.VisionPro.CogRecordDisplay() { Tag = displayName };
- display2D.HandleCreated += (s, e) =>
- {
- try
- {
- display2D.HorizontalScrollBar = false;
- display2D.VerticalScrollBar = false;
- display2D.AutoFit = true;
- display2D.BackColor = System.Drawing.SystemColors.ActiveCaption;
- }
- catch (System.Windows.Forms.AxHost.InvalidActiveXStateException)
- {
- }
- };
- // 绑定双击:弹出预览窗口
- display2D.DoubleClick += (s, e) => OnDisplayDoubleClick(display2D);
- vmRenders.Add(Get2DKey(id), new WindowsFormsHost() { Tag = displayName, Child = display2D });
- // 3D:按配置创建
- if (!GetView3DUse())
- return;
- // 3D
- var display3D = new Cognex.VisionPro3D.Cog3DDisplayV2WF() { Tag = displayName };
- display3D.HandleCreated += (s, e) =>
- {
- try
- {
- display3D.BackColor = System.Drawing.SystemColors.ActiveCaption;
- }
- catch (System.Windows.Forms.AxHost.InvalidActiveXStateException)
- {
- }
- };
- vmRenders.Add(Get3DKey(id), new WindowsFormsHost() { Tag = displayName, Child = display3D });
- }
- private Grid CreateCellGrid(Guid id, string displayName, System.Windows.Media.FontFamily font)
- {
- var is3DEnabled = GetView3DUse();
- 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) });
- var title = new TextBlock()
- {
- Text = displayName,
- HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
- FontWeight = FontWeights.Bold,
- FontFamily = font,
- FontSize = 12,
- Foreground = System.Windows.Media.Brushes.Black
- };
- Titles[id] = title;
- var content = new Grid();
- if(is3DEnabled)
- {
- content.ColumnDefinitions.Add(new ColumnDefinition()); // 2D
- content.ColumnDefinitions.Add(new ColumnDefinition()); // 3D
- content.Children.Add(vmRenders[Get2DKey(id)]);
- content.Children.Add(vmRenders[Get3DKey(id)]);
- Grid.SetColumn(vmRenders[Get2DKey(id)], 0);
- Grid.SetColumn(vmRenders[Get3DKey(id)], 1);
- }
- else
- {
- content.ColumnDefinitions.Add(new ColumnDefinition()); // 仅 2D
- content.Children.Add(vmRenders[Get2DKey(id)]);
- Grid.SetColumn(vmRenders[Get2DKey(id)], 0);
- }
- cell.Children.Add(title);
- cell.Children.Add(content);
- Grid.SetRow(title, 0);
- Grid.SetRow(content, 1);
- return cell;
- }
- Grid grid1 = new Grid();
- Grid grid2 = new Grid();
- Grid grid3 = new Grid();
- Grid grid4 = new Grid();
- Grid grid5 = new Grid();
- Grid grid6 = new Grid();
- Grid grid7 = new Grid();
- Grid grid8 = new Grid();
- Grid grid9 = new Grid();
- Grid grid10 = new Grid();
- Grid grid11 = new Grid();
- Grid grid12 = new Grid();
- Grid grid13 = new Grid();
- Grid grid14 = new Grid();
- Grid grid15 = new Grid();
- Grid grid16 = new Grid();
- Grid grid17 = new Grid();
- Grid grid18 = new Grid();
- Grid grid19 = new Grid();
- Grid grid20 = new Grid();
- private void ViewModel_UpdateLayout(System.Collections.ObjectModel.ObservableCollection<Models.ShowRender> obj)
- {
- System.Windows.Media.FontFamily font = Application.Current.Resources["DefaultFont"] as System.Windows.Media.FontFamily;
-
- if (vmRenders.Count > 0)
- {
- foreach (var render in vmRenders)
- {
- render.Value.Dispose();
- }
- }
- vmRenders.Clear();
- gridRender.Children.Clear();
- gridRender.RowDefinitions.Clear();
- gridRender.ColumnDefinitions.Clear();
- Titles.Clear();
- if (obj == null || obj.Count == 0)
- return;
- // 先创建所有相机的 2D/3D 对
- for (int i = 0; i < obj.Count; i++)
- AddRenderPair(obj[i].Id, obj[i].DisplayName);
- switch (obj.Count)
- {
- case 1:
- {
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- var cell = CreateCellGrid(obj[0].Id, obj[0].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, 0);
- Grid.SetColumn(cell, 0);
- gridRender.ShowGridLines = false;
- break;
- }
- case 2:
- {
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- var cell0 = CreateCellGrid(obj[0].Id, obj[0].DisplayName, font);
- var cell1 = CreateCellGrid(obj[1].Id, obj[1].DisplayName, font);
- gridRender.Children.Add(cell0);
- gridRender.Children.Add(cell1);
- Grid.SetRow(cell0, 0);
- Grid.SetColumn(cell0, 0);
- Grid.SetRow(cell1, 0);
- Grid.SetColumn(cell1, 1);
- break;
- }
- case 3:
- {
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- var cell0 = CreateCellGrid(obj[0].Id, obj[0].DisplayName, font);
- var cell1 = CreateCellGrid(obj[1].Id, obj[1].DisplayName, font);
- var cell2 = CreateCellGrid(obj[2].Id, obj[2].DisplayName, font);
- gridRender.Children.Add(cell0);
- gridRender.Children.Add(cell1);
- gridRender.Children.Add(cell2);
- Grid.SetRow(cell0, 0);
- Grid.SetColumn(cell0, 0);
- Grid.SetRow(cell1, 0);
- Grid.SetColumn(cell1, 1);
- Grid.SetRow(cell2, 1);
- Grid.SetColumn(cell2, 0);
- Grid.SetColumnSpan(cell2, 2);
- break;
- }
- case 4:
- {
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 4; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 2);
- Grid.SetColumn(cell, i % 2);
- }
- break;
- }
- case 5:
- {
- // 你的原布局:上面2个,下面3个(通过嵌套grid6)
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- var top0 = CreateCellGrid(obj[0].Id, obj[0].DisplayName, font);
- var top1 = CreateCellGrid(obj[1].Id, obj[1].DisplayName, font);
- var bottom = new Grid();
- bottom.ColumnDefinitions.Add(new ColumnDefinition());
- bottom.ColumnDefinitions.Add(new ColumnDefinition());
- bottom.ColumnDefinitions.Add(new ColumnDefinition());
- var b0 = CreateCellGrid(obj[2].Id, obj[2].DisplayName, font);
- var b1 = CreateCellGrid(obj[3].Id, obj[3].DisplayName, font);
- var b2 = CreateCellGrid(obj[4].Id, obj[4].DisplayName, font);
- bottom.Children.Add(b0);
- bottom.Children.Add(b1);
- bottom.Children.Add(b2);
- Grid.SetColumn(b0, 0);
- Grid.SetColumn(b1, 1);
- Grid.SetColumn(b2, 2);
- gridRender.Children.Add(top0);
- gridRender.Children.Add(top1);
- gridRender.Children.Add(bottom);
- Grid.SetRow(top0, 0);
- Grid.SetColumn(top0, 0);
- Grid.SetRow(top1, 0);
- Grid.SetColumn(top1, 1);
- Grid.SetRow(bottom, 1);
- Grid.SetColumn(bottom, 0);
- Grid.SetColumnSpan(bottom, 2);
- break;
- }
- case 6:
- {
- // 你的原布局:2行3列
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 6; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 3);
- Grid.SetColumn(cell, i % 3);
- }
- break;
- }
- case 7:
- {
- // 你的原布局:第一行4个,第二行3个(但原来只放到 col 0/1/2)
- // 这里保持原样:第一行4列,第二行3列(第4列留空)
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 7; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- if (i < 4)
- {
- Grid.SetRow(cell, 0);
- Grid.SetColumn(cell, i);
- }
- else
- {
- Grid.SetRow(cell, 1);
- Grid.SetColumn(cell, i - 4);
- }
- }
- break;
- }
- case 8:
- {
- // 2行4列
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- for (int i = 0; i < 4; i++)
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 8; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 4);
- Grid.SetColumn(cell, i % 4);
- }
- break;
- }
- case 9:
- {
- // 3行3列
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 9; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 3);
- Grid.SetColumn(cell, i % 3);
- }
- break;
- }
- case 12:
- {
- // 3行4列
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- for (int i = 0; i < 4; i++)
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 12; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 4);
- Grid.SetColumn(cell, i % 4);
- }
- break;
- }
- case 16:
- {
- // 4行4列
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- for (int i = 0; i < 4; i++)
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 16; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 4);
- Grid.SetColumn(cell, i % 4);
- }
- break;
- }
- case 20:
- {
- // 5行4列
- for (int r = 0; r < 5; r++)
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- for (int c = 0; c < 4; c++)
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = true;
- for (int i = 0; i < 20; i++)
- {
- var cell = CreateCellGrid(obj[i].Id, obj[i].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, i / 4);
- Grid.SetColumn(cell, i % 4);
- }
- break;
- }
- default:
- {
- // 未覆盖的数量:先用简单的 1列多行兜底(避免空白)
- gridRender.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
- gridRender.ColumnDefinitions.Add(new ColumnDefinition());
- gridRender.ShowGridLines = false;
- var cell = CreateCellGrid(obj[0].Id, obj[0].DisplayName, font);
- gridRender.Children.Add(cell);
- Grid.SetRow(cell, 0);
- Grid.SetColumn(cell, 0);
- break;
- }
- }
- }
- /// <summary>
- /// 请求保存图像(在 WinForms 消息循环线程截图,然后派发到后台线程保存)
- /// 使用 SemaphoreSlim 控制并发,支持 CancellationToken 取消
- /// </summary>
- private void RequestSaveImage(ShowRender render, Cognex.VisionPro.CogRecordDisplay disp)
- {
- if (render == null) return;
- if (disp == null) return;
- if (!render.IsSaveImage)
- {
- LogHelper.WriteLogInfo($"[SAVE-IMAGE] 跳过保存: IsSaveImage=false, Id={render.Id}, DisplayName={render.DisplayName}");
- return;
- }
- if (!render.IsDelaySaveImage && string.IsNullOrEmpty(_productService.CurrentProductCode))
- {
- _eventAggregator.GetEvent<TaskMessageNotification>()
- .Publish(new MessageStruct()
- {
- Message = "当前产品SN不存在,无法保存图片!",
- level = MessageLevel.Alarm
- });
- return;
- }
- //LogHelper.WriteLogInfo($"[SAVE-IMAGE] 开始保存流程: Id={render.Id}, SaveModel={render.SaveImageModel}, SavePath={render.SavePath}, IsDelay={render.IsDelaySaveImage}, ProductCode={_productService.CurrentProductCode}");
- bool needRecordedBitmap =
- render.SaveImageModel == ProcedureSaveImageModel.Recorded ||
- render.SaveImageModel == ProcedureSaveImageModel.OriginalAndRecorded;
- var token = GetSafeSaveToken();
- disp.BeginInvoke(new Action(() =>
- {
- Bitmap clonedBitmap = null;
- try
- {
- if (token.IsCancellationRequested) return;
- if (needRecordedBitmap)
- {
- if (disp.IsDisposed || !disp.IsHandleCreated) return;
- Bitmap uiBitmap = (Bitmap)disp.CreateContentBitmap(
- Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom);
- clonedBitmap = (Bitmap)uiBitmap.Clone();
- uiBitmap.Dispose();
- }
- var capturedBitmap = clonedBitmap;
- var capturedRender = render;
- Task.Run(async () =>
- {
- await _saveSemaphore.WaitAsync(token);
- try
- {
- if (token.IsCancellationRequested) return;
- if (capturedRender.IsDelaySaveImage)
- {
- // 延迟保存:存入列表,等 DelaySaveImageNotification 时统一写入
- ICogImage imageDeepCopy = null;
- try
- {
- imageDeepCopy = capturedRender.Image?
- .CopyBase(CogImageCopyModeConstants.CopyPixels);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("延迟保存深拷贝 ICogImage 失败", ex);
- }
- var delayItem = new Models.DelaySaveImage()
- {
- SaveImageModel = capturedRender.SaveImageModel,
- SaveImagePathModel = capturedRender.SaveImagePathModel,
- SavePath = capturedRender.SavePath,
- Image = imageDeepCopy,
- clonedBitmap = capturedBitmap != null
- ? (Bitmap)capturedBitmap.Clone() : null,
- Result = capturedRender.Result,
- IsCompress = capturedRender.IsCompress,
- ImageFileName = capturedRender.ImageFileName,
- Is3DModel = capturedRender.Is3DModel,
- };
- lock (delaySaveImages)
- {
- delaySaveImages.Add(delayItem);
- }
- }
- else
- {
- if (string.IsNullOrEmpty(_productService.CurrentProductCode))
- {
- _eventAggregator.GetEvent<TaskMessageNotification>()
- .Publish(new MessageStruct()
- {
- Message = "当前产品SN不存在,无法保存图片!",
- level = MessageLevel.Alarm
- });
- return;
- }
- LogHelper.WriteLogInfo("开始异步保存图像");
- await SaveImageAsync(
- capturedRender.SaveImageModel,
- capturedRender.SaveImagePathModel,
- capturedRender.SavePath,
- capturedRender.Image,
- capturedBitmap,
- capturedRender.Result,
- capturedRender.IsCompress,
- capturedRender.ImageFileName,
- capturedRender.Is3DModel,
- token
- );
- }
- }
- catch (OperationCanceledException) { }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("后台保存图像任务出错!", ex);
- }
- finally
- {
- try { capturedBitmap?.Dispose(); } catch { }
- _saveSemaphore.Release();
- }
- }, token);
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("RequestSaveImage(UI截图阶段) 出错!", ex);
- try { clonedBitmap?.Dispose(); } catch { }
- }
- }));
- }
- /// <summary>
- /// 异步保存图像(调用者已在后台线程,不再嵌套 Task.Run;通过 CancellationToken 支持取消)
- /// </summary>
- private async Task SaveImageAsync(
- ProcedureSaveImageModel saveImageModel,
- ProcedureSaveImagePathModel saveImagePathModel,
- string path, ICogImage image, Bitmap reimage,
- bool result, bool isCompress, string imageFileName,
- bool is3Dmodel, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(path) || string.IsNullOrWhiteSpace(path) || !Path.IsPathRooted(path))
- {
- LogHelper.WriteLogInfo($"[SAVE-IMAGE] 保存失败:路径无效 path=\"{path}\"");
- _eventAggregator.GetEvent<TaskMessageNotification>()
- .Publish(new Models.MessageStruct() { Message = "保存图像路径为无效的路径!", level = MessageLevel.Alarm });
- return;
- }
- //LogHelper.WriteLogInfo($"[SAVE-IMAGE] SaveImageAsync 开始: path={path}, model={saveImageModel}, pathModel={saveImagePathModel}, result={result}, is3D={is3Dmodel}, fileName={imageFileName}");
- try
- {
- await Task.CompletedTask; // 消除 CS1998:调用者已在后台线程,下方所有 I/O 为同步执行
- cancellationToken.ThrowIfCancellationRequested();
- DateTime now = DateTime.Now;
- //图片名
- string imgName = now.ToString("yyyy-MM-dd HH_mm_ss");
- if (!string.IsNullOrEmpty(imageFileName))
- {
- imgName = imageFileName;
- }
- if (saveImageModel == ProcedureSaveImageModel.Original)
- {
- string Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{imgName}.bmp";
- if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time)
- {
- Originalpath = result
- ? $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp"
- : $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.bmp";
- }
- else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time)
- {
- if (result)
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp";
- else
- return;
- }
- else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time)
- {
- if (!result)
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.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;
- if (productSN != null)
- {
- 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}\\{resultStr}\\{imgName}.bmp";
- }
- cancellationToken.ThrowIfCancellationRequested();
- 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}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{imgName}.bmp";
- if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time)
- {
- Recordedpath = result
- ? $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp"
- : $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.bmp";
- }
- else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time)
- {
- if (result)
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp";
- else
- return;
- }
- else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time)
- {
- if (!result)
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.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}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{resultStr}\\{imgName}.bmp";
- }
- cancellationToken.ThrowIfCancellationRequested();
- if (!Directory.Exists(Path.GetDirectoryName(Recordedpath)))
- Directory.CreateDirectory(Path.GetDirectoryName(Recordedpath));
- if (isCompress)
- {
- ImageHelper.CompressImage(reimage, Recordedpath, 20);
- }
- else
- {
- reimage.Save(Recordedpath);
- }
- }
- else if (saveImageModel == ProcedureSaveImageModel.OriginalAndRecorded)
- {
- string Originalpath = "";
- if (is3Dmodel == true)
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{imgName}.idb";
- }
- else
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{imgName}.bmp";
- }
- string Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{imgName}.bmp";
- if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_OkNG_Time)
- {
- if (result)
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp";
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp";
- }
- else
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.bmp";
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.bmp";
- }
- }
- else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ok_Time)
- {
- if (result)
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp";
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\OK\\{imgName}.bmp";
- }
- else
- {
- return;
- }
- }
- else if (saveImagePathModel == ProcedureSaveImagePathModel.Month_Day_Ng_Time)
- {
- if (!result)
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.bmp";
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\NG\\{imgName}.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;
- if (productSN != null)
- {
- foreach (char c in Path.GetInvalidFileNameChars())
- productSN = productSN.Replace(c, '_');
- }
- string resultStr = result ? "OK" : "NG";
- if (is3Dmodel == true)
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{resultStr}\\{imgName}.idb";
- }
- else
- {
- Originalpath = $"{path}\\Original\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{resultStr}\\{imgName}.bmp";
- }
- Recordedpath = $"{path}\\Recored\\{now:yyyy-MM}\\{now:MM-dd}\\{formulaName}\\{productSN}\\{resultStr}\\{imgName}.bmp";
- }
- cancellationToken.ThrowIfCancellationRequested();
- if (!Directory.Exists(Path.GetDirectoryName(Originalpath)))
- Directory.CreateDirectory(Path.GetDirectoryName(Originalpath));
- using (CogImageFile _cogImageFileTool = new CogImageFile())
- {
- _cogImageFileTool.Open(Originalpath, CogImageFileModeConstants.Write);
- _cogImageFileTool.Append(image);
- _cogImageFileTool.Close();
- }
- cancellationToken.ThrowIfCancellationRequested();
- if (!Directory.Exists(Path.GetDirectoryName(Recordedpath)))
- Directory.CreateDirectory(Path.GetDirectoryName(Recordedpath));
- if (isCompress)
- {
- ImageHelper.CompressImage(reimage, Recordedpath, 20);
- }
- else
- {
- reimage.Save(Recordedpath);
- }
- }
- }
- catch (OperationCanceledException) { }
- catch (Exception ex)
- {
- LogHelper.WriteLogError($"[SAVE-IMAGE] 保存流程运行结果的图片时出错!path={path}", ex);
- }
- finally
- {
- try { reimage?.Dispose(); } catch { }
- }
- }
- /// <summary>
- /// 延迟保存图像处理:isSave=true 时批量保存所有待保存项;false 时丢弃并清理
- /// </summary>
- private async void UpdateDelaySaveImage(bool isSave)
- {
- if (!isSave)
- {
- lock (delaySaveImages)
- {
- foreach (var item in delaySaveImages)
- {
- try { item.clonedBitmap?.Dispose(); } catch { }
- try { item.Image = null; } catch { }
- }
- delaySaveImages.Clear();
- }
- return;
- }
- List<Models.DelaySaveImage> itemsToSave;
- lock (delaySaveImages)
- {
- itemsToSave = new List<Models.DelaySaveImage>(delaySaveImages);
- delaySaveImages.Clear();
- }
- var token = GetSafeSaveToken();
- foreach (var item in itemsToSave)
- {
- try
- {
- if (token.IsCancellationRequested) break;
- if (string.IsNullOrEmpty(_productService.CurrentProductCode))
- {
- _eventAggregator.GetEvent<TaskMessageNotification>()
- .Publish(new MessageStruct()
- {
- Message = "当前产品SN不存在,无法保存图片!",
- level = MessageLevel.Alarm
- });
- break;
- }
- LogHelper.WriteLogInfo("开始异步保存延迟图像");
- await SaveImageAsync(
- item.SaveImageModel,
- item.SaveImagePathModel,
- item.SavePath,
- item.Image,
- item.clonedBitmap,
- item.Result,
- item.IsCompress,
- item.ImageFileName,
- item.Is3DModel,
- token
- );
- }
- catch (OperationCanceledException) { break; }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("延迟保存图像任务出错!", ex);
- }
- finally
- {
- try { item.clonedBitmap?.Dispose(); } catch { }
- try { item.Image = null; } catch { }
- }
- }
- }
- private void ShowVisionRender_Unloaded(object sender, RoutedEventArgs e)
- {
- try { _saveImageCts.Cancel(); _saveImageCts.Dispose(); } catch { }
- // 释放延迟保存列表中的 Bitmap 资源
- lock (delaySaveImages)
- {
- foreach (var item in delaySaveImages)
- {
- try { item.clonedBitmap?.Dispose(); } catch { }
- try { item.Image = null; } catch { }
- }
- delaySaveImages.Clear();
- }
- }
- private void ProductChanged(ProductModel product)
- {
- this.gridRender.Dispatcher.BeginInvoke(new Action(() =>
- {
- var is3DEnabled = GetView3DUse();
- foreach (var item in vmRenders)
- {
- try
- {
- // 2D:永远清理
- var display2D = item.Value.Child as Cognex.VisionPro.CogRecordDisplay;
- if (display2D != null)
- {
- display2D.Record = null;
- display2D.Image = null;
- display2D.StaticGraphics.Clear();
- continue;
- }
- // 3D:仅在启用 3D 时清理
- if (!is3DEnabled)
- continue;
- var display3D = item.Value.Child as Cognex.VisionPro3D.Cog3DDisplayV2WF;
- if (display3D != null)
- {
- display3D.Clear();
- }
- }
- catch (System.Windows.Forms.AxHost.InvalidActiveXStateException)
- {
- // 控件句柄/ActiveX 未就绪时忽略
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogError(Lang.切换产品清除主页图像时出错, ex);
- }
- }
- }));
- }
- private void OnDisplayDoubleClick(CogRecordDisplay disp)
- {
- if (disp == null)
- return;
- try
- {
- // 直接把当前 CogRecordDisplay 传给对话框 VM(VM 内部读取 Image/Record)
- var parameters = new Prism.Services.Dialogs.DialogParameters
- {
- { "Cog", disp }
- };
- // Prism Dialog 必须在 WPF UI 线程调用
- Dispatcher.BeginInvoke(new Action(() =>
- {
- try
- {
- _dialogService.ShowDialog("ImagePreviewWindow", parameters, r => { });
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("打开 ImagePreviewWindow 失败", ex);
- }
- }));
- }
- catch (Exception ex)
- {
- LogHelper.WriteLogError("双击打开预览窗口失败", ex);
- }
- }
- }
- }
|