实现自定义类别功能
This commit is contained in:
parent
76d277b6c2
commit
f9df83ba5b
@ -140,6 +140,7 @@
|
||||
<!-- Core - Configuration Management -->
|
||||
<Compile Include="src\Core\Config\SystemConfig.cs" />
|
||||
<Compile Include="src\Core\Config\ConfigManager.cs" />
|
||||
<Compile Include="src\Core\Config\CustomCategoryConfig.cs" />
|
||||
<Compile Include="src\Core\Database\BackupManager.cs" />
|
||||
<Compile Include="src\Core\Services\TimeTagCalculator.cs" />
|
||||
<Compile Include="src\Core\Services\TimeTagService.cs" />
|
||||
@ -476,6 +477,11 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\default_config.toml</Link>
|
||||
</None>
|
||||
<!-- Custom Categories Configuration File -->
|
||||
<None Include="resources\default_custom_categories.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\default_custom_categories.toml</Link>
|
||||
</None>
|
||||
<!-- Plugin Name File (in resources folder) -->
|
||||
<None Include="resources\TransportPlugin.name.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
77
resources/default_custom_categories.toml
Normal file
77
resources/default_custom_categories.toml
Normal file
@ -0,0 +1,77 @@
|
||||
# 自定义物流类别配置文件
|
||||
# 位置: %ProgramData%/Autodesk/Navisworks Manage 2026/plugins/TransportPlugin/custom_categories.toml
|
||||
# 说明: 本文件定义用户自定义的物流类别,与模型文件无关,可跨项目共享
|
||||
|
||||
# 类别属性说明:
|
||||
# id: 唯一标识(英文,用于存储和内部识别)
|
||||
# display_name: 显示名称(中文)
|
||||
# icon: 图标标识(可选,默认 Box)
|
||||
# traversable: 是否默认可通行(可选,默认 true)
|
||||
# priority: 优先级 1-10(可选,默认 5)
|
||||
# weight: 寻路权重(可选,默认 1.0,越低越优先)
|
||||
# color: UI显示颜色(可选,默认 #9E9E9E)
|
||||
# height_limit_meters: 默认高度限制米数(可选,默认 3.0)
|
||||
# speed_limit_meters_per_second: 默认速度限制米/秒(可选,默认 1.0)
|
||||
# width_limit_meters: 默认宽度限制米数(可选,默认 3.0)
|
||||
|
||||
# 示例自定义类别(可根据需要添加更多)
|
||||
|
||||
[[category]]
|
||||
id = "ramp"
|
||||
display_name = "坡道"
|
||||
icon = "Ramp"
|
||||
traversable = true
|
||||
priority = 4
|
||||
weight = 1.5
|
||||
color = "#FF9800"
|
||||
height_limit_meters = 3.0
|
||||
speed_limit_meters_per_second = 0.5
|
||||
width_limit_meters = 4.0
|
||||
|
||||
[[category]]
|
||||
id = "tunnel"
|
||||
display_name = "隧道"
|
||||
icon = "Tunnel"
|
||||
traversable = true
|
||||
priority = 3
|
||||
weight = 1.8
|
||||
color = "#795548"
|
||||
height_limit_meters = 2.5
|
||||
speed_limit_meters_per_second = 0.3
|
||||
width_limit_meters = 2.5
|
||||
|
||||
[[category]]
|
||||
id = "restricted_area"
|
||||
display_name = "限制区域"
|
||||
icon = "Warning"
|
||||
traversable = false
|
||||
priority = 1
|
||||
weight = 999999.0
|
||||
color = "#F44336"
|
||||
height_limit_meters = 0.0
|
||||
speed_limit_meters_per_second = 0.0
|
||||
width_limit_meters = 0.0
|
||||
|
||||
[[category]]
|
||||
id = "temporary_storage"
|
||||
display_name = "临时堆放区"
|
||||
icon = "Package"
|
||||
traversable = true
|
||||
priority = 6
|
||||
weight = 1.2
|
||||
color = "#FFC107"
|
||||
height_limit_meters = 5.0
|
||||
speed_limit_meters_per_second = 0.8
|
||||
width_limit_meters = 5.0
|
||||
|
||||
[[category]]
|
||||
id = "cleanroom"
|
||||
display_name = "洁净室"
|
||||
icon = "Shield"
|
||||
traversable = true
|
||||
priority = 2
|
||||
weight = 2.0
|
||||
color = "#E1F5FE"
|
||||
height_limit_meters = 3.5
|
||||
speed_limit_meters_per_second = 0.4
|
||||
width_limit_meters = 3.0
|
||||
619
src/Core/Config/CustomCategoryConfig.cs
Normal file
619
src/Core/Config/CustomCategoryConfig.cs
Normal file
@ -0,0 +1,619 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Tomlyn;
|
||||
using Tomlyn.Model;
|
||||
|
||||
namespace NavisworksTransport.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义物流类别配置
|
||||
/// 从用户级 TOML 配置文件加载,独立于模型数据库
|
||||
/// </summary>
|
||||
public class CustomCategoryConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 配置文件路径(用户级,与模型无关)
|
||||
/// </summary>
|
||||
public static string ConfigFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
|
||||
"Autodesk",
|
||||
"Navisworks Manage 2026",
|
||||
"plugins",
|
||||
"TransportPlugin",
|
||||
"custom_categories.toml"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置目录
|
||||
/// </summary>
|
||||
public static string ConfigDirectory => Path.GetDirectoryName(ConfigFilePath);
|
||||
|
||||
/// <summary>
|
||||
/// 自定义类别列表
|
||||
/// </summary>
|
||||
public List<CustomCategoryDefinition> Categories { get; set; } = new List<CustomCategoryDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有类别(包括内置和自定义)
|
||||
/// </summary>
|
||||
public IEnumerable<CategoryDefinition> GetAllCategories()
|
||||
{
|
||||
// 内置类别
|
||||
foreach (var builtIn in BuiltInCategories.All)
|
||||
yield return builtIn;
|
||||
|
||||
// 自定义类别
|
||||
foreach (var custom in Categories)
|
||||
yield return custom;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取类别定义
|
||||
/// </summary>
|
||||
public CategoryDefinition GetCategory(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
return null;
|
||||
|
||||
// 先查内置
|
||||
var builtIn = BuiltInCategories.GetById(id);
|
||||
if (builtIn != null)
|
||||
return builtIn;
|
||||
|
||||
// 再查自定义
|
||||
return Categories.FirstOrDefault(c =>
|
||||
c.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否为内置类别
|
||||
/// </summary>
|
||||
public bool IsBuiltIn(string id)
|
||||
{
|
||||
return BuiltInCategories.GetById(id) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查类别是否存在
|
||||
/// </summary>
|
||||
public bool CategoryExists(string id)
|
||||
{
|
||||
return GetCategory(id) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 TOML 文件加载配置
|
||||
/// </summary>
|
||||
public static CustomCategoryConfig LoadFromFile()
|
||||
{
|
||||
var config = new CustomCategoryConfig();
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(ConfigFilePath))
|
||||
{
|
||||
// 配置文件不存在,创建默认配置
|
||||
config = CreateDefaultConfig();
|
||||
config.SaveToFile();
|
||||
return config;
|
||||
}
|
||||
|
||||
var tomlContent = File.ReadAllText(ConfigFilePath);
|
||||
var tomlTable = Toml.ToModel(tomlContent);
|
||||
|
||||
if (tomlTable.TryGetValue("category", out var categoriesObj) &&
|
||||
categoriesObj is TomlTableArray categoriesArray)
|
||||
{
|
||||
foreach (var categoryTable in categoriesArray)
|
||||
{
|
||||
var category = ParseCategoryFromToml(categoryTable);
|
||||
if (category != null)
|
||||
config.Categories.Add(category);
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[CustomCategoryConfig] 已加载 {config.Categories.Count} 个自定义类别");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[CustomCategoryConfig] 加载配置失败: {ex.Message}");
|
||||
// 加载失败时返回空配置
|
||||
config = new CustomCategoryConfig();
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存配置到 TOML 文件
|
||||
/// </summary>
|
||||
public void SaveToFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(ConfigDirectory);
|
||||
var tomlContent = GenerateTomlContent();
|
||||
File.WriteAllText(ConfigFilePath, tomlContent);
|
||||
LogManager.Info($"[CustomCategoryConfig] 配置已保存: {ConfigFilePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[CustomCategoryConfig] 保存配置失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 默认配置文件路径(插件资源目录)
|
||||
/// </summary>
|
||||
public static string DefaultConfigFilePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(
|
||||
Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
|
||||
"resources",
|
||||
"default_custom_categories.toml"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认配置
|
||||
/// 尝试从默认配置文件加载示例,如果失败则返回空配置
|
||||
/// </summary>
|
||||
private static CustomCategoryConfig CreateDefaultConfig()
|
||||
{
|
||||
var config = new CustomCategoryConfig
|
||||
{
|
||||
Categories = new List<CustomCategoryDefinition>()
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试从默认配置文件加载示例
|
||||
if (File.Exists(DefaultConfigFilePath))
|
||||
{
|
||||
var tomlContent = File.ReadAllText(DefaultConfigFilePath);
|
||||
var tomlTable = Toml.ToModel(tomlContent);
|
||||
|
||||
if (tomlTable.TryGetValue("category", out var categoriesObj) &&
|
||||
categoriesObj is TomlTableArray categoriesArray)
|
||||
{
|
||||
foreach (var categoryTable in categoriesArray)
|
||||
{
|
||||
var category = ParseCategoryFromToml(categoryTable);
|
||||
if (category != null)
|
||||
config.Categories.Add(category);
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[CustomCategoryConfig] 已从默认配置文件加载 {config.Categories.Count} 个示例类别");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[CustomCategoryConfig] 从默认配置文件加载失败: {ex.Message},将使用空配置");
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 TOML 表解析类别定义
|
||||
/// </summary>
|
||||
private static CustomCategoryDefinition ParseCategoryFromToml(TomlTable table)
|
||||
{
|
||||
try
|
||||
{
|
||||
var category = new CustomCategoryDefinition
|
||||
{
|
||||
Id = table.GetString("id"),
|
||||
DisplayName = table.GetString("display_name"),
|
||||
Icon = table.GetString("icon", "Box"),
|
||||
Traversable = table.GetBool("traversable", true),
|
||||
Priority = table.GetInt("priority", 5),
|
||||
Weight = table.GetDouble("weight", 1.0),
|
||||
DisplayColor = ParseColor(table.GetString("color", "#9E9E9E")),
|
||||
Defaults = new CategoryDefaultProperties
|
||||
{
|
||||
HeightLimitMeters = table.GetDouble("height_limit_meters", 3.0),
|
||||
SpeedLimitMetersPerSecond = table.GetDouble("speed_limit_meters_per_second", 1.0),
|
||||
WidthLimitMeters = table.GetDouble("width_limit_meters", 3.0)
|
||||
}
|
||||
};
|
||||
|
||||
// 验证必填字段
|
||||
if (string.IsNullOrEmpty(category.Id) || string.IsNullOrEmpty(category.DisplayName))
|
||||
{
|
||||
LogManager.Warning("[CustomCategoryConfig] 类别定义缺少必填字段 id 或 display_name");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查ID是否与内置类别冲突
|
||||
if (BuiltInCategories.GetById(category.Id) != null)
|
||||
{
|
||||
LogManager.Warning($"[CustomCategoryConfig] 自定义类别ID '{category.Id}' 与内置类别冲突,已跳过");
|
||||
return null;
|
||||
}
|
||||
|
||||
return category;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[CustomCategoryConfig] 解析类别定义失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成 TOML 内容
|
||||
/// </summary>
|
||||
private string GenerateTomlContent()
|
||||
{
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("# 自定义物流类别配置文件");
|
||||
sb.AppendLine("# 位置: %ProgramData%/Autodesk/Navisworks Manage 2026/plugins/TransportPlugin/custom_categories.toml");
|
||||
sb.AppendLine("# 说明: 本文件定义用户自定义的物流类别,与模型文件无关,可跨项目共享");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("# 类别属性说明:");
|
||||
sb.AppendLine("# id: 唯一标识(英文,用于存储和内部识别)");
|
||||
sb.AppendLine("# display_name: 显示名称(中文)");
|
||||
sb.AppendLine("# icon: 图标标识(可选,默认 Box)");
|
||||
sb.AppendLine("# traversable: 是否默认可通行(可选,默认 true)");
|
||||
sb.AppendLine("# priority: 优先级 1-10(可选,默认 5)");
|
||||
sb.AppendLine("# weight: 寻路权重(可选,默认 1.0,越低越优先)");
|
||||
sb.AppendLine("# color: UI显示颜色(可选,默认 #9E9E9E)");
|
||||
sb.AppendLine("# height_limit_meters: 默认高度限制米数(可选,默认 3.0)");
|
||||
sb.AppendLine("# speed_limit_meters_per_second: 默认速度限制米/秒(可选,默认 1.0)");
|
||||
sb.AppendLine("# width_limit_meters: 默认宽度限制米数(可选,默认 3.0)");
|
||||
sb.AppendLine();
|
||||
|
||||
foreach (var category in Categories)
|
||||
{
|
||||
sb.AppendLine("[[category]]");
|
||||
sb.AppendLine($"id = \"{EscapeTomlString(category.Id)}\"");
|
||||
sb.AppendLine($"display_name = \"{EscapeTomlString(category.DisplayName)}\"");
|
||||
sb.AppendLine($"icon = \"{EscapeTomlString(category.Icon)}\"");
|
||||
sb.AppendLine($"traversable = {category.Traversable.ToString().ToLower()}");
|
||||
sb.AppendLine($"priority = {category.Priority}");
|
||||
sb.AppendLine($"weight = {category.Weight:F2}");
|
||||
sb.AppendLine($"color = \"{ColorToHex(category.DisplayColor)}\"");
|
||||
sb.AppendLine($"height_limit_meters = {category.Defaults.HeightLimitMeters:F2}");
|
||||
sb.AppendLine($"speed_limit_meters_per_second = {category.Defaults.SpeedLimitMetersPerSecond:F2}");
|
||||
sb.AppendLine($"width_limit_meters = {category.Defaults.WidthLimitMeters:F2}");
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析颜色字符串
|
||||
/// </summary>
|
||||
private static Color ParseColor(string colorStr)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(colorStr))
|
||||
return Color.Gray;
|
||||
|
||||
// 支持 #RRGGBB 或 #AARRGGBB 格式
|
||||
if (colorStr.StartsWith("#"))
|
||||
colorStr = colorStr.Substring(1);
|
||||
|
||||
if (colorStr.Length == 6)
|
||||
{
|
||||
int r = int.Parse(colorStr.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
int g = int.Parse(colorStr.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
int b = int.Parse(colorStr.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
return Color.FromArgb(r, g, b);
|
||||
}
|
||||
else if (colorStr.Length == 8)
|
||||
{
|
||||
int a = int.Parse(colorStr.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
int r = int.Parse(colorStr.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
int g = int.Parse(colorStr.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
int b = int.Parse(colorStr.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
|
||||
return Color.FromArgb(a, r, g, b);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return Color.Gray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 颜色转十六进制字符串
|
||||
/// </summary>
|
||||
private static string ColorToHex(Color color)
|
||||
{
|
||||
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转义 TOML 字符串
|
||||
/// </summary>
|
||||
private static string EscapeTomlString(string str)
|
||||
{
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return "";
|
||||
return str.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 类别定义基类(内置和自定义共用接口)
|
||||
/// </summary>
|
||||
public abstract class CategoryDefinition
|
||||
{
|
||||
public abstract string Id { get; set; }
|
||||
public abstract string DisplayName { get; set; }
|
||||
public abstract string Icon { get; set; }
|
||||
public abstract bool Traversable { get; set; }
|
||||
public abstract int Priority { get; set; }
|
||||
public abstract double Weight { get; set; }
|
||||
public abstract Color DisplayColor { get; set; }
|
||||
public abstract CategoryDefaultProperties Defaults { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义类别定义
|
||||
/// </summary>
|
||||
public class CustomCategoryDefinition : CategoryDefinition
|
||||
{
|
||||
public override string Id { get; set; }
|
||||
public override string DisplayName { get; set; }
|
||||
public override string Icon { get; set; } = "Box";
|
||||
public override bool Traversable { get; set; } = true;
|
||||
public override int Priority { get; set; } = 5;
|
||||
public override double Weight { get; set; } = 1.0;
|
||||
public override Color DisplayColor { get; set; } = Color.Gray;
|
||||
public override CategoryDefaultProperties Defaults { get; set; } = new CategoryDefaultProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 类别默认属性
|
||||
/// </summary>
|
||||
public class CategoryDefaultProperties
|
||||
{
|
||||
public double HeightLimitMeters { get; set; } = 3.0;
|
||||
public double SpeedLimitMetersPerSecond { get; set; } = 1.0;
|
||||
public double WidthLimitMeters { get; set; } = 3.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内置类别定义(包装 LogisticsElementType 枚举)
|
||||
/// </summary>
|
||||
public class BuiltInCategoryDefinition : CategoryDefinition
|
||||
{
|
||||
private readonly CategoryAttributeManager.LogisticsElementType _elementType;
|
||||
|
||||
public BuiltInCategoryDefinition(CategoryAttributeManager.LogisticsElementType elementType)
|
||||
{
|
||||
_elementType = elementType;
|
||||
}
|
||||
|
||||
public override string Id { get => _elementType.ToString(); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override string DisplayName { get => _elementType.ToString(); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override string Icon { get => GetIconForType(_elementType); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override bool Traversable { get => GetTraversableForType(_elementType); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override int Priority { get => GetPriorityForType(_elementType); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override double Weight { get => GetWeightForType(_elementType); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override Color DisplayColor { get => GetColorForType(_elementType); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
public override CategoryDefaultProperties Defaults { get => GetDefaultsForType(_elementType); set => throw new NotSupportedException("内置类别不支持修改"); }
|
||||
|
||||
public CategoryAttributeManager.LogisticsElementType ElementType => _elementType;
|
||||
|
||||
private static string GetIconForType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物: return "BlockHelper";
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板: return "Rectangle";
|
||||
case CategoryAttributeManager.LogisticsElementType.门: return "Door";
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯: return "Elevator";
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯: return "Stairs";
|
||||
case CategoryAttributeManager.LogisticsElementType.通道: return "Road";
|
||||
case CategoryAttributeManager.LogisticsElementType.走廊: return "Corridor";
|
||||
case CategoryAttributeManager.LogisticsElementType.装卸区: return "Truck";
|
||||
case CategoryAttributeManager.LogisticsElementType.停车位: return "Parking";
|
||||
case CategoryAttributeManager.LogisticsElementType.空轨: return "Air";
|
||||
case CategoryAttributeManager.LogisticsElementType.无关项: return "EyeOff";
|
||||
default: return "Box";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool GetTraversableForType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物:
|
||||
case CategoryAttributeManager.LogisticsElementType.Unknown:
|
||||
case CategoryAttributeManager.LogisticsElementType.无关项:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetPriorityForType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.通道: return 1;
|
||||
case CategoryAttributeManager.LogisticsElementType.走廊: return 2;
|
||||
case CategoryAttributeManager.LogisticsElementType.装卸区: return 3;
|
||||
case CategoryAttributeManager.LogisticsElementType.停车位: return 4;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板: return 5;
|
||||
case CategoryAttributeManager.LogisticsElementType.空轨: return 6;
|
||||
case CategoryAttributeManager.LogisticsElementType.门: return 7;
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯: return 8;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯: return 9;
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物: return 10;
|
||||
default: return 5;
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetWeightForType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.通道: return 0.5;
|
||||
case CategoryAttributeManager.LogisticsElementType.走廊: return 0.6;
|
||||
case CategoryAttributeManager.LogisticsElementType.空轨: return 0.7;
|
||||
case CategoryAttributeManager.LogisticsElementType.装卸区: return 0.8;
|
||||
case CategoryAttributeManager.LogisticsElementType.停车位: return 0.9;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板: return 1.0;
|
||||
case CategoryAttributeManager.LogisticsElementType.门: return 1.2;
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯: return 2.0;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯: return 3.0;
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物:
|
||||
case CategoryAttributeManager.LogisticsElementType.Unknown:
|
||||
return double.MaxValue;
|
||||
default: return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Color GetColorForType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.通道: return Color.FromArgb(76, 175, 80); // Green
|
||||
case CategoryAttributeManager.LogisticsElementType.走廊: return Color.FromArgb(129, 199, 132); // Light Green
|
||||
case CategoryAttributeManager.LogisticsElementType.门: return Color.FromArgb(33, 150, 243); // Blue
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯: return Color.FromArgb(156, 39, 176); // Purple
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯: return Color.FromArgb(255, 87, 34); // Deep Orange
|
||||
case CategoryAttributeManager.LogisticsElementType.装卸区: return Color.FromArgb(255, 152, 0); // Orange
|
||||
case CategoryAttributeManager.LogisticsElementType.停车位: return Color.FromArgb(0, 150, 136); // Teal
|
||||
case CategoryAttributeManager.LogisticsElementType.空轨: return Color.FromArgb(63, 81, 181); // Indigo
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物: return Color.FromArgb(244, 67, 54); // Red
|
||||
case CategoryAttributeManager.LogisticsElementType.无关项: return Color.FromArgb(158, 158, 158); // Gray
|
||||
default: return Color.FromArgb(96, 125, 139); // Blue Gray
|
||||
}
|
||||
}
|
||||
|
||||
private static CategoryDefaultProperties GetDefaultsForType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
var defaults = new CategoryDefaultProperties();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.门:
|
||||
defaults.HeightLimitMeters = 2.5;
|
||||
defaults.SpeedLimitMetersPerSecond = 0.5;
|
||||
defaults.WidthLimitMeters = 2.0;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯:
|
||||
defaults.HeightLimitMeters = 3.0;
|
||||
defaults.SpeedLimitMetersPerSecond = 0.3;
|
||||
defaults.WidthLimitMeters = 2.5;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯:
|
||||
defaults.HeightLimitMeters = 2.8;
|
||||
defaults.SpeedLimitMetersPerSecond = 0.2;
|
||||
defaults.WidthLimitMeters = 1.5;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.通道:
|
||||
defaults.HeightLimitMeters = 4.0;
|
||||
defaults.SpeedLimitMetersPerSecond = 1.5;
|
||||
defaults.WidthLimitMeters = 4.0;
|
||||
break;
|
||||
// 其他使用默认值
|
||||
}
|
||||
|
||||
return defaults;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内置类别管理器
|
||||
/// </summary>
|
||||
public static class BuiltInCategories
|
||||
{
|
||||
private static readonly Dictionary<string, BuiltInCategoryDefinition> _categories;
|
||||
|
||||
static BuiltInCategories()
|
||||
{
|
||||
_categories = new Dictionary<string, BuiltInCategoryDefinition>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (CategoryAttributeManager.LogisticsElementType type in
|
||||
Enum.GetValues(typeof(CategoryAttributeManager.LogisticsElementType)))
|
||||
{
|
||||
var definition = new BuiltInCategoryDefinition(type);
|
||||
_categories[type.ToString()] = definition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有内置类别
|
||||
/// </summary>
|
||||
public static IEnumerable<BuiltInCategoryDefinition> All => _categories.Values;
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取内置类别
|
||||
/// </summary>
|
||||
public static BuiltInCategoryDefinition GetById(string id)
|
||||
{
|
||||
_categories.TryGetValue(id ?? "", out var category);
|
||||
return category;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据枚举类型获取定义
|
||||
/// </summary>
|
||||
public static BuiltInCategoryDefinition GetByType(CategoryAttributeManager.LogisticsElementType type)
|
||||
{
|
||||
return GetById(type.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TOML 扩展方法
|
||||
/// </summary>
|
||||
internal static class TomlExtensions
|
||||
{
|
||||
public static string GetString(this TomlTable table, string key, string defaultValue = "")
|
||||
{
|
||||
if (table.TryGetValue(key, out var value) && value is string str)
|
||||
return str;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static bool GetBool(this TomlTable table, string key, bool defaultValue = false)
|
||||
{
|
||||
if (table.TryGetValue(key, out var value) && value is bool b)
|
||||
return b;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static int GetInt(this TomlTable table, string key, int defaultValue = 0)
|
||||
{
|
||||
if (table.TryGetValue(key, out var value))
|
||||
{
|
||||
if (value is long l) return (int)l;
|
||||
if (value is int i) return i;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static double GetDouble(this TomlTable table, string key, double defaultValue = 0.0)
|
||||
{
|
||||
if (table.TryGetValue(key, out var value))
|
||||
{
|
||||
if (value is double d) return d;
|
||||
if (value is long l) return l;
|
||||
if (value is int i) return i;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1369,7 +1369,7 @@ namespace NavisworksTransport
|
||||
if (document != null)
|
||||
{
|
||||
var allLogisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
var channelItems = CategoryAttributeManager.FilterByLogisticsType(allLogisticsItems, CategoryAttributeManager.LogisticsElementType.通道);
|
||||
var channelItems = CategoryAttributeManager.FilterByLogisticsType(allLogisticsItems, "通道");
|
||||
_walkableAreas.AddRange(channelItems);
|
||||
LogManager.Info($"[SelectChannels] 通过CategoryAttributeManager直接筛选到 {channelItems.Count} 个通道");
|
||||
}
|
||||
@ -4639,7 +4639,7 @@ namespace NavisworksTransport
|
||||
if (layer.IsWalkable)
|
||||
{
|
||||
// 可通行层 - 门特殊处理,其他统一样式
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.门)
|
||||
if (cell.CellType == "门")
|
||||
{
|
||||
if (_showDoorGrid)
|
||||
{
|
||||
@ -4693,7 +4693,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
|
||||
else if (cell.CellType == "Unknown")
|
||||
{
|
||||
// Unknown网格没有高度层,用于调试
|
||||
if (_showUnknownGrid)
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.PathPlanning;
|
||||
using NavisworksTransport.Core.Config;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
@ -186,7 +187,6 @@ namespace NavisworksTransport
|
||||
return successCount;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查模型项是否已有物流属性
|
||||
/// </summary>
|
||||
@ -284,6 +284,44 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定物流元素类型的所有模型项(字符串版本,支持自定义类别)
|
||||
/// </summary>
|
||||
/// <param name="typeId">类型ID(内置类别名称或自定义类别ID)</param>
|
||||
/// <returns>符合条件的模型项列表</returns>
|
||||
public static List<ModelItem> GetLogisticsItemsByType(string typeId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 Search API 搜索当前活动文档
|
||||
using (var search = new Search())
|
||||
{
|
||||
// 搜索整个文档的根项及其所有后代
|
||||
search.Selection.SelectAll();
|
||||
search.Locations = SearchLocations.DescendantsAndSelf;
|
||||
|
||||
var searchConditions = search.SearchConditions;
|
||||
searchConditions.Clear();
|
||||
|
||||
// 搜索条件:必须有物流分类
|
||||
searchConditions.Add(SearchCondition.HasCategoryByDisplayName(LogisticsCategories.LOGISTICS));
|
||||
|
||||
// 搜索条件:类型属性等于指定值
|
||||
searchConditions.Add(
|
||||
SearchCondition.HasPropertyByDisplayName(LogisticsCategories.LOGISTICS, LogisticsProperties.TYPE)
|
||||
.EqualValue(VariantData.FromDisplayString(typeId)));
|
||||
|
||||
// 执行搜索并返回列表
|
||||
return search.FindAll(Application.ActiveDocument, false).ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[CategoryAttributeManager] 获取物流项目失败: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据物流属性筛选模型项
|
||||
/// </summary>
|
||||
@ -333,6 +371,55 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按物流元素类型筛选模型项(字符串版本,支持自定义类别)
|
||||
/// </summary>
|
||||
/// <param name="items">要筛选的模型项集合</param>
|
||||
/// <param name="typeId">类型ID(内置类别名称或自定义类别ID)</param>
|
||||
/// <returns>符合条件的模型项集合</returns>
|
||||
public static ModelItemCollection FilterByLogisticsType(ModelItemCollection items, string typeId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用SearchAPI优化:直接搜索具有指定类型值的物流项目
|
||||
using (var search = new Search())
|
||||
{
|
||||
// 将输入的ModelItemCollection转换为搜索选择
|
||||
search.Selection.CopyFrom(items);
|
||||
search.Locations = SearchLocations.DescendantsAndSelf;
|
||||
|
||||
var searchConditions = search.SearchConditions;
|
||||
searchConditions.Clear();
|
||||
|
||||
// 搜索条件:必须有物流分类
|
||||
searchConditions.Add(SearchCondition.HasCategoryByDisplayName(LogisticsCategories.LOGISTICS));
|
||||
|
||||
// 搜索条件:类型属性等于指定值
|
||||
searchConditions.Add(
|
||||
SearchCondition.HasPropertyByDisplayName(LogisticsCategories.LOGISTICS, LogisticsProperties.TYPE)
|
||||
.EqualValue(VariantData.FromDisplayString(typeId)));
|
||||
|
||||
return search.FindAll(Application.ActiveDocument, false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"按物流类型过滤失败: {ex.Message}");
|
||||
|
||||
// 如果SearchAPI失败,回退到原始实现
|
||||
ModelItemCollection filteredItems = new ModelItemCollection();
|
||||
foreach (ModelItem item in items)
|
||||
{
|
||||
string typeValue = GetLogisticsPropertyValue(item, LogisticsProperties.TYPE);
|
||||
if (typeValue == typeId)
|
||||
{
|
||||
filteredItems.Add(item);
|
||||
}
|
||||
}
|
||||
return filteredItems;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 筛选可通行的模型项
|
||||
/// </summary>
|
||||
@ -985,6 +1072,199 @@ namespace NavisworksTransport
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#region 自定义类别支持
|
||||
|
||||
private static Core.Config.CustomCategoryConfig _customCategoryConfig;
|
||||
private static readonly object _customCategoryLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 自定义类别配置(懒加载)
|
||||
/// </summary>
|
||||
public static Core.Config.CustomCategoryConfig CustomCategories
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_customCategoryConfig == null)
|
||||
{
|
||||
lock (_customCategoryLock)
|
||||
{
|
||||
if (_customCategoryConfig == null)
|
||||
{
|
||||
_customCategoryConfig = Core.Config.CustomCategoryConfig.LoadFromFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
return _customCategoryConfig;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新加载自定义类别配置
|
||||
/// </summary>
|
||||
public static void ReloadCustomCategories()
|
||||
{
|
||||
lock (_customCategoryLock)
|
||||
{
|
||||
_customCategoryConfig = Core.Config.CustomCategoryConfig.LoadFromFile();
|
||||
LogManager.Info("[CategoryAttributeManager] 自定义类别配置已重新加载");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模型项的物流元素类型(字符串形式,支持自定义类别)
|
||||
/// </summary>
|
||||
/// <param name="item">模型项</param>
|
||||
/// <returns>类别ID字符串,如果没有设置则返回"障碍物"</returns>
|
||||
public static string GetCategoryId(ModelItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
string typeValue = GetLogisticsPropertyValue(item, LogisticsProperties.TYPE);
|
||||
if (!string.IsNullOrEmpty(typeValue))
|
||||
{
|
||||
// 先检查是否为内置类别
|
||||
if (Enum.TryParse<LogisticsElementType>(typeValue, out _))
|
||||
{
|
||||
return typeValue;
|
||||
}
|
||||
|
||||
// 再检查是否为自定义类别
|
||||
if (CustomCategories.CategoryExists(typeValue))
|
||||
{
|
||||
return typeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[CategoryAttributeManager] 获取类别ID失败: {ex.Message}");
|
||||
}
|
||||
|
||||
// 默认返回障碍物
|
||||
return LogisticsElementType.障碍物.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取类别定义(内置或自定义)
|
||||
/// </summary>
|
||||
/// <param name="categoryId">类别ID</param>
|
||||
/// <returns>类别定义,如果不存在返回null</returns>
|
||||
public static Core.Config.CategoryDefinition GetCategoryDefinition(string categoryId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(categoryId))
|
||||
return null;
|
||||
|
||||
// 先查内置
|
||||
if (Enum.TryParse<LogisticsElementType>(categoryId, out var builtInType))
|
||||
{
|
||||
return Core.Config.BuiltInCategories.GetByType(builtInType);
|
||||
}
|
||||
|
||||
// 再查自定义
|
||||
return CustomCategories.GetCategory(categoryId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取类别的寻路权重
|
||||
/// </summary>
|
||||
/// <param name="categoryId">类别ID</param>
|
||||
/// <returns>权重值,未知类别返回1.0</returns>
|
||||
public static double GetCategoryWeight(string categoryId)
|
||||
{
|
||||
var definition = GetCategoryDefinition(categoryId);
|
||||
return definition?.Weight ?? 1.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查类别是否可通行
|
||||
/// </summary>
|
||||
/// <param name="categoryId">类别ID</param>
|
||||
/// <returns>是否可通行</returns>
|
||||
public static bool IsCategoryTraversable(string categoryId)
|
||||
{
|
||||
var definition = GetCategoryDefinition(categoryId);
|
||||
return definition?.Traversable ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有可用类别(内置 + 自定义)
|
||||
/// </summary>
|
||||
public static IEnumerable<Core.Config.CategoryDefinition> GetAllAvailableCategories()
|
||||
{
|
||||
return CustomCategories.GetAllCategories();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否为自定义类别
|
||||
/// </summary>
|
||||
public static bool IsCustomCategory(string categoryId)
|
||||
{
|
||||
return !string.IsNullOrEmpty(categoryId) &&
|
||||
!Enum.TryParse<LogisticsElementType>(categoryId, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为选中的模型项添加物流属性(支持自定义类别)
|
||||
/// </summary>
|
||||
public static int AddLogisticsAttributes(
|
||||
ModelItemCollection items,
|
||||
string categoryId,
|
||||
bool isTraversable,
|
||||
int priority,
|
||||
double heightLimit,
|
||||
double speedLimit,
|
||||
double widthLimit)
|
||||
{
|
||||
if (items == null || items.Count == 0)
|
||||
return 0;
|
||||
|
||||
if (string.IsNullOrEmpty(categoryId))
|
||||
{
|
||||
LogManager.Warning("[CategoryAttributeManager] 类别ID为空,无法设置属性");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 准备属性字典
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
{ LogisticsProperties.TYPE, categoryId },
|
||||
{ LogisticsProperties.TRAVERSABLE, isTraversable ? "是" : "否" },
|
||||
{ LogisticsProperties.PRIORITY, priority.ToString() },
|
||||
{ LogisticsProperties.HEIGHT_LIMIT, heightLimit.ToString("F2") + " m" },
|
||||
{ LogisticsProperties.SPEED_LIMIT, speedLimit.ToString("F2") + " m/s" },
|
||||
{ LogisticsProperties.WIDTH_LIMIT, widthLimit.ToString("F2") + " m" }
|
||||
};
|
||||
|
||||
// 准备内部名称字典
|
||||
var propertyInternalNames = new Dictionary<string, string>
|
||||
{
|
||||
{ LogisticsProperties.TYPE, categoryId + "_Internal" },
|
||||
{ LogisticsProperties.TRAVERSABLE, "Traversable_Internal" },
|
||||
{ LogisticsProperties.PRIORITY, "Priority_Internal" },
|
||||
{ LogisticsProperties.HEIGHT_LIMIT, "HeightLimit_Internal" },
|
||||
{ LogisticsProperties.SPEED_LIMIT, "SpeedLimit_Internal" },
|
||||
{ LogisticsProperties.WIDTH_LIMIT, "WidthLimit_Internal" }
|
||||
};
|
||||
|
||||
int successCount = NavisworksComPropertyManager.SetUserDefinedProperties(
|
||||
items,
|
||||
LogisticsCategories.LOGISTICS,
|
||||
LogisticsCategories.CATEGORY_INTERNAL_NAME,
|
||||
properties,
|
||||
propertyInternalNames);
|
||||
|
||||
LogManager.Info($"[属性添加] 添加操作完成,类别: {categoryId}, 成功添加 {successCount} 个模型的属性");
|
||||
|
||||
// 如果是空轨类型,预计算基准路径
|
||||
if (categoryId == "空轨" && successCount > 0)
|
||||
{
|
||||
PreCalculateRailBaselinePaths(items);
|
||||
}
|
||||
|
||||
return successCount;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1286,5 +1566,6 @@ namespace NavisworksTransport
|
||||
|
||||
return passableAreas;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
public int Y { get; set; }
|
||||
public int LayerIndex { get; set; }
|
||||
public double Z { get; set; }
|
||||
public CategoryAttributeManager.LogisticsElementType CellType { get; set; }
|
||||
public string CellType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -219,8 +219,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
continue;
|
||||
|
||||
// 楼梯/电梯区域跳过层0(下方楼板,不是可行走表面)
|
||||
if (li == 0 && (layer.Type == CategoryAttributeManager.LogisticsElementType.楼梯 ||
|
||||
layer.Type == CategoryAttributeManager.LogisticsElementType.电梯))
|
||||
if (li == 0 && (layer.Type == "楼梯" ||
|
||||
layer.Type == "电梯"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -334,8 +334,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
// 检查该网格是否有任何楼梯或电梯层(允许垂直移动)
|
||||
bool hasStairsOrElevator = cell.HeightLayers.Any(layer =>
|
||||
layer.Type == CategoryAttributeManager.LogisticsElementType.楼梯 ||
|
||||
layer.Type == CategoryAttributeManager.LogisticsElementType.电梯);
|
||||
layer.Type == "楼梯" ||
|
||||
layer.Type == "电梯");
|
||||
|
||||
if (!hasStairsOrElevator || cell.HeightLayers.Count < 2)
|
||||
continue;
|
||||
@ -352,8 +352,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
continue;
|
||||
|
||||
// 垂直边只在楼梯/电梯类型之间创建
|
||||
if (layer1.Type != CategoryAttributeManager.LogisticsElementType.楼梯 &&
|
||||
layer1.Type != CategoryAttributeManager.LogisticsElementType.电梯)
|
||||
if (layer1.Type != "楼梯" &&
|
||||
layer1.Type != "电梯")
|
||||
continue;
|
||||
|
||||
if (layer1.PassableHeight.GetSpan() < objectHeight ||
|
||||
@ -369,7 +369,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
continue;
|
||||
|
||||
// 垂直移动速度:电梯较快,楼梯较慢
|
||||
float verticalSpeed = layer1.Type == CategoryAttributeManager.LogisticsElementType.电梯
|
||||
float verticalSpeed = layer1.Type == "电梯"
|
||||
? (float)(baseSpeed * 0.5)
|
||||
: (float)(baseSpeed * 0.3);
|
||||
maxSpeed = Math.Max(maxSpeed, verticalSpeed);
|
||||
@ -1568,7 +1568,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
? endCell.Value.HeightLayers[0].Z
|
||||
: 0;
|
||||
|
||||
if (endCell.Value.CellType == CategoryAttributeManager.LogisticsElementType.门)
|
||||
if (endCell.Value.CellType == "门")
|
||||
{
|
||||
LogManager.Info($"[路径转换-门] 门网格({endGridPos.X},{endGridPos.Y}) HeightLayer[0].Z={endZ:F3}, 位置({endWorldPos.X:F2},{endWorldPos.Y:F2})");
|
||||
}
|
||||
@ -2483,7 +2483,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
checkedPoints++;
|
||||
|
||||
// 特别标记门网格点
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.门)
|
||||
if (cell.CellType == "门")
|
||||
{
|
||||
doorPoints++;
|
||||
LogManager.Info($"[路径高度验证] 检查路径点{i}(门网格): ({point.X:F2}, {point.Y:F2}, {point.Z:F2}) -> 网格({gridPos.X},{gridPos.Y})");
|
||||
@ -2914,9 +2914,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
distanceMap[x, y] = isUnsafeArea ? 0 : int.MaxValue;
|
||||
|
||||
// 统计网格类型
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.障碍物)
|
||||
if (cell.CellType == "障碍物")
|
||||
obstacleCount++;
|
||||
else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
|
||||
else if (cell.CellType == "Unknown")
|
||||
unknownCount++;
|
||||
else if (cell.HasAnyWalkableLayer())
|
||||
walkableCount++;
|
||||
|
||||
@ -306,7 +306,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
double gridCenterElevation = GetElevationAtPoint(triangle, worldPos);
|
||||
|
||||
// 创建高度层(追加模式,不覆盖)
|
||||
var channelType = CategoryAttributeManager.GetLogisticsElementType(channel);
|
||||
var channelType = CategoryAttributeManager.GetCategoryId(channel);
|
||||
var layer = new HeightLayer(
|
||||
z: gridCenterElevation,
|
||||
passableHeight: new HeightInterval(0, 0), // 初始化,后续由SetChannelPassableHeights设置
|
||||
|
||||
@ -20,7 +20,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
return new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道,
|
||||
CellType = "通道",
|
||||
IsInChannel = true,
|
||||
ChannelType = ChannelType.Corridor,
|
||||
RelatedModelItem = channel,
|
||||
@ -42,7 +42,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
return new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.门,
|
||||
CellType = "门",
|
||||
IsInChannel = false,
|
||||
ChannelType = ChannelType.Other,
|
||||
RelatedModelItem = door,
|
||||
@ -62,7 +62,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
return new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.障碍物,
|
||||
CellType = "障碍物",
|
||||
IsInChannel = false,
|
||||
ChannelType = ChannelType.Other,
|
||||
RelatedModelItem = obstacle,
|
||||
@ -84,7 +84,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
return new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.电梯,
|
||||
CellType = "电梯",
|
||||
IsInChannel = false,
|
||||
ChannelType = ChannelType.Other,
|
||||
RelatedModelItem = elevator,
|
||||
@ -106,7 +106,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
return new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.楼梯,
|
||||
CellType = "楼梯",
|
||||
IsInChannel = false,
|
||||
ChannelType = ChannelType.Other,
|
||||
RelatedModelItem = stairs,
|
||||
@ -125,7 +125,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
return new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.Unknown,
|
||||
CellType = "Unknown",
|
||||
IsInChannel = false,
|
||||
ChannelType = ChannelType.Other,
|
||||
RelatedModelItem = null,
|
||||
|
||||
@ -166,7 +166,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var cell = new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.Unknown, // 修改:默认为未知/空洞类型
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.Unknown.ToString(), // 修改:默认为未知/空洞类型
|
||||
IsInChannel = false,
|
||||
HeightLayers = new List<HeightLayer>(),
|
||||
ChannelType = ChannelType.Other
|
||||
@ -264,7 +264,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="cost">遍历成本</param>
|
||||
/// <param name="speedLimit">限速</param>
|
||||
/// <param name="cellType">单元格类型</param>
|
||||
public void SetCell(GridPoint2D gridPosition, bool isWalkable, double cost, double speedLimit, CategoryAttributeManager.LogisticsElementType cellType)
|
||||
public void SetCell(GridPoint2D gridPosition, bool isWalkable, double cost, double speedLimit, string cellType)
|
||||
{
|
||||
if (!IsValidGridPosition(gridPosition))
|
||||
return;
|
||||
@ -273,9 +273,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
var cell = new GridCell
|
||||
{
|
||||
CellType = cellType,
|
||||
IsInChannel = cellType == CategoryAttributeManager.LogisticsElementType.通道,
|
||||
IsInChannel = cellType == "通道",
|
||||
HeightLayers = new List<HeightLayer>(),
|
||||
ChannelType = cellType == CategoryAttributeManager.LogisticsElementType.通道 ? ChannelType.Corridor : ChannelType.Other,
|
||||
ChannelType = cellType == "通道" ? ChannelType.Corridor : ChannelType.Other,
|
||||
SpeedLimit = speedLimit
|
||||
};
|
||||
|
||||
@ -393,8 +393,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
private void ApplyCellValidationRules(ref GridCell cell, double originalCost)
|
||||
{
|
||||
// 🔥 关键验证:确保Unknown和障碍物类型永远不可通行
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown ||
|
||||
cell.CellType == CategoryAttributeManager.LogisticsElementType.障碍物)
|
||||
if (cell.CellType == "Unknown" ||
|
||||
cell.CellType == "障碍物")
|
||||
{
|
||||
// Unknown和障碍物没有可通行层,HasAnyWalkableLayer()自动返回false
|
||||
cell.Cost = double.MaxValue;
|
||||
@ -486,7 +486,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
for (int y = 0; y < Height; y++)
|
||||
{
|
||||
var cell = Cells[x, y];
|
||||
if (cell.IsInChannel || cell.CellType == CategoryAttributeManager.LogisticsElementType.通道)
|
||||
if (cell.IsInChannel || cell.CellType == "通道")
|
||||
{
|
||||
channelCells.Add((new GridPoint2D(x, y), cell));
|
||||
}
|
||||
@ -629,61 +629,27 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (cell.HasAnyWalkableLayer())
|
||||
{
|
||||
totalWalkable++;
|
||||
switch (cell.CellType)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板:
|
||||
walkable_楼板++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.门:
|
||||
walkable_门++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.通道:
|
||||
walkable_通道++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.装卸区:
|
||||
walkable_装卸区++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.停车位:
|
||||
walkable_停车位++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯:
|
||||
walkable_楼梯++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯:
|
||||
walkable_电梯++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.走廊:
|
||||
walkable_走廊++;
|
||||
break;
|
||||
default:
|
||||
walkable_Other++;
|
||||
break;
|
||||
}
|
||||
// 使用字符串比较(支持自定义类别)
|
||||
if (cell.CellType == "楼板") walkable_楼板++;
|
||||
else if (cell.CellType == "门") walkable_门++;
|
||||
else if (cell.CellType == "通道") walkable_通道++;
|
||||
else if (cell.CellType == "装卸区") walkable_装卸区++;
|
||||
else if (cell.CellType == "停车位") walkable_停车位++;
|
||||
else if (cell.CellType == "楼梯") walkable_楼梯++;
|
||||
else if (cell.CellType == "电梯") walkable_电梯++;
|
||||
else if (cell.CellType == "走廊") walkable_走廊++;
|
||||
else walkable_Other++;
|
||||
}
|
||||
else
|
||||
{
|
||||
totalNonWalkable++;
|
||||
switch (cell.CellType)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.Unknown:
|
||||
nonWalkable_Unknown++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物:
|
||||
nonWalkable_障碍物++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板:
|
||||
nonWalkable_楼板++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.门:
|
||||
nonWalkable_门++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.通道:
|
||||
nonWalkable_通道++;
|
||||
break;
|
||||
default:
|
||||
nonWalkable_Other++;
|
||||
break;
|
||||
}
|
||||
// 使用字符串比较(支持自定义类别)
|
||||
if (cell.CellType == "Unknown") nonWalkable_Unknown++;
|
||||
else if (cell.CellType == "障碍物") nonWalkable_障碍物++;
|
||||
else if (cell.CellType == "楼板") nonWalkable_楼板++;
|
||||
else if (cell.CellType == "门") nonWalkable_门++;
|
||||
else if (cell.CellType == "通道") nonWalkable_通道++;
|
||||
else nonWalkable_Other++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -720,9 +686,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
public double Cost { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单元格类型
|
||||
/// 单元格类型(类别ID字符串,支持内置和自定义类别)
|
||||
/// </summary>
|
||||
public CategoryAttributeManager.LogisticsElementType CellType { get; set; }
|
||||
public string CellType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 相关的模型项引用(可选)
|
||||
@ -767,33 +733,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (!HasAnyWalkableLayer())
|
||||
return double.MaxValue;
|
||||
|
||||
switch (CellType)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.通道:
|
||||
return 0.5;
|
||||
case CategoryAttributeManager.LogisticsElementType.装卸区:
|
||||
return 0.8;
|
||||
case CategoryAttributeManager.LogisticsElementType.停车位:
|
||||
return 0.9;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板:
|
||||
return 1.0;
|
||||
case CategoryAttributeManager.LogisticsElementType.门:
|
||||
return 1.2;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼梯:
|
||||
return 3.0;
|
||||
case CategoryAttributeManager.LogisticsElementType.电梯:
|
||||
return 2.0;
|
||||
case CategoryAttributeManager.LogisticsElementType.走廊:
|
||||
return 0.6;
|
||||
case CategoryAttributeManager.LogisticsElementType.Unknown:
|
||||
// Unknown类型(空洞)永远不可通行
|
||||
return double.MaxValue;
|
||||
case CategoryAttributeManager.LogisticsElementType.障碍物:
|
||||
// 障碍物永远不可通行
|
||||
return double.MaxValue;
|
||||
default:
|
||||
return 1.0;
|
||||
}
|
||||
// 使用类别定义获取权重(支持内置和自定义类别)
|
||||
return CategoryAttributeManager.GetCategoryWeight(CellType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -804,7 +745,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var cell = new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.障碍物,
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.障碍物.ToString(),
|
||||
IsInChannel = false,
|
||||
HeightLayers = new List<HeightLayer>(), // 空列表,HasAnyWalkableLayer()返回false
|
||||
ChannelType = ChannelType.Other,
|
||||
@ -823,7 +764,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var cell = new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.门,
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.门.ToString(),
|
||||
IsInChannel = isOpen,
|
||||
HeightLayers = new List<HeightLayer>(),
|
||||
ChannelType = ChannelType.Other,
|
||||
@ -842,7 +783,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
var cell = new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道,
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道.ToString(),
|
||||
IsInChannel = true,
|
||||
HeightLayers = new List<HeightLayer>(),
|
||||
ChannelType = channelType,
|
||||
@ -880,9 +821,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
public double SpeedLimit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 层类型
|
||||
/// 层类型(类别ID字符串,支持自定义类别)
|
||||
/// </summary>
|
||||
public CategoryAttributeManager.LogisticsElementType Type { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否是边界层(用于膨胀计算)
|
||||
@ -900,7 +841,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public HeightLayer(double z, HeightInterval passableHeight, ModelItem sourceItem, double speedLimit, CategoryAttributeManager.LogisticsElementType type)
|
||||
public HeightLayer(double z, HeightInterval passableHeight, ModelItem sourceItem, double speedLimit, string type)
|
||||
{
|
||||
Z = z;
|
||||
PassableHeight = passableHeight;
|
||||
@ -1037,7 +978,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
for (int y = 0; y < GridMap.Height; y++)
|
||||
{
|
||||
if (GridMap.Cells[x, y].CellType == CategoryAttributeManager.LogisticsElementType.通道)
|
||||
if (GridMap.Cells[x, y].CellType == "通道")
|
||||
{
|
||||
channelCellCount++;
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
|
||||
// 物流类型属性
|
||||
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
|
||||
var logisticsType = CategoryAttributeManager.GetCategoryId(item);
|
||||
hashBuilder.Append($"LogType:{logisticsType}|");
|
||||
|
||||
// Transform信息(如果有变换)
|
||||
|
||||
@ -132,7 +132,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 1.6. 批量获取所有"无关项"(不参与网格生成的大型构件)
|
||||
LogManager.Info("【生成网格地图】步骤1.6: 批量获取无关项并收集相关节点");
|
||||
var irrelevantItems = CategoryAttributeManager.GetLogisticsItemsByType(
|
||||
CategoryAttributeManager.LogisticsElementType.无关项);
|
||||
"无关项");
|
||||
LogManager.Info($"【生成网格地图】直接获取到 {irrelevantItems.Count} 个无关项");
|
||||
|
||||
// 智能收集无关项相关节点(包括子节点等)
|
||||
@ -330,7 +330,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 为门添加可通行的高度层
|
||||
var doorLayer = new HeightLayer
|
||||
{
|
||||
Type = CategoryAttributeManager.LogisticsElementType.门,
|
||||
Type = "门",
|
||||
Z = doorBottomZ,
|
||||
PassableHeight = new HeightInterval(doorBottomZ, doorBottomZ + doorHeight),
|
||||
IsWalkable = true, // 关键:门是可通行的
|
||||
@ -382,8 +382,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
// 检查是否是楼梯或电梯网格(有多层且包含楼梯/电梯层)
|
||||
bool hasStairsOrElevator = cell.HeightLayers.Any(layer =>
|
||||
layer.Type == CategoryAttributeManager.LogisticsElementType.楼梯 ||
|
||||
layer.Type == CategoryAttributeManager.LogisticsElementType.电梯);
|
||||
layer.Type == "楼梯" ||
|
||||
layer.Type == "电梯");
|
||||
|
||||
// 为每个高度层设置PassableHeight
|
||||
for (int i = 0; i < cell.HeightLayers.Count; i++)
|
||||
@ -492,7 +492,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
int neighborLayerCount = (neighbor.HeightLayers != null) ? neighbor.HeightLayers.Count : 0;
|
||||
|
||||
// 情况2:邻居是Unknown - 标记当前网格所有层为边界
|
||||
if (neighbor.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
|
||||
if (neighbor.CellType == "Unknown")
|
||||
{
|
||||
for (int li = 0; li < currentLayerCount; li++)
|
||||
{
|
||||
@ -585,8 +585,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
try
|
||||
{
|
||||
var logisticsType = CategoryAttributeManager.GetLogisticsElementType(item);
|
||||
if (logisticsType == CategoryAttributeManager.LogisticsElementType.门)
|
||||
var logisticsType = CategoryAttributeManager.GetCategoryId(item);
|
||||
if (logisticsType == "门")
|
||||
{
|
||||
doorItems.Add(item);
|
||||
}
|
||||
@ -617,7 +617,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 只为通道类型的网格生成扫描点
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel)
|
||||
if (cell.CellType == "通道" && cell.IsInChannel)
|
||||
{
|
||||
// 使用网格的实际Z坐标
|
||||
var worldPos = gridMap.GridToWorld3D(new GridPoint2D(x, y));
|
||||
@ -701,7 +701,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
// 关键修复:只处理通道单元格的高度信息集成
|
||||
// 非通道单元格不应该通过高度扫描而改变其类型或可通行性
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel)
|
||||
if (cell.CellType == "通道" && cell.IsInChannel)
|
||||
{
|
||||
channelCellsWithHeightData++;
|
||||
|
||||
@ -713,7 +713,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 创建更新后的通道GridCell,保持原有属性但更新位置和高度
|
||||
var updatedCell = new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道,
|
||||
CellType = "通道",
|
||||
IsInChannel = true,
|
||||
ChannelType = ChannelType.Corridor,
|
||||
RelatedModelItem = cell.RelatedModelItem, // 保持原有关联
|
||||
@ -733,7 +733,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 创建高度不足的通道GridCell,保持原有属性但设为不可通行
|
||||
var updatedCell = new GridCell
|
||||
{
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道,
|
||||
CellType = "通道",
|
||||
IsInChannel = true,
|
||||
ChannelType = ChannelType.Corridor,
|
||||
RelatedModelItem = cell.RelatedModelItem, // 保持原有关联
|
||||
@ -791,20 +791,20 @@ namespace NavisworksTransport.PathPlanning
|
||||
walkableCount++;
|
||||
switch (cell.CellType)
|
||||
{
|
||||
case CategoryAttributeManager.LogisticsElementType.通道:
|
||||
case "通道":
|
||||
channelCount++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.楼板:
|
||||
case "楼板":
|
||||
openSpaceCount++;
|
||||
break;
|
||||
case CategoryAttributeManager.LogisticsElementType.门:
|
||||
case "门":
|
||||
doorCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.障碍物)
|
||||
if (cell.CellType == "障碍物")
|
||||
{
|
||||
obstacleCount++;
|
||||
}
|
||||
@ -986,7 +986,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
distanceMap[x, y] = double.MaxValue;
|
||||
|
||||
// Unknown网格 → distance=0(整个网格无法通行)
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
|
||||
if (cell.CellType == "Unknown")
|
||||
{
|
||||
distanceMap[x, y] = 0.0;
|
||||
continue;
|
||||
@ -1026,7 +1026,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var layer = cell.HeightLayers[layerIndex];
|
||||
|
||||
// 门保护:不膨胀门类型的层
|
||||
if (layer.Type == CategoryAttributeManager.LogisticsElementType.门)
|
||||
if (layer.Type == "门")
|
||||
continue;
|
||||
|
||||
// 障碍物膨胀条件:不包括障碍物本身(distance=0)
|
||||
@ -1103,7 +1103,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var layer = cell.HeightLayers[layerIndex];
|
||||
|
||||
// 门保护:不膨胀门类型的层
|
||||
if (layer.Type == CategoryAttributeManager.LogisticsElementType.门)
|
||||
if (layer.Type == "门")
|
||||
continue;
|
||||
|
||||
// 楼梯口保护:Layer[0]且存在Layer[1]且高度差小于阈值
|
||||
@ -1386,7 +1386,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var cell = gridMap.Cells[update.X, update.Y];
|
||||
|
||||
// 只更新通道网格,跳过已是障碍物的网格
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel)
|
||||
if (cell.CellType == "通道" && cell.IsInChannel)
|
||||
{
|
||||
var bbox = update.BoundingBox;
|
||||
|
||||
@ -1428,7 +1428,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 如果所有层都被标记为不可通行,标记整个网格为障碍物
|
||||
if (anyLayerModified && !cell.HeightLayers.Any(l => l.IsWalkable))
|
||||
{
|
||||
cell.CellType = CategoryAttributeManager.LogisticsElementType.障碍物;
|
||||
cell.CellType = "障碍物";
|
||||
cell.Cost = double.MaxValue;
|
||||
cell.IsInChannel = false;
|
||||
cell.RelatedModelItem = update.Item;
|
||||
|
||||
@ -264,16 +264,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可用类别筛选选项 - 从枚举动态获取所有类别,并添加"全部"选项
|
||||
/// 可用类别筛选选项 - 从所有可用类别动态获取,并添加"全部"选项
|
||||
/// </summary>
|
||||
public ThreadSafeObservableCollection<string> AvailableCategoryFilters { get; } =
|
||||
new ThreadSafeObservableCollection<string>(
|
||||
new[] { "全部" }.Concat(
|
||||
Enum.GetValues(typeof(CategoryAttributeManager.LogisticsElementType))
|
||||
.Cast<CategoryAttributeManager.LogisticsElementType>()
|
||||
.Select(e => e.ToString())
|
||||
)
|
||||
);
|
||||
new ThreadSafeObservableCollection<string>(new[] { "全部" });
|
||||
|
||||
/// <summary>
|
||||
/// 当前选中的类别筛选条件
|
||||
@ -339,9 +333,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AvailableCategories.Contains(info.ElementType))
|
||||
// 将类别ID转换为显示名称
|
||||
string displayName = GetDisplayNameByCategoryId(info.ElementType);
|
||||
if (string.IsNullOrEmpty(displayName))
|
||||
{
|
||||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的ElementType '{info.ElementType}' 不在可用类别中!");
|
||||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的ElementType '{info.ElementType}' 无法找到对应的类别!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AvailableCategories.Contains(displayName))
|
||||
{
|
||||
LogManager.Error($"[ModelSettingsViewModel] 异常:模型 {logisticsModel.Name} 的类别显示名称 '{displayName}' 不在可用类别中!");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -373,8 +375,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
// 所有验证通过,回填属性
|
||||
SelectedCategory = info.ElementType;
|
||||
// 所有验证通过,回填属性(使用显示名称)
|
||||
SelectedCategory = displayName;
|
||||
IsTraversable = info.IsTraversable;
|
||||
Priority = info.Priority;
|
||||
HeightLimit = info.HeightLimit;
|
||||
@ -675,28 +677,27 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return new { Success = false, Count = 0, Message = "请先选择模型元素" };
|
||||
}
|
||||
|
||||
// 解析选中的类别为枚举
|
||||
if (Enum.TryParse<CategoryAttributeManager.LogisticsElementType>(SelectedCategory, out var elementType))
|
||||
{
|
||||
int successCount = CategoryAttributeManager.AddLogisticsAttributes(
|
||||
selectedItems,
|
||||
elementType,
|
||||
isTraversable: IsTraversable,
|
||||
priority: Priority,
|
||||
heightLimit: HeightLimit,
|
||||
speedLimit: SpeedLimit,
|
||||
widthLimit: WidthLimit);
|
||||
|
||||
return new {
|
||||
Success = true,
|
||||
Count = successCount,
|
||||
Message = $"已为 {successCount} 个元素设置物流属性: {SelectedCategory}"
|
||||
};
|
||||
}
|
||||
else
|
||||
// 根据显示名称查找类别ID
|
||||
string categoryId = GetCategoryIdByDisplayName(SelectedCategory);
|
||||
if (string.IsNullOrEmpty(categoryId))
|
||||
{
|
||||
return new { Success = false, Count = 0, Message = $"无效的物流类别: {SelectedCategory}" };
|
||||
}
|
||||
|
||||
int successCount = CategoryAttributeManager.AddLogisticsAttributes(
|
||||
selectedItems,
|
||||
categoryId,
|
||||
isTraversable: IsTraversable,
|
||||
priority: Priority,
|
||||
heightLimit: HeightLimit,
|
||||
speedLimit: SpeedLimit,
|
||||
widthLimit: WidthLimit);
|
||||
|
||||
return new {
|
||||
Success = true,
|
||||
Count = successCount,
|
||||
Message = $"已为 {successCount} 个元素设置物流属性: {SelectedCategory}"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1286,11 +1287,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
// 初始化物流类别
|
||||
// 初始化物流类别(包括内置和自定义类别)
|
||||
AvailableCategories.Clear();
|
||||
foreach (var elementType in Enum.GetValues(typeof(CategoryAttributeManager.LogisticsElementType)))
|
||||
AvailableCategoryFilters.Clear();
|
||||
AvailableCategoryFilters.Add("全部");
|
||||
|
||||
// 从 CategoryAttributeManager 获取所有可用类别(内置 + 自定义)
|
||||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories();
|
||||
foreach (var category in allCategories)
|
||||
{
|
||||
AvailableCategories.Add(elementType.ToString());
|
||||
AvailableCategories.Add(category.DisplayName);
|
||||
AvailableCategoryFilters.Add(category.DisplayName);
|
||||
}
|
||||
|
||||
// 设置默认选择
|
||||
@ -1423,6 +1430,54 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据显示名称获取类别ID
|
||||
/// </summary>
|
||||
/// <param name="displayName">类别显示名称</param>
|
||||
/// <returns>类别ID,如果未找到返回null</returns>
|
||||
private string GetCategoryIdByDisplayName(string displayName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(displayName))
|
||||
return null;
|
||||
|
||||
// 从所有可用类别中查找
|
||||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories();
|
||||
foreach (var category in allCategories)
|
||||
{
|
||||
if (category.DisplayName == displayName)
|
||||
{
|
||||
return category.Id;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到匹配,直接返回显示名称(可能是旧数据或其他情况)
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据类别ID获取显示名称
|
||||
/// </summary>
|
||||
/// <param name="categoryId">类别ID</param>
|
||||
/// <returns>类别显示名称,如果未找到返回null</returns>
|
||||
private string GetDisplayNameByCategoryId(string categoryId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(categoryId))
|
||||
return null;
|
||||
|
||||
// 从所有可用类别中查找
|
||||
var allCategories = CategoryAttributeManager.GetAllAvailableCategories();
|
||||
foreach (var category in allCategories)
|
||||
{
|
||||
if (category.Id.Equals(categoryId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return category.DisplayName;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没找到匹配,直接返回类别ID(可能是旧数据或其他情况)
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化物流属性为显示字符串
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user