对生成的网格地图进行了缓存,提高路径规划速度
This commit is contained in:
parent
c9b7acbd0a
commit
cd74f857a2
@ -163,6 +163,10 @@
|
||||
<Compile Include="src\PathPlanning\ChannelBasedGridBuilder.cs" />
|
||||
<Compile Include="src\PathPlanning\VerticalScanProcessor.cs" />
|
||||
<Compile Include="src\PathPlanning\PathOptimizer.cs" />
|
||||
<Compile Include="src\PathPlanning\GridCacheKey.cs" />
|
||||
<Compile Include="src\PathPlanning\GridCache.cs" />
|
||||
<Compile Include="src\PathPlanning\GridMapCacheKey.cs" />
|
||||
<Compile Include="src\PathPlanning\GridMapCache.cs" />
|
||||
|
||||
<!-- UI - WPF -->
|
||||
<Compile Include="src\UI\WPF\Views\LogisticsControlPanel.xaml.cs">
|
||||
|
||||
@ -6,7 +6,6 @@ using Roy_T.AStar.Grids;
|
||||
using Roy_T.AStar.Primitives;
|
||||
using Roy_T.AStar.Paths;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.Core;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
@ -117,6 +116,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 路径优化 - 去除共线点
|
||||
pathResult = OptimizePath(pathResult, gridMap);
|
||||
|
||||
// 记录缓存统计信息
|
||||
LogCacheStatistics();
|
||||
|
||||
return pathResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1214,8 +1216,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
int gridX = gridPos.X;
|
||||
int gridY = gridPos.Y;
|
||||
|
||||
LogManager.Info($"[高度检查] 检查点 ({point.X:F2}, {point.Y:F2}, {point.Z:F2}) -> 网格坐标 ({gridX}, {gridY})");
|
||||
|
||||
// 确保坐标在有效范围内
|
||||
if (gridX < 0 || gridX >= gridMap.Width || gridY < 0 || gridY >= gridMap.Height)
|
||||
{
|
||||
@ -1240,7 +1240,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 将绝对坐标转换为相对于网格底面的坐标
|
||||
double relativeZ = point.Z - cell.WorldPosition.Z;
|
||||
bool containsHeight = interval.Contains(relativeZ);
|
||||
LogManager.Info($"[高度检查] 区间 {interval}: 跨度={interval.GetSpan():F2}模型单位 (需要≥{vehicleHeight:F2}模型单位): {spanOk}, 包含相对Z={relativeZ:F2}: {containsHeight}");
|
||||
|
||||
if (spanOk && containsHeight)
|
||||
{
|
||||
@ -1433,10 +1432,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
doorPoints++;
|
||||
LogManager.Info($"[路径高度验证] 检查路径点{i}(门网格): ({point.X:F2}, {point.Y:F2}, {point.Z:F2}) -> 网格({gridPos.X},{gridPos.Y})");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info($"[路径高度验证] 检查路径点{i}({cell.CellType}): ({point.X:F2}, {point.Y:F2}, {point.Z:F2}) -> 网格({gridPos.X},{gridPos.Y})");
|
||||
}
|
||||
|
||||
// 进行高度约束检查
|
||||
bool isPassable = IsPointPassableAtHeight(point, gridMap, vehicleHeight);
|
||||
@ -1879,5 +1874,43 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
return finalSpeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取GridMap缓存统计信息
|
||||
/// </summary>
|
||||
/// <returns>缓存统计报告</returns>
|
||||
public static string GetGridMapCacheStatistics()
|
||||
{
|
||||
return GridMapGenerator.GetCacheStatistics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取GridMap缓存详细统计报告
|
||||
/// </summary>
|
||||
/// <returns>详细统计报告</returns>
|
||||
public static string GetDetailedGridMapCacheReport()
|
||||
{
|
||||
return GridMapGenerator.GetDetailedCacheReport();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除GridMap缓存
|
||||
/// </summary>
|
||||
public static void ClearGridMapCache()
|
||||
{
|
||||
GridMapGenerator.ClearCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录路径规划完成时的缓存统计
|
||||
/// </summary>
|
||||
private void LogCacheStatistics()
|
||||
{
|
||||
var stats = GlobalGridMapCache.Instance.Statistics;
|
||||
if (stats.HitCount + stats.MissCount > 0)
|
||||
{
|
||||
LogManager.Info($"[GridMap缓存统计] {stats.GenerateReport()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
331
src/PathPlanning/GridCache.cs
Normal file
331
src/PathPlanning/GridCache.cs
Normal file
@ -0,0 +1,331 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Roy_T.AStar.Grids;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存项,包含网格和访问时间信息
|
||||
/// </summary>
|
||||
public class GridCacheItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存的A*网格
|
||||
/// </summary>
|
||||
public Grid Grid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后访问时间
|
||||
/// </summary>
|
||||
public DateTime LastAccessTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 访问次数
|
||||
/// </summary>
|
||||
public int AccessCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public GridCacheItem(Grid grid)
|
||||
{
|
||||
Grid = grid ?? throw new ArgumentNullException(nameof(grid));
|
||||
LastAccessTime = DateTime.Now;
|
||||
CreatedTime = DateTime.Now;
|
||||
AccessCount = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录访问
|
||||
/// </summary>
|
||||
public void RecordAccess()
|
||||
{
|
||||
LastAccessTime = DateTime.Now;
|
||||
AccessCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网格缓存统计信息
|
||||
/// </summary>
|
||||
public class GridCacheStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存命中次数
|
||||
/// </summary>
|
||||
public long HitCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存未命中次数
|
||||
/// </summary>
|
||||
public long MissCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前缓存项数量
|
||||
/// </summary>
|
||||
public int CurrentCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大缓存项数量
|
||||
/// </summary>
|
||||
public int MaxCapacity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 被驱逐的缓存项数量
|
||||
/// </summary>
|
||||
public long EvictedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存命中率
|
||||
/// </summary>
|
||||
public double HitRate => HitCount + MissCount > 0 ? (double)HitCount / (HitCount + MissCount) : 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存使用率
|
||||
/// </summary>
|
||||
public double UsageRate => MaxCapacity > 0 ? (double)CurrentCount / MaxCapacity : 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 重置统计信息
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
HitCount = 0;
|
||||
MissCount = 0;
|
||||
EvictedCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成统计报告
|
||||
/// </summary>
|
||||
public string GenerateReport()
|
||||
{
|
||||
return $"网格缓存统计 - 命中率: {HitRate:P2} ({HitCount}命中/{MissCount}未命中), " +
|
||||
$"使用率: {UsageRate:P2} ({CurrentCount}/{MaxCapacity}), " +
|
||||
$"驱逐次数: {EvictedCount}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网格缓存管理器
|
||||
/// 使用LRU策略管理A*网格缓存,支持线程安全访问
|
||||
/// </summary>
|
||||
public class GridCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<GridCacheKey, GridCacheItem> _cache;
|
||||
private readonly object _cleanupLock = new object();
|
||||
private readonly int _maxCapacity;
|
||||
private readonly GridCacheStatistics _statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存统计信息
|
||||
/// </summary>
|
||||
public GridCacheStatistics Statistics => _statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="maxCapacity">最大缓存容量,默认20个网格</param>
|
||||
public GridCache(int maxCapacity = 20)
|
||||
{
|
||||
if (maxCapacity <= 0)
|
||||
throw new ArgumentException("缓存容量必须大于0", nameof(maxCapacity));
|
||||
|
||||
_maxCapacity = maxCapacity;
|
||||
_cache = new ConcurrentDictionary<GridCacheKey, GridCacheItem>();
|
||||
_statistics = new GridCacheStatistics { MaxCapacity = maxCapacity };
|
||||
|
||||
LogManager.Info($"[网格缓存] 初始化完成,最大容量: {_maxCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存的网格
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <returns>网格对象,如果不存在则返回null</returns>
|
||||
public Grid Get(GridCacheKey key)
|
||||
{
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
if (_cache.TryGetValue(key, out var item))
|
||||
{
|
||||
// 记录访问
|
||||
item.RecordAccess();
|
||||
_statistics.HitCount++;
|
||||
|
||||
LogManager.Debug($"[网格缓存] 命中 - {key},访问次数: {item.AccessCount}");
|
||||
return item.Grid;
|
||||
}
|
||||
|
||||
_statistics.MissCount++;
|
||||
LogManager.Debug($"[网格缓存] 未命中 - {key}");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加网格到缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="grid">A*网格</param>
|
||||
public void Put(GridCacheKey key, Grid grid)
|
||||
{
|
||||
if (key == null || grid == null)
|
||||
return;
|
||||
|
||||
var item = new GridCacheItem(grid);
|
||||
|
||||
// 添加或更新缓存项
|
||||
_cache.AddOrUpdate(key, item, (k, existing) =>
|
||||
{
|
||||
LogManager.Debug($"[网格缓存] 更新现有项 - {key}");
|
||||
return item;
|
||||
});
|
||||
|
||||
// 更新统计信息
|
||||
_statistics.CurrentCount = _cache.Count;
|
||||
|
||||
LogManager.Debug($"[网格缓存] 添加 - {key},当前容量: {_cache.Count}/{_maxCapacity}");
|
||||
|
||||
// 检查是否需要清理
|
||||
if (_cache.Count > _maxCapacity)
|
||||
{
|
||||
CleanupCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查缓存中是否包含指定键
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <returns>是否包含</returns>
|
||||
public bool ContainsKey(GridCacheKey key)
|
||||
{
|
||||
return key != null && _cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有缓存
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
var count = _cache.Count;
|
||||
_cache.Clear();
|
||||
_statistics.CurrentCount = 0;
|
||||
_statistics.Reset();
|
||||
|
||||
LogManager.Info($"[网格缓存] 清除所有缓存,共清除 {count} 项");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存的所有键
|
||||
/// </summary>
|
||||
/// <returns>键列表</returns>
|
||||
public IList<GridCacheKey> GetKeys()
|
||||
{
|
||||
return _cache.Keys.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理缓存,使用LRU策略移除最少使用的项
|
||||
/// </summary>
|
||||
private void CleanupCache()
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
// 双重检查,避免重复清理
|
||||
if (_cache.Count <= _maxCapacity)
|
||||
return;
|
||||
|
||||
var targetCount = (int)(_maxCapacity * 0.8); // 清理到80%容量
|
||||
var itemsToRemove = _cache.Count - targetCount;
|
||||
|
||||
LogManager.Info($"[网格缓存] 开始LRU清理,当前: {_cache.Count},目标: {targetCount},需移除: {itemsToRemove}");
|
||||
|
||||
// 按最后访问时间排序,移除最久未访问的项
|
||||
var sortedItems = _cache
|
||||
.OrderBy(kvp => kvp.Value.LastAccessTime)
|
||||
.ThenBy(kvp => kvp.Value.AccessCount) // 相同时间的情况下,优先移除访问次数少的
|
||||
.Take(itemsToRemove)
|
||||
.ToList();
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
if (_cache.TryRemove(item.Key, out _))
|
||||
{
|
||||
removedCount++;
|
||||
_statistics.EvictedCount++;
|
||||
LogManager.Debug($"[网格缓存] 移除 - {item.Key},最后访问: {item.Value.LastAccessTime:yyyy-MM-dd HH:mm:ss}");
|
||||
}
|
||||
}
|
||||
|
||||
_statistics.CurrentCount = _cache.Count;
|
||||
LogManager.Info($"[网格缓存] LRU清理完成,实际移除: {removedCount},当前容量: {_cache.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成缓存详细报告
|
||||
/// </summary>
|
||||
/// <returns>报告字符串</returns>
|
||||
public string GenerateDetailedReport()
|
||||
{
|
||||
var report = new System.Text.StringBuilder();
|
||||
|
||||
report.AppendLine(_statistics.GenerateReport());
|
||||
|
||||
if (_cache.Count > 0)
|
||||
{
|
||||
report.AppendLine($"\n当前缓存项详情:");
|
||||
var sortedItems = _cache
|
||||
.OrderByDescending(kvp => kvp.Value.LastAccessTime)
|
||||
.Take(10) // 只显示最近10项
|
||||
.ToList();
|
||||
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
report.AppendLine($" {item.Key} - 访问{item.Value.AccessCount}次,最后访问: {item.Value.LastAccessTime:MM-dd HH:mm:ss}");
|
||||
}
|
||||
|
||||
if (_cache.Count > 10)
|
||||
{
|
||||
report.AppendLine($" ... 还有 {_cache.Count - 10} 项");
|
||||
}
|
||||
}
|
||||
|
||||
return report.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局网格缓存管理器(单例)
|
||||
/// </summary>
|
||||
public static class GlobalGridCache
|
||||
{
|
||||
private static readonly Lazy<GridCache> _instance = new Lazy<GridCache>(() => new GridCache());
|
||||
|
||||
/// <summary>
|
||||
/// 获取全局缓存实例
|
||||
/// </summary>
|
||||
public static GridCache Instance => _instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 重置全局缓存(主要用于测试)
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
_instance.Value.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
185
src/PathPlanning/GridCacheKey.cs
Normal file
185
src/PathPlanning/GridCacheKey.cs
Normal file
@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
/// <summary>
|
||||
/// 网格缓存键,用于标识唯一的网格配置
|
||||
/// 包含所有影响网格生成的因素:网格地图内容、车辆高度、路径策略、网格尺寸
|
||||
/// </summary>
|
||||
public class GridCacheKey : IEquatable<GridCacheKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 网格地图内容哈希值
|
||||
/// </summary>
|
||||
public string GridMapHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆高度(模型单位)
|
||||
/// </summary>
|
||||
public double VehicleHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 路径规划策略
|
||||
/// </summary>
|
||||
public PathStrategy Strategy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 网格单元大小(模型单位)
|
||||
/// </summary>
|
||||
public double CellSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="gridMapHash">网格地图哈希</param>
|
||||
/// <param name="vehicleHeight">车辆高度</param>
|
||||
/// <param name="strategy">路径策略</param>
|
||||
/// <param name="cellSize">网格尺寸</param>
|
||||
public GridCacheKey(string gridMapHash, double vehicleHeight, PathStrategy strategy, double cellSize)
|
||||
{
|
||||
GridMapHash = gridMapHash ?? throw new ArgumentNullException(nameof(gridMapHash));
|
||||
VehicleHeight = vehicleHeight;
|
||||
Strategy = strategy;
|
||||
CellSize = cellSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个缓存键是否相等
|
||||
/// </summary>
|
||||
public bool Equals(GridCacheKey other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return GridMapHash == other.GridMapHash &&
|
||||
Math.Abs(VehicleHeight - other.VehicleHeight) < 1e-6 &&
|
||||
Strategy == other.Strategy &&
|
||||
Math.Abs(CellSize - other.CellSize) < 1e-6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写Equals方法
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as GridCacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算哈希码,用于Dictionary键
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + (GridMapHash?.GetHashCode() ?? 0);
|
||||
hash = hash * 23 + VehicleHeight.GetHashCode();
|
||||
hash = hash * 23 + Strategy.GetHashCode();
|
||||
hash = hash * 23 + CellSize.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成可读的字符串表示
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
var hashPreview = GridMapHash != null ?
|
||||
GridMapHash.Substring(0, Math.Min(8, GridMapHash.Length)) :
|
||||
"NULL";
|
||||
return $"GridCacheKey[Hash={hashPreview}, " +
|
||||
$"Height={VehicleHeight:F2}, Strategy={Strategy}, CellSize={CellSize:F2}]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据GridMap和参数生成缓存键
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="vehicleHeight">车辆高度</param>
|
||||
/// <param name="strategy">路径策略</param>
|
||||
/// <returns>缓存键</returns>
|
||||
public static GridCacheKey CreateFrom(GridMap gridMap, double vehicleHeight, PathStrategy strategy)
|
||||
{
|
||||
if (gridMap == null)
|
||||
throw new ArgumentNullException(nameof(gridMap));
|
||||
|
||||
var gridMapHash = ComputeGridMapHash(gridMap);
|
||||
return new GridCacheKey(gridMapHash, vehicleHeight, strategy, gridMap.CellSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算GridMap的内容哈希值
|
||||
/// 基于网格尺寸、单元格可通行性、高度约束等关键数据
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <returns>SHA256哈希字符串</returns>
|
||||
private static string ComputeGridMapHash(GridMap gridMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var hashBuilder = new StringBuilder();
|
||||
|
||||
// 基础属性
|
||||
hashBuilder.Append($"W:{gridMap.Width}|H:{gridMap.Height}|CS:{gridMap.CellSize:F6}|");
|
||||
hashBuilder.Append($"OX:{gridMap.Origin.X:F6}|OY:{gridMap.Origin.Y:F6}|OZ:{gridMap.Origin.Z:F6}|");
|
||||
|
||||
// 遍历所有网格单元
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 基本属性
|
||||
hashBuilder.Append($"[{x},{y}]:{(cell.IsWalkable ? "1" : "0")}|{cell.CellType}|");
|
||||
|
||||
// 世界位置
|
||||
hashBuilder.Append($"WP:{cell.WorldPosition.X:F3},{cell.WorldPosition.Y:F3},{cell.WorldPosition.Z:F3}|");
|
||||
|
||||
// 高度约束区间
|
||||
if (cell.PassableHeights != null && cell.PassableHeights.Count > 0)
|
||||
{
|
||||
hashBuilder.Append("PH:");
|
||||
foreach (var interval in cell.PassableHeights)
|
||||
{
|
||||
hashBuilder.Append($"{interval.MinZ:F3}-{interval.MaxZ:F3},");
|
||||
}
|
||||
hashBuilder.Append("|");
|
||||
}
|
||||
else
|
||||
{
|
||||
hashBuilder.Append("PH:NONE|");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算哈希
|
||||
var hashInput = hashBuilder.ToString();
|
||||
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hashInput));
|
||||
|
||||
// 转换为十六进制字符串
|
||||
var hexBuilder = new StringBuilder();
|
||||
foreach (byte b in hashBytes)
|
||||
{
|
||||
hexBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return hexBuilder.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果哈希计算失败,返回基于时间戳的唯一标识
|
||||
// 这样可以确保缓存不会错误命中,但会失去缓存效果
|
||||
LogManager.Warning($"[网格哈希] 计算网格哈希失败: {ex.Message},使用时间戳标识");
|
||||
return $"HASH_FAILED_{DateTime.Now.Ticks}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
347
src/PathPlanning/GridMapCache.cs
Normal file
347
src/PathPlanning/GridMapCache.cs
Normal file
@ -0,0 +1,347 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
/// <summary>
|
||||
/// GridMap缓存项,包含网格地图和访问时间信息
|
||||
/// </summary>
|
||||
public class GridMapCacheItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存的网格地图
|
||||
/// </summary>
|
||||
public GridMap GridMap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后访问时间
|
||||
/// </summary>
|
||||
public DateTime LastAccessTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 访问次数
|
||||
/// </summary>
|
||||
public int AccessCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public GridMapCacheItem(GridMap gridMap)
|
||||
{
|
||||
GridMap = gridMap ?? throw new ArgumentNullException(nameof(gridMap));
|
||||
LastAccessTime = DateTime.Now;
|
||||
CreatedTime = DateTime.Now;
|
||||
AccessCount = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录访问
|
||||
/// </summary>
|
||||
public void RecordAccess()
|
||||
{
|
||||
LastAccessTime = DateTime.Now;
|
||||
AccessCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GridMap缓存统计信息
|
||||
/// </summary>
|
||||
public class GridMapCacheStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存命中次数
|
||||
/// </summary>
|
||||
public long HitCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存未命中次数
|
||||
/// </summary>
|
||||
public long MissCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前缓存项数量
|
||||
/// </summary>
|
||||
public int CurrentCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大缓存项数量
|
||||
/// </summary>
|
||||
public int MaxCapacity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 被驱逐的缓存项数量
|
||||
/// </summary>
|
||||
public long EvictedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 累计节省的构建时间(毫秒)
|
||||
/// </summary>
|
||||
public long TotalTimeSavedMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存命中率
|
||||
/// </summary>
|
||||
public double HitRate => HitCount + MissCount > 0 ? (double)HitCount / (HitCount + MissCount) : 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存使用率
|
||||
/// </summary>
|
||||
public double UsageRate => MaxCapacity > 0 ? (double)CurrentCount / MaxCapacity : 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 重置统计信息
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
HitCount = 0;
|
||||
MissCount = 0;
|
||||
EvictedCount = 0;
|
||||
TotalTimeSavedMs = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成统计报告
|
||||
/// </summary>
|
||||
public string GenerateReport()
|
||||
{
|
||||
return $"GridMap缓存统计 - 命中率: {HitRate:P2} ({HitCount}命中/{MissCount}未命中), " +
|
||||
$"使用率: {UsageRate:P2} ({CurrentCount}/{MaxCapacity}), " +
|
||||
$"驱逐次数: {EvictedCount}, 节省时间: {TotalTimeSavedMs/1000.0:F1}秒";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GridMap缓存管理器
|
||||
/// 使用LRU策略管理GridMap缓存,支持线程安全访问
|
||||
/// </summary>
|
||||
public class GridMapCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<GridMapCacheKey, GridMapCacheItem> _cache;
|
||||
private readonly object _cleanupLock = new object();
|
||||
private readonly int _maxCapacity;
|
||||
private readonly GridMapCacheStatistics _statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存统计信息
|
||||
/// </summary>
|
||||
public GridMapCacheStatistics Statistics => _statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="maxCapacity">最大缓存容量,默认10个GridMap</param>
|
||||
public GridMapCache(int maxCapacity = 10)
|
||||
{
|
||||
if (maxCapacity <= 0)
|
||||
throw new ArgumentException("缓存容量必须大于0", nameof(maxCapacity));
|
||||
|
||||
_maxCapacity = maxCapacity;
|
||||
_cache = new ConcurrentDictionary<GridMapCacheKey, GridMapCacheItem>();
|
||||
_statistics = new GridMapCacheStatistics { MaxCapacity = maxCapacity };
|
||||
|
||||
LogManager.Info($"[GridMap缓存] 初始化完成,最大容量: {_maxCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存的GridMap
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="estimatedBuildTimeMs">预估的构建时间(用于统计节省时间)</param>
|
||||
/// <returns>GridMap对象,如果不存在则返回null</returns>
|
||||
public GridMap Get(GridMapCacheKey key, long estimatedBuildTimeMs = 0)
|
||||
{
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
if (_cache.TryGetValue(key, out var item))
|
||||
{
|
||||
// 记录访问
|
||||
item.RecordAccess();
|
||||
_statistics.HitCount++;
|
||||
|
||||
// 累计节省时间
|
||||
if (estimatedBuildTimeMs > 0)
|
||||
{
|
||||
_statistics.TotalTimeSavedMs += estimatedBuildTimeMs;
|
||||
}
|
||||
|
||||
LogManager.Info($"[GridMap缓存] 命中 - {key},访问次数: {item.AccessCount}");
|
||||
return item.GridMap;
|
||||
}
|
||||
|
||||
_statistics.MissCount++;
|
||||
LogManager.Info($"[GridMap缓存] 未命中 - {key}");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加GridMap到缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="gridMap">GridMap对象</param>
|
||||
public void Put(GridMapCacheKey key, GridMap gridMap)
|
||||
{
|
||||
if (key == null || gridMap == null)
|
||||
return;
|
||||
|
||||
var item = new GridMapCacheItem(gridMap);
|
||||
|
||||
// 添加或更新缓存项
|
||||
_cache.AddOrUpdate(key, item, (k, existing) =>
|
||||
{
|
||||
LogManager.Debug($"[GridMap缓存] 更新现有项 - {key}");
|
||||
return item;
|
||||
});
|
||||
|
||||
// 更新统计信息
|
||||
_statistics.CurrentCount = _cache.Count;
|
||||
|
||||
LogManager.Info($"[GridMap缓存] 添加 - {key},当前容量: {_cache.Count}/{_maxCapacity}");
|
||||
LogManager.Info($"[GridMap缓存] GridMap统计: {gridMap.GetStatistics()}");
|
||||
|
||||
// 检查是否需要清理
|
||||
if (_cache.Count > _maxCapacity)
|
||||
{
|
||||
CleanupCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查缓存中是否包含指定键
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <returns>是否包含</returns>
|
||||
public bool ContainsKey(GridMapCacheKey key)
|
||||
{
|
||||
return key != null && _cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有缓存
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
var count = _cache.Count;
|
||||
_cache.Clear();
|
||||
_statistics.CurrentCount = 0;
|
||||
_statistics.Reset();
|
||||
|
||||
LogManager.Info($"[GridMap缓存] 清除所有缓存,共清除 {count} 项");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存的所有键
|
||||
/// </summary>
|
||||
/// <returns>键列表</returns>
|
||||
public IList<GridMapCacheKey> GetKeys()
|
||||
{
|
||||
return _cache.Keys.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理缓存,使用LRU策略移除最少使用的项
|
||||
/// </summary>
|
||||
private void CleanupCache()
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
// 双重检查,避免重复清理
|
||||
if (_cache.Count <= _maxCapacity)
|
||||
return;
|
||||
|
||||
var targetCount = Math.Max(1, (int)(_maxCapacity * 0.7)); // 清理到70%容量
|
||||
var itemsToRemove = _cache.Count - targetCount;
|
||||
|
||||
LogManager.Info($"[GridMap缓存] 开始LRU清理,当前: {_cache.Count},目标: {targetCount},需移除: {itemsToRemove}");
|
||||
|
||||
// 按最后访问时间排序,移除最久未访问的项
|
||||
var sortedItems = _cache
|
||||
.OrderBy(kvp => kvp.Value.LastAccessTime)
|
||||
.ThenBy(kvp => kvp.Value.AccessCount) // 相同时间的情况下,优先移除访问次数少的
|
||||
.Take(itemsToRemove)
|
||||
.ToList();
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
if (_cache.TryRemove(item.Key, out _))
|
||||
{
|
||||
removedCount++;
|
||||
_statistics.EvictedCount++;
|
||||
LogManager.Debug($"[GridMap缓存] 移除 - {item.Key},最后访问: {item.Value.LastAccessTime:yyyy-MM-dd HH:mm:ss}");
|
||||
}
|
||||
}
|
||||
|
||||
_statistics.CurrentCount = _cache.Count;
|
||||
LogManager.Info($"[GridMap缓存] LRU清理完成,实际移除: {removedCount},当前容量: {_cache.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成缓存详细报告
|
||||
/// </summary>
|
||||
/// <returns>报告字符串</returns>
|
||||
public string GenerateDetailedReport()
|
||||
{
|
||||
var report = new System.Text.StringBuilder();
|
||||
|
||||
report.AppendLine(_statistics.GenerateReport());
|
||||
|
||||
if (_cache.Count > 0)
|
||||
{
|
||||
report.AppendLine($"\n当前缓存项详情:");
|
||||
var sortedItems = _cache
|
||||
.OrderByDescending(kvp => kvp.Value.LastAccessTime)
|
||||
.Take(5) // 只显示最近5项
|
||||
.ToList();
|
||||
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
var gridStats = item.Value.GridMap?.GetStatistics() ?? "N/A";
|
||||
report.AppendLine($" {item.Key}");
|
||||
report.AppendLine($" 访问{item.Value.AccessCount}次,最后访问: {item.Value.LastAccessTime:MM-dd HH:mm:ss}");
|
||||
report.AppendLine($" GridMap: {gridStats}");
|
||||
}
|
||||
|
||||
if (_cache.Count > 5)
|
||||
{
|
||||
report.AppendLine($" ... 还有 {_cache.Count - 5} 项");
|
||||
}
|
||||
}
|
||||
|
||||
return report.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局GridMap缓存管理器(单例)
|
||||
/// </summary>
|
||||
public static class GlobalGridMapCache
|
||||
{
|
||||
private static readonly Lazy<GridMapCache> _instance = new Lazy<GridMapCache>(() => new GridMapCache());
|
||||
|
||||
/// <summary>
|
||||
/// 获取全局缓存实例
|
||||
/// </summary>
|
||||
public static GridMapCache Instance => _instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 重置全局缓存(主要用于测试)
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
_instance.Value.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
243
src/PathPlanning/GridMapCacheKey.cs
Normal file
243
src/PathPlanning/GridMapCacheKey.cs
Normal file
@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Autodesk.Navisworks.Api;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
/// <summary>
|
||||
/// GridMap缓存键,用于标识唯一的GridMap生成配置
|
||||
/// 基于通道数据、车辆尺寸、网格尺寸等关键参数
|
||||
/// </summary>
|
||||
public class GridMapCacheKey : IEquatable<GridMapCacheKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 通道数据哈希值(所有可通行物流构件)
|
||||
/// </summary>
|
||||
public string ChannelDataHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 边界范围哈希
|
||||
/// </summary>
|
||||
public string BoundsHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 网格单元大小(模型单位)
|
||||
/// </summary>
|
||||
public double CellSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆半径(米)
|
||||
/// </summary>
|
||||
public double VehicleRadius { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 安全间隙(米)
|
||||
/// </summary>
|
||||
public double SafetyMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆高度(米)
|
||||
/// </summary>
|
||||
public double VehicleHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public GridMapCacheKey(string channelDataHash, string boundsHash, double cellSize,
|
||||
double vehicleRadius, double safetyMargin, double vehicleHeight)
|
||||
{
|
||||
ChannelDataHash = channelDataHash ?? throw new ArgumentNullException(nameof(channelDataHash));
|
||||
BoundsHash = boundsHash ?? throw new ArgumentNullException(nameof(boundsHash));
|
||||
CellSize = cellSize;
|
||||
VehicleRadius = vehicleRadius;
|
||||
SafetyMargin = safetyMargin;
|
||||
VehicleHeight = vehicleHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个缓存键是否相等
|
||||
/// </summary>
|
||||
public bool Equals(GridMapCacheKey other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return ChannelDataHash == other.ChannelDataHash &&
|
||||
BoundsHash == other.BoundsHash &&
|
||||
Math.Abs(CellSize - other.CellSize) < 1e-6 &&
|
||||
Math.Abs(VehicleRadius - other.VehicleRadius) < 1e-6 &&
|
||||
Math.Abs(SafetyMargin - other.SafetyMargin) < 1e-6 &&
|
||||
Math.Abs(VehicleHeight - other.VehicleHeight) < 1e-6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写Equals方法
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as GridMapCacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算哈希码,用于Dictionary键
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + (ChannelDataHash?.GetHashCode() ?? 0);
|
||||
hash = hash * 23 + (BoundsHash?.GetHashCode() ?? 0);
|
||||
hash = hash * 23 + CellSize.GetHashCode();
|
||||
hash = hash * 23 + VehicleRadius.GetHashCode();
|
||||
hash = hash * 23 + SafetyMargin.GetHashCode();
|
||||
hash = hash * 23 + VehicleHeight.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成可读的字符串表示
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
var channelPreview = ChannelDataHash != null ?
|
||||
ChannelDataHash.Substring(0, Math.Min(8, ChannelDataHash.Length)) :
|
||||
"NULL";
|
||||
var boundsPreview = BoundsHash != null ?
|
||||
BoundsHash.Substring(0, Math.Min(8, BoundsHash.Length)) :
|
||||
"NULL";
|
||||
|
||||
return $"GridMapKey[Ch={channelPreview}, Bounds={boundsPreview}, " +
|
||||
$"Cell={CellSize:F2}, VR={VehicleRadius:F1}m, SM={SafetyMargin:F1}m, VH={VehicleHeight:F1}m]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据参数生成GridMap缓存键
|
||||
/// </summary>
|
||||
/// <param name="document">Navisworks文档</param>
|
||||
/// <param name="bounds">边界范围</param>
|
||||
/// <param name="cellSize">网格大小</param>
|
||||
/// <param name="vehicleRadius">车辆半径(米)</param>
|
||||
/// <param name="safetyMargin">安全间隙(米)</param>
|
||||
/// <param name="vehicleHeight">车辆高度(米)</param>
|
||||
/// <returns>缓存键</returns>
|
||||
public static GridMapCacheKey CreateFrom(Document document, BoundingBox3D bounds,
|
||||
double cellSize, double vehicleRadius,
|
||||
double safetyMargin, double vehicleHeight)
|
||||
{
|
||||
if (document == null)
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
|
||||
var channelDataHash = ComputeChannelDataHash(document);
|
||||
var boundsHash = ComputeBoundsHash(bounds);
|
||||
|
||||
return new GridMapCacheKey(channelDataHash, boundsHash, cellSize,
|
||||
vehicleRadius, safetyMargin, vehicleHeight);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算通道数据哈希值
|
||||
/// 基于所有可通行物流构件的ID、属性、几何等信息
|
||||
/// </summary>
|
||||
/// <param name="document">Navisworks文档</param>
|
||||
/// <returns>通道数据哈希</returns>
|
||||
private static string ComputeChannelDataHash(Document document)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var hashBuilder = new StringBuilder();
|
||||
|
||||
// 获取所有可通行的物流模型项
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
|
||||
|
||||
// 按InstanceGuid排序确保一致性
|
||||
var sortedItems = allChannelItems.OrderBy(item => item.InstanceGuid.ToString()).ToList();
|
||||
|
||||
hashBuilder.Append($"ChannelCount:{sortedItems.Count}|");
|
||||
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
// 基本标识信息
|
||||
hashBuilder.Append($"ID:{item.InstanceGuid}|");
|
||||
hashBuilder.Append($"DisplayName:{item.DisplayName ?? "NULL"}|");
|
||||
|
||||
// 几何信息(包围盒)
|
||||
var bbox = item.BoundingBox();
|
||||
if (bbox != null)
|
||||
{
|
||||
hashBuilder.Append($"BBox:{bbox.Min.X:F3},{bbox.Min.Y:F3},{bbox.Min.Z:F3}-");
|
||||
hashBuilder.Append($"{bbox.Max.X:F3},{bbox.Max.Y:F3},{bbox.Max.Z:F3}|");
|
||||
}
|
||||
|
||||
// 物流类型属性
|
||||
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
|
||||
hashBuilder.Append($"LogType:{logisticsType}|");
|
||||
|
||||
// Transform信息(如果有变换)
|
||||
var transform = item.Transform;
|
||||
if (transform != null)
|
||||
{
|
||||
// 只包含关键的变换信息
|
||||
hashBuilder.Append($"TX:{transform.Translation.X:F3},{transform.Translation.Y:F3},{transform.Translation.Z:F3}|");
|
||||
}
|
||||
}
|
||||
|
||||
// 计算最终哈希
|
||||
var hashInput = hashBuilder.ToString();
|
||||
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hashInput));
|
||||
|
||||
var hexBuilder = new StringBuilder();
|
||||
foreach (byte b in hashBytes)
|
||||
{
|
||||
hexBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return hexBuilder.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[通道哈希] 计算通道数据哈希失败: {ex.Message},使用时间戳标识");
|
||||
return $"CHANNEL_HASH_FAILED_{DateTime.Now.Ticks}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算边界范围哈希值
|
||||
/// </summary>
|
||||
/// <param name="bounds">边界范围</param>
|
||||
/// <returns>边界哈希</returns>
|
||||
private static string ComputeBoundsHash(BoundingBox3D bounds)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var boundsString = $"Bounds:{bounds.Min.X:F6},{bounds.Min.Y:F6},{bounds.Min.Z:F6}-" +
|
||||
$"{bounds.Max.X:F6},{bounds.Max.Y:F6},{bounds.Max.Z:F6}";
|
||||
|
||||
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(boundsString));
|
||||
|
||||
var hexBuilder = new StringBuilder();
|
||||
foreach (byte b in hashBytes)
|
||||
{
|
||||
hexBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return hexBuilder.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[边界哈希] 计算边界哈希失败: {ex.Message},使用时间戳标识");
|
||||
return $"BOUNDS_HASH_FAILED_{DateTime.Now.Ticks}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.Core;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
@ -60,7 +57,30 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("【生成网格地图】开始生成网格地图");
|
||||
// 生成缓存键
|
||||
var cacheKey = GridMapCacheKey.CreateFrom(document, bounds, cellSize, vehicleRadius, safetyMargin, vehicleHeight);
|
||||
|
||||
// 尝试从缓存获取
|
||||
var cachedGridMap = GlobalGridMapCache.Instance.Get(cacheKey, 5000); // 预估生成需要5秒
|
||||
if (cachedGridMap != null)
|
||||
{
|
||||
LogManager.Info($"【GridMap缓存】使用缓存网格地图 - {cacheKey}");
|
||||
LogManager.Info($"【GridMap缓存】缓存GridMap统计: {cachedGridMap.GetStatistics()}");
|
||||
|
||||
// 更新规划起终点信息(这些信息可能会变化,不影响核心网格结构)
|
||||
if (planningStartPoint != default(Point3D) && planningEndPoint != default(Point3D))
|
||||
{
|
||||
cachedGridMap.PlanningStartPoint = planningStartPoint;
|
||||
cachedGridMap.PlanningEndPoint = planningEndPoint;
|
||||
cachedGridMap.HasPlanningPoints = true;
|
||||
}
|
||||
|
||||
return cachedGridMap;
|
||||
}
|
||||
|
||||
// 缓存未命中,生成新的GridMap
|
||||
LogManager.Info("【生成网格地图】缓存未命中,开始生成新网格地图");
|
||||
LogManager.Info($"【生成网格地图】缓存键: {cacheKey}");
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
// 第一步:统一转换所有米制参数为模型单位
|
||||
@ -150,6 +170,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
|
||||
LogManager.Info($"【生成网格地图】网格地图生成完成: {channelCoverage.GridMap.GetStatistics()}");
|
||||
LogManager.Info($"【生成网格地图】总耗时: {elapsed:F1}ms");
|
||||
|
||||
// 将生成的GridMap添加到缓存
|
||||
GlobalGridMapCache.Instance.Put(cacheKey, channelCoverage.GridMap);
|
||||
LogManager.Info($"【GridMap缓存】网格地图已缓存,缓存键: {cacheKey}");
|
||||
|
||||
return channelCoverage.GridMap;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -159,6 +184,33 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取GridMap缓存统计信息
|
||||
/// </summary>
|
||||
/// <returns>缓存统计报告</returns>
|
||||
public static string GetCacheStatistics()
|
||||
{
|
||||
return GlobalGridMapCache.Instance.Statistics.GenerateReport();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取GridMap缓存详细统计报告
|
||||
/// </summary>
|
||||
/// <returns>详细统计报告</returns>
|
||||
public static string GetDetailedCacheReport()
|
||||
{
|
||||
return GlobalGridMapCache.Instance.GenerateDetailedReport();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除GridMap缓存
|
||||
/// </summary>
|
||||
public static void ClearCache()
|
||||
{
|
||||
GlobalGridMapCache.Instance.Clear();
|
||||
LogManager.Info("[GridMap缓存] 手动清除所有缓存");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理门元素,将其设置为可通行网格
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user