NavisworksTransport/src/PathPlanning/GridMapCacheKey.cs

238 lines
9.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 LogisticsDataHash { 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 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;
}
/// <summary>
/// 检查两个缓存键是否相等
/// </summary>
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;
}
/// <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 + (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;
}
}
/// <summary>
/// 生成可读的字符串表示
/// </summary>
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]";
}
/// <summary>
/// 根据参数生成GridMap缓存键
/// </summary>
/// <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(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);
}
/// <summary>
/// 计算物流数据哈希值
/// 基于所有物流属性构件包括通道、门、无关项等的ID、属性、几何等信息
/// </summary>
/// <returns>物流数据哈希</returns>
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}";
}
}
/// <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}";
}
}
}
}