using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; using Autodesk.Navisworks.Api; namespace NavisworksTransport.PathPlanning { /// /// GridMap缓存键,用于标识唯一的GridMap生成配置 /// 基于所有物流属性构件、车辆尺寸、网格尺寸等关键参数 /// public class GridMapCacheKey : IEquatable { /// /// 物流数据哈希值(所有物流属性构件,包括通道、门、无关项等) /// public string LogisticsDataHash { get; set; } /// /// 边界范围哈希 /// public string BoundsHash { get; set; } /// /// 网格单元大小(模型单位) /// public double CellSize { get; set; } /// /// 车辆半径(米) /// public double VehicleRadius { get; set; } /// /// 安全间隙(米) /// public double SafetyMargin { get; set; } /// /// 车辆高度(米) /// public double VehicleHeight { get; set; } /// /// 构造函数 /// public GridMapCacheKey(string logisticsDataHash, string boundsHash, double cellSize, double vehicleRadius, double safetyMargin, double vehicleHeight) { LogisticsDataHash = logisticsDataHash ?? throw new ArgumentNullException(nameof(logisticsDataHash)); BoundsHash = boundsHash ?? throw new ArgumentNullException(nameof(boundsHash)); CellSize = cellSize; VehicleRadius = vehicleRadius; SafetyMargin = safetyMargin; VehicleHeight = vehicleHeight; } /// /// 检查两个缓存键是否相等 /// public bool Equals(GridMapCacheKey other) { if (other == null) return false; if (ReferenceEquals(this, other)) return true; return LogisticsDataHash == other.LogisticsDataHash && 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; } /// /// 重写Equals方法 /// public override bool Equals(object obj) { return Equals(obj as GridMapCacheKey); } /// /// 计算哈希码,用于Dictionary键 /// public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + (LogisticsDataHash?.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; } } /// /// 生成可读的字符串表示 /// public override string ToString() { var logisticsPreview = LogisticsDataHash != null ? LogisticsDataHash.Substring(0, Math.Min(8, LogisticsDataHash.Length)) : "NULL"; var boundsPreview = BoundsHash != null ? BoundsHash.Substring(0, Math.Min(8, BoundsHash.Length)) : "NULL"; return $"GridMapKey[Logistics={logisticsPreview}, Bounds={boundsPreview}, " + $"Cell={CellSize:F2}, VR={VehicleRadius:F1}m, SM={SafetyMargin:F1}m, VH={VehicleHeight:F1}m]"; } /// /// 根据参数生成GridMap缓存键 /// /// 边界范围 /// 网格大小 /// 车辆半径(米) /// 安全间隙(米) /// 车辆高度(米) /// 缓存键 public static GridMapCacheKey CreateFrom(BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, double vehicleHeight) { var logisticsDataHash = ComputeLogisticsDataHash(); var boundsHash = ComputeBoundsHash(bounds); return new GridMapCacheKey(logisticsDataHash, boundsHash, cellSize, vehicleRadius, safetyMargin, vehicleHeight); } /// /// 计算物流数据哈希值 /// 基于所有物流属性构件(包括通道、门、无关项等)的ID、属性、几何等信息 /// /// 物流数据哈希 private static string ComputeLogisticsDataHash() { try { using (var sha256 = SHA256.Create()) { var hashBuilder = new StringBuilder(); // 获取所有有物流属性的模型项(不仅仅是可通行项) var allLogisticsItems = CategoryAttributeManager.GetAllLogisticsItems(); // 按InstanceGuid排序确保一致性 var sortedItems = allLogisticsItems.OrderBy(item => item.InstanceGuid.ToString()).ToList(); hashBuilder.Append($"LogisticsCount:{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 $"LOGISTICS_HASH_FAILED_{DateTime.Now.Ticks}"; } } /// /// 计算边界范围哈希值 /// /// 边界范围 /// 边界哈希 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}"; } } } }