对生成的网格地图进行了缓存,提高路径规划速度

This commit is contained in:
tian 2025-09-27 22:50:52 +08:00
parent c9b7acbd0a
commit cd74f857a2
7 changed files with 1207 additions and 12 deletions

View File

@ -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">

View File

@ -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()}");
}
}
}
}

View 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();
}
}
}

View 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}";
}
}
}
}

View 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();
}
}
}

View 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}";
}
}
}
}

View File

@ -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>