增加了配置管理功能,配置文件保存为toml格式,可在配置窗口编辑;实现了日志级别管理功能

This commit is contained in:
tian 2025-10-11 12:18:33 +08:00
parent a46568f43e
commit 7343133f12
23 changed files with 1627 additions and 966 deletions

View File

@ -16,7 +16,7 @@ powershell -Command "& 'C:\...\MSBuild.exe' AStarTestRunner.csproj /p:Configurat
bin\Debug\AStarTestRunner.exe
```
**调试**: 日志在 `C:\ProgramData\Autodesk\Navisworks Manage 2026\NavisworksTransport\logs\debug.log`
**调试**: 日志在 `"C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\NavisworksTransportPlugin\logs\debug.log"`
## 架构

View File

@ -98,6 +98,12 @@
<Private>True</Private>
</Reference>
<!-- Tomlyn TOML Parser -->
<Reference Include="Tomlyn">
<HintPath>packages\Tomlyn.0.19.0\lib\netstandard2.0\Tomlyn.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
@ -128,7 +134,11 @@
<!-- Core - Document State Management -->
<Compile Include="src\Core\DocumentStateManager.cs" />
<!-- Core - Configuration Management -->
<Compile Include="src\Core\Config\SystemConfig.cs" />
<Compile Include="src\Core\Config\ConfigManager.cs" />
<!-- Commands - Command Pattern Framework (for testing) -->
<Compile Include="src\Commands\IPathPlanningCommand.cs" />
<Compile Include="src\Commands\CommandBase.cs" />
@ -204,6 +214,9 @@
<Compile Include="src\UI\WPF\Views\LogViewerDialog.xaml.cs">
<DependentUpon>LogViewerDialog.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\ConfigEditorDialog.xaml.cs">
<DependentUpon>ConfigEditorDialog.xaml</DependentUpon>
</Compile>
<Compile Include="src\UI\WPF\Views\CollisionReportDialog.xaml.cs">
<DependentUpon>CollisionReportDialog.xaml</DependentUpon>
</Compile>
@ -308,6 +321,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\ConfigEditorDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="src\UI\WPF\Views\CollisionReportDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

56
TestConfigManager.cs Normal file
View File

@ -0,0 +1,56 @@
using System;
using NavisworksTransport.Core.Config;
namespace NavisworksTransport.Tests
{
/// <summary>
/// 配置管理器测试程序
/// 使用方法:在 MainPlugin 的 Execute 方法中临时调用 TestConfigManager.RunTests()
/// </summary>
public class TestConfigManager
{
public static void RunTests()
{
Console.WriteLine("=== 开始测试配置管理器 ===");
Console.WriteLine();
// 测试 1加载或创建默认配置
Console.WriteLine("测试 1: 加载或创建默认配置");
var config = ConfigManager.Instance.Current;
Console.WriteLine($"配置文件路径: {ConfigManager.ConfigFilePath}");
Console.WriteLine($"网格尺寸: {config.GridGeneration.CellSizeMeters} 米");
Console.WriteLine($"车辆半径: {config.GridGeneration.VehicleRadiusMeters} 米");
Console.WriteLine($"路径策略: {config.PathPlanning.DefaultPathStrategy}");
Console.WriteLine();
// 测试 2修改配置并保存
Console.WriteLine("测试 2: 修改配置并保存");
config.GridGeneration.CellSizeMeters = 0.6;
config.PathPlanning.DefaultPathStrategy = "SafetyFirst";
config.Visualization.EnableGridVisualization = true;
ConfigManager.Instance.SaveConfig(config);
Console.WriteLine("配置已保存");
Console.WriteLine();
// 测试 3重新加载配置
Console.WriteLine("测试 3: 重新加载配置");
ConfigManager.Instance.Reload();
var reloadedConfig = ConfigManager.Instance.Current;
Console.WriteLine($"重载后网格尺寸: {reloadedConfig.GridGeneration.CellSizeMeters} 米");
Console.WriteLine($"重载后路径策略: {reloadedConfig.PathPlanning.DefaultPathStrategy}");
Console.WriteLine($"重载后网格可视化: {reloadedConfig.Visualization.EnableGridVisualization}");
Console.WriteLine();
// 测试 4重置为默认配置
Console.WriteLine("测试 4: 重置为默认配置");
ConfigManager.Instance.ResetToDefault();
var defaultConfig = ConfigManager.Instance.Current;
Console.WriteLine($"重置后网格尺寸: {defaultConfig.GridGeneration.CellSizeMeters} 米");
Console.WriteLine($"重置后路径策略: {defaultConfig.PathPlanning.DefaultPathStrategy}");
Console.WriteLine();
Console.WriteLine("=== 测试完成 ===");
Console.WriteLine($"请检查配置文件: {ConfigManager.ConfigFilePath}");
}
}
}

76
config.toml.example Normal file
View File

@ -0,0 +1,76 @@
# NavisworksTransport 系统配置文件
# 单位说明:长度单位均为米(m),时间单位为秒(s)
# 自动生成时间: 2025-10-11 10:50:00
[grid_generation]
# 网格单元大小(米)- 推荐值0.3-0.8
cell_size_meters = 0.5
# 车辆半径(米)- 用于障碍物膨胀计算
vehicle_radius_meters = 0.3
# 安全间隙(米)- 额外的安全距离
safety_margin_meters = 0.2
# 膨胀半径(网格单元数)
inflation_radius_cells = 2
# 扫描高度(米)
scan_height_meters = 0.1
[path_planning]
# 最大高度差(米)- 楼梯、坡道可接受的高度阈值
max_height_diff_meters = 0.35
# 车辆尺寸(米)
vehicle_height_meters = 2.0
vehicle_length_meters = 2.5
vehicle_width_meters = 1.8
# 默认路径策略:"Shortest" | "Straightest" | "SafetyFirst"
default_path_strategy = "Straightest"
# 启用路径优化(减少转弯点)
enable_path_optimization = true
[visualization]
# 网格点大小(像素)
grid_point_size = 5.0
# 路径线宽(像素)
path_line_width = 2.0
# 路径点大小(模型单位)
path_point_size = 50.0
# 启用网格可视化
enable_grid_visualization = false
# 启用多层网格可视化
enable_multi_layer_visualization = true
[animation]
# 默认动画速度(米/秒)
default_speed_meters_per_second = 1.5
# 动画帧率(帧/秒)
frame_rate = 30
# 步进间隔(毫秒)
step_interval_ms = 100
# 启用平滑插值
enable_smooth_interpolation = true
[collision]
# 碰撞检测步长(米)
detection_step_meters = 0.1
# 碰撞容差(米)
collision_tolerance_meters = 0.01
# 启用实时碰撞检测
enable_realtime_detection = true
# 自动生成碰撞报告
auto_generate_report = true

View File

@ -2,6 +2,10 @@
## 功能点
### [2025/10/11]
1. [x] 功能实现系统配置管理采用toml格式保存配置文件
### [2025/10/09]
1. [x] 优化支持楼梯场景的3D路径规划

View File

@ -0,0 +1,374 @@
using System;
using System.IO;
using System.Text;
using Tomlyn;
using Tomlyn.Model;
using NavisworksTransport.Utils;
namespace NavisworksTransport.Core.Config
{
/// <summary>
/// 系统配置管理器
/// 负责 TOML 配置文件的读取、保存和管理
/// </summary>
public class ConfigManager
{
private static ConfigManager _instance;
private static readonly object _lock = new object();
/// <summary>
/// 单例实例
/// </summary>
public static ConfigManager Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ConfigManager();
}
}
}
return _instance;
}
}
/// <summary>
/// 配置文件路径
/// </summary>
public static string ConfigFilePath
{
get
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Autodesk",
"Navisworks Manage 2026",
"plugins",
"NavisworksTransportPlugin",
"config.toml"
);
}
}
/// <summary>
/// 配置目录
/// </summary>
public static string ConfigDirectory
{
get { return Path.GetDirectoryName(ConfigFilePath); }
}
private SystemConfig _currentConfig;
private ConfigManager()
{
_currentConfig = LoadOrCreateDefault();
}
/// <summary>
/// 获取当前配置
/// </summary>
public SystemConfig Current
{
get { return _currentConfig; }
}
/// <summary>
/// 加载配置文件,如果不存在则创建默认配置
/// </summary>
public SystemConfig LoadOrCreateDefault()
{
try
{
if (!File.Exists(ConfigFilePath))
{
LogManager.Info($"配置文件不存在,创建默认配置: {ConfigFilePath}");
var defaultConfig = new SystemConfig();
SaveConfig(defaultConfig);
return defaultConfig;
}
LogManager.Info($"加载配置文件: {ConfigFilePath}");
var tomlContent = File.ReadAllText(ConfigFilePath, Encoding.UTF8);
var config = LoadFromToml(tomlContent);
LogManager.Info("配置文件加载成功");
return config;
}
catch (Exception ex)
{
LogManager.Error($"加载配置文件失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
// 加载失败时返回默认配置
LogManager.Info("使用默认配置");
return new SystemConfig();
}
}
/// <summary>
/// 从 TOML 字符串加载配置
/// </summary>
private SystemConfig LoadFromToml(string tomlContent)
{
var model = Toml.ToModel(tomlContent);
var config = new SystemConfig();
// 加载网格生成配置
if (model.ContainsKey("grid_generation"))
{
var gridGen = model["grid_generation"] as TomlTable;
if (gridGen != null)
{
config.GridGeneration.CellSizeMeters = GetDouble(gridGen, "cell_size_meters", 0.5);
config.GridGeneration.VehicleRadiusMeters = GetDouble(gridGen, "vehicle_radius_meters", 0.3);
config.GridGeneration.SafetyMarginMeters = GetDouble(gridGen, "safety_margin_meters", 0.2);
config.GridGeneration.InflationRadiusCells = GetInt(gridGen, "inflation_radius_cells", 2);
config.GridGeneration.ScanHeightMeters = GetDouble(gridGen, "scan_height_meters", 0.1);
}
}
// 加载路径规划配置
if (model.ContainsKey("path_planning"))
{
var pathPlan = model["path_planning"] as TomlTable;
if (pathPlan != null)
{
config.PathPlanning.MaxHeightDiffMeters = GetDouble(pathPlan, "max_height_diff_meters", 0.35);
config.PathPlanning.VehicleHeightMeters = GetDouble(pathPlan, "vehicle_height_meters", 2.0);
config.PathPlanning.VehicleLengthMeters = GetDouble(pathPlan, "vehicle_length_meters", 2.5);
config.PathPlanning.VehicleWidthMeters = GetDouble(pathPlan, "vehicle_width_meters", 1.8);
config.PathPlanning.DefaultPathStrategy = GetString(pathPlan, "default_path_strategy", "Straightest");
config.PathPlanning.EnablePathOptimization = GetBool(pathPlan, "enable_path_optimization", true);
}
}
// 加载可视化配置
if (model.ContainsKey("visualization"))
{
var visual = model["visualization"] as TomlTable;
if (visual != null)
{
config.Visualization.GridPointSize = GetDouble(visual, "grid_point_size", 5.0);
config.Visualization.PathLineWidth = GetDouble(visual, "path_line_width", 2.0);
config.Visualization.PathPointSize = GetDouble(visual, "path_point_size", 50.0);
config.Visualization.EnableGridVisualization = GetBool(visual, "enable_grid_visualization", false);
config.Visualization.EnableMultiLayerVisualization = GetBool(visual, "enable_multi_layer_visualization", true);
}
}
// 加载动画配置
if (model.ContainsKey("animation"))
{
var anim = model["animation"] as TomlTable;
if (anim != null)
{
config.Animation.DefaultSpeedMetersPerSecond = GetDouble(anim, "default_speed_meters_per_second", 1.5);
config.Animation.FrameRate = GetInt(anim, "frame_rate", 30);
config.Animation.StepIntervalMs = GetInt(anim, "step_interval_ms", 100);
config.Animation.EnableSmoothInterpolation = GetBool(anim, "enable_smooth_interpolation", true);
}
}
// 加载碰撞检测配置
if (model.ContainsKey("collision"))
{
var collision = model["collision"] as TomlTable;
if (collision != null)
{
config.Collision.DetectionStepMeters = GetDouble(collision, "detection_step_meters", 0.1);
config.Collision.CollisionToleranceMeters = GetDouble(collision, "collision_tolerance_meters", 0.01);
config.Collision.EnableRealtimeDetection = GetBool(collision, "enable_realtime_detection", true);
config.Collision.AutoGenerateReport = GetBool(collision, "auto_generate_report", true);
}
}
return config;
}
/// <summary>
/// 保存配置到文件
/// </summary>
public void SaveConfig(SystemConfig config)
{
try
{
// 确保目录存在
Directory.CreateDirectory(ConfigDirectory);
var tomlContent = ConvertToToml(config);
File.WriteAllText(ConfigFilePath, tomlContent, Encoding.UTF8);
_currentConfig = config;
LogManager.Info($"配置文件已保存: {ConfigFilePath}");
}
catch (Exception ex)
{
LogManager.Error($"保存配置文件失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
throw;
}
}
/// <summary>
/// 将配置转换为 TOML 字符串
/// </summary>
private string ConvertToToml(SystemConfig config)
{
var sb = new StringBuilder();
// 添加文件头注释
sb.AppendLine("# NavisworksTransport 系统配置文件");
sb.AppendLine("# 单位说明:长度单位均为米(m),时间单位为秒(s)");
sb.AppendLine("# 自动生成时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
sb.AppendLine();
// 网格生成配置
sb.AppendLine("[grid_generation]");
sb.AppendLine("# 网格单元大小(米)- 推荐值0.3-0.8");
sb.AppendLine($"cell_size_meters = {config.GridGeneration.CellSizeMeters}");
sb.AppendLine();
sb.AppendLine("# 车辆半径(米)- 用于障碍物膨胀计算");
sb.AppendLine($"vehicle_radius_meters = {config.GridGeneration.VehicleRadiusMeters}");
sb.AppendLine();
sb.AppendLine("# 安全间隙(米)- 额外的安全距离");
sb.AppendLine($"safety_margin_meters = {config.GridGeneration.SafetyMarginMeters}");
sb.AppendLine();
sb.AppendLine("# 膨胀半径(网格单元数)");
sb.AppendLine($"inflation_radius_cells = {config.GridGeneration.InflationRadiusCells}");
sb.AppendLine();
sb.AppendLine("# 扫描高度(米)");
sb.AppendLine($"scan_height_meters = {config.GridGeneration.ScanHeightMeters}");
sb.AppendLine();
// 路径规划配置
sb.AppendLine("[path_planning]");
sb.AppendLine("# 最大高度差(米)- 楼梯、坡道可接受的高度阈值");
sb.AppendLine($"max_height_diff_meters = {config.PathPlanning.MaxHeightDiffMeters}");
sb.AppendLine();
sb.AppendLine("# 车辆尺寸(米)");
sb.AppendLine($"vehicle_height_meters = {config.PathPlanning.VehicleHeightMeters}");
sb.AppendLine($"vehicle_length_meters = {config.PathPlanning.VehicleLengthMeters}");
sb.AppendLine($"vehicle_width_meters = {config.PathPlanning.VehicleWidthMeters}");
sb.AppendLine();
sb.AppendLine("# 默认路径策略:\"Shortest\" | \"Straightest\" | \"SafetyFirst\"");
sb.AppendLine($"default_path_strategy = \"{config.PathPlanning.DefaultPathStrategy}\"");
sb.AppendLine();
sb.AppendLine("# 启用路径优化(减少转弯点)");
sb.AppendLine($"enable_path_optimization = {config.PathPlanning.EnablePathOptimization.ToString().ToLower()}");
sb.AppendLine();
// 可视化配置
sb.AppendLine("[visualization]");
sb.AppendLine("# 网格点大小(像素)");
sb.AppendLine($"grid_point_size = {config.Visualization.GridPointSize}");
sb.AppendLine();
sb.AppendLine("# 路径线宽(像素)");
sb.AppendLine($"path_line_width = {config.Visualization.PathLineWidth}");
sb.AppendLine();
sb.AppendLine("# 路径点大小(模型单位)");
sb.AppendLine($"path_point_size = {config.Visualization.PathPointSize}");
sb.AppendLine();
sb.AppendLine("# 启用网格可视化");
sb.AppendLine($"enable_grid_visualization = {config.Visualization.EnableGridVisualization.ToString().ToLower()}");
sb.AppendLine();
sb.AppendLine("# 启用多层网格可视化");
sb.AppendLine($"enable_multi_layer_visualization = {config.Visualization.EnableMultiLayerVisualization.ToString().ToLower()}");
sb.AppendLine();
// 动画配置
sb.AppendLine("[animation]");
sb.AppendLine("# 默认动画速度(米/秒)");
sb.AppendLine($"default_speed_meters_per_second = {config.Animation.DefaultSpeedMetersPerSecond}");
sb.AppendLine();
sb.AppendLine("# 动画帧率(帧/秒)");
sb.AppendLine($"frame_rate = {config.Animation.FrameRate}");
sb.AppendLine();
sb.AppendLine("# 步进间隔(毫秒)");
sb.AppendLine($"step_interval_ms = {config.Animation.StepIntervalMs}");
sb.AppendLine();
sb.AppendLine("# 启用平滑插值");
sb.AppendLine($"enable_smooth_interpolation = {config.Animation.EnableSmoothInterpolation.ToString().ToLower()}");
sb.AppendLine();
// 碰撞检测配置
sb.AppendLine("[collision]");
sb.AppendLine("# 碰撞检测步长(米)");
sb.AppendLine($"detection_step_meters = {config.Collision.DetectionStepMeters}");
sb.AppendLine();
sb.AppendLine("# 碰撞容差(米)");
sb.AppendLine($"collision_tolerance_meters = {config.Collision.CollisionToleranceMeters}");
sb.AppendLine();
sb.AppendLine("# 启用实时碰撞检测");
sb.AppendLine($"enable_realtime_detection = {config.Collision.EnableRealtimeDetection.ToString().ToLower()}");
sb.AppendLine();
sb.AppendLine("# 自动生成碰撞报告");
sb.AppendLine($"auto_generate_report = {config.Collision.AutoGenerateReport.ToString().ToLower()}");
return sb.ToString();
}
/// <summary>
/// 重新加载配置
/// </summary>
public void Reload()
{
_currentConfig = LoadOrCreateDefault();
}
/// <summary>
/// 重置为默认配置
/// </summary>
public void ResetToDefault()
{
var defaultConfig = new SystemConfig();
SaveConfig(defaultConfig);
}
// 辅助方法:从 TomlTable 获取值
private double GetDouble(TomlTable table, string key, double defaultValue)
{
if (table.ContainsKey(key))
{
var value = table[key];
if (value is long longValue)
return (double)longValue;
if (value is double doubleValue)
return doubleValue;
}
return defaultValue;
}
private int GetInt(TomlTable table, string key, int defaultValue)
{
if (table.ContainsKey(key) && table[key] is long longValue)
{
return (int)longValue;
}
return defaultValue;
}
private bool GetBool(TomlTable table, string key, bool defaultValue)
{
if (table.ContainsKey(key) && table[key] is bool boolValue)
{
return boolValue;
}
return defaultValue;
}
private string GetString(TomlTable table, string key, string defaultValue)
{
if (table.ContainsKey(key) && table[key] is string stringValue)
{
return stringValue;
}
return defaultValue;
}
}
}

View File

@ -0,0 +1,207 @@
using System;
namespace NavisworksTransport.Core.Config
{
/// <summary>
/// 系统配置根类
/// </summary>
public class SystemConfig
{
/// <summary>
/// 网格生成配置
/// </summary>
public GridGenerationConfig GridGeneration { get; set; } = new GridGenerationConfig();
/// <summary>
/// 路径规划配置
/// </summary>
public PathPlanningConfig PathPlanning { get; set; } = new PathPlanningConfig();
/// <summary>
/// 可视化配置
/// </summary>
public VisualizationConfig Visualization { get; set; } = new VisualizationConfig();
/// <summary>
/// 动画配置
/// </summary>
public AnimationConfig Animation { get; set; } = new AnimationConfig();
/// <summary>
/// 碰撞检测配置
/// </summary>
public CollisionConfig Collision { get; set; } = new CollisionConfig();
}
/// <summary>
/// 网格生成配置
/// </summary>
public class GridGenerationConfig
{
/// <summary>
/// 网格单元大小(米)
/// 推荐值0.3-0.8
/// </summary>
public double CellSizeMeters { get; set; } = 0.5;
/// <summary>
/// 车辆半径(米)
/// 用于障碍物膨胀计算
/// </summary>
public double VehicleRadiusMeters { get; set; } = 0.3;
/// <summary>
/// 安全间隙(米)
/// 额外的安全距离
/// </summary>
public double SafetyMarginMeters { get; set; } = 0.2;
/// <summary>
/// 膨胀半径(网格单元数)
/// 障碍物膨胀的单元格数量
/// </summary>
public int InflationRadiusCells { get; set; } = 2;
/// <summary>
/// 扫描高度(米)
/// 障碍物扫描的垂直步进高度
/// </summary>
public double ScanHeightMeters { get; set; } = 0.1;
}
/// <summary>
/// 路径规划配置
/// </summary>
public class PathPlanningConfig
{
/// <summary>
/// 最大高度差(米)
/// 楼梯、坡道可接受的高度阈值
/// </summary>
public double MaxHeightDiffMeters { get; set; } = 0.35;
/// <summary>
/// 车辆高度(米)
/// 用于通行性判断
/// </summary>
public double VehicleHeightMeters { get; set; } = 2.0;
/// <summary>
/// 车辆长度(米)
/// 用于转弯半径计算
/// </summary>
public double VehicleLengthMeters { get; set; } = 2.5;
/// <summary>
/// 车辆宽度(米)
/// 用于通道宽度判断
/// </summary>
public double VehicleWidthMeters { get; set; } = 1.8;
/// <summary>
/// 默认路径策略
/// "Shortest" - 最短路径优先
/// "Straightest" - 直线优先
/// "SafetyFirst" - 安全优先
/// </summary>
public string DefaultPathStrategy { get; set; } = "Straightest";
/// <summary>
/// 路径优化启用
/// 是否启用路径点优化(减少转弯点)
/// </summary>
public bool EnablePathOptimization { get; set; } = true;
}
/// <summary>
/// 可视化配置
/// </summary>
public class VisualizationConfig
{
/// <summary>
/// 网格点大小(像素)
/// </summary>
public double GridPointSize { get; set; } = 5.0;
/// <summary>
/// 路径线宽(像素)
/// </summary>
public double PathLineWidth { get; set; } = 2.0;
/// <summary>
/// 路径点大小(模型单位)
/// </summary>
public double PathPointSize { get; set; } = 50.0;
/// <summary>
/// 启用网格可视化
/// 是否默认显示网格地图
/// </summary>
public bool EnableGridVisualization { get; set; } = false;
/// <summary>
/// 启用多层网格可视化
/// 是否显示多个高度层的网格
/// </summary>
public bool EnableMultiLayerVisualization { get; set; } = true;
}
/// <summary>
/// 动画配置
/// </summary>
public class AnimationConfig
{
/// <summary>
/// 动画速度(米/秒)
/// 默认车辆移动速度
/// </summary>
public double DefaultSpeedMetersPerSecond { get; set; } = 1.5;
/// <summary>
/// 动画帧率(帧/秒)
/// </summary>
public int FrameRate { get; set; } = 30;
/// <summary>
/// 步进间隔(毫秒)
/// 步进模式下每步的时间间隔
/// </summary>
public int StepIntervalMs { get; set; } = 100;
/// <summary>
/// 启用平滑插值
/// 是否在路径点之间使用平滑插值
/// </summary>
public bool EnableSmoothInterpolation { get; set; } = true;
}
/// <summary>
/// 碰撞检测配置
/// </summary>
public class CollisionConfig
{
/// <summary>
/// 碰撞检测步长(米)
/// 动画过程中每次检测的移动距离
/// </summary>
public double DetectionStepMeters { get; set; } = 0.1;
/// <summary>
/// 碰撞容差(米)
/// 判定为碰撞的最小距离
/// </summary>
public double CollisionToleranceMeters { get; set; } = 0.01;
/// <summary>
/// 启用实时碰撞检测
/// 动画播放时是否实时检测碰撞
/// </summary>
public bool EnableRealtimeDetection { get; set; } = true;
/// <summary>
/// 自动生成碰撞报告
/// 动画结束后是否自动生成报告
/// </summary>
public bool AutoGenerateReport { get; set; } = true;
}
}

View File

@ -15,7 +15,7 @@ namespace NavisworksTransport
/// </summary>
public PathClickToolPlugin()
{
LogManager.WriteLog("[ToolPlugin] PathClickToolPlugin实例已创建");
LogManager.Debug("[ToolPlugin] PathClickToolPlugin实例已创建");
}
/// <summary>
/// 点击事件,传递精确的点击坐标和对象
@ -50,40 +50,40 @@ namespace NavisworksTransport
/// </summary>
public override bool MouseDown(View view, KeyModifiers modifiers, ushort button, int x, int y, double timeOffset)
{
LogManager.WriteLog("[ToolPlugin] ★★★ MouseDown方法被调用 ★★★");
LogManager.Debug("[ToolPlugin] ★★★ MouseDown方法被调用 ★★★");
try
{
LogManager.WriteLog("[ToolPlugin] ===== 鼠标点击事件 =====");
LogManager.WriteLog($"[ToolPlugin] 屏幕坐标: ({x}, {y})");
LogManager.WriteLog($"[ToolPlugin] 按键: {button}, 修饰键: {modifiers}");
LogManager.Debug("[ToolPlugin] ===== 鼠标点击事件 =====");
LogManager.Debug($"[ToolPlugin] 屏幕坐标: ({x}, {y})");
LogManager.Debug($"[ToolPlugin] 按键: {button}, 修饰键: {modifiers}");
// 只处理左键点击
if (button == 1) // 左键
{
LogManager.WriteLog("[ToolPlugin] 检测到左键点击,开始处理");
LogManager.Debug("[ToolPlugin] 检测到左键点击,开始处理");
// 使用PickItemFromPoint获取精确的3D坐标
PickItemResult itemResult = view.PickItemFromPoint(x, y);
if (itemResult != null)
{
LogManager.WriteLog("[ToolPlugin] ✓ PickItemFromPoint成功");
LogManager.WriteLog($"[ToolPlugin] 精确3D坐标: ({itemResult.Point.X:F3}, {itemResult.Point.Y:F3}, {itemResult.Point.Z:F3})");
LogManager.WriteLog($"[ToolPlugin] 选中对象: {itemResult.ModelItem?.DisplayName ?? "NULL"}");
LogManager.Debug("[ToolPlugin] ✓ PickItemFromPoint成功");
LogManager.Debug($"[ToolPlugin] 精确3D坐标: ({itemResult.Point.X:F3}, {itemResult.Point.Y:F3}, {itemResult.Point.Z:F3})");
LogManager.Debug($"[ToolPlugin] 选中对象: {itemResult.ModelItem?.DisplayName ?? "NULL"}");
// 触发事件将精确坐标传递给PathPlanningManager
MouseClicked?.Invoke(this, itemResult);
LogManager.WriteLog("[ToolPlugin] 事件已触发");
LogManager.Debug("[ToolPlugin] 事件已触发");
}
else
{
LogManager.WriteLog("[ToolPlugin] ✗ PickItemFromPoint返回null未点击到对象");
LogManager.Debug("[ToolPlugin] ✗ PickItemFromPoint返回null未点击到对象");
}
}
else
{
LogManager.WriteLog($"[ToolPlugin] 忽略非左键点击: {button}");
LogManager.Debug($"[ToolPlugin] 忽略非左键点击: {button}");
}
// 返回false表示继续传递事件给其他处理程序
@ -91,8 +91,8 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[ToolPlugin] 异常: {ex.Message}");
LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}");
LogManager.Error($"[ToolPlugin] 异常: {ex.Message}");
LogManager.Error($"[ToolPlugin] 堆栈: {ex.StackTrace}");
return false;
}
}

View File

@ -52,7 +52,7 @@ namespace NavisworksTransport
var currentTool = Application.MainDocument.Tool.Value;
if (currentTool != Tool.CustomToolPlugin)
{
LogManager.WriteLog($"[InputMonitor] 用户按空格键,当前工具为{currentTool},重新激活工具");
LogManager.Info($"[InputMonitor] 用户按空格键,当前工具为{currentTool},重新激活工具");
pathManager.ReactivateToolPlugin();
return true;
}
@ -63,7 +63,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[InputMonitor] 处理键盘事件异常: {ex.Message}");
LogManager.Error($"[InputMonitor] 处理键盘事件异常: {ex.Message}");
return false;
}
}

View File

@ -114,7 +114,7 @@ namespace NavisworksTransport
{
// 设置静态引用,确保所有地方都使用同一个实例
_activePathManager = this;
LogManager.WriteLog($"[路径管理] 设置_activePathManager静态引用, ManagerId: {_managerId}");
LogManager.Debug($"[路径管理] 设置_activePathManager静态引用, ManagerId: {_managerId}");
// 尝试初始化路径分析数据库
EnsureDatabaseInitialized();
@ -156,21 +156,21 @@ namespace NavisworksTransport
_renderPlugin = PathPointRenderPlugin.Instance;
if (_renderPlugin != null)
{
LogManager.WriteLog("[路径管理] ✅ PathPointRenderPlugin实例获取成功");
LogManager.WriteLog($"[路径管理] 渲染插件状态 - 启用: {_renderPlugin.IsEnabled}, 标记数量: {_renderPlugin.MarkerCount}");
LogManager.Info("[路径管理] ✅ PathPointRenderPlugin实例获取成功");
LogManager.Debug($"[路径管理] 渲染插件状态 - 启用: {_renderPlugin.IsEnabled}, 标记数量: {_renderPlugin.MarkerCount}");
// 推送默认的网格大小和车辆参数,确保渲染插件有合理的初始值
InitializeRenderPluginDefaults();
}
else
{
LogManager.WriteLog("[路径管理] ❌ PathPointRenderPlugin实例尚未就绪将在后续尝试获取");
LogManager.Warning("[路径管理] ❌ PathPointRenderPlugin实例尚未就绪将在后续尝试获取");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径管理] PathPointRenderPlugin实例获取失败: {ex.Message}");
LogManager.WriteLog($"[路径管理] 异常堆栈: {ex.StackTrace}");
LogManager.Error($"[路径管理] PathPointRenderPlugin实例获取失败: {ex.Message}");
LogManager.Error($"[路径管理] 异常堆栈: {ex.StackTrace}");
_renderPlugin = null;
}
@ -797,26 +797,26 @@ namespace NavisworksTransport
{
try
{
LogManager.WriteLog("[事件清理] ===== 开始执行StopClickTool - 完整事件订阅清理 =====");
LogManager.Debug("[事件清理] ===== 开始执行StopClickTool - 完整事件订阅清理 =====");
// 1. 停用ToolPlugin并清理事件订阅
DeactivateToolPlugin();
// 2. 简化的事件订阅清理 - 移除危险的反射操作
LogManager.WriteLog("[事件清理] 执行安全的事件订阅清理");
LogManager.Debug("[事件清理] 执行安全的事件订阅清理");
try
{
// 安全地移除事件订阅,多次取消订阅同一处理程序是安全的
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
LogManager.WriteLog("[事件清理] 已安全移除PathPlanningManager.OnToolPluginMouseClicked订阅");
LogManager.Debug("[事件清理] 已安全移除PathPlanningManager.OnToolPluginMouseClicked订阅");
}
catch (Exception cleanupEx)
{
LogManager.WriteLog($"[事件清理] 事件清理过程异常: {cleanupEx.Message}");
LogManager.Error($"[事件清理] 事件清理过程异常: {cleanupEx.Message}");
}
// 注意移除了PathEditState设置StopClickTool只管理工具插件状态不修改业务逻辑状态
LogManager.WriteLog("[事件清理] ===== StopClickTool执行完成所有事件订阅已清理 =====");
LogManager.Debug("[事件清理] ===== StopClickTool执行完成所有事件订阅已清理 =====");
}
catch (Exception ex)
{
@ -2402,80 +2402,80 @@ namespace NavisworksTransport
// 如果已经激活,检查是否需要订阅事件
if (_isToolPluginActive)
{
LogManager.WriteLog("[ToolPlugin] ToolPlugin已激活检查事件订阅状态");
LogManager.Debug("[ToolPlugin] ToolPlugin已激活检查事件订阅状态");
if (subscribeToEvents)
{
// 确保事件订阅(避免重复订阅)
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
LogManager.WriteLog("[ToolPlugin] 已确保PathPlanningManager事件订阅");
LogManager.Debug("[ToolPlugin] 已确保PathPlanningManager事件订阅");
}
return true;
}
try
{
LogManager.WriteLog("[ToolPlugin] ===== 开始激活ToolPlugin =====");
LogManager.WriteLog($"[ToolPlugin] 订阅事件: {subscribeToEvents}");
LogManager.WriteLog($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}");
LogManager.WriteLog($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}");
LogManager.Debug("[ToolPlugin] ===== 开始激活ToolPlugin =====");
LogManager.Debug($"[ToolPlugin] 订阅事件: {subscribeToEvents}");
LogManager.Debug($"[ToolPlugin] 当前应用程序状态: {Application.IsAutomated}");
LogManager.Debug($"[ToolPlugin] 当前文档状态: {Application.ActiveDocument?.Title ?? "NULL"}");
// 1. 加载插件程序集
LogManager.WriteLog("[ToolPlugin] 步骤1: 加载插件程序集");
LogManager.Debug("[ToolPlugin] 步骤1: 加载插件程序集");
var assemblyPath = PathClickToolPlugin.AssemblyPath;
LogManager.WriteLog($"[ToolPlugin] 程序集路径: {assemblyPath}");
LogManager.WriteLog($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}");
LogManager.Debug($"[ToolPlugin] 程序集路径: {assemblyPath}");
LogManager.Debug($"[ToolPlugin] 程序集文件是否存在: {System.IO.File.Exists(assemblyPath)}");
Application.Plugins.AddPluginAssembly(assemblyPath);
LogManager.WriteLog("[ToolPlugin] ✓ 程序集加载完成");
LogManager.Debug("[ToolPlugin] ✓ 程序集加载完成");
// 2. 查找插件
LogManager.WriteLog("[ToolPlugin] 步骤2: 查找插件");
LogManager.Debug("[ToolPlugin] 步骤2: 查找插件");
ToolPluginRecord toolPluginRecord = (ToolPluginRecord)Application.Plugins.FindPlugin("PathClickTool.NavisworksTransport");
if (toolPluginRecord == null)
{
LogManager.WriteLog("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件");
LogManager.Error("[ToolPlugin] ✗ 错误: 无法找到PathClickTool插件");
return false;
}
LogManager.WriteLog("[ToolPlugin] ✓ 插件查找成功");
LogManager.Debug("[ToolPlugin] ✓ 插件查找成功");
// 3. 加载插件
LogManager.WriteLog("[ToolPlugin] 步骤3: 加载插件");
LogManager.Debug("[ToolPlugin] 步骤3: 加载插件");
var loadedPlugin = toolPluginRecord.LoadPlugin();
if (loadedPlugin == null)
{
LogManager.WriteLog("[ToolPlugin] ✗ 错误: 插件加载失败");
LogManager.Error("[ToolPlugin] ✗ 错误: 插件加载失败");
return false;
}
LogManager.WriteLog("[ToolPlugin] ✓ 插件加载成功");
LogManager.Debug("[ToolPlugin] ✓ 插件加载成功");
// 4. 设置为活动工具
LogManager.WriteLog("[ToolPlugin] 步骤4: 设置为活动工具");
LogManager.Debug("[ToolPlugin] 步骤4: 设置为活动工具");
// 先重置工具状态,清除可能的导航工具
// 这一步很重要确保从导航工具如Pan、Orbit切换回自定义工具
Application.MainDocument.Tool.Value = Tool.None;
LogManager.WriteLog("[ToolPlugin] 已重置工具状态为None");
LogManager.Debug("[ToolPlugin] 已重置工具状态为None");
// 然后设置自定义工具插件
Application.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin);
LogManager.WriteLog("[ToolPlugin] ✓ 工具设置成功");
LogManager.Debug("[ToolPlugin] ✓ 工具设置成功");
// 5. 根据参数决定是否订阅点击事件
if (subscribeToEvents)
{
LogManager.WriteLog("[ToolPlugin] 步骤5: 订阅点击事件");
LogManager.Debug("[ToolPlugin] 步骤5: 订阅点击事件");
PathClickToolPlugin.MouseClicked += OnToolPluginMouseClicked;
LogManager.WriteLog("[ToolPlugin] ✓ PathPlanningManager事件订阅成功");
LogManager.Debug("[ToolPlugin] ✓ PathPlanningManager事件订阅成功");
}
else
{
LogManager.WriteLog("[ToolPlugin] 步骤5: 跳过事件订阅(由外部组件管理)");
LogManager.Debug("[ToolPlugin] 步骤5: 跳过事件订阅(由外部组件管理)");
}
_isToolPluginActive = true;
LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin激活完成 =====");
LogManager.Info("[ToolPlugin] ===== ToolPlugin激活完成 =====");
return true;
}
catch (Exception ex)
@ -2503,7 +2503,7 @@ namespace NavisworksTransport
var currentTool = Application.MainDocument.Tool.Value;
if (currentTool != Tool.CustomToolPlugin)
{
LogManager.WriteLog($"[ReactivateToolPlugin] 当前工具为{currentTool}开始重新激活ToolPlugin");
LogManager.Debug($"[ReactivateToolPlugin] 当前工具为{currentTool}开始重新激活ToolPlugin");
// 重要先将激活标志设置为false强制执行完整的激活流程
_isToolPluginActive = false;
@ -2511,23 +2511,23 @@ namespace NavisworksTransport
// 调用现有的激活方法,但不重复订阅事件
if (ActivateToolPlugin(false))
{
LogManager.WriteLog("[ReactivateToolPlugin] ToolPlugin重新激活成功");
LogManager.Info("[ReactivateToolPlugin] ToolPlugin重新激活成功");
RaiseStatusChanged("工具已重新激活", PathPlanningStatusType.Info);
}
else
{
LogManager.WriteLog("[ReactivateToolPlugin] ToolPlugin重新激活失败");
LogManager.Warning("[ReactivateToolPlugin] ToolPlugin重新激活失败");
RaiseErrorOccurred("无法重新激活编辑工具");
}
}
else
{
LogManager.WriteLog("[ReactivateToolPlugin] 当前已是CustomToolPlugin无需重新激活");
LogManager.Debug("[ReactivateToolPlugin] 当前已是CustomToolPlugin无需重新激活");
}
}
else
{
LogManager.WriteLog($"[ReactivateToolPlugin] 跳过重新激活 - PathEditState: {PathEditState}");
LogManager.Debug($"[ReactivateToolPlugin] 跳过重新激活 - PathEditState: {PathEditState}");
}
}
catch (Exception ex)
@ -2547,7 +2547,7 @@ namespace NavisworksTransport
// 如果在自动路径规划模式则跳过处理应该由PathEditingViewModel处理
if (IsInAutoPathMode)
{
LogManager.WriteLog("[ToolPlugin事件] 当前在自动路径规划模式跳过PathPlanningManager处理");
LogManager.Debug("[ToolPlugin事件] 当前在自动路径规划模式跳过PathPlanningManager处理");
return;
}
@ -2567,12 +2567,12 @@ namespace NavisworksTransport
private void ProcessManualPathEditing(PickItemResult pickResult)
{
// 检查当前选中的通道状态
LogManager.WriteLog($"[手动编辑] 当前_selectedChannels状态: {(_walkableAreas == null ? "NULL" : $"{_walkableAreas.Count}")}");
LogManager.Debug($"[手动编辑] 当前_selectedChannels状态: {(_walkableAreas == null ? "NULL" : $"{_walkableAreas.Count}")}");
// 如果没有选中的通道,尝试实时搜索
if (_walkableAreas == null || _walkableAreas.Count == 0)
{
LogManager.WriteLog("[手动编辑] 没有预选通道,开始实时搜索可通行的物流模型");
LogManager.Debug("[手动编辑] 没有预选通道,开始实时搜索可通行的物流模型");
SearchAndSetTraversableChannels();
}
@ -2582,7 +2582,7 @@ namespace NavisworksTransport
bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
LogManager.WriteLog($"[手动编辑] 在可通行的物流模型内: {isInTraversableLogisticsModel}");
LogManager.Debug($"[手动编辑] 在可通行的物流模型内: {isInTraversableLogisticsModel}");
if (isInTraversableLogisticsModel)
{
@ -2590,50 +2590,50 @@ namespace NavisworksTransport
if (PathEditState == PathEditState.AddingPoints)
{
// 添加路径点模式 - 使用预览点
LogManager.WriteLog("[手动编辑] 设置预览点位置");
LogManager.Debug("[手动编辑] 设置预览点位置");
var previewPoint = SetPreviewPoint(pickResult.Point);
if (previewPoint != null)
{
LogManager.WriteLog($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}");
LogManager.Debug($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}");
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 预览点设置失败");
LogManager.Warning("[手动编辑] ✗ 预览点设置失败");
}
}
else if (PathEditState == PathEditState.EditingPoint)
{
// 修改路径点模式 - 设置预览位置
LogManager.WriteLog("[手动编辑] 设置修改路径点预览位置");
LogManager.Debug("[手动编辑] 设置修改路径点预览位置");
SetEditingPreviewPoint(pickResult.Point);
LogManager.WriteLog($"[手动编辑] ✓ 修改路径点预览位置已设置: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
LogManager.Debug($"[手动编辑] ✓ 修改路径点预览位置已设置: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
}
else
{
// 其他编辑模式 - 保持原有逻辑
LogManager.WriteLog("[手动编辑] 调用AddPathPointIn3D添加路径点");
LogManager.Debug("[手动编辑] 调用AddPathPointIn3D添加路径点");
var pathPoint = AddPathPointIn3D(pickResult.Point);
if (pathPoint != null)
{
LogManager.WriteLog($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
LogManager.Debug($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 路径点添加失败");
LogManager.Warning("[手动编辑] ✗ 路径点添加失败");
}
}
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 点击位置不在可通行的物流模型内");
LogManager.Debug("[手动编辑] ✗ 点击位置不在可通行的物流模型内");
RaiseErrorOccurred("点击位置不在物流通道内,请选择有效的物流路径位置");
}
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 未找到可通行的物流模型");
LogManager.Warning("[手动编辑] ✗ 未找到可通行的物流模型");
RaiseErrorOccurred("未找到可通行的物流通道,请先选择或配置物流通道");
}
}
@ -2650,13 +2650,13 @@ namespace NavisworksTransport
{
// 使用CategoryAttributeManager统一接口获取物流项目
var logisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems();
LogManager.WriteLog($"[搜索通道] CategoryAttributeManager找到 {logisticsItemsCollection.Count} 个物流模型");
LogManager.Debug($"[搜索通道] CategoryAttributeManager找到 {logisticsItemsCollection.Count} 个物流模型");
if (logisticsItemsCollection.Count > 0)
{
// 筛选出可通行的物流模型
var traversableItems = CategoryAttributeManager.FilterTraversableItems(logisticsItemsCollection);
LogManager.WriteLog($"[搜索通道] 筛选出 {traversableItems.Count} 个可通行的物流模型");
LogManager.Debug($"[搜索通道] 筛选出 {traversableItems.Count} 个可通行的物流模型");
// 临时设置为选中通道
if (_walkableAreas == null) _walkableAreas = new List<ModelItem>();
@ -2665,14 +2665,14 @@ namespace NavisworksTransport
foreach (ModelItem item in traversableItems)
{
_walkableAreas.Add(item);
LogManager.WriteLog($"[搜索通道] 添加可通行模型: '{item.DisplayName}'");
LogManager.Debug($"[搜索通道] 添加可通行模型: '{item.DisplayName}'");
}
}
}
}
catch (Exception searchEx)
{
LogManager.WriteLog($"[搜索通道] 实时搜索失败: {searchEx.Message}");
LogManager.Error($"[搜索通道] 实时搜索失败: {searchEx.Message}");
}
}
@ -2768,7 +2768,7 @@ namespace NavisworksTransport
{
if (!_isToolPluginActive)
{
LogManager.WriteLog("[ToolPlugin] ToolPlugin未激活无需停用");
LogManager.Debug("[ToolPlugin] ToolPlugin未激活无需停用");
return true;
}
@ -2785,11 +2785,11 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[ToolPlugin] 重置工具状态失败: {ex.Message}");
LogManager.Warning($"[ToolPlugin] 重置工具状态失败: {ex.Message}");
}
_isToolPluginActive = false;
LogManager.WriteLog("[ToolPlugin] ===== ToolPlugin停用完成 =====");
LogManager.Debug("[ToolPlugin] ===== ToolPlugin停用完成 =====");
return true;
}
catch (Exception ex)
@ -2809,27 +2809,27 @@ namespace NavisworksTransport
{
try
{
LogManager.WriteLog($"[工具插件重初始化] 开始强制重新初始化ToolPlugin订阅事件: {subscribeToEvents}");
LogManager.Debug($"[工具插件重初始化] 开始强制重新初始化ToolPlugin订阅事件: {subscribeToEvents}");
// 1. 停用当前ToolPlugin
bool deactivated = DeactivateToolPlugin();
LogManager.WriteLog($"[工具插件重初始化] 停用结果: {deactivated}");
LogManager.Debug($"[工具插件重初始化] 停用结果: {deactivated}");
// 2. 强制重置激活状态
_isToolPluginActive = false;
LogManager.WriteLog("[工具插件重初始化] 已重置激活状态标志");
LogManager.Debug("[工具插件重初始化] 已重置激活状态标志");
// 3. 重新激活ToolPlugin
bool activated = ActivateToolPlugin(subscribeToEvents);
LogManager.WriteLog($"[工具插件重初始化] 激活结果(事件订阅: {subscribeToEvents}: {activated}");
LogManager.Debug($"[工具插件重初始化] 激活结果(事件订阅: {subscribeToEvents}: {activated}");
if (activated)
{
LogManager.WriteLog("[工具插件重初始化] ToolPlugin重新初始化成功已获得鼠标焦点");
LogManager.Info("[工具插件重初始化] ToolPlugin重新初始化成功已获得鼠标焦点");
}
else
{
LogManager.WriteLog("[工具插件重初始化] ToolPlugin激活失败");
LogManager.Error("[工具插件重初始化] ToolPlugin激活失败");
}
return activated;

View File

@ -447,7 +447,7 @@ namespace NavisworksTransport
catch (Exception ex)
{
// 渲染异常应该静默处理避免影响Navisworks主程序
LogManager.WriteLog($"[渲染异常] {ex.Message}");
LogManager.Error($"[渲染异常] {ex.Message}", ex);
// 不输出堆栈信息,避免日志过量
}
}
@ -462,7 +462,6 @@ namespace NavisworksTransport
{
if (pathRoute == null)
{
LogManager.WriteLog("[路径渲染] 路径数据为空");
return;
}
@ -481,12 +480,11 @@ namespace NavisworksTransport
_pathVisualizations[pathRoute.Id] = visualization;
}
LogManager.WriteLog($"[路径渲染] 渲染路径: {pathRoute.Name}, 点数: {pathRoute.Points.Count}");
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径渲染] 渲染路径失败: {ex.Message}");
LogManager.Error($"[路径渲染] 渲染路径失败: {ex.Message}", ex);
}
}
@ -497,7 +495,6 @@ namespace NavisworksTransport
{
if (pathRoute == null)
{
LogManager.WriteLog("[点标记渲染] 路径数据为空");
return;
}
@ -523,22 +520,12 @@ namespace NavisworksTransport
{
_pathVisualizations[pathRoute.Id] = visualization;
}
// 网格可视化只打印统计日志,不打印详细日志
if (!isGridVisualization)
{
LogManager.WriteLog($"[点标记渲染] 渲染点标记: {pathRoute.Name}, 点数: {pathRoute.Points.Count}");
}
else
{
LogManager.WriteLog($"[网格可视化] 渲染网格点: {pathRoute.Points.Count} 个可通行单元");
}
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.WriteLog($"[点标记渲染] 渲染点标记失败: {ex.Message}");
LogManager.Error($"[点标记渲染] 渲染点标记失败: {ex.Message}", ex);
}
}
@ -572,12 +559,6 @@ namespace NavisworksTransport
}
visualization.LastUpdated = DateTime.Now;
// 网格可视化不打印详细构建日志,避免日志泛滥
if (!isGridVisualization)
{
LogManager.WriteLog($"[点标记构建] 已构建 {visualization.PointMarkers.Count} 个点标记,无连线");
}
}
/// <summary>
@ -591,7 +572,6 @@ namespace NavisworksTransport
if (_pathVisualizations.TryGetValue(pathId, out var visualization))
{
BuildVisualization(visualization);
LogManager.WriteLog($"[路径刷新] 刷新路径: {pathId}");
RequestViewRefresh();
}
}
@ -628,7 +608,6 @@ namespace NavisworksTransport
if (normalPathIds.Count > 0)
{
LogManager.WriteLog($"[路径刷新] 刷新普通路径: {normalPathIds.Count}个, 模式: {_visualizationMode}");
RequestViewRefresh();
}
}
@ -644,7 +623,6 @@ namespace NavisworksTransport
{
if (_pathVisualizations.Remove(pathId))
{
LogManager.WriteLog($"[路径移除] 移除路径: {pathId}");
RequestViewRefresh();
}
}
@ -659,7 +637,6 @@ namespace NavisworksTransport
{
var count = _pathVisualizations.Count;
_pathVisualizations.Clear();
LogManager.WriteLog($"[路径清空] 清空所有路径,共{count}个");
RequestViewRefresh();
}
}
@ -682,23 +659,10 @@ namespace NavisworksTransport
var excludedSet = new HashSet<string>(excludedPathIds.Where(id => !string.IsNullOrEmpty(id)));
var toRemove = _pathVisualizations.Keys.Where(id => !excludedSet.Contains(id)).ToList();
// 记录当前状态
LogManager.WriteLog($"[选择性清空] 当前路径数: {_pathVisualizations.Count}, 排除路径数: {excludedSet.Count}, 待清理路径数: {toRemove.Count}");
// 检查排除的路径是否实际存在
var existingExcluded = excludedSet.Where(id => _pathVisualizations.ContainsKey(id)).ToList();
var nonExistingExcluded = excludedSet.Where(id => !_pathVisualizations.ContainsKey(id)).ToList();
if (nonExistingExcluded.Any())
{
LogManager.WriteLog($"[选择性清空] 注意:尝试保留的路径不存在: {string.Join(", ", nonExistingExcluded)}");
}
if (existingExcluded.Any())
{
LogManager.WriteLog($"[选择性清空] 将保留的现有路径: {string.Join(", ", existingExcluded)}");
}
int removedCount = 0;
foreach (var pathId in toRemove)
@ -709,7 +673,6 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"[选择性清空] 成功清空{removedCount}个路径,保留{_pathVisualizations.Count}个路径");
if (removedCount > 0)
{
RequestViewRefresh();
@ -799,8 +762,6 @@ namespace NavisworksTransport
CreatedTime = DateTime.Now
};
visualization.PointMarkers.Add(grayEndMarker);
LogManager.WriteLog($"[路径可视化] 添加未到达终点: ({originalEndPoint.X:F2}, {originalEndPoint.Y:F2}, {originalEndPoint.Z:F2}), 完成度: {visualization.PathRoute.CompletionPercentage:F1}%");
}
visualization.LastUpdated = DateTime.Now;
@ -918,7 +879,6 @@ namespace NavisworksTransport
public void SetGridSize(double gridSizeInMeters)
{
_currentGridSizeInMeters = gridSizeInMeters;
LogManager.WriteLog($"[网格点大小] 设置网格大小: {gridSizeInMeters:F2}米");
}
/// <summary>
@ -935,8 +895,6 @@ namespace NavisworksTransport
_vehicleHeight = vehicleHeight;
_safetyMargin = safetyMargin;
LogManager.WriteLog($"[车辆参数] 更新车辆参数: 长={vehicleLength:F2}m, 宽={vehicleWidth:F2}m, 高={vehicleHeight:F2}m, 安全间隙={safetyMargin:F2}m");
// 车辆参数改变时刷新车辆空间模式下的所有普通路径
if (_visualizationMode == PathVisualizationMode.VehicleSpace)
{
@ -1032,7 +990,6 @@ namespace NavisworksTransport
var visualization = GetPathVisualization(pathId);
if (visualization == null)
{
LogManager.WriteLog($"[路径编辑] 未找到路径: {pathId}");
return;
}
@ -1056,12 +1013,11 @@ namespace NavisworksTransport
// 重建可视化
BuildVisualization(visualization);
LogManager.WriteLog($"[路径编辑] 添加路径点: {newPoint.Name} 到路径 {pathId}");
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径编辑] 添加路径点失败: {ex.Message}");
LogManager.Error($"[路径编辑] 添加路径点失败: {ex.Message}", ex);
}
}
@ -1075,7 +1031,6 @@ namespace NavisworksTransport
var visualization = GetPathVisualization(pathId);
if (visualization == null)
{
LogManager.WriteLog($"[路径编辑] 未找到路径: {pathId}");
return;
}
@ -1092,17 +1047,16 @@ namespace NavisworksTransport
// 重建可视化
BuildVisualization(visualization);
LogManager.WriteLog($"[路径编辑] 移除路径点: 索引 {pointIndex} 从路径 {pathId}");
RequestViewRefresh();
}
else
{
LogManager.WriteLog($"[路径编辑] 未找到索引为 {pointIndex} 的路径点");
LogManager.Warning($"[路径编辑] 未找到索引为 {pointIndex} 的路径点");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径编辑] 移除路径点失败: {ex.Message}");
LogManager.Error($"[路径编辑] 移除路径点失败: {ex.Message}", ex);
}
}
@ -1117,7 +1071,6 @@ namespace NavisworksTransport
var visualization = GetPathVisualization(pathId);
if (visualization == null)
{
LogManager.WriteLog($"[路径编辑] 未找到路径: {pathId}");
return;
}
@ -1137,17 +1090,16 @@ namespace NavisworksTransport
// 重建可视化
BuildVisualization(visualization);
LogManager.WriteLog($"[路径编辑] 更新路径点: 索引 {pointIndex} 在路径 {pathId}");
RequestViewRefresh();
}
else
{
LogManager.WriteLog($"[路径编辑] 未找到索引为 {pointIndex} 的路径点");
LogManager.Warning($"[路径编辑] 未找到索引为 {pointIndex} 的路径点");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[路径编辑] 更新路径点失败: {ex.Message}");
LogManager.Error($"[路径编辑] 更新路径点失败: {ex.Message}", ex);
}
}
@ -1190,14 +1142,11 @@ namespace NavisworksTransport
{
if (previewPoint == null)
{
LogManager.WriteLog("[预览点渲染] 预览点为null跳过渲染");
return;
}
lock (_lockObject)
{
LogManager.WriteLog($"[预览点渲染] 开始渲染预览点: {previewPoint.Name}");
// 获取适当的半径
double radius = GetRadiusForPointType(previewPoint.Type);
@ -1214,8 +1163,6 @@ namespace NavisworksTransport
IsVisible = true,
PathPoint = previewPoint
};
LogManager.WriteLog($"[预览点渲染] 预览点标记已创建: 位置({previewPoint.Position.X:F2}, {previewPoint.Position.Y:F2}, {previewPoint.Position.Z:F2}), 半径: {radius:F2}, 颜色: 灰色");
// 请求刷新视图
RequestViewRefresh();
@ -1223,8 +1170,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览点渲染] 渲染预览点失败: {ex.Message}");
LogManager.WriteLog($"[预览点渲染] 堆栈跟踪: {ex.StackTrace}");
LogManager.Error($"[预览点渲染] 渲染预览点失败: {ex.Message}", ex);
}
}
@ -1239,22 +1185,17 @@ namespace NavisworksTransport
{
if (_previewMarker != null || _previewLines.Count > 0)
{
LogManager.WriteLog($"[预览点渲染] 清除预览点和连线: {_previewMarker?.PathPoint?.Name ?? ""}");
_previewMarker = null;
_previewLines.Clear();
// 请求刷新视图
RequestViewRefresh();
}
else
{
LogManager.WriteLog("[预览点渲染] 没有预览点或连线需要清除");
}
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览点渲染] 清除预览点失败: {ex.Message}");
LogManager.Error($"[预览点渲染] 清除预览点失败: {ex.Message}", ex);
}
}
@ -1269,7 +1210,6 @@ namespace NavisworksTransport
{
if (previewPoint == null || pathPoints == null || pathPoints.Count == 0)
{
LogManager.WriteLog("[预览连线渲染] 参数无效,跳过预览连线渲染");
return;
}
@ -1306,11 +1246,10 @@ namespace NavisworksTransport
};
_previewLines.Add(line2);
LogManager.WriteLog($"[预览连线渲染] 已创建预览连线: {prevPoint.Name} -> {previewPoint.Name} -> {nextPoint.Name}");
}
else
{
LogManager.WriteLog("[预览连线渲染] 未找到合适的线段插入预览点");
LogManager.Warning("[预览连线渲染] 未找到合适的线段插入预览点");
}
// 请求刷新视图
@ -1319,7 +1258,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览连线渲染] 渲染预览连线失败: {ex.Message}");
LogManager.Error($"[预览连线渲染] 渲染预览连线失败: {ex.Message}", ex);
}
}
@ -1334,7 +1273,6 @@ namespace NavisworksTransport
{
if (_previewLines.Count > 0)
{
LogManager.WriteLog($"[预览连线渲染] 清除 {_previewLines.Count} 条预览连线");
_previewLines.Clear();
// 请求刷新视图
@ -1344,7 +1282,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览连线渲染] 清除预览连线失败: {ex.Message}");
LogManager.Error($"[预览连线渲染] 清除预览连线失败: {ex.Message}", ex);
}
}
@ -1358,14 +1296,11 @@ namespace NavisworksTransport
{
if (previewRoute == null || previewRoute.Points.Count == 0)
{
LogManager.WriteLog("[预览路径渲染] 预览路径为null或没有路径点跳过渲染");
return;
}
lock (_lockObject)
{
LogManager.WriteLog($"[预览路径渲染] 开始渲染预览路径: {previewRoute.Name}, 点数: {previewRoute.Points.Count}");
// 只清理预览元素,不影响原有路径
ClearPreviewPoint();
ClearPreviewLines();
@ -1416,23 +1351,19 @@ namespace NavisworksTransport
Radius = GetLineRadius()
};
_previewLines.Add(line2);
}
LogManager.WriteLog($"[预览路径渲染] 已渲染编辑预览: 预览点 + {_previewLines.Count} 条连线");
}
}
else
{
LogManager.WriteLog("[预览路径渲染] 未找到预览点,跳过渲染");
LogManager.Warning("[预览路径渲染] 未找到预览点,跳过渲染");
}
RequestViewRefresh();
LogManager.WriteLog("[预览路径渲染] 预览路径渲染完成");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览路径渲染] 渲染预览路径失败: {ex.Message}");
LogManager.WriteLog($"[预览路径渲染] 堆栈跟踪: {ex.StackTrace}");
LogManager.Error($"[预览路径渲染] 渲染预览路径失败: {ex.Message}", ex);
}
}
@ -1445,8 +1376,6 @@ namespace NavisworksTransport
{
lock (_lockObject)
{
LogManager.WriteLog("[清理预览] 开始清理所有预览渲染");
// 清理预览点
ClearPreviewPoint();
@ -1454,12 +1383,11 @@ namespace NavisworksTransport
ClearPreviewLines();
RequestViewRefresh();
LogManager.WriteLog("[清理预览] 预览清理完成");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[清理预览] 清理预览失败: {ex.Message}");
LogManager.Error($"[清理预览] 清理预览失败: {ex.Message}", ex);
}
}
@ -1502,9 +1430,7 @@ namespace NavisworksTransport
// 转换为模型单位
double radiusInModelUnits = baseRadiusInMeters * metersToModelUnits;
//LogManager.WriteLog($"[半径计算] 类型={pointType}, 网格大小={_currentGridSizeInMeters:F2}m, 标准尺寸={standardRadiusInMeters:F2}m, 基础半径={baseRadiusInMeters:F2}m, 最终半径={radiusInModelUnits:F2}");
return radiusInModelUnits;
}
@ -1517,52 +1443,38 @@ namespace NavisworksTransport
try
{
var units = Application.ActiveDocument.Units;
//LogManager.WriteLog($"[单位检测] API返回的文档单位: {units}");
switch (units)
{
case Units.Millimeters:
//LogManager.WriteLog("[单位检测] 确认为毫米单位,转换系数=1000");
return 1000.0; // 1米 = 1000毫米
case Units.Centimeters:
//LogManager.WriteLog("[单位检测] 确认为厘米单位,转换系数=100");
return 100.0; // 1米 = 100厘米
case Units.Meters:
//LogManager.WriteLog("[单位检测] 确认为米单位,转换系数=1");
return 1.0; // 1米 = 1米
case Units.Inches:
//LogManager.WriteLog("[单位检测] 确认为英寸单位,转换系数=39.37");
return 39.37; // 1米 = 39.37英寸
case Units.Feet:
//LogManager.WriteLog("[单位检测] 确认为英尺单位,转换系数=3.281");
return 3.281; // 1米 = 3.281英尺
case Units.Kilometers:
//LogManager.WriteLog("[单位检测] 确认为公里单位,转换系数=0.001");
return 0.001; // 1米 = 0.001公里
case Units.Micrometers:
//LogManager.WriteLog("[单位检测] 确认为微米单位,转换系数=1000000");
return 1000000.0; // 1米 = 1000000微米
case Units.Microinches:
//LogManager.WriteLog("[单位检测] 确认为微英寸单位,转换系数=39370078.74");
return 39370078.74; // 1米 = 39370078.74微英寸
case Units.Mils:
//LogManager.WriteLog("[单位检测] 确认为密尔单位,转换系数=39370.08");
return 39370.08; // 1米 = 39370.08密尔
case Units.Yards:
//LogManager.WriteLog("[单位检测] 确认为码单位,转换系数=1.094");
return 1.094; // 1米 = 1.094码
case Units.Miles:
//LogManager.WriteLog("[单位检测] 确认为英里单位,转换系数=0.000621");
return 0.000621; // 1米 = 0.000621英里
return 0.000621; // 1米 = 0.000621英里
default:
//LogManager.WriteLog($"[单位检测] 未知单位类型: {units},使用默认米单位,转换系数=1");
return 1.0;
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[单位检测] API调用失败: {ex.Message}");
LogManager.WriteLog("[单位检测] 使用默认米单位,转换系数=1");
LogManager.Error($"[单位检测] API调用失败: {ex.Message}");
return 1.0;
}
}
@ -1659,7 +1571,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[车辆空间渲染] 渲染车辆通行空间失败: {ex.Message}");
LogManager.Error($"[车辆空间渲染] 渲染车辆通行空间失败: {ex.Message}", ex);
}
}
@ -1686,7 +1598,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[视图刷新] 失败: {ex.Message}");
LogManager.Error($"[视图刷新] 失败: {ex.Message}", ex);
}
}

View File

@ -148,7 +148,7 @@ namespace NavisworksTransport
properties,
propertyInternalNames);
LogManager.WriteLog($"[属性添加] 添加操作完成,成功添加 {successCount} 个模型的属性");
LogManager.Info($"[属性添加] 添加操作完成,成功添加 {successCount} 个模型的属性");
return successCount;
}
@ -584,7 +584,7 @@ namespace NavisworksTransport
propertyInternalNames,
overwriteExisting: true);
LogManager.WriteLog($"[属性更新] 更新操作完成,成功更新 {successCount} 个模型的属性");
LogManager.Info($"[属性更新] 更新操作完成,成功更新 {successCount} 个模型的属性");
return successCount;
}
@ -653,7 +653,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"获取通用属性值时发生错误: {ex.Message}");
LogManager.Error($"获取通用属性值时发生错误: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"获取通用属性值时发生错误: {ex.Message}");
return null;
}
@ -683,7 +683,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"获取修剪后的通用属性值时发生错误: {ex.Message}");
LogManager.Error($"获取修剪后的通用属性值时发生错误: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"获取修剪后的通用属性值时发生错误: {ex.Message}");
return null;
}
@ -714,7 +714,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"安全获取属性值时发生错误: {ex.Message}");
LogManager.Error($"安全获取属性值时发生错误: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"安全获取属性值时发生错误: {ex.Message}");
return null;
}
@ -744,7 +744,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"安全获取通用属性值时发生错误: {ex.Message}");
LogManager.Error($"安全获取通用属性值时发生错误: {ex.Message}");
System.Diagnostics.Debug.WriteLine($"安全获取通用属性值时发生错误: {ex.Message}");
return null;
}

View File

@ -79,36 +79,36 @@ namespace NavisworksTransport
// 🎯 存在属性时:使用正确的索引进行更新
// 注意SetUserDefined的索引是从1开始的
int updateIndex = existingIndex + 1;
LogManager.WriteLog($"[COM属性管理] 发现现有属性类别在索引 {existingIndex},将使用索引 {updateIndex} 进行更新");
LogManager.Debug($"[COM属性管理] 发现现有属性类别在索引 {existingIndex},将使用索引 {updateIndex} 进行更新");
try
{
// 使用正确的索引覆盖现有PropertyCategory
propertyNode.SetUserDefined(updateIndex, categoryDisplayName,
categoryInternalName, propertyCategory);
LogManager.WriteLog($"[COM属性管理] ✅ 成功覆盖现有属性类别 (index={updateIndex})");
LogManager.Info($"[COM属性管理] ✅ 成功覆盖现有属性类别 (index={updateIndex})");
}
catch (Exception setEx)
{
LogManager.WriteLog($"[COM属性管理] ❌ 覆盖属性失败,错误: {setEx.Message}");
LogManager.Error($"[COM属性管理] ❌ 覆盖属性失败,错误: {setEx.Message}");
throw;
}
}
else
{
// 🆕 不存在属性时使用索引0创建新属性
LogManager.WriteLog("[COM属性管理] 未发现现有属性类别,将创建新的属性类别");
LogManager.Debug("[COM属性管理] 未发现现有属性类别,将创建新的属性类别");
try
{
// 使用参数0表示创建新的PropertyCategory
propertyNode.SetUserDefined(0, categoryDisplayName,
categoryInternalName, propertyCategory);
LogManager.WriteLog("[COM属性管理] ✅ 成功创建新的属性类别 (index=0)");
LogManager.Info("[COM属性管理] ✅ 成功创建新的属性类别 (index=0)");
}
catch (Exception setEx)
{
LogManager.WriteLog($"[COM属性管理] ❌ 创建属性失败,错误: {setEx.Message}");
LogManager.Error($"[COM属性管理] ❌ 创建属性失败,错误: {setEx.Message}");
throw;
}
}
@ -117,7 +117,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[COM属性管理] 处理单个模型项时发生错误: {ex.Message}");
LogManager.Error($"[COM属性管理] 处理单个模型项时发生错误: {ex.Message}");
}
finally
{
@ -141,7 +141,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[COM属性管理] 设置属性时发生错误: {ex.Message}");
LogManager.Error($"[COM属性管理] 设置属性时发生错误: {ex.Message}");
}
finally
{
@ -152,7 +152,7 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"[COM属性管理] 操作完成,成功处理 {successCount} 个模型的属性");
LogManager.Info($"[COM属性管理] 操作完成,成功处理 {successCount} 个模型的属性");
return successCount;
}
@ -174,7 +174,7 @@ namespace NavisworksTransport
int successCount = 0;
ComApi.InwOpSelection comSelection = null;
LogManager.WriteLog($"[COM属性管理] 开始删除 {items.Count} 个模型的属性类别: {categoryDisplayName}");
LogManager.Info($"[COM属性管理] 开始删除 {items.Count} 个模型的属性类别: {categoryDisplayName}");
try
{
@ -196,7 +196,7 @@ namespace NavisworksTransport
if (relativeIndex < 0)
{
LogManager.WriteLog($"[COM属性管理] 该模型没有属性类别 '{categoryDisplayName}',跳过");
LogManager.Debug($"[COM属性管理] 该模型没有属性类别 '{categoryDisplayName}',跳过");
continue;
}
@ -206,11 +206,11 @@ namespace NavisworksTransport
propertyNode.RemoveUserDefined(userDefinedIndex);
successCount++;
LogManager.WriteLog($"[COM属性管理] ✅ 成功删除属性类别 (索引={userDefinedIndex})");
LogManager.Info($"[COM属性管理] ✅ 成功删除属性类别 (索引={userDefinedIndex})");
}
catch (Exception ex)
{
LogManager.WriteLog($"[COM属性管理] ❌ 删除单个模型项属性时发生错误: {ex.Message}");
LogManager.Error($"[COM属性管理] ❌ 删除单个模型项属性时发生错误: {ex.Message}");
}
finally
{
@ -229,7 +229,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[COM属性管理] ❌ 删除属性时发生错误: {ex.Message}");
LogManager.Error($"[COM属性管理] ❌ 删除属性时发生错误: {ex.Message}");
}
finally
{
@ -240,7 +240,7 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"[COM属性管理] 删除操作完成,成功删除 {successCount} 个模型的属性");
LogManager.Info($"[COM属性管理] 删除操作完成,成功删除 {successCount} 个模型的属性");
return successCount;
}
@ -317,11 +317,11 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"[COM API读取] 模型: {item.DisplayName}, 属性: {propertyName} - 未找到");
LogManager.Debug($"[COM API读取] 模型: {item.DisplayName}, 属性: {propertyName} - 未找到");
}
catch (Exception ex)
{
LogManager.WriteLog($"通过COM API获取属性值时发生错误: {ex.Message}");
LogManager.Error($"通过COM API获取属性值时发生错误: {ex.Message}");
}
finally
{
@ -360,7 +360,7 @@ namespace NavisworksTransport
try
{
LogManager.WriteLog($"[COM属性管理] 开始更新模型 {item.DisplayName} 的属性 {propertyName} = {propertyValue}");
LogManager.Debug($"[COM属性管理] 开始更新模型 {item.DisplayName} 的属性 {propertyName} = {propertyValue}");
// 转换ModelItem为COM路径
comPath = ComApiBridge.ToInwOaPath(item);
@ -373,7 +373,7 @@ namespace NavisworksTransport
if (existingIndex < 0)
{
LogManager.WriteLog($"[COM属性管理] 模型没有属性类别 '{categoryDisplayName}',无法更新属性");
LogManager.Warning($"[COM属性管理] 模型没有属性类别 '{categoryDisplayName}',无法更新属性");
return false;
}
@ -405,7 +405,7 @@ namespace NavisworksTransport
if (existingAttribute == null)
{
LogManager.WriteLog($"[COM属性管理] 无法获取现有的属性类别 '{categoryDisplayName}'");
LogManager.Warning($"[COM属性管理] 无法获取现有的属性类别 '{categoryDisplayName}'");
return false;
}
@ -449,7 +449,7 @@ namespace NavisworksTransport
propertyNode.SetUserDefined(updateIndex, categoryDisplayName,
categoryInternalName, newPropertyCategory);
LogManager.WriteLog($"[COM属性管理] ✅ 成功更新属性 {propertyName} = {propertyValue} (index={updateIndex})");
LogManager.Info($"[COM属性管理] ✅ 成功更新属性 {propertyName} = {propertyValue} (index={updateIndex})");
return true;
}
finally
@ -466,7 +466,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[COM属性管理] ❌ 更新属性失败,错误: {ex.Message}");
LogManager.Error($"[COM属性管理] ❌ 更新属性失败,错误: {ex.Message}");
return false;
}
finally
@ -527,13 +527,13 @@ namespace NavisworksTransport
try
{
int userDefinedIndex = 0; // 用户定义属性的索引计数器
LogManager.WriteLog($"[相对索引查找] 开始查找属性分类 '{categoryDisplayName}' 的相对索引");
LogManager.Debug($"[相对索引查找] 开始查找属性分类 '{categoryDisplayName}' 的相对索引");
foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes())
{
try
{
LogManager.WriteLog($"[相对索引查找] 检查属性: UserDefined={attribute.UserDefined}, ClassUserName='{attribute.ClassUserName ?? ""}'");
LogManager.Debug($"[相对索引查找] 检查属性: UserDefined={attribute.UserDefined}, ClassUserName='{attribute.ClassUserName ?? ""}'");
if (attribute.UserDefined)
{
@ -541,12 +541,12 @@ namespace NavisworksTransport
// ClassName是系统生成的如"LcOaPropOverrideCat"),我们无法控制
if (attribute.ClassUserName == categoryDisplayName)
{
LogManager.WriteLog($"[相对索引查找] ✅ 找到属性分类,相对索引为: {userDefinedIndex}, ClassUserName='{attribute.ClassUserName}'");
LogManager.Debug($"[相对索引查找] ✅ 找到属性分类,相对索引为: {userDefinedIndex}, ClassUserName='{attribute.ClassUserName}'");
return userDefinedIndex; // 返回在用户定义属性中的索引位置
}
// 只有用户定义的属性才计入索引
userDefinedIndex++;
LogManager.WriteLog($"[相对索引查找] 发现其他用户定义属性 '{attribute.ClassUserName}',当前索引计数: {userDefinedIndex}");
LogManager.Debug($"[相对索引查找] 发现其他用户定义属性 '{attribute.ClassUserName}',当前索引计数: {userDefinedIndex}");
}
}
finally
@ -558,12 +558,12 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"[相对索引查找] ❌ 未找到属性分类 '{categoryDisplayName}',总共检查了 {userDefinedIndex} 个用户定义属性");
LogManager.Debug($"[相对索引查找] ❌ 未找到属性分类 '{categoryDisplayName}',总共检查了 {userDefinedIndex} 个用户定义属性");
return -1; // 未找到返回-1
}
catch (Exception ex)
{
LogManager.WriteLog($"[相对索引查找] 查找属性相对索引时发生错误: {ex.Message}");
LogManager.Error($"[相对索引查找] 查找属性相对索引时发生错误: {ex.Message}");
return -1;
}
}
@ -599,7 +599,7 @@ namespace NavisworksTransport
try
{
LogManager.WriteLog($"[COM楼层管理] 开始设置模型 {item.DisplayName} 的楼层属性");
LogManager.Debug($"[COM楼层管理] 开始设置模型 {item.DisplayName} 的楼层属性");
// 转换ModelItem为COM路径
comPath = ComApiBridge.ToInwOaPath(item);
@ -640,39 +640,39 @@ namespace NavisworksTransport
{
// 🎯 存在属性时:使用正确的索引进行更新
int updateIndex = existingIndex + 1;
LogManager.WriteLog($"[COM楼层管理] 发现现有楼层属性在索引 {existingIndex},将使用索引 {updateIndex} 进行更新");
LogManager.Debug($"[COM楼层管理] 发现现有楼层属性在索引 {existingIndex},将使用索引 {updateIndex} 进行更新");
try
{
propertyNode.SetUserDefined(updateIndex, categoryDisplayName,
categoryInternalName, propertyCategory);
LogManager.WriteLog($"[COM楼层管理] ✅ 成功覆盖现有楼层属性类别 (index={updateIndex})");
LogManager.Info($"[COM楼层管理] ✅ 成功覆盖现有楼层属性类别 (index={updateIndex})");
}
catch (Exception setEx)
{
LogManager.WriteLog($"[COM楼层管理] ❌ 覆盖楼层属性失败,错误: {setEx.Message}");
LogManager.Error($"[COM楼层管理] ❌ 覆盖楼层属性失败,错误: {setEx.Message}");
throw;
}
}
else
{
// 🆕 不存在属性时使用索引0创建新属性
LogManager.WriteLog("[COM楼层管理] 未发现现有楼层属性,将创建新的属性类别");
LogManager.Debug("[COM楼层管理] 未发现现有楼层属性,将创建新的属性类别");
try
{
propertyNode.SetUserDefined(0, categoryDisplayName,
categoryInternalName, propertyCategory);
LogManager.WriteLog("[COM楼层管理] ✅ 成功创建新的楼层属性类别 (index=0)");
LogManager.Info("[COM楼层管理] ✅ 成功创建新的楼层属性类别 (index=0)");
}
catch (Exception setEx)
{
LogManager.WriteLog($"[COM楼层管理] ❌ 创建楼层属性失败,错误: {setEx.Message}");
LogManager.Error($"[COM楼层管理] ❌ 创建楼层属性失败,错误: {setEx.Message}");
throw;
}
}
LogManager.WriteLog($"[COM楼层管理] ✅ 成功设置楼层属性");
LogManager.Info($"[COM楼层管理] ✅ 成功设置楼层属性");
return true;
}
finally
@ -685,7 +685,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"[COM楼层管理] ❌ 设置楼层属性失败,错误: {ex.Message}");
LogManager.Error($"[COM楼层管理] ❌ 设置楼层属性失败,错误: {ex.Message}");
return false;
}
finally
@ -752,7 +752,7 @@ namespace NavisworksTransport
if (property.name == propertyInternalName)
{
string value = property.value?.ToString();
LogManager.WriteLog($"[COM楼层读取] 模型: {item.DisplayName}, 属性: {propertyInternalName}, 值: {value}");
LogManager.Debug($"[COM楼层读取] 模型: {item.DisplayName}, 属性: {propertyInternalName}, 值: {value}");
return value;
}
}
@ -787,7 +787,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"通过COM API获取楼层属性值时发生错误: {ex.Message}");
LogManager.Error($"通过COM API获取楼层属性值时发生错误: {ex.Message}");
return null;
}
finally

View File

@ -100,21 +100,6 @@ namespace NavisworksTransport.PathPlanning
Width = (int)Math.Ceiling(worldWidth / cellSize);
Height = (int)Math.Ceiling(worldHeight / cellSize);
// 检查网格大小是否超出限制
const int MAX_GRID_SIZE = 10000; // 最大10000x10000网格
const long MAX_TOTAL_CELLS = 50000000; // 最大5000万个单元格
if (Width > MAX_GRID_SIZE || Height > MAX_GRID_SIZE)
{
throw new ArgumentException($"网格尺寸过大: {Width}x{Height},最大允许{MAX_GRID_SIZE}x{MAX_GRID_SIZE}。请增加网格单元格大小或减小搜索区域。");
}
long totalCells = (long)Width * Height;
if (totalCells > MAX_TOTAL_CELLS)
{
throw new ArgumentException($"网格单元格总数过多: {totalCells:N0},最大允许{MAX_TOTAL_CELLS:N0}。请增加网格单元格大小或减小搜索区域。");
}
// 设置原点为边界框的最小点
Origin = bounds.Min;
@ -153,14 +138,8 @@ namespace NavisworksTransport.PathPlanning
{
for (int y = 0; y < Height; y++)
{
// 计算2D世界坐标并设置合理的初始Z坐标使用地面高度而不是0
var worldPos2D = GridToWorld2D(new GridPoint2D(x, y));
// 使用边界框最小Z值地面高度作为Unknown网格的初始高度
var worldPos = new Point3D(worldPos2D.X, worldPos2D.Y, Bounds.Min.Z);
var cell = new GridCell
{
// IsWalkable字段已删除通行性由HeightLayers决定
CellType = CategoryAttributeManager.LogisticsElementType.Unknown, // 修改:默认为未知/空洞类型
IsInChannel = false,
HeightLayers = new List<HeightLayer>(),
@ -260,7 +239,6 @@ namespace NavisworksTransport.PathPlanning
var worldPos = GridToWorld3D(gridPosition);
var cell = new GridCell
{
// IsWalkable字段已删除通行性由HeightLayers决定
CellType = cellType,
IsInChannel = cellType == CategoryAttributeManager.LogisticsElementType.,
HeightLayers = new List<HeightLayer>(),
@ -385,7 +363,6 @@ namespace NavisworksTransport.PathPlanning
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown ||
cell.CellType == CategoryAttributeManager.LogisticsElementType.)
{
// IsWalkable由层决定不再在网格级别设置
// Unknown和障碍物没有可通行层HasAnyWalkableLayer()自动返回false
cell.Cost = double.MaxValue;
}
@ -794,7 +771,6 @@ namespace NavisworksTransport.PathPlanning
{
var cell = new GridCell
{
// IsWalkable由层决定障碍物没有层或所有层IsWalkable=false
CellType = CategoryAttributeManager.LogisticsElementType.,
IsInChannel = false,
HeightLayers = new List<HeightLayer>(), // 空列表HasAnyWalkableLayer()返回false
@ -805,25 +781,6 @@ namespace NavisworksTransport.PathPlanning
return cell;
}
/// <summary>
/// 创建开放空间单元格
/// </summary>
/// <returns>开放空间单元格</returns>
public static GridCell CreateOpenSpace()
{
var cell = new GridCell
{
// IsWalkable字段已删除通行性由HeightLayers决定
CellType = CategoryAttributeManager.LogisticsElementType.,
IsInChannel = false,
HeightLayers = new List<HeightLayer>(),
ChannelType = ChannelType.Other,
SpeedLimit = 0
};
cell.Cost = cell.GetCost();
return cell;
}
/// <summary>
/// 创建门单元格
/// </summary>
@ -833,7 +790,6 @@ namespace NavisworksTransport.PathPlanning
{
var cell = new GridCell
{
// IsWalkable字段已删除通行性由HeightLayers决定
CellType = CategoryAttributeManager.LogisticsElementType.,
IsInChannel = isOpen,
HeightLayers = new List<HeightLayer>(),
@ -853,7 +809,6 @@ namespace NavisworksTransport.PathPlanning
{
var cell = new GridCell
{
// IsWalkable字段已删除通行性由HeightLayers决定
CellType = CategoryAttributeManager.LogisticsElementType.,
IsInChannel = true,
HeightLayers = new List<HeightLayer>(),

View File

@ -134,7 +134,6 @@ namespace NavisworksTransport.PathPlanning
// 智能收集无关项相关节点(包括子节点等)
var irrelevantRelatedItems = CollectRelatedItems(irrelevantItems);
LogManager.Info($"【生成网格地图】智能收集到 {irrelevantRelatedItems.Count} 个无关项相关节点(将被排除)");
var irrelevantItemsSet = irrelevantRelatedItems; // 使用收集后的完整集合
// 2. 先设置PassableHeights确保障碍物高度检查时有正确的高度范围
LogManager.Info("【生成网格地图】步骤2: 为可通行网格设置高度约束");
@ -143,7 +142,7 @@ namespace NavisworksTransport.PathPlanning
// 2.5. 处理障碍物(包围盒检测) - 使用高性能优化版本
LogManager.Info("【生成网格地图】步骤2.5: 高性能包围盒遍历处理障碍物");
ProcessObstaclesWithBoundingBox(channelCoverage.GridMap, traversableRelatedItems, scanHeightInModelUnits, channelCoverage, irrelevantItemsSet);
ProcessObstaclesWithBoundingBox(channelCoverage.GridMap, traversableRelatedItems, scanHeightInModelUnits, channelCoverage, irrelevantRelatedItems);
LogManager.Info($"【阶段2.5完成】障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
@ -159,7 +158,7 @@ namespace NavisworksTransport.PathPlanning
MarkBoundaryLayers(channelCoverage.GridMap, maxHeightDiffInModelUnits);
LogManager.Info($"【阶段2.7完成】边界层标记后网格统计: {channelCoverage.GridMap.GetStatistics()}");
// 3. 应用车辆尺寸膨胀(如果需要)
// 3. 应用车辆尺寸膨胀
if (vehicleRadius > 0 || safetyMargin > 0)
{
LogManager.Info("【生成网格地图】步骤3: 应用车辆膨胀");
@ -168,28 +167,22 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"【阶段3完成】车辆膨胀后网格统计: {channelCoverage.GridMap.GetStatistics()}");
}
// 注意:边界膨胀已集成到车辆膨胀中,无需单独处理
// 4. 设置规划起点和终点信息
if (planningStartPoint != default(Point3D) && planningEndPoint != default(Point3D))
{
channelCoverage.GridMap.PlanningStartPoint = planningStartPoint;
channelCoverage.GridMap.PlanningEndPoint = planningEndPoint;
channelCoverage.GridMap.HasPlanningPoints = true;
// 注意网格的Z坐标已在通道生成时设置为实际通道高度无需重新计算
}
// 将通道数据设置到GridMap中供后续PathPlanningManager使用
// 将通道数据设置到GridMap中
if (traversableRelatedItems != null && traversableRelatedItems.Any())
{
channelCoverage.GridMap.ChannelItems = traversableRelatedItems.ToList();
LogManager.Info($"【生成网格地图】已将 {traversableRelatedItems.Count} 个通道数据设置到GridMap中");
}
else if (channelCoverage.ChannelItems != null && channelCoverage.ChannelItems.Any())
{
channelCoverage.GridMap.ChannelItems = new List<ModelItem>(channelCoverage.ChannelItems);
LogManager.Info($"【生成网格地图】已将 {channelCoverage.ChannelItems.Count} 个通道数据设置到GridMap中");
}
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
@ -835,7 +828,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[高效膨胀] 膨胀半径: {inflationRadius}个网格单元");
// 使用高效的距离变换算法
ApplyVehicleInflationOptimized(gridMap, inflationRadius);
ApplyVehicleInflation(gridMap, inflationRadius);
}
catch (Exception ex)
{
@ -848,7 +841,7 @@ namespace NavisworksTransport.PathPlanning
/// 多层膨胀算法 - 分两次处理:障碍物膨胀 + 边界膨胀
/// 核心原则:障碍物从外围膨胀(不含本身),边界从边界本身膨胀(包含边界)
/// </summary>
private void ApplyVehicleInflationOptimized(GridMap gridMap, int inflationRadius)
private void ApplyVehicleInflation(GridMap gridMap, int inflationRadius)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
@ -1127,299 +1120,6 @@ namespace NavisworksTransport.PathPlanning
return (boundaryCount, inflatedCount);
}
/// <summary>
/// 旧的2D膨胀算法已废弃保留用于参考
/// </summary>
private void ApplyVehicleInflationOptimized_Old2D(GridMap gridMap, int inflationRadius)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
LogManager.Info($"【8连通膨胀】使用正确的8连通距离变换算法网格大小: {gridMap.Width}x{gridMap.Height}");
// 使用浮点数距离矩阵支持√2对角线距离
var distanceMap = new double[gridMap.Width, gridMap.Height];
const double SQRT2 = 1.414213562373095; // √2
// 初始化距离矩阵
int obstacleCount = 0;
int unknownCount = 0;
int boundaryCount = 0;
for (int x = 0; x < gridMap.Width; x++)
{
for (int y = 0; y < gridMap.Height; y++)
{
var cell = gridMap.Cells[x, y];
// 膨胀源点1.障碍物 2.Unknown网格 3.边界层网格
if (!cell.HasAnyWalkableLayer())
{
distanceMap[x, y] = 0.0;
obstacleCount++;
}
else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
{
distanceMap[x, y] = 0.0;
unknownCount++;
}
else if (cell.HeightLayers != null && cell.HeightLayers.Any(layer => layer.IsBoundary))
{
// 包含边界层的网格作为膨胀源点
distanceMap[x, y] = 0.0;
boundaryCount++;
}
else
{
distanceMap[x, y] = double.MaxValue;
}
}
}
LogManager.Info($"【8连通膨胀】膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}, 边界: {boundaryCount}");
// 使用标准的距离变换算法 - 正向扫描
for (int x = 0; x < gridMap.Width; x++)
{
for (int y = 0; y < gridMap.Height; y++)
{
if (distanceMap[x, y] == double.MaxValue)
{
double minDist = double.MaxValue;
// 检查左侧和上方的邻居4连通
if (x > 0)
minDist = Math.Min(minDist, distanceMap[x - 1, y] + 1.0);
if (y > 0)
minDist = Math.Min(minDist, distanceMap[x, y - 1] + 1.0);
// 检查对角线邻居8连通扩展
if (x > 0 && y > 0)
minDist = Math.Min(minDist, distanceMap[x - 1, y - 1] + SQRT2);
if (x > 0 && y < gridMap.Height - 1)
minDist = Math.Min(minDist, distanceMap[x - 1, y + 1] + SQRT2);
if (minDist != double.MaxValue)
distanceMap[x, y] = minDist;
}
}
}
LogManager.Info($"【8连通膨胀】正向扫描完成耗时: {stopwatch.ElapsedMilliseconds}ms");
// 反向扫描
for (int x = gridMap.Width - 1; x >= 0; x--)
{
for (int y = gridMap.Height - 1; y >= 0; y--)
{
if (distanceMap[x, y] != 0.0)
{
double minDist = distanceMap[x, y];
// 检查右侧和下方的邻居4连通
if (x < gridMap.Width - 1)
minDist = Math.Min(minDist, distanceMap[x + 1, y] + 1.0);
if (y < gridMap.Height - 1)
minDist = Math.Min(minDist, distanceMap[x, y + 1] + 1.0);
// 检查对角线邻居8连通扩展
if (x < gridMap.Width - 1 && y < gridMap.Height - 1)
minDist = Math.Min(minDist, distanceMap[x + 1, y + 1] + SQRT2);
if (x < gridMap.Width - 1 && y > 0)
minDist = Math.Min(minDist, distanceMap[x + 1, y - 1] + SQRT2);
distanceMap[x, y] = minDist;
}
}
}
LogManager.Info($"【8连通膨胀】反向扫描完成耗时: {stopwatch.ElapsedMilliseconds}ms");
// 应用膨胀结果
int inflatedCells = 0;
int processedCells = 0;
int totalCells = gridMap.Width * gridMap.Height;
int skippedCells = 0;
int protectedDoorCells = 0;
LogManager.Info($"【8连通膨胀】开始应用膨胀结果总单元格数: {totalCells}");
try
{
for (int x = 0; x < gridMap.Width; x++)
{
for (int y = 0; y < gridMap.Height; y++)
{
processedCells++;
// 每处理50万个单元格输出一次进度
if (processedCells % 500000 == 0)
{
double progress = (double)processedCells / totalCells * 100;
LogManager.Info($"【8连通膨胀】应用进度: {progress:F1}% ({processedCells}/{totalCells})");
}
var gridPos = new GridPoint2D(x, y);
var cell = gridMap.Cells[x, y];
// 只有满足以下所有条件才进行膨胀:
// 1. 当前位置原本是可通行的
// 2. 距离值不是无穷大(有有效的距离计算)
// 3. 距离小于等于膨胀半径(使用浮点数比较)
// 4. 距离值大于0不是原始障碍物
// 5. 不是门类型(门保护)
if (gridMap.IsWalkable(gridPos) &&
distanceMap[x, y] != double.MaxValue &&
distanceMap[x, y] > 0.0 &&
distanceMap[x, y] <= inflationRadius)
{
// 门元素保护:跳过门类型的网格,不将其膨胀为障碍物
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.)
{
protectedDoorCells++;
// 跳过门网格,保持其可通行状态
continue;
}
// 膨胀处理:保留层结构,只将所有层标记为不可通行
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
{
// 有高度层:将所有层标记为不可通行
for (int i = 0; i < cell.HeightLayers.Count; i++)
{
var layer = cell.HeightLayers[i];
layer.IsWalkable = false;
cell.HeightLayers[i] = layer;
}
}
// 更新网格类型和属性
cell.CellType = CategoryAttributeManager.LogisticsElementType.;
cell.Cost = double.MaxValue;
cell.IsInChannel = false;
gridMap.Cells[x, y] = cell;
inflatedCells++;
}
else if (distanceMap[x, y] == double.MaxValue || distanceMap[x, y] > inflationRadius)
{
skippedCells++;
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"【8连通膨胀】应用膨胀结果时发生错误: {ex.Message},已处理{processedCells}/{totalCells}个单元格");
throw;
}
// 单独处理包围盒边界膨胀
int boundaryInflatedCells = ApplyBoundaryInflationPost(gridMap, inflationRadius);
stopwatch.Stop();
LogManager.Info($"【8连通膨胀】车辆膨胀完成: 膨胀半径={inflationRadius}格, 新增障碍物={inflatedCells}个, 边界膨胀={boundaryInflatedCells}个, 跳过={skippedCells}个, 保护门网格={protectedDoorCells}个, 总耗时={stopwatch.ElapsedMilliseconds}ms");
}
/// <summary>
/// 在距离变换膨胀完成后,额外处理包围盒边界膨胀
/// 从包围盒边界向内膨胀指定层数,确保车辆与边界保持安全距离
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="inflationRadius">膨胀半径(网格层数)</param>
/// <returns>被膨胀的边界网格数量</returns>
private int ApplyBoundaryInflationPost(GridMap gridMap, int inflationRadius)
{
int boundaryInflatedCount = 0;
LogManager.Info($"[边界后处理] 开始处理包围盒边界膨胀,膨胀半径: {inflationRadius}层");
// 逐层向内膨胀
for (int layer = 0; layer < inflationRadius; layer++)
{
int layerInflatedCount = 0;
// 处理下边界 (y = layer)
if (layer < gridMap.Height)
{
for (int x = 0; x < gridMap.Width; x++)
{
var gridPos = new GridPoint2D(x, layer);
if (gridMap.IsWalkable(gridPos))
{
// 创建障碍物GridCell
var worldPos = gridMap.GridToWorld3D(gridPos);
var obstacleCell = GridCellBuilder.Obstacle(worldPos, null);
gridMap.PlaceCell(gridPos, obstacleCell);
layerInflatedCount++;
}
}
}
// 处理上边界 (y = maxY - layer)
if (gridMap.Height - 1 - layer >= 0 && gridMap.Height - 1 - layer != layer) // 避免重复处理
{
for (int x = 0; x < gridMap.Width; x++)
{
var gridPos = new GridPoint2D(x, gridMap.Height - 1 - layer);
if (gridMap.IsWalkable(gridPos))
{
// 创建障碍物GridCell
var worldPos = gridMap.GridToWorld3D(gridPos);
var obstacleCell = GridCellBuilder.Obstacle(worldPos, null);
gridMap.PlaceCell(gridPos, obstacleCell);
layerInflatedCount++;
}
}
}
// 处理左边界 (x = layer)
if (layer < gridMap.Width)
{
for (int y = layer + 1; y < gridMap.Height - layer - 1; y++) // 避免角点重复
{
var gridPos = new GridPoint2D(layer, y);
if (gridMap.IsWalkable(gridPos))
{
// 创建障碍物GridCell
var worldPos = gridMap.GridToWorld3D(gridPos);
var obstacleCell = GridCellBuilder.Obstacle(worldPos, null);
gridMap.PlaceCell(gridPos, obstacleCell);
layerInflatedCount++;
}
}
}
// 处理右边界 (x = maxX - layer)
if (gridMap.Width - 1 - layer >= 0 && gridMap.Width - 1 - layer != layer) // 避免重复处理
{
for (int y = layer + 1; y < gridMap.Height - layer - 1; y++) // 避免角点重复
{
var gridPos = new GridPoint2D(gridMap.Width - 1 - layer, y);
if (gridMap.IsWalkable(gridPos))
{
// 创建障碍物GridCell
var worldPos = gridMap.GridToWorld3D(gridPos);
var obstacleCell = GridCellBuilder.Obstacle(worldPos, null);
gridMap.PlaceCell(gridPos, obstacleCell);
layerInflatedCount++;
}
}
}
boundaryInflatedCount += layerInflatedCount;
LogManager.Info($"[边界后处理] 第{layer + 1}层膨胀完成,新增障碍物: {layerInflatedCount}个");
// 如果这一层没有可膨胀的网格,提前结束
if (layerInflatedCount == 0)
{
LogManager.Info($"[边界后处理] 第{layer + 1}层无可膨胀网格,膨胀结束");
break;
}
}
LogManager.Info($"[边界后处理] 边界膨胀完成,新增障碍物: {boundaryInflatedCount}个");
return boundaryInflatedCount;
}
/// <summary>
/// 收集物流项目的相关节点
/// 根据节点类型决定收集策略:几何体节点收集父节点和同级节点,集合节点收集子节点
@ -1590,8 +1290,6 @@ namespace NavisworksTransport.PathPlanning
private Dictionary<ModelItem, ItemProperties> PostProcessGeometryItems(
List<ModelItem> geometryItems,
HashSet<ModelItem> traversableItemsSet,
GridMap gridMap,
double scanHeightInModelUnits,
HashSet<ModelItem> irrelevantItemsSet)
{
LogManager.Info("[轻量级后处理] 开始处理Search API筛选结果");
@ -1665,7 +1363,6 @@ namespace NavisworksTransport.PathPlanning
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
LogManager.Info($"[轻量级后处理] 完成,输入 {totalItems} 项,有效结果 {itemCache.Count} 项API调用 {apiCalls} 次,耗时: {elapsed:F1}ms");
LogManager.Info($"[轻量级后处理] 筛除统计 - 通道相关: {skippedByChannel}, 无关项: {skippedByIrrelevant}, 无边界框: {skippedByBoundingBox}");
LogManager.Info("[轻量级后处理] 已移除全局高度预过滤,将在障碍物遍历阶段进行精确高度检查");
return itemCache;
}
@ -1694,7 +1391,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {traversableItemsSet.Count}");
// 阶段2轻量级后处理只对预筛选结果进行必要的API调用
var itemCache = PostProcessGeometryItems(geometryItems, traversableItemsSet, gridMap, scanHeightInModelUnits, irrelevantItemsSet);
var itemCache = PostProcessGeometryItems(geometryItems, traversableItemsSet, irrelevantItemsSet);
// 阶段3条件过滤并行数值计算- 使用50%CPU核心优化
LogManager.Info("[高性能障碍物处理] 阶段3: 并行条件过滤");
@ -1864,7 +1561,7 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info($"[高性能障碍物处理] 性能分解 - Search API预筛选+后处理: {searchTime:F1}ms, 过滤: {filterElapsed:F1}ms, 计算: {gridCalcElapsed:F1}ms, 更新: {updateElapsed:F1}ms");
// 性能对比统计
LogManager.Info($"[性能优化统计] 处理几何体项目: {geometryItems.Count}, 最终有效项目: {validItems.Count}, 优化比率: {(1.0 - (double)validItems.Count / geometryItems.Count):P1}");
LogManager.Info($"[性能优化统计] 处理几何体项目: {geometryItems.Count}, 最终有效项目: {validItems.Count}, 优化比率: {1.0 - (double)validItems.Count / geometryItems.Count:P1}");
LogManager.Info($"[精确高度过滤统计] 高度检查项目: {totalCheckedItems}, 高度过滤项目: {filteredItems}, 精确过滤率: {(double)filteredItems / Math.Max(1, totalCheckedItems):P1}");
LogManager.Info($"[并行优化统计] 使用 {optimalParallelism}/{Environment.ProcessorCount} CPU核心每核心处理 {Math.Ceiling((double)validItems.Count / optimalParallelism)} 个项目");
}
@ -1889,10 +1586,6 @@ namespace NavisworksTransport.PathPlanning
try
{
// 网格坐标(x,y)代表左下角,覆盖世界坐标范围:
// X: [Origin.X + x*CellSize, Origin.X + (x+1)*CellSize)
// Y: [Origin.Y + y*CellSize, Origin.Y + (y+1)*CellSize)
// 直接使用WorldToGrid进行坐标转换
var minGridPos = gridMap.WorldToGrid(new Point3D(bbox.Min.X, bbox.Min.Y, 0));
var maxGridPos = gridMap.WorldToGrid(new Point3D(bbox.Max.X, bbox.Max.Y, 0));

View File

@ -210,7 +210,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 1. 清理现有的路径可视化,但保留网格可视化
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.WriteLog("[路径可视化] 已清理现有路径显示(保留网格可视化)");
// 2. 如果有选中的路径,则显示该路径
if (_selectedPathRoute != null && _selectedPathRoute.Points.Count > 0)
@ -230,7 +229,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
LogManager.WriteLog("[路径可视化] 没有选中的路径,仅清理显示");
LogManager.Debug("[路径可视化] 没有选中的路径,仅清理显示");
}
// 3. 恢复网格可视化如果网格可视化已启用且当前路径有关联的GridMap
@ -240,8 +239,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
var currentRoute = _pathPlanningManager.CurrentRoute;
if (currentRoute?.AssociatedGridMap != null)
{
LogManager.WriteLog("[路径可视化] 检测到网格可视化已启用且当前路径有GridMap恢复网格显示");
// 通过PathPlanningManager的RefreshGridVisualization方法恢复网格
// 这会使用当前路径的AssociatedGridMap来重新渲染网格
var refreshMethod = typeof(PathPlanningManager).GetMethod("RefreshGridVisualization",
@ -249,7 +246,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (refreshMethod != null)
{
refreshMethod.Invoke(_pathPlanningManager, null);
LogManager.WriteLog("[路径可视化] 网格可视化已恢复");
}
else
{
@ -258,7 +254,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
LogManager.WriteLog("[路径可视化] 当前路径没有关联的GridMap跳过网格恢复");
LogManager.Debug("[路径可视化] 当前路径没有关联的GridMap跳过网格恢复");
}
}
}
@ -800,7 +796,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (PathPointRenderPlugin.Instance != null && _autoPathStartPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
LogManager.WriteLog($"[选择起点] 已清除之前的起点标记ID: {_autoPathStartPointRoute.Id}");
_autoPathStartPointRoute = null; // 清除引用
}
@ -858,7 +853,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (PathPointRenderPlugin.Instance != null && _autoPathEndPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
LogManager.WriteLog($"[选择终点] 已清除之前的终点标记ID: {_autoPathEndPointRoute.Id}");
LogManager.Debug($"[选择终点] 已清除之前的终点标记ID: {_autoPathEndPointRoute.Id}");
_autoPathEndPointRoute = null; // 清除引用
}
@ -913,7 +908,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info("=== 开始执行自动路径规划 ===");
// 确保在开始自动路径规划前清理任何残留的事件订阅
LogManager.WriteLog("[自动路径规划] 规划前清理事件订阅状态");
CleanupAutoPathEventSubscriptions();
if (_pathPlanningManager != null)
{
@ -921,7 +915,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
// 清理所有临时路径,确保干净的起始状态
LogManager.WriteLog("[自动路径规划] 清理所有临时路径和历史渲染对象");
ClearTemporaryAutoPathMarkers();
if (PathPointRenderPlugin.Instance != null)
{
@ -929,12 +922,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.WriteLog("[自动路径规划] 已清理历史路径对象(保留网格可视化)");
}
else
{
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
LogManager.WriteLog("[自动路径规划] 已清理历史路径对象(无网格可视化需保留)");
}
}
@ -978,7 +969,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"路径规划成功,共 {pathRoute.Points.Count} 个点");
// 自动路径规划完成后,执行完整的事件订阅清理
LogManager.WriteLog("[自动路径规划] 规划完成后清理事件订阅状态");
CleanupAutoPathEventSubscriptions();
if (_pathPlanningManager != null)
{
@ -999,7 +989,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 这里的逻辑已经在上方处理过了
// 失败情况下也要清理事件订阅和临时标记
LogManager.WriteLog("[自动路径规划] 规划失败后清理所有状态");
CleanupAutoPathEventSubscriptions();
if (_pathPlanningManager != null)
{
@ -1016,12 +1005,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
{
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
LogManager.WriteLog("[自动路径规划] 规划失败后已清理渲染对象(保留网格可视化)");
}
else
{
PathPointRenderPlugin.Instance.ClearPathsExcept(); // 清理所有路径,不保留任何内容
LogManager.WriteLog("[自动路径规划] 规划失败后已清理渲染对象(无网格可视化需保留)");
}
}
}
@ -1888,22 +1875,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private async void OnAutoPathMouseClicked(object sender, PickItemResult pickResult)
{
try
{
LogManager.WriteLog("[自动路径事件] ===== OnAutoPathMouseClicked被调用 =====");
{
if (pickResult == null)
{
LogManager.WriteLog("[自动路径事件] pickResult为null退出处理");
return;
}
var point3D = pickResult.Point;
LogManager.WriteLog($"[自动路径事件] 接收到点击事件: ({point3D.X:F2}, {point3D.Y:F2}, {point3D.Z:F2})");
LogManager.WriteLog($"[自动路径事件] IsSelectingStartPoint: {IsSelectingStartPoint}, IsSelectingEndPoint: {IsSelectingEndPoint}");
if (IsSelectingStartPoint)
{
LogManager.WriteLog("[自动路径事件] 设置起点并停止点击工具");
SetAutoPathStartPoint(point3D);
await SafeExecuteAsync(() =>
{
@ -1916,7 +1897,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else if (IsSelectingEndPoint)
{
LogManager.WriteLog("[自动路径事件] 设置终点并停止点击工具");
SetAutoPathEndPoint(point3D);
await SafeExecuteAsync(() =>
{
@ -1929,7 +1909,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
LogManager.WriteLog("[自动路径事件] 非选择状态,直接清理事件订阅");
CleanupAutoPathEventSubscriptions();
_pathPlanningManager?.EnableMouseHandling(); // 恢复PathPlanningManager的鼠标处理
}
@ -2076,7 +2055,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
VehicleWidth,
VehicleHeight,
SafetyMargin);
LogManager.WriteLog($"[车辆参数同步] 已同步车辆参数到渲染插件: 长={VehicleLength:F2}m, 宽={VehicleWidth:F2}m, 高={VehicleHeight:F2}m, 安全间隙={SafetyMargin:F2}m");
}
else
{
@ -2115,16 +2093,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void CleanupAutoPathEventSubscriptions()
{
try
{
LogManager.WriteLog("[事件清理] 开始清理自动路径事件订阅");
{
// 简单安全地移除事件订阅
// 由于C#事件处理机制,多次取消订阅同一个处理程序是安全的
// 即使处理程序未订阅,取消操作也不会抛出异常
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
LogManager.WriteLog("[事件清理] 已安全移除PathClickToolPlugin.MouseClicked事件订阅");
LogManager.WriteLog("[事件清理] 自动路径事件订阅清理完成");
PathClickToolPlugin.MouseClicked -= OnAutoPathMouseClicked;
}
catch (Exception ex)
{
@ -2139,16 +2112,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void ClearTemporaryAutoPathMarkers()
{
try
{
LogManager.WriteLog("[临时标记清理] 开始清理自动路径临时标记");
{
if (PathPointRenderPlugin.Instance != null)
{
// 清除临时的起点标记使用正确的路径ID
if (_autoPathStartPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathStartPointRoute.Id);
LogManager.WriteLog($"[临时标记清理] 已清除起点标记ID: {_autoPathStartPointRoute.Id}");
_autoPathStartPointRoute = null;
}
@ -2156,15 +2126,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (_autoPathEndPointRoute != null)
{
PathPointRenderPlugin.Instance.RemovePath(_autoPathEndPointRoute.Id);
LogManager.WriteLog($"[临时标记清理] 已清除终点标记ID: {_autoPathEndPointRoute.Id}");
_autoPathEndPointRoute = null;
}
LogManager.WriteLog("[临时标记清理] 自动路径临时标记清理完成");
}
else
{
LogManager.WriteLog("[临时标记清理] PathPointRenderPlugin实例为null无法清理标记");
LogManager.Warning("[临时标记清理] PathPointRenderPlugin实例为null无法清理标记");
}
}
catch (Exception ex)

View File

@ -18,11 +18,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#region
private readonly UIStateManager _uiStateManager;
// 系统管理相关字段
private bool _isAutoSaveEnabled = true;
private bool _isDebugModeEnabled = false;
// 网格可视化开关字段
private bool _showWalkableGrid = false;
private bool _showObstacleGrid = false;
@ -31,8 +27,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 路径可视化模式字段
private bool _isStandardLineMode = true;
private bool _isVehicleSpaceMode = false;
// 日志管理字段
private ObservableCollection<string> _logLevels;
private string _selectedLogLevel = "Info";
private string _pluginVersion = "v1.0";
private string _navisworksVersion = "2026";
private string _memoryUsage = "0 MB";
@ -50,24 +49,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#region
/// <summary>
/// 是否启用自动保存
/// </summary>
public bool IsAutoSaveEnabled
{
get => _isAutoSaveEnabled;
set => SetProperty(ref _isAutoSaveEnabled, value);
}
/// <summary>
/// 是否启用调试模式
/// </summary>
public bool IsDebugModeEnabled
{
get => _isDebugModeEnabled;
set => SetProperty(ref _isDebugModeEnabled, value);
}
/// <summary>
/// 是否显示可通行网格点
/// </summary>
@ -165,8 +146,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 日志级别集合
/// </summary>
@ -182,11 +161,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public string SelectedLogLevel
{
get => _selectedLogLevel;
set => SetProperty(ref _selectedLogLevel, value);
set
{
if (SetProperty(ref _selectedLogLevel, value))
{
// 将字符串转换为LogLevel枚举并设置
if (Enum.TryParse<LogLevel>(value, out var level))
{
LogManager.SetLogLevel(level);
LogManager.Info($"日志级别已更改为: {value}");
UpdateMainStatus($"日志级别已设置为: {value}");
}
}
}
}
/// <summary>
/// 插件版本
/// </summary>
@ -231,12 +220,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#region
public ICommand ClearLogCommand { get; private set; }
public ICommand ExportLogCommand { get; private set; }
public ICommand ViewLogCommand { get; private set; }
public ICommand OpenSettingsCommand { get; private set; }
public ICommand ResetSettingsCommand { get; private set; }
public ICommand ImportConfigCommand { get; private set; }
public ICommand CheckUpdateCommand { get; private set; }
public ICommand GeneratePerformanceReportCommand { get; private set; }
public ICommand DiagnosticCommand { get; private set; }
@ -258,8 +243,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Error("UIStateManager初始化失败");
throw new InvalidOperationException("UIStateManager初始化失败");
}
// 初始化线程安全的集合
// 初始化日志级别集合
LogLevels = new ThreadSafeObservableCollection<string>();
// 初始化命令
@ -300,8 +285,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Error("UIStateManager初始化失败");
throw new InvalidOperationException("UIStateManager初始化失败");
}
// 初始化线程安全的集合
// 初始化日志级别集合
LogLevels = new ThreadSafeObservableCollection<string>();
// 初始化命令
@ -335,11 +320,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
// 系统管理命令
ViewLogCommand = new RelayCommand(() => ExecuteViewLog());
ClearLogCommand = new RelayCommand(() => ExecuteClearLog());
ExportLogCommand = new RelayCommand(() => ExecuteExportLog());
OpenSettingsCommand = new RelayCommand(() => ExecuteOpenSettings());
ResetSettingsCommand = new RelayCommand(() => ExecuteResetSettings());
ImportConfigCommand = new RelayCommand(() => ExecuteImportConfig());
CheckUpdateCommand = new RelayCommand(() => ExecuteCheckUpdate());
GeneratePerformanceReportCommand = new RelayCommand(() => ExecuteGeneratePerformanceReport());
DiagnosticCommand = new RelayCommand(() => ExecuteDiagnostic());
@ -455,22 +436,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
// 初始化日志级别
LogLevels.Clear();
var logLevels = new[] { "Debug", "Info", "Warning", "Error" };
foreach (var level in logLevels)
{
LogLevels.Add(level);
}
SelectedLogLevel = "Info";
LogLevels.Add("Debug");
LogLevels.Add("Info");
LogLevels.Add("Warning");
LogLevels.Add("Error");
// 获取当前日志级别
var currentLevel = LogManager.GetLogLevel();
SelectedLogLevel = currentLevel.ToString();
// 初始化系统信息
PluginVersion = "v1.0";
NavisworksVersion = "2026";
UpdateMainStatus("系统管理初始化完成");
});
// 启动性能监控
StartPerformanceMonitoring();
LogManager.Info("系统管理设置初始化完成");
}, "初始化系统管理设置");
}
@ -608,129 +591,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}, "查看日志");
}
/// <summary>
/// 清空日志
/// </summary>
private void ExecuteClearLog()
{
SafeExecute(() =>
{
try
{
// 弹出确认对话框
var result = System.Windows.MessageBox.Show(
"确定要清空日志吗?此操作不可撤销。",
"确认清空日志",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Question);
if (result == System.Windows.MessageBoxResult.Yes)
{
// 调用LogManager清空日志
LogManager.ClearLog();
UpdateMainStatus("日志已清空");
LogManager.Info("通过系统管理清空日志");
// 显示成功消息
System.Windows.MessageBox.Show("日志已成功清空。", "操作完成",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Information);
}
else
{
UpdateMainStatus("取消清空日志");
LogManager.Info("用户取消清空日志操作");
}
}
catch (Exception ex)
{
UpdateMainStatus("清空日志失败");
LogManager.Error($"清空日志失败: {ex.Message}", ex);
// 显示错误消息给用户
System.Windows.MessageBox.Show($"清空日志失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}, "清空日志");
}
/// <summary>
/// 导出日志
/// </summary>
private void ExecuteExportLog()
{
SafeExecute(() =>
{
try
{
// 创建保存文件对话框
var saveFileDialog = new Microsoft.Win32.SaveFileDialog
{
Title = "导出日志文件",
Filter = "日志文件 (*.log)|*.log|文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*",
DefaultExt = "log",
FileName = $"NavisworksTransport_Log_{DateTime.Now:yyyyMMdd_HHmmss}.log"
};
if (saveFileDialog.ShowDialog() == true)
{
var logFilePath = LogManager.LogFilePath;
if (System.IO.File.Exists(logFilePath))
{
// 从源日志文件复制到目标位置
System.IO.File.Copy(logFilePath, saveFileDialog.FileName, true);
UpdateMainStatus($"日志已导出到: {System.IO.Path.GetFileName(saveFileDialog.FileName)}");
LogManager.Info($"日志已导出到: {saveFileDialog.FileName}");
// 显示成功消息并询问是否打开文件夹
var result = System.Windows.MessageBox.Show(
$"日志已成功导出到:\n{saveFileDialog.FileName}\n\n是否打开文件所在文件夹",
"导出完成",
System.Windows.MessageBoxButton.YesNo,
System.Windows.MessageBoxImage.Information);
if (result == System.Windows.MessageBoxResult.Yes)
{
// 打开文件所在文件夹并选中文件
System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{saveFileDialog.FileName}\"");
}
}
else
{
// 如果日志文件不存在,创建一个空的日志文件
System.IO.File.WriteAllText(saveFileDialog.FileName, $"NavisworksTransport 日志文件\n导出时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\n\n日志文件为空或不存在。\n");
UpdateMainStatus("日志文件不存在,已创建空文件");
LogManager.Warning("日志文件不存在,已创建空的导出文件");
System.Windows.MessageBox.Show("原始日志文件不存在,已创建空的导出文件。", "注意",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Warning);
}
}
else
{
UpdateMainStatus("取消导出日志");
LogManager.Info("用户取消导出日志操作");
}
}
catch (Exception ex)
{
UpdateMainStatus("导出日志失败");
LogManager.Error($"导出日志失败: {ex.Message}", ex);
// 显示错误消息给用户
System.Windows.MessageBox.Show($"导出日志失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}, "导出日志");
}
/// <summary>
/// 打开设置
/// </summary>
@ -738,40 +598,62 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
SafeExecute(() =>
{
// TODO: 实现设置对话框
UpdateMainStatus("打开插件设置");
LogManager.Info("打开设置");
try
{
// 创建并显示配置编辑器对话框
var configEditorDialog = new NavisworksTransport.UI.WPF.Views.ConfigEditorDialog();
// 尝试设置窗口所有者
try
{
var mainWindow = System.Windows.Application.Current.MainWindow;
if (mainWindow != null && mainWindow.IsLoaded)
{
configEditorDialog.Owner = mainWindow;
}
else
{
foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
{
if (window.IsActive && window.IsLoaded)
{
configEditorDialog.Owner = window;
break;
}
}
}
}
catch (Exception ownerEx)
{
LogManager.Warning($"设置配置编辑器Owner失败: {ownerEx.Message}");
}
// 显示对话框
bool? result = configEditorDialog.ShowDialog();
if (result == true)
{
UpdateMainStatus("配置已更新");
LogManager.Info("配置编辑器:用户保存了配置");
}
else
{
UpdateMainStatus("配置编辑已取消");
LogManager.Info("配置编辑器:用户取消了修改");
}
}
catch (Exception ex)
{
UpdateMainStatus("打开配置编辑器失败");
LogManager.Error($"打开配置编辑器失败: {ex.Message}", ex);
System.Windows.MessageBox.Show($"打开配置编辑器失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK,
System.Windows.MessageBoxImage.Error);
}
}, "打开设置");
}
/// <summary>
/// 重置设置
/// </summary>
private void ExecuteResetSettings()
{
SafeExecute(() =>
{
// TODO: 实现设置重置功能
UpdateMainStatus("设置已重置为默认值");
IsAutoSaveEnabled = true;
IsDebugModeEnabled = false;
LogManager.Info("重置设置");
}, "重置设置");
}
/// <summary>
/// 导入配置
/// </summary>
private void ExecuteImportConfig()
{
SafeExecute(() =>
{
// TODO: 实现配置导入功能
UpdateMainStatus("配置导入完成");
LogManager.Info("导入配置");
}, "导入配置");
}
/// <summary>
/// 检查更新 (已优化为Idle事件监听)
/// </summary>
@ -1000,8 +882,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary>
public bool IsValidState()
{
return _uiStateManager != null &&
LogLevels != null;
return _uiStateManager != null && LogLevels != null;
}
/// <summary>

View File

@ -0,0 +1,214 @@
<!--
NavisworksTransport 配置编辑器对话框 - 采用与主界面一致的Navisworks 2026风格
功能说明:
1. 加载和显示 TOML 配置文件
2. 支持直接编辑配置文件内容
3. 提供保存、重载和恢复默认功能
4. 语法高亮和错误提示
5. 与主界面保持一致的视觉风格
设计原则:使用统一的蓝色主题和样式规范,针对配置文件编辑优化
-->
<Window x:Class="NavisworksTransport.UI.WPF.Views.ConfigEditorDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="系统参数配置"
Height="700"
Width="900"
ResizeMode="CanResize"
WindowStartupLocation="CenterOwner"
MinHeight="500"
MinWidth="700">
<Window.Resources>
<!-- 引用共享的Navisworks 2026样式资源 -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/NavisworksTransportPlugin;component/src/UI/WPF/Resources/NavisworksStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<!-- 配置编辑器特有的样式 -->
<Style x:Key="ConfigTextBoxStyle" TargetType="TextBox">
<Setter Property="FontFamily" Value="Consolas, Courier New"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Background" Value="#FFFEFEFE"/>
<Setter Property="Foreground" Value="#FF333333"/>
<Setter Property="BorderBrush" Value="#FFD4E7FF"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="IsReadOnly" Value="False"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="TextWrapping" Value="NoWrap"/>
<Setter Property="AcceptsReturn" Value="True"/>
<Setter Property="AcceptsTab" Value="True"/>
<Setter Property="Padding" Value="10"/>
</Style>
<Style x:Key="InfoPanelStyle" TargetType="Border">
<Setter Property="Background" Value="#FFFFF8DC"/>
<Setter Property="BorderBrush" Value="#FFFFC107"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="3"/>
<Setter Property="Padding" Value="12,8"/>
<Setter Property="Margin" Value="0,0,0,10"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 工具栏区域 -->
<Border Grid.Row="0"
BorderBrush="#FFD4E7FF"
BorderThickness="0,0,0,1"
Background="#FFF8FBFF"
Padding="15,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 左侧工具按钮 -->
<StackPanel Grid.Column="0" Orientation="Horizontal">
<Button Content="保存"
Click="SaveButton_Click"
Style="{StaticResource ActionButtonStyle}"
Margin="0,0,10,0"
ToolTip="保存配置文件"/>
<Button Content="重新加载"
Click="ReloadButton_Click"
Style="{StaticResource SecondaryButtonStyle}"
Margin="0,0,10,0"
ToolTip="放弃当前修改,重新加载配置文件"/>
<Button Content="恢复默认"
Click="ResetToDefaultButton_Click"
Style="{StaticResource SecondaryButtonStyle}"
Margin="0,0,10,0"
ToolTip="恢复为默认配置"/>
<!-- 分隔线 -->
<Border Width="1"
Background="#FFD4E7FF"
Margin="5,2"/>
<Button Content="打开配置文件夹"
Click="OpenConfigFolderButton_Click"
Style="{StaticResource SecondaryButtonStyle}"
Margin="10,0,0,0"
ToolTip="在资源管理器中打开配置文件所在目录"/>
</StackPanel>
<!-- 右侧文件信息 -->
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Label Content="配置文件:"
VerticalAlignment="Center"
Style="{StaticResource ParameterLabelStyle}"/>
<Label Name="ConfigFilePathLabel"
Content="config.toml"
VerticalAlignment="Center"
Style="{StaticResource ParameterLabelStyle}"
Foreground="#FF0066CC"
Margin="5,0,0,0"/>
</StackPanel>
</Grid>
</Border>
<!-- 帮助信息面板 -->
<Border Grid.Row="1"
Style="{StaticResource InfoPanelStyle}"
Margin="15,15,15,0">
<StackPanel>
<TextBlock FontWeight="SemiBold"
Foreground="#FF856404"
Margin="0,0,0,5">
<Run Text="💡 "/>
<Run Text="配置说明"/>
</TextBlock>
<TextBlock TextWrapping="Wrap"
FontSize="10"
Foreground="#FF856404"
LineHeight="18">
• TOML 格式配置文件,支持注释(#开头)和分组([section]<LineBreak/>
• 所有长度单位为米(m),时间单位为秒(s)<LineBreak/>
• 修改后点击"保存"按钮生效,建议先备份原配置<LineBreak/>
• 语法错误会在保存时提示,请确保格式正确
</TextBlock>
</StackPanel>
</Border>
<!-- 配置内容编辑区域 -->
<Border Grid.Row="2"
BorderBrush="#FFD4E7FF"
BorderThickness="1"
Background="White"
Margin="15,10">
<TextBox Name="ConfigContentTextBox"
Style="{StaticResource ConfigTextBoxStyle}"
TextChanged="ConfigContentTextBox_TextChanged"/>
</Border>
<!-- 底部状态栏和按钮 -->
<Border Grid.Row="3"
BorderBrush="#FFD4E7FF"
BorderThickness="0,1,0,0"
Background="#FFF8FBFF"
Padding="15,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 状态信息 -->
<StackPanel Grid.Column="0" Orientation="Horizontal">
<Label Name="StatusLabel"
Content="配置已加载"
Style="{StaticResource ParameterLabelStyle}"
Foreground="#FF2B8A2B"/>
<Label Name="FileSizeLabel"
Content="大小: 0 KB"
Style="{StaticResource ParameterLabelStyle}"
Margin="20,0,0,0"/>
<Label Name="LineCountLabel"
Content="行数: 0"
Style="{StaticResource ParameterLabelStyle}"
Margin="20,0,0,0"/>
<Label Name="ModifiedLabel"
Content=""
Style="{StaticResource ParameterLabelStyle}"
Foreground="#FFFF6B00"
Margin="20,0,0,0"/>
</StackPanel>
<!-- 右侧按钮 -->
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Content="应用"
Click="ApplyButton_Click"
Style="{StaticResource ActionButtonStyle}"
Margin="0,0,10,0"
ToolTip="应用配置并保持窗口打开"/>
<Button Content="确定"
Click="OkButton_Click"
Style="{StaticResource ActionButtonStyle}"
Margin="0,0,10,0"
ToolTip="保存配置并关闭窗口"/>
<Button Content="取消"
Click="CancelButton_Click"
Style="{StaticResource SecondaryButtonStyle}"
ToolTip="放弃修改并关闭窗口"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>

View File

@ -0,0 +1,271 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using NavisworksTransport.Core.Config;
using NavisworksTransport.Utils;
namespace NavisworksTransport.UI.WPF.Views
{
/// <summary>
/// 配置编辑器对话框
/// </summary>
public partial class ConfigEditorDialog : Window
{
private string _originalContent;
private bool _isModified;
public ConfigEditorDialog()
{
InitializeComponent();
LoadConfigContent();
UpdateStatus();
}
/// <summary>
/// 加载配置文件内容
/// </summary>
private void LoadConfigContent()
{
try
{
string configPath = ConfigManager.ConfigFilePath;
// 如果配置文件不存在,创建默认配置
if (!File.Exists(configPath))
{
ConfigManager.Instance.Current.GetType(); // 触发单例初始化
}
// 读取配置文件
if (File.Exists(configPath))
{
_originalContent = File.ReadAllText(configPath, System.Text.Encoding.UTF8);
ConfigContentTextBox.Text = _originalContent;
ConfigFilePathLabel.Content = Path.GetFileName(configPath);
StatusLabel.Content = "配置已加载";
StatusLabel.Foreground = System.Windows.Media.Brushes.Green;
}
else
{
StatusLabel.Content = "配置文件不存在";
StatusLabel.Foreground = System.Windows.Media.Brushes.Red;
}
_isModified = false;
ModifiedLabel.Content = "";
}
catch (Exception ex)
{
LogManager.Error($"加载配置文件失败: {ex.Message}");
MessageBox.Show($"加载配置文件失败:\n{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 更新状态信息
/// </summary>
private void UpdateStatus()
{
try
{
string configPath = ConfigManager.ConfigFilePath;
if (File.Exists(configPath))
{
var fileInfo = new FileInfo(configPath);
FileSizeLabel.Content = $"大小: {fileInfo.Length / 1024.0:F2} KB";
}
int lineCount = ConfigContentTextBox.LineCount;
LineCountLabel.Content = $"行数: {lineCount}";
}
catch (Exception ex)
{
LogManager.Error($"更新状态信息失败: {ex.Message}");
}
}
/// <summary>
/// 保存配置
/// </summary>
private bool SaveConfig()
{
try
{
string configPath = ConfigManager.ConfigFilePath;
string content = ConfigContentTextBox.Text;
// 验证 TOML 格式
try
{
Tomlyn.Toml.ToModel(content);
}
catch (Exception tomlEx)
{
MessageBox.Show($"TOML 格式错误:\n{tomlEx.Message}\n\n请检查配置文件语法。",
"格式错误", MessageBoxButton.OK, MessageBoxImage.Warning);
return false;
}
// 确保目录存在
Directory.CreateDirectory(ConfigManager.ConfigDirectory);
// 保存文件
File.WriteAllText(configPath, content, System.Text.Encoding.UTF8);
// 重新加载配置
ConfigManager.Instance.Reload();
_originalContent = content;
_isModified = false;
ModifiedLabel.Content = "";
StatusLabel.Content = "配置已保存";
StatusLabel.Foreground = System.Windows.Media.Brushes.Green;
UpdateStatus();
LogManager.Info("配置文件已保存");
return true;
}
catch (Exception ex)
{
LogManager.Error($"保存配置失败: {ex.Message}");
MessageBox.Show($"保存配置失败:\n{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
// 事件处理器
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
SaveConfig();
}
private void ReloadButton_Click(object sender, RoutedEventArgs e)
{
if (_isModified)
{
var result = MessageBox.Show(
"当前有未保存的修改,重新加载将丢失这些修改。\n\n确定要继续吗",
"确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
}
LoadConfigContent();
StatusLabel.Content = "配置已重新加载";
StatusLabel.Foreground = System.Windows.Media.Brushes.Green;
}
private void ResetToDefaultButton_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show(
"确定要恢复为默认配置吗?\n\n当前配置将被覆盖此操作无法撤销。",
"确认", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
try
{
ConfigManager.Instance.ResetToDefault();
LoadConfigContent();
StatusLabel.Content = "已恢复默认配置";
StatusLabel.Foreground = System.Windows.Media.Brushes.Green;
LogManager.Info("配置已恢复为默认值");
}
catch (Exception ex)
{
LogManager.Error($"恢复默认配置失败: {ex.Message}");
MessageBox.Show($"恢复默认配置失败:\n{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
private void OpenConfigFolderButton_Click(object sender, RoutedEventArgs e)
{
try
{
string configDir = ConfigManager.ConfigDirectory;
if (!Directory.Exists(configDir))
{
Directory.CreateDirectory(configDir);
}
Process.Start("explorer.exe", configDir);
LogManager.Info($"打开配置文件夹: {configDir}");
}
catch (Exception ex)
{
LogManager.Error($"打开配置文件夹失败: {ex.Message}");
MessageBox.Show($"打开配置文件夹失败:\n{ex.Message}",
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ApplyButton_Click(object sender, RoutedEventArgs e)
{
SaveConfig();
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
if (_isModified)
{
if (SaveConfig())
{
DialogResult = true;
Close();
}
}
else
{
DialogResult = true;
Close();
}
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (_isModified)
{
var result = MessageBox.Show(
"有未保存的修改,确定要关闭吗?",
"确认", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes)
return;
}
DialogResult = false;
Close();
}
private void ConfigContentTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (_originalContent != null)
{
_isModified = ConfigContentTextBox.Text != _originalContent;
if (_isModified)
{
ModifiedLabel.Content = "● 已修改";
}
else
{
ModifiedLabel.Content = "";
}
UpdateStatus();
}
}
}
}

View File

@ -46,20 +46,14 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,15" Padding="12">
<StackPanel>
<Label Content="日志管理" Style="{StaticResource SectionHeaderStyle}"/>
<!-- 日志操作按钮 -->
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<Button Content="查看日志"
Command="{Binding ViewLogCommand}"
<Button Content="查看日志"
Command="{Binding ViewLogCommand}"
Style="{StaticResource ActionButtonStyle}"/>
<Button Content="清空日志"
Command="{Binding ClearLogCommand}"
Style="{StaticResource SecondaryButtonStyle}"/>
<Button Content="导出日志"
Command="{Binding ExportLogCommand}"
Style="{StaticResource SecondaryButtonStyle}"/>
</StackPanel>
<!-- 日志级别设置 -->
<Grid Margin="0,5,0,10">
<Grid.ColumnDefinitions>
@ -67,15 +61,15 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="日志级别:" Style="{StaticResource ParameterLabelStyle}" Width="80"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding LogLevels}"
SelectedItem="{Binding SelectedLogLevel}"
Width="100"
Margin="5,0,0,0"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding LogLevels}"
SelectedItem="{Binding SelectedLogLevel}"
Width="100"
Margin="5,0,0,0"
ToolTip="选择要记录的最低日志级别"/>
</Grid>
</StackPanel>
</Border>
@ -86,26 +80,9 @@ NavisworksTransport 系统管理页签视图 - 采用与其他页签一致的Nav
<!-- 设置操作按钮 -->
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<Button Content="设置选项"
Command="{Binding OpenSettingsCommand}"
<Button Content="参数设置"
Command="{Binding OpenSettingsCommand}"
Style="{StaticResource ActionButtonStyle}"/>
<Button Content="重置设置"
Command="{Binding ResetSettingsCommand}"
Style="{StaticResource SecondaryButtonStyle}"/>
<Button Content="导入配置"
Command="{Binding ImportConfigCommand}"
Style="{StaticResource SecondaryButtonStyle}"/>
</StackPanel>
<!-- 选项设置 -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<CheckBox Content="启用自动保存"
IsChecked="{Binding IsAutoSaveEnabled}"
Margin="0,0,20,0"
VerticalAlignment="Center"/>
<CheckBox Content="启用调试模式"
IsChecked="{Binding IsDebugModeEnabled}"
VerticalAlignment="Center"/>
</StackPanel>
<!-- 网格可视化开关 -->

View File

@ -26,25 +26,25 @@ namespace NavisworksTransport
try
{
LogManager.WriteLog($"开始提取模型 {modelItem.DisplayName} 的几何数据");
LogManager.WriteLog($"模型项类型: {modelItem.GetType().Name}");
LogManager.WriteLog($"模型项GUID: {modelItem.InstanceGuid}");
LogManager.Debug($"开始提取模型 {modelItem.DisplayName} 的几何数据");
LogManager.Debug($"模型项类型: {modelItem.GetType().Name}");
LogManager.Debug($"模型项GUID: {modelItem.InstanceGuid}");
// 详细检查模型是否有几何数据
bool hasGeometry = modelItem.HasGeometry;
LogManager.WriteLog($"[关键检查] ModelItem.HasGeometry = {hasGeometry}");
LogManager.Debug($"[关键检查] ModelItem.HasGeometry = {hasGeometry}");
if (!hasGeometry)
{
LogManager.WriteLog("[关键发现] 模型项没有几何数据 - HasGeometry 返回 false");
LogManager.WriteLog($"模型项详细信息:");
LogManager.WriteLog($" - 显示名称: {modelItem.DisplayName}");
LogManager.WriteLog($" - 实例GUID: {modelItem.InstanceGuid}");
LogManager.Debug("[关键发现] 模型项没有几何数据 - HasGeometry 返回 false");
LogManager.Debug($"模型项详细信息:");
LogManager.Debug($" - 显示名称: {modelItem.DisplayName}");
LogManager.Debug($" - 实例GUID: {modelItem.InstanceGuid}");
// 检查子项数量来判断是否为叶节点
int childrenCount = modelItem.Children.Count();
LogManager.WriteLog($" - 子项数量: {childrenCount}");
LogManager.WriteLog($" - 是否为叶节点: {childrenCount == 0}");
LogManager.Debug($" - 子项数量: {childrenCount}");
LogManager.Debug($" - 是否为叶节点: {childrenCount == 0}");
// 尝试检查包围盒
try
@ -52,72 +52,72 @@ namespace NavisworksTransport
var bbox = modelItem.BoundingBox();
if (bbox != null)
{
LogManager.WriteLog($" - 包围盒存在: {bbox.Min} - {bbox.Max}");
LogManager.Debug($" - 包围盒存在: {bbox.Min} - {bbox.Max}");
}
else
{
LogManager.WriteLog($" - 包围盒不存在");
LogManager.Debug($" - 包围盒不存在");
}
}
catch (Exception bboxEx)
{
LogManager.WriteLog($" - 获取包围盒失败: {bboxEx.Message}");
LogManager.Warning($" - 获取包围盒失败: {bboxEx.Message}");
}
return points;
}
LogManager.WriteLog("[几何检查通过] 模型项有几何数据,继续处理");
LogManager.Debug("[几何检查通过] 模型项有几何数据,继续处理");
// 处理多实例问题 - 确保只获取当前实例的几何
var targetItem = modelItem;
int instanceCount = targetItem.Instances.Count();
LogManager.WriteLog($"原始模型实例数: {instanceCount}");
LogManager.Debug($"原始模型实例数: {instanceCount}");
while (targetItem.Instances.Count() > 1)
{
targetItem = targetItem.Parent;
if (targetItem == null) break;
LogManager.WriteLog($"向上查找父项: {targetItem?.DisplayName}, 实例数: {targetItem?.Instances.Count()}");
LogManager.Debug($"向上查找父项: {targetItem?.DisplayName}, 实例数: {targetItem?.Instances.Count()}");
}
if (targetItem == null)
{
LogManager.WriteLog("无法找到合适的几何节点");
LogManager.Warning("无法找到合适的几何节点");
return points;
}
// 再次检查目标项的几何状态
bool targetHasGeometry = targetItem.HasGeometry;
LogManager.WriteLog($"[目标项检查] 使用几何节点: {targetItem.DisplayName}");
LogManager.WriteLog($"[目标项检查] 实例数: {targetItem.Instances.Count()}");
LogManager.WriteLog($"[目标项检查] HasGeometry = {targetHasGeometry}");
LogManager.Debug($"[目标项检查] 使用几何节点: {targetItem.DisplayName}");
LogManager.Debug($"[目标项检查] 实例数: {targetItem.Instances.Count()}");
LogManager.Debug($"[目标项检查] HasGeometry = {targetHasGeometry}");
if (!targetHasGeometry)
{
LogManager.WriteLog("[目标项问题] 目标节点也没有几何数据");
LogManager.Warning("[目标项问题] 目标节点也没有几何数据");
return points;
}
// 使用优化的几何提取方法
var triangles = ExtractTriangles(targetItem);
LogManager.WriteLog($"提取到 {triangles.Count} 个三角形");
LogManager.Debug($"提取到 {triangles.Count} 个三角形");
if (triangles.Count > 0)
{
// 生成轮廓
points = GenerateOutlineFromTriangles(triangles, tolerance);
LogManager.WriteLog($"生成轮廓包含 {points.Count} 个点");
LogManager.Info($"生成轮廓包含 {points.Count} 个点");
}
else
{
LogManager.WriteLog("未提取到三角形数据");
LogManager.Warning("未提取到三角形数据");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"提取几何数据失败: {ex.Message}");
LogManager.WriteLog($"堆栈跟踪: {ex.StackTrace}");
LogManager.Error($"提取几何数据失败: {ex.Message}");
LogManager.Error($"堆栈跟踪: {ex.StackTrace}");
}
return points;
@ -144,11 +144,11 @@ namespace NavisworksTransport
var comState = ComStateManager.GetState();
comSelection = ComApiBridge.ToInwOpSelection(modelCollection);
LogManager.WriteLog($"COM 选择创建成功,路径数: {comSelection.Paths().Count}");
LogManager.Debug($"COM 选择创建成功,路径数: {comSelection.Paths().Count}");
// 获取所有片段(已移除错误的去重逻辑)
var allFragments = GetAllFragments(comSelection);
LogManager.WriteLog($"获取到 {allFragments.Count} 个片段");
LogManager.Debug($"获取到 {allFragments.Count} 个片段");
try
{
@ -164,11 +164,11 @@ namespace NavisworksTransport
var fragmentTriangles = callback.GetTriangles();
triangles.AddRange(fragmentTriangles);
LogManager.WriteLog($"片段生成了 {fragmentTriangles.Count} 个三角形");
LogManager.Debug($"片段生成了 {fragmentTriangles.Count} 个三角形");
}
catch (Exception ex)
{
LogManager.WriteLog($"处理片段失败: {ex.Message}");
LogManager.Error($"处理片段失败: {ex.Message}");
}
}
}
@ -186,14 +186,14 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"释放片段COM对象失败: {ex.Message}");
LogManager.Warning($"释放片段COM对象失败: {ex.Message}");
}
}
}
}
catch (Exception ex)
{
LogManager.WriteLog($"提取三角形失败: {ex.Message}");
LogManager.Error($"提取三角形失败: {ex.Message}");
}
finally
{
@ -407,7 +407,7 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"边计数完成,唯一边数: {edgeCount.Count}");
LogManager.Debug($"边计数完成,唯一边数: {edgeCount.Count}");
// 找出只出现一次的边(边界边)
int boundaryCount = 0;
@ -426,22 +426,22 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"边界边分析完成:");
LogManager.WriteLog($" 边界边数量: {boundaryCount}");
LogManager.WriteLog($" 共享边数量: {sharedCount}");
LogManager.WriteLog($" 边界/总边比例: {(double)boundaryCount / edges.Count:P1}");
LogManager.Debug($"边界边分析完成:");
LogManager.Debug($" 边界边数量: {boundaryCount}");
LogManager.Debug($" 共享边数量: {sharedCount}");
LogManager.Debug($" 边界/总边比例: {(double)boundaryCount / edges.Count:P1}");
// 验证边界边的连通性
if (boundaryEdges.Count > 0)
{
var connectivity = AnalyzeBoundaryConnectivity(boundaryEdges);
LogManager.WriteLog($"边界边连通性分析: {connectivity}");
LogManager.Debug($"边界边连通性分析: {connectivity}");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"查找边界边失败: {ex.Message}");
LogManager.Error($"查找边界边失败: {ex.Message}");
}
return boundaryEdges;
@ -501,18 +501,18 @@ namespace NavisworksTransport
try
{
LogManager.WriteLog($"开始构建轮廓,总边数: {edges.Count}");
LogManager.Debug($"开始构建轮廓,总边数: {edges.Count}");
while (remainingEdges.Count > 0 && contourCount < 10) // 最多处理10个轮廓
{
contourCount++;
LogManager.WriteLog($"=== 开始构建第 {contourCount} 个轮廓 ===");
LogManager.Debug($"=== 开始构建第 {contourCount} 个轮廓 ===");
var currentContour = BuildSingleContour(remainingEdges);
if (currentContour.Count >= 3) // 至少3个点才能形成有效轮廓
{
LogManager.WriteLog($"第 {contourCount} 个轮廓包含 {currentContour.Count} 个点");
LogManager.Debug($"第 {contourCount} 个轮廓包含 {currentContour.Count} 个点");
allOutlinePoints.AddRange(currentContour);
// 添加轮廓分隔符(使用特殊坐标标记)
@ -523,23 +523,23 @@ namespace NavisworksTransport
}
else
{
LogManager.WriteLog($"第 {contourCount} 个轮廓点数不足({currentContour.Count}),跳过");
LogManager.Debug($"第 {contourCount} 个轮廓点数不足({currentContour.Count}),跳过");
break; // 如果轮廓太小,可能是噪声,停止处理
}
}
LogManager.WriteLog($"轮廓构建完成,共 {contourCount} 个轮廓,总点数: {allOutlinePoints.Count}");
LogManager.Info($"轮廓构建完成,共 {contourCount} 个轮廓,总点数: {allOutlinePoints.Count}");
// 如果有多个轮廓,返回最大的外部轮廓
if (contourCount > 1)
{
LogManager.WriteLog("检测到多个轮廓,返回最大轮廓作为外部边界");
LogManager.Debug("检测到多个轮廓,返回最大轮廓作为外部边界");
return GetLargestContour(allOutlinePoints);
}
}
catch (Exception ex)
{
LogManager.WriteLog($"构建轮廓失败: {ex.Message}");
LogManager.Error($"构建轮廓失败: {ex.Message}");
}
return allOutlinePoints;
@ -566,7 +566,7 @@ namespace NavisworksTransport
var currentPoint = currentEdge.End;
var startPoint = currentEdge.Start;
LogManager.WriteLog($"轮廓起点: ({startPoint.X:F2}, {startPoint.Y:F2})");
LogManager.Debug($"轮廓起点: ({startPoint.X:F2}, {startPoint.Y:F2})");
// 连接后续的边
int maxIterations = remainingEdges.Count + 10; // 防止无限循环
@ -609,25 +609,25 @@ namespace NavisworksTransport
if (Math.Abs(currentPoint.X - startPoint.X) < tolerance &&
Math.Abs(currentPoint.Y - startPoint.Y) < tolerance)
{
LogManager.WriteLog($"轮廓已闭合,点数: {contour.Count}");
LogManager.Debug($"轮廓已闭合,点数: {contour.Count}");
break;
}
}
else
{
LogManager.WriteLog($"无法找到连接边,轮廓中断,点数: {contour.Count}");
LogManager.Warning($"无法找到连接边,轮廓中断,点数: {contour.Count}");
break;
}
}
if (iteration >= maxIterations)
{
LogManager.WriteLog($"轮廓构建达到最大迭代次数,强制停止");
LogManager.Warning($"轮廓构建达到最大迭代次数,强制停止");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"构建单个轮廓失败: {ex.Message}");
LogManager.Error($"构建单个轮廓失败: {ex.Message}");
}
return contour;
@ -671,7 +671,7 @@ namespace NavisworksTransport
if (contours.Count == 0)
{
LogManager.WriteLog("未找到有效轮廓");
LogManager.Warning("未找到有效轮廓");
return allPoints;
}
@ -685,7 +685,7 @@ namespace NavisworksTransport
if (contour.Count >= 3)
{
var area = CalculateContourArea(contour);
LogManager.WriteLog($"轮廓 {i + 1} 面积: {area:F2}, 点数: {contour.Count}");
LogManager.Debug($"轮廓 {i + 1} 面积: {area:F2}, 点数: {contour.Count}");
if (area > maxArea)
{
@ -695,17 +695,17 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"选择最大轮廓,面积: {maxArea:F2}, 原始点数: {largestContour.Count}");
LogManager.Debug($"选择最大轮廓,面积: {maxArea:F2}, 原始点数: {largestContour.Count}");
// 清理和排序最大轮廓
var cleanedContour = CleanAndSortContour(largestContour);
LogManager.WriteLog($"清理后轮廓点数: {cleanedContour.Count}");
LogManager.Debug($"清理后轮廓点数: {cleanedContour.Count}");
return cleanedContour;
}
catch (Exception ex)
{
LogManager.WriteLog($"获取最大轮廓失败: {ex.Message}");
LogManager.Error($"获取最大轮廓失败: {ex.Message}");
return allPoints; // 返回原始点集合
}
}
@ -723,7 +723,7 @@ namespace NavisworksTransport
{
if (contour.Count < 3)
{
LogManager.WriteLog($"轮廓点数不足: {contour.Count}");
LogManager.Warning($"轮廓点数不足: {contour.Count}");
return contour;
}
@ -750,26 +750,26 @@ namespace NavisworksTransport
}
}
LogManager.WriteLog($"去重后点数: {uniquePoints.Count}");
LogManager.Debug($"去重后点数: {uniquePoints.Count}");
if (uniquePoints.Count < 3)
{
LogManager.WriteLog("去重后点数不足,返回原始轮廓");
LogManager.Warning("去重后点数不足,返回原始轮廓");
return contour;
}
// 第2步排序点以形成正确的轮廓
result = SortPointsIntoContour(uniquePoints);
LogManager.WriteLog($"排序后轮廓点数: {result.Count}");
LogManager.Debug($"排序后轮廓点数: {result.Count}");
for (int i = 0; i < result.Count; i++)
{
LogManager.WriteLog($" 排序点 {i}: ({result[i].X:F2}, {result[i].Y:F2})");
LogManager.Debug($" 排序点 {i}: ({result[i].X:F2}, {result[i].Y:F2})");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"清理轮廓失败: {ex.Message}");
LogManager.Error($"清理轮廓失败: {ex.Message}");
return contour;
}
@ -796,7 +796,7 @@ namespace NavisworksTransport
var remainingPoints = new List<Point2D>(points);
remainingPoints.Remove(startPoint);
LogManager.WriteLog($"轮廓起始点: ({startPoint.X:F2}, {startPoint.Y:F2})");
LogManager.Debug($"轮廓起始点: ({startPoint.X:F2}, {startPoint.Y:F2})");
// 使用最近邻算法按顺序连接点
var currentPoint = startPoint;
@ -825,11 +825,11 @@ namespace NavisworksTransport
remainingPoints.Remove(nearestPoint.Value);
currentPoint = nearestPoint.Value;
LogManager.WriteLog($"下一个点: ({nearestPoint.Value.X:F2}, {nearestPoint.Value.Y:F2}), 距离: {minDistance:F2}");
LogManager.Debug($"下一个点: ({nearestPoint.Value.X:F2}, {nearestPoint.Value.Y:F2}), 距离: {minDistance:F2}");
}
else
{
LogManager.WriteLog("无法找到下一个最近点");
LogManager.Warning("无法找到下一个最近点");
break;
}
}
@ -844,12 +844,12 @@ namespace NavisworksTransport
Math.Pow(lastPoint.Y - firstPoint.Y, 2)
);
LogManager.WriteLog($"轮廓闭合距离: {closingDistance:F2}");
LogManager.Debug($"轮廓闭合距离: {closingDistance:F2}");
}
}
catch (Exception ex)
{
LogManager.WriteLog($"排序轮廓点失败: {ex.Message}");
LogManager.Error($"排序轮廓点失败: {ex.Message}");
return points;
}
@ -940,7 +940,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"射线-三角形相交计算出错: {ex.Message}");
LogManager.Error($"射线-三角形相交计算出错: {ex.Message}");
return false;
}
}
@ -1041,11 +1041,11 @@ namespace NavisworksTransport
if (_transformMatrix != null)
{
LogManager.WriteLog($"几何回调处理器使用预处理矩阵,元素数量: {_transformMatrix.Length}");
LogManager.Debug($"几何回调处理器使用预处理矩阵,元素数量: {_transformMatrix.Length}");
}
else
{
LogManager.WriteLog("几何回调处理器使用单位矩阵(无变换)");
LogManager.Debug("几何回调处理器使用单位矩阵(无变换)");
}
}
@ -1089,7 +1089,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"处理三角形失败: {ex.Message}");
LogManager.Error($"处理三角形失败: {ex.Message}");
}
}
@ -1130,7 +1130,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.WriteLog($"坐标变换失败: {ex.Message}");
LogManager.Error($"坐标变换失败: {ex.Message}");
return localPoint;
}
}

View File

@ -3,6 +3,17 @@ using System.IO;
namespace NavisworksTransport
{
/// <summary>
/// 日志级别枚举
/// </summary>
public enum LogLevel
{
Debug = 0,
Info = 1,
Warning = 2,
Error = 3
}
/// <summary>
/// 统一日志管理器
/// 提供全局日志记录功能
@ -10,11 +21,16 @@ namespace NavisworksTransport
public static class LogManager
{
private static readonly string _logFilePath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Autodesk", "Navisworks Manage 2026", "NavisworksTransport", "logs", "debug.log");
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"Autodesk", "Navisworks Manage 2026", "plugins", "NavisworksTransportPlugin", "logs", "debug.log");
private static readonly object _lockObject = new object();
/// <summary>
/// 当前日志级别,默认为 Info
/// </summary>
private static LogLevel _currentLevel = LogLevel.Info;
/// <summary>
/// 静态构造函数,确保日志目录存在并清空旧日志
/// </summary>
@ -43,6 +59,31 @@ namespace NavisworksTransport
/// </summary>
public static string LogFilePath => _logFilePath;
/// <summary>
/// 设置日志级别
/// </summary>
public static void SetLogLevel(LogLevel level)
{
_currentLevel = level;
Info($"日志级别已设置为: {level}");
}
/// <summary>
/// 获取当前日志级别
/// </summary>
public static LogLevel GetLogLevel()
{
return _currentLevel;
}
/// <summary>
/// 检查是否应该记录指定级别的日志
/// </summary>
private static bool ShouldLog(LogLevel level)
{
return level >= _currentLevel;
}
/// <summary>
/// 清空调试日志
/// </summary>
@ -81,10 +122,10 @@ namespace NavisworksTransport
}
/// <summary>
/// 写入调试日志同时输出到Debug和文件
/// 写入调试日志同时输出到Debug和文件- 私有方法,强制使用带级别的方法
/// </summary>
/// <param name="message">日志消息</param>
public static void WriteLog(string message)
private static void WriteLog(string message)
{
var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}";
System.Diagnostics.Debug.WriteLine(logMessage);
@ -108,7 +149,10 @@ namespace NavisworksTransport
/// <param name="message">日志消息</param>
public static void Info(string message)
{
WriteLog($"[INFO] {message}");
if (ShouldLog(LogLevel.Info))
{
WriteLog($"[INFO] {message}");
}
}
/// <summary>
@ -117,7 +161,10 @@ namespace NavisworksTransport
/// <param name="message">日志消息</param>
public static void Warning(string message)
{
WriteLog($"[WARN] {message}");
if (ShouldLog(LogLevel.Warning))
{
WriteLog($"[WARN] {message}");
}
}
/// <summary>
@ -126,7 +173,10 @@ namespace NavisworksTransport
/// <param name="message">日志消息</param>
public static void Error(string message)
{
WriteLog($"[ERROR] {message}");
if (ShouldLog(LogLevel.Error))
{
WriteLog($"[ERROR] {message}");
}
}
/// <summary>
@ -136,8 +186,11 @@ namespace NavisworksTransport
/// <param name="exception">异常对象</param>
public static void Error(string message, Exception exception)
{
WriteLog($"[ERROR] {message}: {exception.Message}");
WriteLog($"[ERROR] 堆栈跟踪: {exception.StackTrace}");
if (ShouldLog(LogLevel.Error))
{
WriteLog($"[ERROR] {message}: {exception.Message}");
WriteLog($"[ERROR] 堆栈跟踪: {exception.StackTrace}");
}
}
/// <summary>
@ -146,7 +199,10 @@ namespace NavisworksTransport
/// <param name="message">日志消息</param>
public static void Debug(string message)
{
WriteLog($"[DEBUG] {message}");
if (ShouldLog(LogLevel.Debug))
{
WriteLog($"[DEBUG] {message}");
}
}
/// <summary>

View File

@ -26,12 +26,12 @@ namespace NavisworksTransport.Utils
// 方法: 空集合的SetHidden操作几乎零开销的伪操作
var emptyCollection = new ModelItemCollection();
document.Models.SetHidden(emptyCollection, false);
LogManager.WriteLog($"[{logPrefix}] ✅ 已执行轻量级缓存刷新");
LogManager.Debug($"[{logPrefix}] ✅ 已执行轻量级缓存刷新");
}
}
catch (Exception refreshEx)
{
LogManager.WriteLog($"[{logPrefix}] ⚠️ 缓存刷新失败,但不影响主要操作: {refreshEx.Message}");
LogManager.Warning($"[{logPrefix}] ⚠️ 缓存刷新失败,但不影响主要操作: {refreshEx.Message}");
}
}