238 lines
9.2 KiB
C#
238 lines
9.2 KiB
C#
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}";
|
||
}
|
||
}
|
||
}
|
||
} |