|
@@ -7,6 +7,7 @@ using System.Collections.Concurrent;
|
|
|
using System.Collections.Generic;
|
|
using System.Collections.Generic;
|
|
|
using System.IO;
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
using System.Linq;
|
|
|
|
|
+using System.Threading;
|
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks;
|
|
|
using System.Windows.Documents;
|
|
using System.Windows.Documents;
|
|
|
using System.Windows.Interop;
|
|
using System.Windows.Interop;
|
|
@@ -524,37 +525,50 @@ namespace TeamAAS_VP.Services
|
|
|
var p = GetProduct(id);
|
|
var p = GetProduct(id);
|
|
|
if (p == null) return null;
|
|
if (p == null) return null;
|
|
|
|
|
|
|
|
|
|
+ // 释放上一个产品的 ToolBlock 内存
|
|
|
|
|
+ UnloadPreviousProduct(id);
|
|
|
|
|
+
|
|
|
var root = GetProductsRootFullPath();
|
|
var root = GetProductsRootFullPath();
|
|
|
string filePath = Path.Combine(root, p.Name, p.Name + ".cfg");
|
|
string filePath = Path.Combine(root, p.Name, p.Name + ".cfg");
|
|
|
var dir = Path.GetDirectoryName(filePath);
|
|
var dir = Path.GetDirectoryName(filePath);
|
|
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
|
|
|
|
|
|
|
- //foreach (var camera in p.CameraProcedures)
|
|
|
|
|
- //{
|
|
|
|
|
- // foreach (var prc in camera.ProcedureModels)
|
|
|
|
|
- // {
|
|
|
|
|
- // string prcPath = Path.Combine(dir, camera.Camera, prc.Name + ".vpp");
|
|
|
|
|
- // prc.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock;
|
|
|
|
|
- // }
|
|
|
|
|
- //}
|
|
|
|
|
-
|
|
|
|
|
|
|
+ // 收集所有需要加载的流程(ToolBlock 为空的)
|
|
|
|
|
+ var toLoad = new List<(string Path, ProcedureModel Model)>();
|
|
|
foreach (var camera in p.CameraProcedures)
|
|
foreach (var camera in p.CameraProcedures)
|
|
|
{
|
|
{
|
|
|
foreach (var prc in camera.ProcedureModels)
|
|
foreach (var prc in camera.ProcedureModels)
|
|
|
{
|
|
{
|
|
|
if (prc.ToolBlock != null) continue;
|
|
if (prc.ToolBlock != null) continue;
|
|
|
-
|
|
|
|
|
string prcPath = Path.Combine(dir, camera.Camera, prc.Name + ".vpp");
|
|
string prcPath = Path.Combine(dir, camera.Camera, prc.Name + ".vpp");
|
|
|
|
|
+ toLoad.Add((prcPath, prc));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 在独立 STA 线程中加载 VPP,避免 COM 封送到 UI 线程
|
|
|
|
|
- if (!_vppCache.TryGetValue(prcPath, out var cached))
|
|
|
|
|
|
|
+ if (toLoad.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ var parallelOptions = new ParallelOptions
|
|
|
|
|
+ {
|
|
|
|
|
+ MaxDegreeOfParallelism = Math.Min(toLoad.Count, Environment.ProcessorCount * 2)
|
|
|
|
|
+ };
|
|
|
|
|
+ Parallel.ForEach(toLoad, parallelOptions, item =>
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
{
|
|
{
|
|
|
- cached = VisionProHelper.LoadVisionProObjectAsync<CogToolBlock>(prcPath).GetAwaiter().GetResult();
|
|
|
|
|
- _vppCache.TryAdd(prcPath, cached);
|
|
|
|
|
|
|
+ if (!_vppCache.TryGetValue(item.Path, out var cached))
|
|
|
|
|
+ {
|
|
|
|
|
+ cached = VisionProHelper.LoadVisionProObjectAsync<CogToolBlock>(item.Path).GetAwaiter().GetResult();
|
|
|
|
|
+ _vppCache.TryAdd(item.Path, cached);
|
|
|
|
|
+ }
|
|
|
|
|
+ item.Model.ToolBlock = cached;
|
|
|
}
|
|
}
|
|
|
- prc.ToolBlock = cached;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ LogHelper.logerror.Error($"加载产品视觉流程时出错,路径:{item.Path}", ex);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
return p;
|
|
return p;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -569,36 +583,53 @@ namespace TeamAAS_VP.Services
|
|
|
var p = GetProduct(id);
|
|
var p = GetProduct(id);
|
|
|
if (p == null) return null;
|
|
if (p == null) return null;
|
|
|
|
|
|
|
|
|
|
+ // 释放上一个产品的 ToolBlock 内存
|
|
|
|
|
+ UnloadPreviousProduct(id);
|
|
|
|
|
+
|
|
|
var root = GetProductsRootFullPath();
|
|
var root = GetProductsRootFullPath();
|
|
|
string filePath = Path.Combine(root, p.Name, p.Name + ".cfg");
|
|
string filePath = Path.Combine(root, p.Name, p.Name + ".cfg");
|
|
|
var dir = Path.GetDirectoryName(filePath);
|
|
var dir = Path.GetDirectoryName(filePath);
|
|
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
|
|
|
|
|
|
|
- string temp = string.Empty;
|
|
|
|
|
- // 加载每个相机的流程ToolBlock
|
|
|
|
|
- try
|
|
|
|
|
|
|
+ // 收集所有需要加载的流程(ToolBlock 为空的)
|
|
|
|
|
+ var toLoad = new List<(string Path, ProcedureModel Model)>();
|
|
|
|
|
+ foreach (var camera in p.CameraProcedures)
|
|
|
{
|
|
{
|
|
|
- foreach (var camera in p.CameraProcedures)
|
|
|
|
|
|
|
+ foreach (var prc in camera.ProcedureModels)
|
|
|
{
|
|
{
|
|
|
- foreach (var prc in camera.ProcedureModels)
|
|
|
|
|
- {
|
|
|
|
|
- if (prc.ToolBlock != null) continue;
|
|
|
|
|
|
|
+ if (prc.ToolBlock != null) continue;
|
|
|
|
|
+ string prcPath = Path.Combine(dir, camera.Camera, prc.Name + ".vpp");
|
|
|
|
|
+ toLoad.Add((prcPath, prc));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- string prcPath = Path.Combine(dir, camera.Camera, prc.Name + ".vpp");
|
|
|
|
|
- temp = prcPath;
|
|
|
|
|
- // 异步加载 VPP,避免同步 I/O 阻塞调用线程(在独立 STA 线程中执行)
|
|
|
|
|
- if (!_vppCache.TryGetValue(prcPath, out var cached))
|
|
|
|
|
|
|
+ if (toLoad.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ // 限制并发数量,避免同时创建过多 STA 线程
|
|
|
|
|
+ var semaphore = new SemaphoreSlim(Math.Min(toLoad.Count, Environment.ProcessorCount * 2));
|
|
|
|
|
+ var tasks = toLoad.Select(async item =>
|
|
|
|
|
+ {
|
|
|
|
|
+ await semaphore.WaitAsync();
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!_vppCache.TryGetValue(item.Path, out var cached))
|
|
|
{
|
|
{
|
|
|
- cached = await VisionProHelper.LoadVisionProObjectAsync<CogToolBlock>(prcPath);
|
|
|
|
|
- _vppCache.TryAdd(prcPath, cached);
|
|
|
|
|
|
|
+ cached = await VisionProHelper.LoadVisionProObjectAsync<CogToolBlock>(item.Path);
|
|
|
|
|
+ _vppCache.TryAdd(item.Path, cached);
|
|
|
}
|
|
}
|
|
|
- prc.ToolBlock = cached;
|
|
|
|
|
|
|
+ item.Model.ToolBlock = cached;
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- catch (Exception ex)
|
|
|
|
|
- {
|
|
|
|
|
- LogHelper.logerror.Error($"加载产品视觉流程时出错,路径:{temp}", ex);
|
|
|
|
|
|
|
+ catch (Exception ex)
|
|
|
|
|
+ {
|
|
|
|
|
+ LogHelper.logerror.Error($"加载产品视觉流程时出错,路径:{item.Path}", ex);
|
|
|
|
|
+ }
|
|
|
|
|
+ finally
|
|
|
|
|
+ {
|
|
|
|
|
+ semaphore.Release();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ await Task.WhenAll(tasks);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
return p;
|
|
@@ -675,6 +706,45 @@ namespace TeamAAS_VP.Services
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 释放上一个产品(非目标产品)所有流程的 ToolBlock,释放 VisionPro 对象占用的内存。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void UnloadPreviousProduct(Guid targetProductId)
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_sync)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_currentProduct == Guid.Empty || _currentProduct == targetProductId)
|
|
|
|
|
+ return;
|
|
|
|
|
+
|
|
|
|
|
+ if (_products.TryGetValue(_currentProduct, out var oldProduct))
|
|
|
|
|
+ {
|
|
|
|
|
+ UnloadProductToolBlocks(oldProduct);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 释放指定产品所有流程的 ToolBlock 引用,释放 VisionPro 对象占用的内存。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private void UnloadProductToolBlocks(ProductModel product)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (product?.CameraProcedures == null) return;
|
|
|
|
|
+ foreach (var camera in product.CameraProcedures)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (camera.ProcedureModels == null) continue;
|
|
|
|
|
+ foreach (var prc in camera.ProcedureModels)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (prc.ToolBlock != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ prc.ToolBlock.Dispose();
|
|
|
|
|
+ prc.ToolBlock = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 清空 VPP 缓存,因为缓存的 ToolBlock 已被 Dispose
|
|
|
|
|
+ _vppCache.Clear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// 获取当前产品模型(若未设置则返回 null)。
|
|
/// 获取当前产品模型(若未设置则返回 null)。
|
|
|
/// </summary>
|
|
/// </summary>
|