From a625a498a14d48b27cf5fbeae298192775758137 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 14 Aug 2025 09:30:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8DockPanePlugin=E4=BB=A3=E6=9B=BFAddInP?= =?UTF-8?q?lugin=EF=BC=8C=E6=8E=A7=E4=BB=B6UI=E4=BD=BF=E7=94=A8WPF?= =?UTF-8?q?=EF=BC=8C=E5=B1=9E=E6=80=A7=E7=AE=A1=E7=90=86=E5=92=8C=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E8=AE=BE=E7=BD=AE=E4=B8=A4=E4=B8=AA=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E7=9A=84=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .kiro/specs/dockpane-migration/design.md | 516 +++++ .../specs/dockpane-migration/requirements.md | 96 + .kiro/specs/dockpane-migration/tasks.md | 253 ++ CHANGELOG.md | 19 + GEMINI.md | 78 - NavisworksTransportPlugin.csproj | 65 +- QWEN.md | 150 ++ VERSION.md | 2 +- deploy-from-anywhere.bat | 109 + deploy-plugin.bat | 69 + deploy-plugin.ps1 | 90 + doc/design/2026/Guidelines.md | 38 + doc/design/2026/Idea.md | 44 + doc/migration/File_Organization_Plan.md | 53 - doc/migration/File_Reorganization_Complete.md | 0 doc/migration/Project_Configuration_Review.md | 279 --- src/Core/Animation/PathAnimationManager.cs | 917 ++++++++ src/Core/MainPlugin.cs | 751 +++--- src/Core/PathPlanningManager.cs | 91 +- .../Properties/CategoryAttributeManager.cs | 9 +- src/Core/TestPlugin.cs | 30 + .../NavisworksTransport.Tian.name.txt | 29 + src/UI/Forms/SimpleDockPaneControl.cs | 205 ++ src/UI/WPF/Commands/RelayCommand.cs | 102 + src/UI/WPF/LogisticsControlPanel.xaml | 217 ++ src/UI/WPF/LogisticsControlPanel.xaml.cs | 194 ++ src/UI/WPF/Models/LogisticsModel.cs | 72 + src/UI/WPF/Models/PathRouteViewModel.cs | 197 ++ .../ViewModels/LogisticsControlViewModel.cs | 2029 +++++++++++++++++ src/UI/WPF/ViewModels/ViewModelBase.cs | 82 + src/UI/WPF/Views/AnimationControlView.xaml | 76 + src/UI/WPF/Views/AnimationControlView.xaml.cs | 15 + src/UI/WPF/Views/ModelSettingsView.xaml | 75 + src/UI/WPF/Views/ModelSettingsView.xaml.cs | 15 + src/UI/WPF/Views/PathEditingView.xaml | 78 + src/UI/WPF/Views/PathEditingView.xaml.cs | 15 + src/UI/WPF/Views/SystemManagementView.xaml | 96 + src/UI/WPF/Views/SystemManagementView.xaml.cs | 15 + 38 files changed, 6362 insertions(+), 809 deletions(-) create mode 100644 .kiro/specs/dockpane-migration/design.md create mode 100644 .kiro/specs/dockpane-migration/requirements.md create mode 100644 .kiro/specs/dockpane-migration/tasks.md delete mode 100644 GEMINI.md create mode 100644 QWEN.md create mode 100644 deploy-from-anywhere.bat create mode 100644 deploy-plugin.bat create mode 100644 deploy-plugin.ps1 create mode 100644 doc/design/2026/Guidelines.md create mode 100644 doc/design/2026/Idea.md delete mode 100644 doc/migration/File_Organization_Plan.md delete mode 100644 doc/migration/File_Reorganization_Complete.md delete mode 100644 doc/migration/Project_Configuration_Review.md create mode 100644 src/Core/Animation/PathAnimationManager.cs create mode 100644 src/Core/TestPlugin.cs create mode 100644 src/Resources/NavisworksTransport.Tian.name.txt create mode 100644 src/UI/Forms/SimpleDockPaneControl.cs create mode 100644 src/UI/WPF/Commands/RelayCommand.cs create mode 100644 src/UI/WPF/LogisticsControlPanel.xaml create mode 100644 src/UI/WPF/LogisticsControlPanel.xaml.cs create mode 100644 src/UI/WPF/Models/LogisticsModel.cs create mode 100644 src/UI/WPF/Models/PathRouteViewModel.cs create mode 100644 src/UI/WPF/ViewModels/LogisticsControlViewModel.cs create mode 100644 src/UI/WPF/ViewModels/ViewModelBase.cs create mode 100644 src/UI/WPF/Views/AnimationControlView.xaml create mode 100644 src/UI/WPF/Views/AnimationControlView.xaml.cs create mode 100644 src/UI/WPF/Views/ModelSettingsView.xaml create mode 100644 src/UI/WPF/Views/ModelSettingsView.xaml.cs create mode 100644 src/UI/WPF/Views/PathEditingView.xaml create mode 100644 src/UI/WPF/Views/PathEditingView.xaml.cs create mode 100644 src/UI/WPF/Views/SystemManagementView.xaml create mode 100644 src/UI/WPF/Views/SystemManagementView.xaml.cs diff --git a/.kiro/specs/dockpane-migration/design.md b/.kiro/specs/dockpane-migration/design.md new file mode 100644 index 0000000..e7592a9 --- /dev/null +++ b/.kiro/specs/dockpane-migration/design.md @@ -0,0 +1,516 @@ +# Design Document + +## Overview + +本设计文档详细描述了将Navisworks物流路径规划插件从AddInPlugin架构迁移到DockPanePlugin架构的技术实现方案。基于Navisworks 2026 API文档中的DockPanePlugin示例代码,我们将重新设计插件架构以提供可停靠的用户界面面板,并利用WPF技术栈实现更现代化的用户体验。 + +## Architecture + +### 插件架构变更 + +#### 当前架构 (AddInPlugin) +```csharp +[PluginAttribute("Basic", "Tian", ToolTip = "Transport Plugin", DisplayName = "Transport Plugin")] +[AddInPlugin(AddInLocation.AddIn)] +public class Main : AddInPlugin +{ + public override int Execute(params string[] parameters) + { + // 显示模态对话框 + ShowDockPane(); + return 0; + } +} +``` + +#### 目标架构 (DockPanePlugin) +```csharp +[Plugin("NavisworksTransport.Main", "Tian", + DisplayName = "物流路径规划", + ToolTip = "物流路径规划和动画仿真插件")] +[DockPanePlugin(420, 700)] // 宽度420, 高度700 +[Strings("NavisworksTransport.Tian.name")] +public class Main : DockPanePlugin +{ + public override Control CreateControlPane() + { + // 创建WPF用户控件并用ElementHost托管 + var wpfControl = new LogisticsControlPanel(); + var elementHost = new ElementHost + { + Dock = DockStyle.Fill, + Child = wpfControl + }; + return elementHost; + } + + public override void DestroyControlPane(Control pane) + { + // 清理资源和事件订阅 + if (pane is ElementHost host && host.Child is LogisticsControlPanel wpfControl) + { + wpfControl.Cleanup(); + } + pane.Dispose(); + } +} +``` + +### WPF用户控件架构 + +#### 主控件结构 +``` +LogisticsControlPanel (UserControl) +├── MainTabControl (TabControl) +│ ├── ModelSettingsTab (TabItem) +│ │ └── ModelSettingsView (UserControl) +│ ├── PathEditingTab (TabItem) +│ │ └── PathEditingView (UserControl) +│ ├── AnimationControlTab (TabItem) +│ │ └── AnimationControlView (UserControl) +│ └── SystemManagementTab (TabItem) +│ └── SystemManagementView (UserControl) +└── BottomPanel (StackPanel) + ├── HelpButton (Button) + ├── AboutButton (Button) + └── CloseButton (Button) +``` + +## Components and Interfaces + +### 1. 主插件类 (Main) + +**职责:** +- 实现DockPanePlugin接口 +- 管理插件生命周期 +- 创建和销毁控件面板 + +**关键方法:** +- `CreateControlPane()`: 创建ElementHost并托管WPF控件 +- `DestroyControlPane(Control pane)`: 清理资源和释放控件 + +### 2. WPF主控件 (LogisticsControlPanel) + +**职责:** +- 作为所有功能的容器 +- 管理Tab页面切换 +- 处理全局事件和状态 + +**关键属性和方法:** +```csharp +public partial class LogisticsControlPanel : UserControl +{ + // 子视图引用 + private ModelSettingsView _modelSettingsView; + private PathEditingView _pathEditingView; + private AnimationControlView _animationControlView; + private SystemManagementView _systemManagementView; + + // 管理器实例 + private PathPlanningManager _pathPlanningManager; + private static bool _isSessionInitialized = false; + + public LogisticsControlPanel() + { + InitializeComponent(); + InitializeSession(); + InitializeViews(); + SubscribeToEvents(); + } + + public void Cleanup() + { + UnsubscribeFromEvents(); + CleanupManagers(); + } +} +``` + +### 3. 子视图控件 + +#### ModelSettingsView +- 类别属性设置 +- 物流模型列表 +- 可见性控制 +- 统计信息显示 + +#### PathEditingView +- 路径列表管理 +- 当前路径编辑 +- 路径文件管理 + +#### AnimationControlView +- 动画参数设置 +- 播放控制 +- 碰撞检测 + +#### SystemManagementView +- 模型分层拆分 +- 日志管理 +- 插件设置 +- 系统信息 + +### 4. MVVM架构支持 + +#### ViewModel基类 +```csharp +public abstract class ViewModelBase : INotifyPropertyChanged +{ + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetProperty(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } +} +``` + +#### 主ViewModel +```csharp +public class LogisticsControlViewModel : ViewModelBase +{ + private string _selectedModelsText; + private string _instructionText; + private ObservableCollection _logisticsModels; + + public string SelectedModelsText + { + get => _selectedModelsText; + set => SetProperty(ref _selectedModelsText, value); + } + + public string InstructionText + { + get => _instructionText; + set => SetProperty(ref _instructionText, value); + } + + public ObservableCollection LogisticsModels + { + get => _logisticsModels; + set => SetProperty(ref _logisticsModels, value); + } + + // Commands + public ICommand RefreshCommand { get; } + public ICommand ClearSelectionCommand { get; } + public ICommand ShowAllCommand { get; } + public ICommand HideAllCommand { get; } +} +``` + +## Data Models + +### 1. 数据传输对象 + +#### LogisticsModel +```csharp +public class LogisticsModel : INotifyPropertyChanged +{ + private string _name; + private string _category; + private string _attributes; + private bool _isVisible; + + public string Name + { + get => _name; + set => SetProperty(ref _name, value); + } + + public string Category + { + get => _category; + set => SetProperty(ref _category, value); + } + + public string Attributes + { + get => _attributes; + set => SetProperty(ref _attributes, value); + } + + public bool IsVisible + { + get => _isVisible; + set => SetProperty(ref _isVisible, value); + } + + public ModelItem NavisworksItem { get; set; } + + // INotifyPropertyChanged implementation + public event PropertyChangedEventHandler PropertyChanged; + + private void SetProperty(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return; + field = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} +``` + +#### PathRoute +```csharp +public class PathRoute : INotifyPropertyChanged +{ + private string _name; + private ObservableCollection _points; + private bool _isActive; + + public string Name + { + get => _name; + set => SetProperty(ref _name, value); + } + + public ObservableCollection Points + { + get => _points; + set => SetProperty(ref _points, value); + } + + public bool IsActive + { + get => _isActive; + set => SetProperty(ref _isActive, value); + } + + // INotifyPropertyChanged implementation + public event PropertyChangedEventHandler PropertyChanged; + + private void SetProperty(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return; + field = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} +``` + +### 2. 业务逻辑管理器 + +现有的管理器类保持不变,但需要适配WPF的事件模型: +- `PathPlanningManager` +- `PathDataManager` +- `VisibilityManager` +- `ModelSplitterManager` +- `LogisticsAnimationManager` + +## Error Handling + +### 1. WPF异常处理 + +```csharp +public partial class LogisticsControlPanel : UserControl +{ + public LogisticsControlPanel() + { + InitializeComponent(); + + // 订阅WPF异常事件 + Dispatcher.UnhandledException += OnDispatcherUnhandledException; + } + + private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + LogManager.Error($"[WPF异常] {e.Exception.Message}"); + LogManager.Error($"[WPF异常] 堆栈信息: {e.Exception.StackTrace}"); + + // 标记异常已处理,避免程序崩溃 + e.Handled = true; + + // 显示用户友好的错误信息 + MessageBox.Show($"界面操作发生错误: {e.Exception.Message}", + "错误", MessageBoxButton.OK, MessageBoxImage.Warning); + } +} +``` + +### 2. 线程安全处理 + +```csharp +public class ThreadSafeHelper +{ + public static void InvokeOnUIThread(Action action) + { + if (Application.Current?.Dispatcher?.CheckAccess() == true) + { + action(); + } + else + { + Application.Current?.Dispatcher?.Invoke(action); + } + } + + public static T InvokeOnUIThread(Func func) + { + if (Application.Current?.Dispatcher?.CheckAccess() == true) + { + return func(); + } + else + { + return Application.Current.Dispatcher.Invoke(func); + } + } +} +``` + +## Testing Strategy + +### 1. 单元测试 + +#### 插件生命周期测试 +```csharp +[TestClass] +public class MainPluginTests +{ + [TestMethod] + public void CreateControlPane_ShouldReturnElementHost() + { + // Arrange + var plugin = new Main(); + + // Act + var control = plugin.CreateControlPane(); + + // Assert + Assert.IsInstanceOfType(control, typeof(ElementHost)); + Assert.IsNotNull(((ElementHost)control).Child); + Assert.IsInstanceOfType(((ElementHost)control).Child, typeof(LogisticsControlPanel)); + } + + [TestMethod] + public void DestroyControlPane_ShouldDisposeControl() + { + // Arrange + var plugin = new Main(); + var control = plugin.CreateControlPane(); + + // Act + plugin.DestroyControlPane(control); + + // Assert + Assert.IsTrue(control.IsDisposed); + } +} +``` + +#### WPF控件测试 +```csharp +[TestClass] +public class LogisticsControlPanelTests +{ + [TestMethod] + public void Constructor_ShouldInitializeAllViews() + { + // Arrange & Act + var control = new LogisticsControlPanel(); + + // Assert + Assert.IsNotNull(control.FindName("MainTabControl")); + Assert.IsNotNull(control.FindName("ModelSettingsTab")); + Assert.IsNotNull(control.FindName("PathEditingTab")); + Assert.IsNotNull(control.FindName("AnimationControlTab")); + Assert.IsNotNull(control.FindName("SystemManagementTab")); + } +} +``` + +### 2. 集成测试 + +#### Navisworks API集成测试 +```csharp +[TestClass] +public class NavisworksIntegrationTests +{ + [TestMethod] + public void SelectionChanged_ShouldUpdateViewModel() + { + // 需要在Navisworks环境中运行 + // 测试选择变更事件是否正确更新WPF界面 + } + + [TestMethod] + public void PathPlanning_ShouldCreateVisualPath() + { + // 测试路径规划功能是否在WPF界面中正确显示 + } +} +``` + +### 3. 用户界面测试 + +#### WPF自动化测试 +```csharp +[TestClass] +public class UIAutomationTests +{ + [TestMethod] + public void TabSwitching_ShouldWorkCorrectly() + { + // 使用WPF测试框架测试Tab切换功能 + } + + [TestMethod] + public void DataBinding_ShouldUpdateUI() + { + // 测试数据绑定是否正确更新界面 + } +} +``` + +## Performance Considerations + +### 1. WPF渲染优化 + +- 使用虚拟化ListView和TreeView处理大量数据 +- 实现延迟加载避免界面卡顿 +- 使用异步操作处理耗时任务 + +### 2. 内存管理 + +- 正确实现IDisposable接口 +- 及时取消事件订阅避免内存泄漏 +- 使用WeakReference处理长期引用 + +### 3. 线程优化 + +- 将Navisworks API调用保持在主线程 +- 使用BackgroundWorker处理后台任务 +- 合理使用Dispatcher.BeginInvoke避免界面阻塞 + +## Migration Strategy + +### 阶段1:基础架构迁移 +1. 创建DockPanePlugin基础结构 +2. 实现ElementHost托管机制 +3. 创建基本的WPF用户控件框架 + +### 阶段2:功能模块迁移 +1. 迁移模型设置功能到WPF +2. 迁移路径编辑功能到WPF +3. 迁移动画控制功能到WPF +4. 迁移系统管理功能到WPF + +### 阶段3:优化和测试 +1. 实现MVVM模式和数据绑定 +2. 优化性能和用户体验 +3. 完善错误处理和异常管理 +4. 进行全面测试和调试 + +### 阶段4:部署和验证 +1. 更新部署配置 +2. 验证插件注册和加载 +3. 进行用户验收测试 +4. 文档更新和培训 \ No newline at end of file diff --git a/.kiro/specs/dockpane-migration/requirements.md b/.kiro/specs/dockpane-migration/requirements.md new file mode 100644 index 0000000..e65119d --- /dev/null +++ b/.kiro/specs/dockpane-migration/requirements.md @@ -0,0 +1,96 @@ +# Requirements Document + +## Introduction + +本需求文档定义了将现有的Navisworks物流路径规划插件从AddInPlugin架构迁移到DockPanePlugin架构的功能需求。根据技术方案文档,DockPanePlugin能够提供更好的用户体验,允许创建可停靠的面板,并支持WPF技术栈以实现更丰富的交互式导航控件。 + +## Requirements + +### Requirement 1 + +**User Story:** 作为插件开发者,我希望将现有的AddInPlugin改为DockPanePlugin,以便提供可停靠的用户界面面板。 + +#### Acceptance Criteria + +1. WHEN 插件加载时 THEN 系统应创建一个可停靠的面板而不是模态对话框 +2. WHEN 用户关闭面板时 THEN 面板应能够被重新打开而不重启Navisworks +3. WHEN 面板显示时 THEN 用户应能够将面板停靠到Navisworks界面的不同位置 +4. WHEN 面板创建时 THEN 系统应正确实现CreateControlPane和DestroyControlPane方法 + +### Requirement 2 + +**User Story:** 作为用户,我希望插件界面使用WPF技术,以便获得更现代化和响应式的用户体验。 + +#### Acceptance Criteria + +1. WHEN 面板显示时 THEN 界面应使用WPF而不是Windows Forms +2. WHEN 用户与界面交互时 THEN 界面应提供流畅的响应和现代化的视觉效果 +3. WHEN 面板调整大小时 THEN WPF控件应正确响应布局变化 +4. WHEN 面板内容更新时 THEN WPF数据绑定应正确工作 + +### Requirement 3 + +**User Story:** 作为开发者,我希望保持现有的所有功能,以便用户在迁移后不会丢失任何特性。 + +#### Acceptance Criteria + +1. WHEN 迁移完成时 THEN 所有现有的类别设置功能应正常工作 +2. WHEN 迁移完成时 THEN 所有现有的路径编辑功能应正常工作 +3. WHEN 迁移完成时 THEN 所有现有的动画控制功能应正常工作 +4. WHEN 迁移完成时 THEN 所有现有的系统管理功能应正常工作 +5. WHEN 迁移完成时 THEN 所有事件处理和数据管理逻辑应保持不变 + +### Requirement 4 + +**User Story:** 作为开发者,我希望正确处理DockPanePlugin的生命周期管理,以便避免内存泄漏和资源冲突。 + +#### Acceptance Criteria + +1. WHEN CreateControlPane被调用时 THEN 系统应正确创建并返回WPF用户控件 +2. WHEN DestroyControlPane被调用时 THEN 系统应正确释放所有资源和事件订阅 +3. WHEN 面板重复创建时 THEN 系统应避免重复订阅事件或创建重复实例 +4. WHEN Navisworks关闭时 THEN 所有插件资源应被正确清理 + +### Requirement 5 + +**User Story:** 作为开发者,我希望利用ElementHost控件来托管WPF内容,以便在Navisworks的.NET Framework环境中正确运行。 + +#### Acceptance Criteria + +1. WHEN 创建面板时 THEN 系统应使用ElementHost控件托管WPF用户控件 +2. WHEN WPF控件需要与Navisworks API交互时 THEN ElementHost应正确处理线程调用 +3. WHEN 面板大小改变时 THEN ElementHost应正确调整WPF内容的大小 +4. WHEN 处理用户输入时 THEN ElementHost应正确转发鼠标和键盘事件 + +### Requirement 6 + +**User Story:** 作为开发者,我希望更新插件属性和注册方式,以便正确注册为DockPanePlugin。 + +#### Acceptance Criteria + +1. WHEN 插件编译时 THEN 系统应使用正确的DockPanePlugin属性而不是AddInPlugin属性 +2. WHEN 插件加载时 THEN Navisworks应将插件识别为可停靠面板插件 +3. WHEN 插件在Navisworks中显示时 THEN 插件应出现在正确的菜单位置 +4. WHEN 插件属性设置时 THEN DisplayName、ToolTip等属性应正确显示 + +### Requirement 7 + +**User Story:** 作为开发者,我希望创建合适的WPF用户控件结构,以便替换现有的Windows Forms界面。 + +#### Acceptance Criteria + +1. WHEN 创建WPF控件时 THEN 系统应创建包含所有现有功能的用户控件 +2. WHEN WPF控件布局时 THEN 应使用适当的WPF布局容器(如Grid、StackPanel等) +3. WHEN WPF控件绑定数据时 THEN 应使用MVVM模式或适当的数据绑定机制 +4. WHEN WPF控件处理事件时 THEN 应正确处理WPF事件模型 + +### Requirement 8 + +**User Story:** 作为开发者,我希望确保API调用的兼容性,以便所有Navisworks API调用在新架构下正常工作。 + +#### Acceptance Criteria + +1. WHEN 从WPF控件调用Navisworks API时 THEN 所有API调用应在正确的线程上执行 +2. WHEN 处理Navisworks事件时 THEN 事件处理应正确更新WPF界面 +3. WHEN 访问Document和Selection对象时 THEN 应正确处理跨线程访问 +4. WHEN 使用Graphics类绘制时 THEN 绘制操作应与WPF界面正确协调 \ No newline at end of file diff --git a/.kiro/specs/dockpane-migration/tasks.md b/.kiro/specs/dockpane-migration/tasks.md new file mode 100644 index 0000000..20055fd --- /dev/null +++ b/.kiro/specs/dockpane-migration/tasks.md @@ -0,0 +1,253 @@ +# Implementation Plan + +- [x] 1. 创建DockPanePlugin基础架构 + + + + - 修改Main类继承DockPanePlugin而不是AddInPlugin + - 更新插件属性为[Plugin]和[DockPanePlugin] + - 实现CreateControlPane()和DestroyControlPane()方法 + - 创建ElementHost托管机制 + - _Requirements: 1.1, 1.2, 1.3, 1.4_ + + + + + + + +- [ ] 2. 创建WPF用户控件框架 +- [x] 2.1 创建主WPF用户控件LogisticsControlPanel + + - 创建LogisticsControlPanel.xaml和.xaml.cs文件 + + - 实现基本的TabControl布局结构 + - 添加底部操作按钮面板 + - 实现Cleanup()方法用于资源清理 + - _Requirements: 2.1, 2.2, 2.3_ + +- [x] 2.2 创建MVVM基础架构 + + - 实现ViewModelBase基类支持INotifyPropertyChanged + - 创建LogisticsControlViewModel主视图模型 + - 实现ThreadSafeHelper线程安全辅助类 + - 创建数据模型类LogisticsModel和PathRoute + + + + - _Requirements: 2.2, 2.4, 5.2_ + +- [x] 2.3 创建子视图用户控件 + + + - 创建ModelSettingsView.xaml用户控件 + - 创建PathEditingView.xaml用户控件 + - 创建AnimationControlView.xaml用户控件 + - 创建SystemManagementView.xaml用户控件 + - _Requirements: 2.1, 2.3, 7.1, 7.2_ + +- [x] 3. 迁移模型设置功能到WPF + + +- [x] 3.1 实现类别属性设置界面 + + - 将类别下拉框转换为WPF ComboBox + - 实现数据绑定连接CategoryAttributeManager + - 添加选择提示和状态显示标签 + - 测试类别选择功能 + - _Requirements: 3.1, 3.2, 7.3, 8.2_ + +- [x] 3.2 实现物流模型列表界面 + + - 将ListView转换为WPF ListView + - 实现ObservableCollection数据绑定 + - 添加模型属性编辑功能 + - 实现选择同步机制 + - _Requirements: 3.1, 3.2, 7.3, 8.1_ + + + +- [x] 3.3 实现可见性控制界面 + + - 转换可见性控制按钮为WPF Button + - 实现Command模式处理按钮点击 + - 连接VisibilityManager业务逻辑 + - 添加统计信息显示 + - _Requirements: 3.1, 3.2, 7.4, 8.2_ + +- [ ] 4. 迁移路径编辑功能到WPF +- [x] 4.1 实现路径列表管理界面 + + + - 转换路径列表ListView为WPF版本 + - 实现路径创建、删除、重命名功能 + - 连接PathPlanningManager业务逻辑 + - 添加路径状态显示 + - _Requirements: 3.3, 7.3, 8.1, 8.2_ + +- [x] 4.2 实现当前路径编辑界面 + + + + - 转换路径点列表为WPF ListView + - 实现路径点添加、删除、编辑功能 + - 添加3D交互支持 + - 实现路径可视化更新 + - _Requirements: 3.3, 7.4, 8.1, 8.3_ + +- [x] 4.3 实现路径文件管理界面 + + + - 添加路径导入导出按钮 + - 实现文件对话框集成 + - 连接PathDataManager文件操作 + - 添加操作状态反馈 + - _Requirements: 3.3, 8.1_ + +- [ ] 5. 迁移动画控制功能到WPF +- [x] 5.1 实现动画参数设置界面 + + + - 转换动画参数控件为WPF版本 + - 实现数据绑定和验证 + - 连接LogisticsAnimationManager + - 添加参数预览功能 + - _Requirements: 3.4, 7.3, 8.1_ + +- [x] 5.2 实现播放控制界面 + + - 转换播放控制按钮为WPF版本 + - 实现进度条和状态显示 + - 添加播放速度控制 + - 连接动画播放逻辑 + - _Requirements: 3.4, 7.4, 8.2_ + +- [x] 5.3 实现碰撞检测界面 + + - 转换碰撞检测控件为WPF版本 + - 连接ClashDetectiveIntegration + - 实现碰撞结果显示 + - 添加碰撞报告功能 + - _Requirements: 3.4, 8.1, 8.2_ + +- [ ] 6. 迁移系统管理功能到WPF +- [x] 6.1 实现模型分层拆分界面 + + + - 转换ModelSplitterDialog为WPF版本 + - 实现楼层选择和属性筛选 + - 连接ModelSplitterManager业务逻辑 + - 添加拆分进度显示 + - _Requirements: 3.5, 7.1, 8.1_ + +- [x] 6.2 实现日志管理界面 + + - 创建日志查看和管理控件 + - 实现日志级别筛选 + - 添加日志导出功能 + - 连接LogManager + - _Requirements: 3.5, 7.3_ + +- [x] 6.3 实现插件设置界面 + + + - 创建设置选项控件 + - 实现设置保存和加载 + - 添加设置验证 + - 实现设置重置功能 + - _Requirements: 3.5, 7.3_ + +- [ ] 7. 实现生命周期管理和资源清理 +- [ ] 7.1 实现插件生命周期管理 + - 完善CreateControlPane()方法 + - 实现DestroyControlPane()资源清理 + - 添加重复创建保护机制 + - 测试面板创建和销毁 + - _Requirements: 4.1, 4.2, 4.3, 4.4_ + +- [ ] 7.2 实现事件订阅管理 + - 创建事件订阅管理器 + - 实现自动取消订阅机制 + - 添加事件处理异常保护 + - 测试事件生命周期 + - _Requirements: 4.3, 4.4, 8.2_ + +- [ ] 7.3 实现ElementHost集成 + - 完善ElementHost配置 + - 实现WPF与WinForms的交互 + - 添加大小调整处理 + - 测试跨技术栈事件传递 + - _Requirements: 5.1, 5.2, 5.3, 5.4_ + +- [ ] 8. 实现错误处理和线程安全 +- [ ] 8.1 实现WPF异常处理 + - 添加Dispatcher.UnhandledException处理 + - 实现用户友好的错误提示 + - 连接GlobalExceptionHandler + - 测试异常恢复机制 + - _Requirements: 4.4, 8.1, 8.2_ + +- [ ] 8.2 实现线程安全机制 + - 完善ThreadSafeHelper实现 + - 确保Navisworks API调用线程安全 + - 实现UI更新线程调度 + - 测试跨线程操作 + - _Requirements: 5.2, 8.1, 8.3, 8.4_ + +- [ ] 8.3 实现内存管理 + - 添加IDisposable实现 + - 实现WeakReference长期引用 + - 添加内存泄漏检测 + - 优化大对象处理 + - _Requirements: 4.4, 7.3_ + +- [ ] 9. 更新插件注册和部署配置 +- [ ] 9.1 更新插件属性和注册 + - 修改Plugin属性配置 + - 更新DisplayName和ToolTip + - 创建本地化字符串文件 + - 测试插件识别和加载 + - _Requirements: 6.1, 6.2, 6.3, 6.4_ + +- [ ] 9.2 更新项目配置 + - 添加WPF相关引用 + - 更新编译配置 + - 修改输出路径设置 + - 测试编译和部署 + - _Requirements: 6.1, 6.4_ + +- [ ] 9.3 创建本地化支持 + - 创建字符串资源文件 + - 实现多语言支持 + - 添加本地化测试 + - 验证字符串显示 + - _Requirements: 6.4_ + +- [ ] 10. 进行全面测试和优化 +- [ ] 10.1 创建单元测试 + - 编写插件生命周期测试 + - 创建WPF控件测试 + - 实现ViewModel测试 + - 添加业务逻辑测试 + - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_ + +- [ ] 10.2 进行集成测试 + - 测试Navisworks API集成 + - 验证事件处理机制 + - 测试数据绑定功能 + - 验证线程安全性 + - _Requirements: 8.1, 8.2, 8.3, 8.4_ + +- [ ] 10.3 进行性能优化 + - 优化WPF渲染性能 + - 实现虚拟化列表 + - 添加延迟加载机制 + - 优化内存使用 + - _Requirements: 2.3, 7.3_ + +- [ ] 10.4 进行用户验收测试 + - 验证所有现有功能正常工作 + - 测试新的可停靠面板体验 + - 收集用户反馈 + - 修复发现的问题 + - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 456709e..917d86c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # NavisworksTransport 变更日志 +## [0.3.0] - 2025-08-13 + +### 重大更新 + +- **建立了Navisworks2026新开发分支** + - 基于.NET Framework 4.8 + - 用DockPanePlugin代替了AddInPlugin + - 控件的用户界面使用WPF(Windows Presentation Foundation)构建 + - 实现了属性管理和路径设置两个功能的迁移(还剩下动画仿真和系统管理) + +### 功能优化 +- **物流属性管理** + - 增加了限宽属性 + - 支持设置多个物流元素 + +- **可见性控制** + - 用一个checkbox代替多个按钮 + - 优化了物流元素的可见性显示 + ## [0.2.0] - 2025-07-21 ### 重大功能突破 🎯 diff --git a/GEMINI.md b/GEMINI.md deleted file mode 100644 index e4c978c..0000000 --- a/GEMINI.md +++ /dev/null @@ -1,78 +0,0 @@ -# NavisworksTransport 项目说明 - -## 概述 -NavisworksTransport 是一个针对 Autodesk Navisworks Manage 2026 开发的插件,旨在简化移动模型沿确定路径进行物理碰撞或干涉检测的流程。通过自动化 Animator 动画创建、Clash Detective 碰撞测试配置与运行,并提供直观的图形化碰撞结果显示,本插件大大提高工作效率,为用户提供一个快速验证施工物流和设备移动可行性的工具。 - -## 项目结构 -``` -NavisworksTransportPlugin/ -├── src/ # 源代码目录 -│ ├── Core/ # 核心业务逻辑 -│ │ ├── MainPlugin.cs # 插件主类和用户界面 -│ │ ├── PathPlanningManager.cs # 路径规划核心业务逻辑 -│ │ ├── CategoryAttributeManager.cs # COM API封装和属性管理 -│ │ ├── PathPointRenderPlugin.cs # 3D路径点渲染插件 -│ │ ├── PathClickToolPlugin.cs # 3D视图路径点点击工具 -│ │ ├── LogisticsAnimationManager.cs # 物流动画管理器 -│ │ ├── ClashDetectiveIntegration.cs # 碰撞检测集成管理器 -│ │ └── ... # 其他核心模块 -│ ├── UI/ # 用户界面 -│ │ └── Forms/ # Windows Forms 窗体 -│ ├── Utils/ # 工具类 -│ └── Legacy/ # 旧版本兼容代码 -├── tests/ # 测试代码目录 -│ └── ... # 测试文件 -├── Properties/ # 项目属性文件 -│ └── AssemblyInfo.cs # 程序集信息 -└── ... -``` - -## 核心功能 -1. **类别属性分配**: 为模型项目添加物流类别属性(门、电梯、楼梯、通道、障碍物等) -2. **模型分层转换和可见性控制**: 控制模型的显示和隐藏 -3. **3D路径规划**: 在3D视图中创建和编辑物流路径 -4. **A*路径规划算法**: 计算最优路径 -5. **动态碰撞检测**: 集成 Clash Detective 进行动态碰撞检测 -6. **动画和时间线集成**: 创建基于路径点的对象动画 -7. **导航地图输出**: 将路径规划结果导出为图片和视频 -8. **模型切分及导出**: 根据楼层或属性切分模型并导出为独立文件 - -## 技术架构 -- **开发语言**: C# -- **目标平台**: .NET Framework 4.8 -- **API**: Autodesk Navisworks API (包括 COM API 和 .NET API) -- **UI框架**: Windows Forms - -## 系统要求 -- Windows 10 或更高版本 -- Navisworks Manage 2026 -- .NET Framework 4.8 - -## API 参考 -本项目包含 Navisworks 2026 的 API 文档和示例代码,位于 `doc/api` 目录下: -- **COM API**: - - 文档: `doc/api/COM/documentation` - - 示例: `doc/api/COM/examples` -- **.NET API**: - - 文档: `doc/api/NET/documentation` - - 示例: `doc/api/NET/examples` - -这些资源对于理解和使用 Navisworks API 进行开发非常有帮助。 - -## 设计方案 -针对 Navisworks 2026 的详细设计方案文档位于 `doc/design/2026` 目录下: -- [Navisworks 物流路径规划方案](doc/design/2026/Navisworks%20物流路径规划方案_.md) -- [Navisworks自动路径规划实现方案](doc/design/2026/Navisworks自动路径规划实现方案_.md) - -## 开发与构建 -1. **构建**: 使用 Visual Studio 打开 `NavisworksTransport.sln` 解决方案文件并编译项目。 -2. **安装**: 编译后插件会自动安装到 Navisworks 插件目录。 - -## 使用说明 -1. 在 Navisworks 中选择要设置属性的模型项目 -2. 点击 "附加模块" 选项卡中的 "Transport Plugin" -3. 在弹出窗口中进行类别设置、路径编辑、动画控制等操作 - -## 开发文档 -- [开发任务文档](doc/working/类别属性功能开发任务.md) -- [需求文档](doc/requirement/user_requiement.md) \ No newline at end of file diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj index c0d49b0..1ecdf7f 100644 --- a/NavisworksTransportPlugin.csproj +++ b/NavisworksTransportPlugin.csproj @@ -19,7 +19,7 @@ true full false - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin\ + bin\Debug\ DEBUG;TRACE;NAVISWORKS_2026 prompt 4 @@ -83,11 +83,13 @@ + + @@ -99,7 +101,6 @@ - @@ -118,6 +119,31 @@ Form + + UserControl + + + + + LogisticsControlPanel.xaml + + + ModelSettingsView.xaml + + + PathEditingView.xaml + + + AnimationControlView.xaml + + + SystemManagementView.xaml + + + + + + @@ -130,11 +156,44 @@ - + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + + + + + PreserveNewest + NavisworksTransport.Tian.name.txt + + + diff --git a/QWEN.md b/QWEN.md new file mode 100644 index 0000000..8ac7eaf --- /dev/null +++ b/QWEN.md @@ -0,0 +1,150 @@ +# NavisworksTransport 项目上下文 (QWEN.md) + +## 项目概述 + +NavisworksTransport 是一个针对 Autodesk Navisworks Manage 2026 开发的插件。其核心目标是简化在 Navisworks 环境中进行物流路径规划、动画仿真和碰撞检测的工作流程。 + +该插件旨在通过自动化 Animator 动画创建、Clash Detective 碰撞测试配置与运行,并提供直观的图形化碰撞结果显示,大大提高工作效率,为用户提供一个快速验证施工物流和设备移动可行性的工具。 + +### 核心功能 + +1. **类别属性分配**: + * 为模型项目添加预定义的物流类别属性(门、电梯、楼梯、通道、障碍物、装卸区、停车位、检查点)。 + * 支持批量处理和用户友好的界面(按钮式对话框)。 + * 通过 Navisworks COM API 确保属性正确添加。 + +2. **路径规划与编辑**: + * 允许用户在3D视图中交互式地定义移动对象的路径点。 + * 管理多条路径,支持路径的创建、编辑、删除、导入、导出和历史记录。 + * 提供路径可视化(3D标记)和基础验证(如点数、长度)。 + +3. **动画仿真**: + * 集成 Navisworks Timeliner API,根据规划的路径自动创建对象动画。 + * 提供动画控制界面,允许用户设置播放速度、持续时间、帧率、循环等参数。 + +4. **碰撞检测**: + * 集成 Navisworks Clash API,自动配置并运行基于路径动画的动态碰撞测试。 + * 提供碰撞检测状态和摘要信息。 + +5. **可见性控制**: + * 提供模型分层拆分和仅显示物流相关元素的功能,以优化视图和性能。 + +6. **用户界面**: + * 通过 Navisworks 的 Ribbon 界面添加自定义按钮启动插件。 + * 提供一个停靠窗格 (DockPane) 作为主控制面板,使用 WPF 构建。 + * 控制面板包含多个 Tab 页:类别设置、路径编辑、检测动画、系统管理。 + +## 技术架构 + +### 编程语言与框架 + +* **语言**: C# +* **框架**: .NET Framework 4.8 +* **UI框架**: Windows Forms (部分遗留) 和 Windows Presentation Foundation (WPF) +* **API**: Autodesk Navisworks API (包括 COM API, Timeliner API, Clash API) + +### 项目结构 + +``` +NavisworksTransportPlugin/ +├── src/ +│ ├── Core/ # 核心业务逻辑 +│ │ ├── Animation/ # 动画管理 (LogisticsAnimationManager, TimeLinerIntegrationManager) +│ │ ├── Collision/ # 碰撞检测 (ClashDetectiveIntegration) +│ │ ├── Properties/ # 属性管理 (CategoryAttributeManager) +│ │ ├── MainPlugin.cs # 插件主入口和旧版UI +│ │ ├── PathPlanningManager.cs # 路径规划核心逻辑 +│ │ ├── PathPlanningModels.cs # 路径数据模型 +│ │ ├── VisibilityManager.cs # 可见性控制 +│ │ └── ... # 其他核心组件 +│ ├── UI/ +│ │ ├── Forms/ # 旧版WinForms UI (逐渐被WPF替代) +│ │ └── WPF/ # 新版WPF UI +│ │ ├── ViewModels/ # MVVM ViewModels +│ │ ├── Views/ # MVVM Views (UserControls) +│ │ ├── LogisticsControlPanel.xaml # 主停靠面板 +│ │ └── ... +│ ├── Utils/ # 工具类 (日志、几何、坐标转换等) +│ └── Legacy/ # 为兼容性保留的旧代码 +├── Properties/ +│ └── AssemblyInfo.cs +├── NavisworksTransportPlugin.csproj # 项目文件 +└── ... +``` + +### 核心组件 + +1. **`MainPlugin` (`MainPlugin.cs`)**: + * 插件的入口点,继承自 `Autodesk.Navisworks.Api.Plugins.DockPanePlugin`。 + * 负责初始化插件、创建和管理主停靠窗格。 + * 包含旧版的 Windows Forms UI 逻辑(已部分迁移到 WPF)。 + +2. **`LogisticsControlPanel` (`LogisticsControlPanel.xaml`, `LogisticsControlPanel.xaml.cs`)**: + * WPF 用户控件,作为主停靠窗格的内容。 + * 使用 MVVM 模式,通过 `DataContext` 绑定到 `LogisticsControlViewModel`。 + +3. **`LogisticsControlViewModel` (`LogisticsControlViewModel.cs`)**: + * WPF UI 的核心逻辑处理中心。 + * 管理 UI 状态、数据绑定、用户命令(ICommand)。 + * 与 Core 层(如 `PathPlanningManager`)交互,驱动业务逻辑。 + +4. **`PathPlanningManager` (`PathPlanningManager.cs`)**: + * 路径规划的核心业务逻辑。 + * 管理路径 (`PathRoute`) 和路径点 (`PathPoint`) 的创建、编辑、验证。 + * 管理路径编辑状态(查看、创建、编辑)。 + * 处理与 Navisworks 模型的交互(选择、边界计算)。 + * 提供事件供 UI 层订阅。 + +5. **`CategoryAttributeManager` (`CategoryAttributeManager.cs`)**: + * 负责通过 Navisworks COM API 为模型项添加、更新、删除自定义物流属性。 + * 定义了物流类别 (`LogisticsCategories`) 和属性 (`LogisticsProperties`)。 + * 提供了筛选模型项的方法。 + +6. **`LogisticsAnimationManager`, `TimeLinerIntegrationManager`**: + * 负责与 Navisworks Timeliner 集成,根据路径数据创建动画。 + +7. **`ClashDetectiveIntegration`**: + * 负责与 Navisworks Clash Detective 集成,配置和运行碰撞测试。 + +8. **`VisibilityManager`**: + * 管理模型元素的可见性,实现"仅显示物流元素"等功能。 + +9. **`LogManager`**: + * 统一日志管理工具。 + +## 开发与构建 + +### 系统要求 + +* Windows 10 或更高版本 +* Navisworks Manage 2026 +* .NET Framework 4.8 +* Visual Studio (推荐) + +### 构建过程 + +1. 使用 Visual Studio 打开 `NavisworksTransport.sln` 解决方案文件。 +2. 确保项目引用指向正确的 Navisworks 2026 API DLL 文件(路径在 `.csproj` 文件中定义)。 +3. 选择 "Debug" 或 "Release" 配置。 +4. 构建项目 (`Ctrl+Shift+B`)。 +5. 输出文件为 `NavisworksTransportPlugin.dll`,位于 `bin\Debug\` 或 `bin\Release\` 目录下。 + +### 部署与安装 + +1. 编译生成 `NavisworksTransportPlugin.dll`。 +2. 插件会自动安装到 Navisworks 插件目录: + `[Navisworks安装路径]\Plugins\NavisworksTransportPlugin\` +3. 重启 Navisworks 即可在 "附加模块" 选项卡中找到插件。 + +### 运行与调试 + +* 在 Visual Studio 中可以直接调试插件,方法是设置 Navisworks 可执行文件(如 `NwAddinDev.exe` 或 `Navisworks.exe`)为启动外部程序。 +* 插件运行时会在 `%AppData%\Autodesk Navisworks Manage 2026\PluginsData\NavisworksTransportPlugin` 目录下生成日志文件。 + +## 开发约定与实践 + +* **MVVM 模式**: 新的 UI 开发强烈推荐使用 WPF 和 MVVM 模式,将 UI 逻辑 (`ViewModel`) 与 UI 呈现 (`View`) 分离。 +* **全局异常处理**: 使用 `GlobalExceptionHandler` 类来捕获和处理未处理的异常,提供用户友好的错误提示。 +* **日志记录**: 使用 `LogManager` 进行统一的日志记录,便于调试和问题追踪。 +* **安全执行**: 关键操作包装在 `GlobalExceptionHandler.SafeExecute` 方法中,以提高稳定性。 +* **COM API 使用**: 在需要与 Navisworks 属性系统深度交互时,谨慎使用 COM API,并注意处理缓存和刷新问题。 \ No newline at end of file diff --git a/VERSION.md b/VERSION.md index 341cf11..9325c3c 100644 --- a/VERSION.md +++ b/VERSION.md @@ -1 +1 @@ -0.2.0 \ No newline at end of file +0.3.0 \ No newline at end of file diff --git a/deploy-from-anywhere.bat b/deploy-from-anywhere.bat new file mode 100644 index 0000000..a5c32c1 --- /dev/null +++ b/deploy-from-anywhere.bat @@ -0,0 +1,109 @@ +@echo off +echo === Navisworks Transport Plugin Deployment (Auto-locate) === +echo. + +REM Try to find the project directory +set "PROJECT_DIR=" + +REM Check current directory first +if exist "NavisworksTransportPlugin.csproj" ( + set "PROJECT_DIR=%CD%" + goto :found +) + +REM Check if we're in a subdirectory, go up one level +cd .. +if exist "NavisworksTransportPlugin.csproj" ( + set "PROJECT_DIR=%CD%" + goto :found +) + +REM Check common locations +if exist "C:\Users\Tellme\apps\NavisworksTransport\NavisworksTransportPlugin.csproj" ( + set "PROJECT_DIR=C:\Users\Tellme\apps\NavisworksTransport" + goto :found +) + +REM If not found, ask user +echo ERROR: Could not locate NavisworksTransportPlugin.csproj +echo Please navigate to the project directory and run deploy-plugin.bat +echo. +echo Expected project structure: +echo NavisworksTransportPlugin.csproj +echo bin\Debug\NavisworksTransportPlugin.dll +echo. +pause +exit /b 1 + +:found +echo Found project at: %PROJECT_DIR% +cd /d "%PROJECT_DIR%" +echo. + +REM Check if compiled files exist +if not exist "bin\Debug\NavisworksTransportPlugin.dll" ( + echo ERROR: Plugin file not found, please compile the project first + echo Run command: dotnet build NavisworksTransportPlugin.csproj + echo. + echo Current directory contents: + dir /b + echo. + if exist "bin" ( + echo bin directory contents: + dir bin /b + if exist "bin\Debug" ( + echo bin\Debug directory contents: + dir "bin\Debug" /b + ) + ) + pause + exit /b 1 +) + +REM Set target directory (Navisworks installation Plugins folder) +set "TARGET_DIR=C:\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin" + +echo Target directory: %TARGET_DIR% +echo NOTE: Administrator privileges required to write to this directory +echo. + +REM Create directories +if not exist "%TARGET_DIR%" ( + mkdir "%TARGET_DIR%" 2>nul + if errorlevel 1 ( + echo ERROR: Failed to create directory. Please run as Administrator. + pause + exit /b 1 + ) + echo [OK] Created plugin directory +) + +REM Copy files +copy "bin\Debug\NavisworksTransportPlugin.dll" "%TARGET_DIR%\" >nul 2>&1 +if errorlevel 1 ( + echo ERROR: Failed to copy plugin file. Please run as Administrator. + pause + exit /b 1 +) +echo [OK] Copied plugin file: NavisworksTransportPlugin.dll + +if exist "bin\Debug\NavisworksTransport.Tian.name.txt" ( + copy "bin\Debug\NavisworksTransport.Tian.name.txt" "%TARGET_DIR%\" >nul 2>&1 + if errorlevel 1 ( + echo WARNING: Failed to copy localization file + ) else ( + echo [OK] Copied localization file: NavisworksTransport.Tian.name.txt + ) +) + +echo. +echo SUCCESS: Plugin deployed successfully! +echo. +echo Next steps: +echo 1. Start Navisworks Manage 2026 +echo 2. Look for "Logistics Path Planning" dockable panel +echo 3. If not visible, check Plugin Manager +echo. +echo Plugin location: %TARGET_DIR% +echo. +pause \ No newline at end of file diff --git a/deploy-plugin.bat b/deploy-plugin.bat new file mode 100644 index 0000000..8ea4243 --- /dev/null +++ b/deploy-plugin.bat @@ -0,0 +1,69 @@ +@echo off +echo === Navisworks Transport Plugin Deployment === +echo. +echo Current directory: %CD% +echo. + +REM Check if we're in the right directory +if not exist "NavisworksTransportPlugin.csproj" ( + echo ERROR: NavisworksTransportPlugin.csproj not found in current directory + echo Please run this script from the project root directory + pause + exit /b 1 +) + +REM Check if compiled files exist +if not exist "bin\Debug\NavisworksTransportPlugin.dll" ( + echo ERROR: Plugin file not found, please compile the project first + echo Run command: dotnet build NavisworksTransportPlugin.csproj + echo. + echo Checking bin directory... + if exist "bin" ( + echo bin directory exists + dir bin /b + if exist "bin\Debug" ( + echo bin\Debug directory exists + dir "bin\Debug" /b + ) else ( + echo bin\Debug directory does not exist + ) + ) else ( + echo bin directory does not exist + ) + pause + exit /b 1 +) + +REM Set target directory (Navisworks installation Plugins folder) +set "TARGET_DIR=C:\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin" + +echo Target directory: %TARGET_DIR% +echo NOTE: Administrator privileges required to write to this directory +echo. + +REM Create directories +if not exist "%TARGET_DIR%" ( + mkdir "%TARGET_DIR%" + echo [OK] Created plugin directory +) + +REM Copy files +copy "bin\Debug\NavisworksTransportPlugin.dll" "%TARGET_DIR%\" >nul +echo [OK] Copied plugin file: NavisworksTransportPlugin.dll + +if exist "bin\Debug\NavisworksTransport.Tian.name.txt" ( + copy "bin\Debug\NavisworksTransport.Tian.name.txt" "%TARGET_DIR%\" >nul + echo [OK] Copied localization file: NavisworksTransport.Tian.name.txt +) + +echo. +echo SUCCESS: Plugin deployed successfully! +echo. +echo Next steps: +echo 1. Start Navisworks Manage 2026 +echo 2. Look for "Logistics Path Planning" dockable panel +echo 3. If not visible, check Plugin Manager +echo. +echo Plugin location: %TARGET_DIR% +echo. +pause \ No newline at end of file diff --git a/deploy-plugin.ps1 b/deploy-plugin.ps1 new file mode 100644 index 0000000..224d507 --- /dev/null +++ b/deploy-plugin.ps1 @@ -0,0 +1,90 @@ +# Navisworks Transport Plugin 部署脚本 +# 用于将编译后的插件文件复制到Navisworks 2026插件目录 + +param( + [switch]$Debug = $false # 是否包含调试文件 +) + +# 定义路径 +$sourceDir = ".\bin\Debug" +$pluginDll = "$sourceDir\NavisworksTransportPlugin.dll" +$pluginPdb = "$sourceDir\NavisworksTransportPlugin.pdb" +$localizationFile = "$sourceDir\src\Resources\NavisworksTransport.Tian.name.txt" + +# 目标目录 +$systemPluginDir = "${env:ProgramFiles}\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin" + +Write-Host "=== Navisworks Transport Plugin 部署工具 ===" -ForegroundColor Green +Write-Host "" + +# 检查源文件是否存在 +if (-not (Test-Path $pluginDll)) { + Write-Host "错误: 找不到插件文件 $pluginDll" -ForegroundColor Red + Write-Host "请先编译项目: dotnet build NavisworksTransportPlugin.csproj" -ForegroundColor Yellow + exit 1 +} + +# 使用标准插件目录 +$targetDir = $systemPluginDir +Write-Host "部署到Navisworks插件目录: $targetDir" -ForegroundColor Yellow +Write-Host "注意: 需要管理员权限" -ForegroundColor Yellow + +# 创建目标目录 +try { + if (-not (Test-Path $targetDir)) { + New-Item -ItemType Directory -Path $targetDir -Force | Out-Null + Write-Host "✓ 创建目录: $targetDir" -ForegroundColor Green + } + + # 创建资源子目录 + $resourcesTargetDir = "$targetDir\src\Resources" + if (-not (Test-Path $resourcesTargetDir)) { + New-Item -ItemType Directory -Path $resourcesTargetDir -Force | Out-Null + Write-Host "✓ 创建资源目录: $resourcesTargetDir" -ForegroundColor Green + } +} catch { + Write-Host "错误: 无法创建目录 $targetDir" -ForegroundColor Red + Write-Host "错误详情: $($_.Exception.Message)" -ForegroundColor Red + exit 1 +} + +# 复制文件 +try { + # 复制主插件文件 + Copy-Item $pluginDll $targetDir -Force + Write-Host "✓ 复制插件文件: NavisworksTransportPlugin.dll" -ForegroundColor Green + + # 复制调试文件(如果需要) + if ($Debug -and (Test-Path $pluginPdb)) { + Copy-Item $pluginPdb $targetDir -Force + Write-Host "✓ 复制调试文件: NavisworksTransportPlugin.pdb" -ForegroundColor Green + } + + # 复制本地化文件 + if (Test-Path $localizationFile) { + Copy-Item $localizationFile $resourcesTargetDir -Force + Write-Host "✓ 复制本地化文件: NavisworksTransport.Tian.name.txt" -ForegroundColor Green + } + +} catch { + Write-Host "错误: 复制文件失败" -ForegroundColor Red + Write-Host "错误详情: $($_.Exception.Message)" -ForegroundColor Red + exit 1 +} + +Write-Host "" +Write-Host "🎉 插件部署成功!" -ForegroundColor Green +Write-Host "" +Write-Host "下一步操作:" -ForegroundColor Yellow +Write-Host "1. 启动 Navisworks Manage 2026" -ForegroundColor White +Write-Host "2. 在菜单中查找 '物流路径规划' 面板" -ForegroundColor White +Write-Host "3. 如果没有出现,请检查插件管理器中是否已加载" -ForegroundColor White +Write-Host "" +Write-Host "插件位置: $targetDir" -ForegroundColor Cyan +Write-Host "" + +# 显示Navisworks插件管理器信息 +Write-Host "💡 提示:" -ForegroundColor Yellow +Write-Host "- 如果插件没有自动加载,请在Navisworks中打开 '主页' > '插件管理器'" -ForegroundColor White +Write-Host "- 确保 'NavisworksTransportPlugin' 已启用" -ForegroundColor White +Write-Host "- 插件将显示为可停靠面板,而不是在附加模块选项卡中" -ForegroundColor White \ No newline at end of file diff --git a/doc/design/2026/Guidelines.md b/doc/design/2026/Guidelines.md new file mode 100644 index 0000000..68eade5 --- /dev/null +++ b/doc/design/2026/Guidelines.md @@ -0,0 +1,38 @@ + + +### Navisworks帮助文档 +- 将 Animator 用于软碰撞 +软碰撞结合使用了 Animator 和 Clash Detective 的主要功能。 + +项目模型可能包含临时项目(如工作软件包、船、起重机、安装等)的动态表示。 + +可以使用“Animator”窗口创建包含这些对象的动画场景,以使它们围绕项目现场移动,或更改其尺寸等。某些正在移动的对象可能会发生碰撞。 + +设置软碰撞可以对该碰撞进行自动检查。运行软碰撞会话时,在场景序列的每个步骤,都会使用“Clash Detective”检查是否发生了碰撞。如果发生碰撞,将记录碰撞发生的时间以及导致碰撞的事件。 + +例如,您可以查看软碰撞结果并重新安排对象的移动以消除此类碰撞。 + +为软碰撞准备动态工作软件包 +需要在覆盖所需面积或体积的项目模型中对每个要为其创建动画的对象建模;例如,可以使用半透明块。 +必须使用 Autodesk Navisworks 中的 Animator 窗口来创建具有所需对象的动画场景。 +链接到对象动画以进行软碰撞 +在 Navisworks 中,打开包含对象动画场景的项目模型文件。 + +如果尚未打开 Animator 窗口,请单击“常用”选项卡 >“工具”面板 > Animator 。 + +播放动画。检查动画对象是否在正确的位置、以正确的尺寸显示等等。 + +如果尚未打开“Clash Detective”窗口,请单击“常用”选项卡 >“工具”面板 >“Clash Detective”。 + +单击“选择”选项卡。 + +在“选择 A”和“选择 B”窗格中,选择要测试的对象。 + +在“链接”下拉框中,选择要链接到的动画场景,如“Scene1”。 + +在“步长”框中,输入要在查找动画中的碰撞时使用的“时间间隔大小”。 + +单击“运行检测”按钮。“Clash Detective”将在每个时间间隔检查动画中是否存在碰撞。找到的碰撞数将显示在“Clash Detective”窗口的顶部。 + +注意:如果动画场景很大,则此类碰撞检测将始终比普通碰撞检测需要更长的时间才能完成运行。 +现在可以查看找到的碰撞。 \ No newline at end of file diff --git a/doc/design/2026/Idea.md b/doc/design/2026/Idea.md new file mode 100644 index 0000000..ec9eb3c --- /dev/null +++ b/doc/design/2026/Idea.md @@ -0,0 +1,44 @@ +# 项目开发思路 + +## **1\. 使用场景** +1. 在Navisworks中打开物流相关的楼层nwd文件(前提:将建筑按单独的楼层保存) +2. 在物流控件中新建任务,任务包含一个物流场景的所有信息,支持保存和打开 +2. 选择物流运动模型(在物流模型文件列表中选择,可以是车辆、人员等,也可以选择自定义模型,指定长宽高),设置速度限制、安全距离等信息。 +3. 可以对运动模型经过的物流组件,指定物流类型(门、通道、电梯等),可以设置限高、限宽、限速等参数。 +4. 用物流导航控件,指定物流起点和终点,点自动吸附在通道表面,用颜色球表示。(移动过程可以使用Navisworks的漫游/飞行工具) +5. 在物流导航控件中选择【生成路径】,自动生成从起点到终点的物流路径,包含一系列路径点和之间的连线,用颜色球和圆柱线表示。用标签显示路径的名称、长度、路径点数量等。 +6. 在物流导航控件中选择【仿真动画】,设置动画时长、速度、检测距离间隔、碰撞检测间隙后,点击运行,物流运动模型开始从起点沿着指定路径运动,如果发生碰撞,高亮显示物流模型和冲突模型,用标签显示速度、位置、碰撞数量等。 +7. 仿真动画结束,提交结果页面,显示各初始参数和统计信息。可以导出路径文件、导航地图和碰撞报告。 +8. 对生成的路径,可以进行手工编辑,修改各点位置和限制参数。 +9. 可以对修改后的路径,重新运行仿真动画。 + +## **2\. 设计思路** + +### **2.1\. UI** +1. 用浮动控件做物流导航控件 +2. 用停靠窗口做物流控件 + +### **2.2\. 自动路径生成** +1. 用A*算法进行路径规划 + +### **2.3\. 仿真动画** +1. 用变换实现物流物体的移动,同时进行碰撞检测 +2. 动画按固定帧率(如30帧)进行,碰撞检测按检测距离间隔进行 +3. 【可选】碰撞检测结果同步到ClashDetective + +### **2.4\. 导航地图** +1. 导航地图可以用俯视图加路径生成图片 +2. 导航视频可以用视点动画生成 + +### **2.5\. 分层保存** +1. 可以按楼层搜索,保存选择项 +2. 按物流属性搜索,保存选择项 +3. 保存当前选择项 + +### **2.5\. 物流属性** +1. 子节点继承父节点的物流属性,不直接设置 + +## 编译命令 +``` +& "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" NavisworksTransportPlugin.csproj /p:Configuration=Debug /p:Platform=AnyCPU /verbosity:minimal +``` \ No newline at end of file diff --git a/doc/migration/File_Organization_Plan.md b/doc/migration/File_Organization_Plan.md deleted file mode 100644 index bece6c3..0000000 --- a/doc/migration/File_Organization_Plan.md +++ /dev/null @@ -1,53 +0,0 @@ -# 源代码文件重组计划 - -## 📁 目标目录结构 - -``` -src/ -├── Core/ # 核心功能模块 -│ ├── Animation/ # 动画系统 -│ ├── Collision/ # 碰撞检测 -│ └── Properties/ # 属性管理 -├── Legacy/ # 2017兼容代码 -├── UI/ # 用户界面 -│ ├── Forms/ # Windows Forms界面 -│ └── WPF/ # WPF界面 -└── Utils/ # 工具类 -``` - -## 📋 文件移动计划 - -### Core/Animation/ (动画系统) -- `PathAnimationManager.cs` → `Legacy/PathAnimationManager.cs` (标记为遗留代码) -- `TimeLinerIntegrationManager.cs` → `Core/Animation/TimeLinerIntegrationManager.cs` - -### Core/Collision/ (碰撞检测) -- `ClashDetectiveIntegration.cs` → `Core/Collision/ClashDetectiveIntegration.cs` -- `ClashDetectiveIntegrationTest.cs` → `Core/Collision/ClashDetectiveIntegrationTest.cs` - -### Core/Properties/ (属性管理) -- `CategoryAttributeManager.cs` → `Core/Properties/CategoryAttributeManager.cs` -- `AttributeGrouper.cs` → `Core/Properties/AttributeGrouper.cs` - -### UI/Forms/ (Windows Forms界面) -- `LogisticsPropertyEditDialog.cs` → `UI/Forms/LogisticsPropertyEditDialog.cs` -- `ModelSplitterDialog.cs` → `UI/Forms/ModelSplitterDialog.cs` - -### Utils/ (工具类) -- `LogManager.cs` → `Utils/LogManager.cs` -- `CoordinateConverter.cs` → `Utils/CoordinateConverter.cs` -- `GeometryExtractor.cs` → `Utils/GeometryExtractor.cs` -- `FloorDetector.cs` → `Utils/FloorDetector.cs` -- `NavisworksFileExporter.cs` → `Utils/NavisworksFileExporter.cs` - -### 根目录保留 (主要插件文件) -- `MainPlugin.cs` (保持在src根目录) -- `PathClickToolPlugin.cs` (保持在src根目录) -- `PathPointRenderPlugin.cs` (保持在src根目录) - -### 需要重新分类的文件 -- `ModelSplitterManager.cs` → `Core/ModelSplitterManager.cs` -- `PathDataManager.cs` → `Core/PathDataManager.cs` -- `PathPlanningManager.cs` → `Core/PathPlanningManager.cs` -- `PathPlanningModels.cs` → `Core/PathPlanningModels.cs` -- `VisibilityManager.cs` → `Core/VisibilityManager.cs` \ No newline at end of file diff --git a/doc/migration/File_Reorganization_Complete.md b/doc/migration/File_Reorganization_Complete.md deleted file mode 100644 index e69de29..0000000 diff --git a/doc/migration/Project_Configuration_Review.md b/doc/migration/Project_Configuration_Review.md deleted file mode 100644 index 2314854..0000000 --- a/doc/migration/Project_Configuration_Review.md +++ /dev/null @@ -1,279 +0,0 @@ -# Navisworks 2026 项目配置检查报告 - -## 📋 当前配置状态 - -### ✅ 正确配置项 - -1. **目标框架版本**:`.NET Framework 4.8` ✅ - - 符合Navisworks 2026要求 - - 支持最新的C#语言特性 - -2. **平台目标**:`x64` ✅ - - 正确设置为64位平台 - - 与Navisworks 2026架构匹配 - -3. **输出路径**:直接输出到Navisworks插件目录 ✅ - - Debug模式直接部署到插件目录 - - 便于开发和测试 - -4. **核心API引用**:已正确引用主要DLL ✅ - - `Autodesk.Navisworks.Api.dll` - - `Autodesk.Navisworks.ComApi.dll` - - `Autodesk.Navisworks.Interop.ComApi.dll` - - `Autodesk.Navisworks.Timeliner.dll` - - `Autodesk.Navisworks.Clash.dll` - -### ⚠️ 需要优化的配置项 - -#### 1. 缺少2026新功能相关的DLL引用 - -根据迁移分析,需要添加以下引用以支持新功能: - -```xml - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Api.Controls.dll - False - - - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Api.Plugins.dll - False - - - - - - - -``` - -#### 2. 动画系统问题确认 🔥 - -**重大发现**:当前动画实现确实使用了手动Transform变换! - -从`PathAnimationManager.cs`代码中可以看到: -```csharp -// ❌ 问题代码:手动变换实现 -doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false); - -// ❌ 问题代码:Timer + Thread.Sleep模式 -_animationTimer = new Timer(); -_animationTimer.Interval = 50; // 20 FPS -_animationTimer.Tick += AnimationTimer_Tick; -``` - -**性能问题**: -- 使用`Timer.Tick`事件,只能达到20fps -- 手动计算插值位置,CPU占用高 -- 缺乏专业动画控制功能 - -#### 3. 项目结构需要重组 - -当前所有代码都在`src/`目录下,建议按功能模块重新组织: - -``` -src/ -├── Core/ # 核心API封装 -│ ├── Animation/ # 动画系统 -│ ├── Properties/ # 属性管理 -│ └── Collision/ # 碰撞检测 -├── Legacy/ # 2017兼容代码 -├── UI/ # 用户界面 -│ ├── WPF/ # WPF控件 -│ └── Forms/ # Windows Forms -└── Utils/ # 工具类 -``` - -## 🚀 推荐的配置更新 - -### 1. 更新项目文件 - -```xml - - - - - Debug - AnyCPU - {1A0124F6-3DEB-4153-8760-F568AD9393EE} - Library - Properties - NavisworksTransport - NavisworksTransportPlugin - v4.8 - 512 - true - - true - - - - - true - full - false - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Plugins\NavisworksTransportPlugin\ - DEBUG;TRACE;NAVISWORKS_2026 - prompt - 4 - x64 - - - - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Api.dll - False - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.ComApi.dll - False - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Interop.ComApi.dll - False - False - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Interop.ComApiAutomation.dll - False - False - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Timeliner.dll - False - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Clash.dll - False - - - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Controls.dll - False - - - ..\..\..\..\Program Files\Autodesk\Navisworks Manage 2026\Autodesk.Navisworks.Interop.Timeliner.dll - False - False - - - - - - - - - - - - - - - - - - - - - - - - -``` - -### 2. 验证DLL文件存在 - -✅ **已验证**:以下DLL文件在Navisworks 2026安装目录中存在: - -``` -C:\Program Files\Autodesk\Navisworks Manage 2026\ -├── Autodesk.Navisworks.Api.dll ✅ (4.39MB) -├── Autodesk.Navisworks.ComApi.dll ✅ (415KB) -├── Autodesk.Navisworks.Interop.ComApi.dll ✅ (313KB) -├── Autodesk.Navisworks.Interop.ComApiAutomation.dll ✅ (19KB) -├── Autodesk.Navisworks.Timeliner.dll ✅ (526KB) -├── Autodesk.Navisworks.Interop.Timeliner.dll ✅ (19KB) -├── Autodesk.Navisworks.Clash.dll ✅ (551KB) -└── Autodesk.Navisworks.Controls.dll ✅ (147KB) 🆕发现 -``` - -**重要发现**: -- ✅ 找到了 `Autodesk.Navisworks.Controls.dll` - 支持WPF控件集成 -- ❌ 没有找到 `Autodesk.Navisworks.Api.Plugins.dll` - 可能不存在或名称不同 -- ✅ TimeLiner相关DLL完整,支持4D动画集成 -- ✅ Clash相关DLL完整,支持增强碰撞检测 - -### 3. 添加条件编译符号 - -在项目中添加`NAVISWORKS_2026`条件编译符号,用于区分2017和2026版本的代码: - -```csharp -#if NAVISWORKS_2026 - // 使用2026新功能 - var animationSet = new AnimationSet(document, "物流动画"); -#else - // 使用2017兼容代码 - // 手动变换实现 -#endif -``` - -## 🎯 下一步行动 - -### 立即执行: -1. **验证DLL引用**:检查所有必需的DLL文件是否存在 -2. **更新项目文件**:添加新的引用和WPF支持 -3. **测试编译**:确保项目能够正常编译 - -### 准备迁移: -1. **创建动画系统分支**:为动画系统重构创建独立分支 -2. **备份现有代码**:保留当前手动变换实现作为备用 -3. **建立测试环境**:准备动画性能对比测试 - -## 📊 配置检查清单 - -- [x] .NET Framework 4.8 ✅ -- [x] x64平台目标 ✅ -- [x] 基础API引用 ✅ -- [x] DLL文件验证 ✅ **所有必需DLL都存在** -- [ ] 新功能API引用 ⚠️ **需要添加Controls和Timeliner.Interop** -- [ ] WPF支持配置 ⚠️ **需要添加WPF引用** -- [ ] 条件编译符号 ⚠️ **需要添加NAVISWORKS_2026** -- [ ] 项目结构重组 ⚠️ **建议重新组织代码结构** - -## 🎯 立即行动建议 - -### 1. 更新项目文件(高优先级) -我已经创建了更新后的项目文件:`NavisworksTransportPlugin_2026_Updated.csproj` - -**主要改进**: -- ✅ 添加了所有必需的2026 API引用 -- ✅ 配置了WPF支持 -- ✅ 添加了条件编译符号`NAVISWORKS_2026` -- ✅ 为新的动画系统预留了代码结构 -- ✅ 添加了JSON支持(用于DELMIA集成) - -### 2. 验证配置(立即执行) -```powershell -# 1. 备份当前项目文件 -Copy-Item "NavisworksTransportPlugin.csproj" "NavisworksTransportPlugin_backup.csproj" - -# 2. 替换为新的项目文件 -Copy-Item "NavisworksTransportPlugin_2026_Updated.csproj" "NavisworksTransportPlugin.csproj" - -# 3. 在Visual Studio中重新加载项目 -# 4. 尝试编译,验证所有引用正确 -``` - -## 🚨 重要发现 - -**动画系统确实需要重构**!当前实现存在以下问题: -1. 手动Transform变换,性能差 -2. 20fps限制,不够流畅 -3. 缺乏专业动画控制 -4. 维护成本高 - -这证实了我们的迁移分析是正确的,动画系统重构应该是最高优先级的任务。 \ No newline at end of file diff --git a/src/Core/Animation/PathAnimationManager.cs b/src/Core/Animation/PathAnimationManager.cs new file mode 100644 index 0000000..d6cbb31 --- /dev/null +++ b/src/Core/Animation/PathAnimationManager.cs @@ -0,0 +1,917 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using Autodesk.Navisworks.Api; +using NavisApplication = Autodesk.Navisworks.Api.Application; + +namespace NavisworksTransport +{ + /// + /// 定义动画播放的状态 + /// + public enum AnimationState + { + Idle, // 空闲,未生成动画 + Ready, // 已就绪,动画已生成但未播放 + Playing, // 播放中 + Paused, // 暂停 + Stopped, // 已停止 + Finished // 已完成 + } + + /// + /// 路径动画管理器 - 基于TimeLiner和动态变换实现沿路径的动画效果 + /// 注意:由于Navisworks API限制,无法直接使用Animator API,因此使用OverridePermanentTransform实现动画 + /// 已集成 TimeLiner 功能,支持在 TimeLiner 中显示和管理动画任务 + /// + public class PathAnimationManager + { + private ModelItem _animatedObject; + private List _pathPoints; + private Timer _animationTimer; + private double _animationDuration = 10.0; // 动画总时长(秒) + private DateTime _animationStartTime; + private Transform3D _originalTransform; + private Point3D _originalCenter; // 存储部件的原始中心位置 + private Point3D _currentPosition; // 存储部件的当前位置 + private AnimationState _currentState = AnimationState.Idle; + + // TimeLiner 集成 + private TimeLinerIntegrationManager _timeLinerManager; + private string _currentTaskId; + + // --- 新增事件 --- + /// + /// 当动画状态发生改变时触发 + /// + public event EventHandler StateChanged; + + /// + /// 当动画进度更新时触发 (0-100) + /// + public event EventHandler ProgressChanged; + + // 动画完成事件 (旧版,保留兼容性) + public event EventHandler AnimationCompleted; + + /// + /// 当检测到碰撞时触发 + /// + public event EventHandler CollisionDetected; + + public PathAnimationManager() + { + _pathPoints = new List(); + + // 初始化 TimeLiner 集成 + try + { + _timeLinerManager = new TimeLinerIntegrationManager(); + LogManager.Info("PathAnimationManager 已集成 TimeLiner 功能"); + } + catch (Exception ex) + { + LogManager.Warning($"TimeLiner 集成初始化失败,将使用基础动画功能: {ex.Message}"); + _timeLinerManager = null; + } + } + + /// + /// 设置动画参数 + /// + /// 要动画化的模型对象 + /// 路径点列表 + /// 动画持续时间(秒) + public void SetupAnimation(ModelItem animatedObject, List pathPoints, double durationSeconds = 10.0) + { + try + { + if (animatedObject == null) + throw new ArgumentNullException(nameof(animatedObject)); + + if (pathPoints == null || pathPoints.Count < 2) + throw new ArgumentException("路径点数量必须至少为2个", nameof(pathPoints)); + + // 添加路径点坐标有效性验证 + LogManager.Info("=== 动画管理器坐标验证 ==="); + bool hasInvalidCoordinates = false; + for (int i = 0; i < pathPoints.Count; i++) + { + var point = pathPoints[i]; + bool isValid = !double.IsNaN(point.X) && !double.IsNaN(point.Y) && !double.IsNaN(point.Z) && + !double.IsInfinity(point.X) && !double.IsInfinity(point.Y) && !double.IsInfinity(point.Z); + + LogManager.Info($"路径点[{i}]坐标验证: ({point.X:F6},{point.Y:F6},{point.Z:F6}) - {(isValid ? "有效" : "无效")}"); + + if (!isValid) + { + hasInvalidCoordinates = true; + LogManager.Error($"检测到无效坐标: 路径点[{i}] = ({point.X},{point.Y},{point.Z})"); + } + + // 检查是否为零坐标(可能表示数据丢失) + if (isValid && point.X == 0.0 && point.Y == 0.0 && point.Z == 0.0) + { + LogManager.Warning($"路径点[{i}]坐标为零,可能存在数据丢失问题"); + } + } + + if (hasInvalidCoordinates) + { + throw new ArgumentException("路径点包含无效坐标(NaN或无穷大),无法创建动画"); + } + + LogManager.Info("=== 坐标验证完成 ==="); + + _animatedObject = animatedObject; + _pathPoints = new List(pathPoints); + _animationDuration = durationSeconds; + + // 保存原始变换以便重置 + _originalTransform = GetCurrentTransform(_animatedObject); + + // 保存车辆的原始中心位置 + var originalBoundingBox = animatedObject.BoundingBox(); + _originalCenter = originalBoundingBox.Center; + + // 关键修复:将车辆立即移动到路径起点 + // 这样动画就从路径起点开始,而不是从车辆当前位置开始 + MoveVehicleToPathStart(); + + // 记录文档单位信息 + var documentUnits = GetDocumentUnitsInfo(); + + // 添加详细的调试信息 + LogManager.Info($"动画设置完成:对象={_animatedObject.DisplayName}, 路径点数={_pathPoints.Count}, 时长={_animationDuration}秒"); + LogManager.Info($"文档单位: {documentUnits}"); + LogManager.Info($"路径起点: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})"); + LogManager.Info($"路径终点: ({_pathPoints[_pathPoints.Count-1].X:F2},{_pathPoints[_pathPoints.Count-1].Y:F2},{_pathPoints[_pathPoints.Count-1].Z:F2})"); + + var totalDist = CalculateTotalPathDistance(); + LogManager.Info($"路径总长度: {totalDist:F2}"); + LogManager.Info($"车辆已移动到路径起点,动画将从起点开始"); + LogManager.Info($"=== 调试信息结束 ==="); + } + catch (Exception ex) + { + LogManager.Error($"设置动画失败: {ex.Message}"); + throw; + } + } + + /// + /// 将车辆移动到路径起点 + /// + private void MoveVehicleToPathStart() + { + try + { + if (_pathPoints.Count == 0) return; + + var doc = NavisApplication.ActiveDocument; + var modelItems = new ModelItemCollection { _animatedObject }; + + // 计算从部件原始中心到路径起点的偏移 + var startOffset = new Vector3D( + _pathPoints[0].X - _originalCenter.X, + _pathPoints[0].Y - _originalCenter.Y, + _pathPoints[0].Z - _originalCenter.Z + ); + + // 创建变换并应用 + var startTransform = Transform3D.CreateTranslation(startOffset); + doc.Models.OverridePermanentTransform(modelItems, startTransform, false); + + // 更新当前位置为路径起点 + _currentPosition = _pathPoints[0]; + + LogManager.Info($"部件已移动到路径起点,偏移: ({startOffset.X:F2},{startOffset.Y:F2},{startOffset.Z:F2})"); + } + catch (Exception ex) + { + LogManager.Error($"移动部件到路径起点失败: {ex.Message}"); + } + } + + /// + /// 开始播放动画 + /// + public void StartAnimation() + { + try + { + if (_animatedObject == null || _pathPoints.Count < 2) + { + throw new InvalidOperationException("请先调用SetupAnimation设置动画参数"); + } + + // 停止之前的动画 + StopAnimation(); + + // 创建 TimeLiner 任务(如果可用) + if (_timeLinerManager != null && _timeLinerManager.IsTimeLinerAvailable) + { + var taskName = $"{_animatedObject.DisplayName}_运输_{DateTime.Now:HHmmss}"; + var duration = TimeSpan.FromSeconds(_animationDuration); + + LogManager.Info($"创建 TimeLiner 任务: {taskName}"); + + _currentTaskId = _timeLinerManager.CreateTransportTask( + taskName, + _pathPoints, + duration, + _animatedObject); + + if (!string.IsNullOrEmpty(_currentTaskId)) + { + LogManager.Info($"✓ TimeLiner 任务创建成功: {taskName} (ID: {_currentTaskId})"); + } + else + { + LogManager.Warning("TimeLiner 任务创建失败,继续使用基础动画功能"); + } + } + else + { + LogManager.Info("TimeLiner 不可用,使用基础动画功能"); + } + + // 设置动态碰撞检测(简化版本) + SetupDynamicClashDetection(); + + // 初始化动画状态 + _animationStartTime = DateTime.Now; + + // 创建并启动定时器(每50ms更新一次,实现流畅动画) + _animationTimer = new Timer(); + _animationTimer.Interval = 50; // 20 FPS + _animationTimer.Tick += AnimationTimer_Tick; + _animationTimer.Start(); + + SetState(AnimationState.Playing); + LogManager.Info("动画开始播放"); + } + catch (Exception ex) + { + LogManager.Error($"启动动画失败: {ex.Message}"); + throw; + } + } + + /// + /// 停止动画 + /// + public void StopAnimation() + { + try + { + if (_animationTimer != null) + { + _animationTimer.Stop(); + _animationTimer.Dispose(); + _animationTimer = null; + + SetState(AnimationState.Stopped); + LogManager.Info("动画已停止"); + } + + // 动画停止时不创建碰撞测试汇总,由动画完成事件统一处理 + LogManager.Info("动画停止,等待动画完成事件统一处理碰撞测试..."); + + // 更新 TimeLiner 任务状态 + if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId)) + { + _timeLinerManager.UpdateTaskProgress(_currentTaskId, 0.0, AnimationState.Stopped); + } + } + catch (Exception ex) + { + LogManager.Error($"停止动画失败: {ex.Message}"); + } + } + + /// + /// 重置动画对象到原始位置 + /// + public void ResetAnimation() + { + try + { + StopAnimation(); // 停止当前动画 + + // 恢复对象的原始变换 + if (_animatedObject != null) + { + var modelItems = new ModelItemCollection { _animatedObject }; + NavisApplication.ActiveDocument.Models.OverridePermanentTransform(modelItems, _originalTransform, false); + LogManager.Info($"部件 {_animatedObject.DisplayName} 已重置到原始位置"); + } + + ProgressChanged?.Invoke(this, 0); // 重置进度条 + SetState(AnimationState.Ready); // 重置后回到就绪状态 + } + catch (Exception ex) + { + LogManager.Error($"重置动画失败: {ex.Message}"); + throw; + } + } + + /// + /// 动画定时器事件处理 + /// + private void AnimationTimer_Tick(object sender, EventArgs e) + { + try + { + double elapsedSeconds = (DateTime.Now - _animationStartTime).TotalSeconds; + double progress = Math.Min(elapsedSeconds / _animationDuration, 1.0); + + // 更新UI进度条 + ProgressChanged?.Invoke(this, (int)(progress * 100)); + + // 同步进度到 TimeLiner(如果可用) + if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId)) + { + _timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState); + } + + Point3D newPosition = InterpolatePosition(progress); + UpdateObjectPosition(newPosition); + + // 使用 Clash Detective 集成进行碰撞检测 + CheckAndHighlightCollisionsWithClashDetective(); + + if (progress >= 1.0) + { + StopAnimation(); + SetState(AnimationState.Finished); // 标记为完成 + AnimationCompleted?.Invoke(this, EventArgs.Empty); // 触发旧版完成事件 + LogManager.Info("动画播放完成"); + + // 动画结束后统一创建所有碰撞测试(基于官方示例的批量处理) + LogManager.Info("动画播放完成,开始创建最终的碰撞测试汇总..."); + ClashDetectiveIntegration.Instance.CreateAllAnimationCollisionTests(); + } + } + catch (Exception ex) + { + LogManager.Error($"动画帧更新失败: {ex.Message}"); + StopAnimation(); // 发生错误时停止动画 + } + } + + /// + /// 根据进度插值计算当前位置 + /// + private Point3D InterpolatePosition(double progress) + { + if (_pathPoints.Count < 2) + return _pathPoints[0]; + + // 确保进度在0-1范围内 + progress = Math.Max(0.0, Math.Min(1.0, progress)); + + // 如果进度达到100%,直接返回终点 + if (progress >= 1.0) + { + return _pathPoints[_pathPoints.Count - 1]; + } + + // 计算总路径长度 + var totalDistance = CalculateTotalPathDistance(); + + // 检查总距离是否有效 + if (totalDistance <= 0.0 || double.IsNaN(totalDistance) || double.IsInfinity(totalDistance)) + { + LogManager.Error($"路径总长度无效: {totalDistance},返回起点坐标"); + return _pathPoints[0]; + } + + var targetDistance = totalDistance * progress; + + // 找到当前应该在哪两个点之间 + var accumulatedDistance = 0.0; + for (int i = 0; i < _pathPoints.Count - 1; i++) + { + var segmentDistance = CalculateDistance(_pathPoints[i], _pathPoints[i + 1]); + + // 检查段距离是否有效 + if (double.IsNaN(segmentDistance) || double.IsInfinity(segmentDistance)) + { + LogManager.Error($"路径段[{i}-{i+1}]距离无效: {segmentDistance},跳过此段"); + continue; + } + + if (accumulatedDistance + segmentDistance >= targetDistance) + { + // 在这个线段内 + var segmentProgress = segmentDistance > 0 ? (targetDistance - accumulatedDistance) / segmentDistance : 0.0; + // 确保段内进度也在0-1范围内 + segmentProgress = Math.Max(0.0, Math.Min(1.0, segmentProgress)); + + var interpolatedPoint = InterpolatePoints(_pathPoints[i], _pathPoints[i + 1], segmentProgress); + + // 验证插值结果 + if (double.IsNaN(interpolatedPoint.X) || double.IsNaN(interpolatedPoint.Y) || double.IsNaN(interpolatedPoint.Z)) + { + LogManager.Error($"插值计算结果无效: ({interpolatedPoint.X},{interpolatedPoint.Y},{interpolatedPoint.Z}),返回起点"); + return _pathPoints[0]; + } + + return interpolatedPoint; + } + + accumulatedDistance += segmentDistance; + } + + // 如果到达这里,返回最后一个点 + return _pathPoints[_pathPoints.Count - 1]; + } + + /// + /// 在两点间插值 + /// + private Point3D InterpolatePoints(Point3D point1, Point3D point2, double t) + { + return new Point3D( + point1.X + (point2.X - point1.X) * t, + point1.Y + (point2.Y - point1.Y) * t, + point1.Z + (point2.Z - point1.Z) * t + ); + } + + /// + /// 计算路径总长度 + /// + private double CalculateTotalPathDistance() + { + var totalDistance = 0.0; + for (int i = 0; i < _pathPoints.Count - 1; i++) + { + totalDistance += CalculateDistance(_pathPoints[i], _pathPoints[i + 1]); + } + return totalDistance; + } + + /// + /// 获取对象当前位置 + /// + private Point3D GetObjectPosition(ModelItem item) + { + try + { + if (item == null) return new Point3D(0, 0, 0); + + var bounds = item.BoundingBox(); + if (bounds != null) + { + return new Point3D( + (bounds.Min.X + bounds.Max.X) / 2, + (bounds.Min.Y + bounds.Max.Y) / 2, + (bounds.Min.Z + bounds.Max.Z) / 2 + ); + } + return new Point3D(0, 0, 0); + } + catch (Exception ex) + { + LogManager.Error($"获取对象位置失败: {ex.Message}"); + return new Point3D(0, 0, 0); + } + } + + /// + /// 计算两点间距离 + /// + private double CalculateDistance(Point3D point1, Point3D point2) + { + var dx = point2.X - point1.X; + var dy = point2.Y - point1.Y; + var dz = point2.Z - point1.Z; + return Math.Sqrt(dx * dx + dy * dy + dz * dz); + } + + /// + /// 更新对象位置 + /// + private void UpdateObjectPosition(Point3D newPosition) + { + try + { + var doc = NavisApplication.ActiveDocument; + var modelItems = new ModelItemCollection { _animatedObject }; + + // 正确的增量变换:计算从当前位置到新位置的偏移 + var incrementalOffset = new Vector3D( + newPosition.X - _currentPosition.X, + newPosition.Y - _currentPosition.Y, + newPosition.Z - _currentPosition.Z + ); + + // 创建增量变换 + var incrementalTransform = Transform3D.CreateTranslation(incrementalOffset); + + // 应用增量变换(不重置之前的变换) + doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false); + + LogManager.Debug($"部件位置更新:从({_currentPosition.X:F2},{_currentPosition.Y:F2},{_currentPosition.Z:F2})到({newPosition.X:F2},{newPosition.Y:F2},{newPosition.Z:F2}),增量偏移({incrementalOffset.X:F2},{incrementalOffset.Y:F2},{incrementalOffset.Z:F2})"); + + // 更新当前位置 + _currentPosition = newPosition; + } + catch (Exception ex) + { + LogManager.Error($"更新部件位置失败: {ex.Message}"); + } + } + + /// + /// 从车辆变换矩阵中获取真实的缩放系数 + /// 注意:此方法已废弃但保留,用于车辆模型的缩放处理。 + /// 现在使用场馆部件进行动画,通常不需要复杂的缩放计算。 + /// + private double GetVehicleScaleFactor() + { + try + { + var transform = _animatedObject.Transform; + var linear = transform.Linear; + + // 获取变换矩阵的第一行的长度作为缩放系数 + // 变换矩阵: (a, b, c) + // (d, e, f) + // (g, h, i) + // 缩放系数 ≈ sqrt(a² + b² + c²) + var scaleX = Math.Sqrt( + linear.Get(0, 0) * linear.Get(0, 0) + + linear.Get(0, 1) * linear.Get(0, 1) + + linear.Get(0, 2) * linear.Get(0, 2) + ); + + LogManager.Debug($"车辆缩放系数计算: {scaleX:F2} (变换矩阵第一行模长)"); + + // 如果缩放系数接近1550(39.37²),说明是米到英寸的平方缩放 + if (Math.Abs(scaleX - 1550.0) < 10.0) + { + var linearScale = Math.Sqrt(scaleX); + LogManager.Info($"检测到车辆使用平方缩放系数: {scaleX:F2},已修正为线性缩放系数: {linearScale:F2}"); + return linearScale; + } + // 如果缩放系数接近39.37,说明是简单的米到英寸缩放 + else if (Math.Abs(scaleX - 39.37) < 1.0) + { + LogManager.Info($"检测到车辆使用线性缩放系数: {scaleX:F2}"); + return scaleX; + } + // 如果接近1,说明没有缩放 + else if (Math.Abs(scaleX - 1.0) < 0.1) + { + LogManager.Info($"车辆无缩放: {scaleX:F2}"); + return 1.0; + } + else + { + LogManager.Warning($"未知的车辆缩放系数: {scaleX:F2},使用原值"); + return scaleX; + } + } + catch (Exception ex) + { + LogManager.Error($"获取车辆缩放系数失败: {ex.Message},使用默认值1.0"); + return 1.0; + } + } + + /// + /// 获取文档单位信息(用于日志记录) + /// + private string GetDocumentUnitsInfo() + { + try + { + var doc = NavisApplication.ActiveDocument; + var units = doc.Units; + LogManager.Debug($"[单位检测] 文档单位: {units}"); + return units.ToString(); + } + catch (Exception ex) + { + LogManager.Error($"获取文档单位信息失败: {ex.Message}"); + return "Unknown"; + } + } + + /// + /// 获取模型当前变换 + /// + private Transform3D GetCurrentTransform(ModelItem item) + { + // 获取包围盒中心作为参考点 + var boundingBox = item.BoundingBox(); + var center = boundingBox.Center; + + // 创建基于中心点的单位变换 + return Transform3D.CreateTranslation(new Vector3D(center.X, center.Y, center.Z)); + } + + /// + /// 使用 Clash Detective 集成进行碰撞检测并使用缓存的碰撞结果 + /// + private void CheckAndHighlightCollisionsWithClashDetective() + { + try + { + if (_animatedObject == null) + return; + + // 使用 Clash Detective 集成进行碰撞检测 + var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(_animatedObject); + + // 高亮显示碰撞对象 + ClashDetectiveIntegration.Instance.HighlightCollisions(collisionResults); + + // 缓存碰撞结果,动画结束后统一处理 + if (collisionResults.Count > 0) + { + LogManager.Info($"=== [动画运行中] 检测到 {collisionResults.Count} 个碰撞,开始记录详细位置 ==="); + + // 缓存所有碰撞结果,包含位置信息 + var animatedObjectPosition = GetObjectPosition(_animatedObject); + LogManager.Info($"[动画位置] 动画对象 {_animatedObject.DisplayName}: ({animatedObjectPosition.X:F2},{animatedObjectPosition.Y:F2},{animatedObjectPosition.Z:F2})"); + + int collisionIndex = 0; + foreach (var collision in collisionResults) + { + collisionIndex++; + var collisionObjectPosition = GetObjectPosition(collision.Item2); + LogManager.Info($"[碰撞位置{collisionIndex}] 动画对象 vs {collision.Item2.DisplayName}:"); + LogManager.Info($" 动画物体位置: ({animatedObjectPosition.X:F2},{animatedObjectPosition.Y:F2},{animatedObjectPosition.Z:F2})"); + LogManager.Info($" 碰撞物体位置: ({collisionObjectPosition.X:F2},{collisionObjectPosition.Y:F2},{collisionObjectPosition.Z:F2})"); + LogManager.Info($" 两物体距离: {CalculateDistance(animatedObjectPosition, collisionObjectPosition):F2}"); + LogManager.Info($" 碰撞状态: 已检测到碰撞"); + + ClashDetectiveIntegration.Instance.CacheCollisionDuringAnimation(_animatedObject, animatedObjectPosition, collision.Item2, collisionObjectPosition); + } + + LogManager.Info($"=== [动画运行中] 位置记录完成 ==="); + } + + LogManager.Debug($"碰撞检测完成: {collisionResults.Count} 个碰撞 (已缓存)"); + } + catch (Exception ex) + { + LogManager.Error($"碰撞检测失败: {ex.Message}"); + } + } + + /// + /// 设置动态碰撞检测(实时模式,集成 Clash Detective) + /// + private void SetupDynamicClashDetection() + { + try + { + // 初始化 Clash Detective 集成 + ClashDetectiveIntegration.Instance.Initialize(); + + // 订阅碰撞检测事件 + ClashDetectiveIntegration.Instance.CollisionDetected += OnClashDetectiveCollisionDetected; + + LogManager.Info("动态碰撞检测设置完成(实时模式)"); + } + catch (Exception ex) + { + LogManager.Error($"设置动态碰撞检测失败: {ex.Message}"); + } + } + + /// + /// 处理 Clash Detective 碰撞检测事件 + /// + private void OnClashDetectiveCollisionDetected(object sender, CollisionDetectedEventArgs e) + { + try + { + // 将碰撞结果转发到动画管理器的事件 + OnCollisionDetected(e); + + LogManager.Debug($"Clash Detective 检测到 {e.CollisionCount} 个碰撞"); + } + catch (Exception ex) + { + LogManager.Error($"处理 Clash Detective 碰撞事件失败: {ex.Message}"); + } + } + + /// + /// 触发碰撞检测事件 + /// + protected virtual void OnCollisionDetected(CollisionDetectedEventArgs e) + { + CollisionDetected?.Invoke(this, e); + } + + // 已删除:CheckAndHighlightCollisions - 使用Clash Detective集成代替 + + /// + /// 检查两个包围盒是否相交 + /// + private bool BoundingBoxesIntersect(BoundingBox3D box1, BoundingBox3D box2) + { + return !(box1.Max.X < box2.Min.X || box2.Max.X < box1.Min.X || + box1.Max.Y < box2.Min.Y || box2.Max.Y < box1.Min.Y || + box1.Max.Z < box2.Min.Z || box2.Max.Z < box1.Min.Z); + } + + /// + /// 设置动画持续时间 + /// + public void SetAnimationDuration(double durationSeconds) + { + _animationDuration = Math.Max(1.0, durationSeconds); + } + + /// + /// 获取动画状态 + /// + public bool IsAnimating => _animationTimer != null && _animationTimer.Enabled; + + /// + /// 获取 TimeLiner 是否可用 + /// + public bool IsTimeLinerAvailable => _timeLinerManager?.IsTimeLinerAvailable ?? false; + + /// + /// 获取当前动画状态 + /// + public AnimationState CurrentState => _currentState; + + /// + /// 获取当前 TimeLiner 任务ID + /// + public string CurrentTaskId => _currentTaskId; + + /// + /// 获取 TimeLiner 集成管理器 + /// + public TimeLinerIntegrationManager TimeLinerManager => _timeLinerManager; + + /// + /// 资源清理 + /// + public void Dispose() + { + StopAnimation(); + ResetAnimation(); + + // 清理 TimeLiner 资源 + if (_timeLinerManager != null) + { + try + { + _timeLinerManager.Dispose(); + _timeLinerManager = null; + LogManager.Info("TimeLiner 集成资源已清理"); + } + catch (Exception ex) + { + LogManager.Warning($"清理 TimeLiner 资源时出现警告: {ex.Message}"); + } + } + } + + /// + /// 快速测试TimeLiner API的可用性 + /// + public bool TestTimeLinerAPI() + { + try + { + var doc = NavisApplication.ActiveDocument; + + // 尝试访问TimeLiner + var timeliner = doc.Timeliner; + if (timeliner != null) + { + LogManager.Info("TimeLiner API基本可用"); + return true; + } + else + { + LogManager.Info("TimeLiner API不可用"); + return false; + } + } + catch (Exception ex) + { + LogManager.Info($"TimeLiner API测试失败: {ex.Message}"); + return false; + } + } + + /// + /// 简化的动画设置方法,支持直接传入部件和路径 + /// + /// 部件模型 + /// 路径点列表 + /// 动画持续时间(秒) + public bool SetupSimpleAnimation(ModelItem component, List pathPoints, double durationSeconds = 10.0) + { + try + { + if (component == null) + { + LogManager.Error("部件模型不能为空"); + return false; + } + + if (pathPoints == null || pathPoints.Count < 2) + { + LogManager.Error("路径点数量必须至少为2个"); + return false; + } + + // 使用现有的SetupAnimation方法 + SetupAnimation(component, pathPoints, durationSeconds); + + LogManager.Info($"简化动画设置成功:部件={component.DisplayName}, 路径点数={pathPoints.Count}, 时长={durationSeconds}秒"); + return true; + } + catch (Exception ex) + { + LogManager.Error($"简化动画设置失败: {ex.Message}"); + return false; + } + } + + /// + /// 获取当前选中的部件模型(简化版本) + /// + /// 选中的部件模型,如果没有选中或选中多个则返回null + public static ModelItem GetSelectedComponent() + { + try + { + var doc = NavisApplication.ActiveDocument; + var selectedItems = doc.CurrentSelection.SelectedItems; + + if (selectedItems.Count == 1) + { + var item = selectedItems.First; + if (item.HasGeometry) + { + LogManager.Info($"已获取选中部件: {item.DisplayName}"); + return item; + } + else + { + LogManager.Warning("选中的项目没有几何体,可能不适合用于动画"); + return null; + } + } + else if (selectedItems.Count == 0) + { + LogManager.Info("没有选中任何项目"); + return null; + } + else + { + LogManager.Info($"选中了{selectedItems.Count}个项目,请只选择一个部件"); + return null; + } + } + catch (Exception ex) + { + LogManager.Error($"获取选中部件失败: {ex.Message}"); + return null; + } + } + + /// + /// 设置并触发状态变更事件 + /// + /// 新的动画状态 + private void SetState(AnimationState newState) + { + if (_currentState != newState) + { + _currentState = newState; + StateChanged?.Invoke(this, _currentState); + } + } + + /// + /// 创建动画 + /// + /// 要动画化的模型对象 + /// 路径点列表 + /// 动画持续时间(秒) + public void CreateAnimation(ModelItem animatedObject, List pathPoints, double durationSeconds = 10.0) + { + SetupAnimation(animatedObject, pathPoints, durationSeconds); + SetState(AnimationState.Ready); + } + } +} \ No newline at end of file diff --git a/src/Core/MainPlugin.cs b/src/Core/MainPlugin.cs index afbe294..c39973f 100644 --- a/src/Core/MainPlugin.cs +++ b/src/Core/MainPlugin.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; +using System.Windows.Forms.Integration; using NavisApplication = Autodesk.Navisworks.Api.Application; namespace NavisworksTransport @@ -20,7 +21,7 @@ namespace NavisworksTransport public string OperationType { get; set; } // "修改" 或 "删除" public int AffectedCount { get; set; } // 受影响的模型数量 public string Message { get; set; } // 操作描述信息 - + public LogisticsAttributeChangedEventArgs(string operationType, int affectedCount, string message = "") { OperationType = operationType; @@ -35,21 +36,21 @@ namespace NavisworksTransport public static class GlobalExceptionHandler { private static bool _isInitialized = false; - + /// /// 初始化全局异常处理器 /// public static void Initialize() { if (_isInitialized) return; - + try { // 捕获未处理的异常 AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; System.Windows.Forms.Application.ThreadException += OnThreadException; _isInitialized = true; - + LogManager.Info("[全局异常] 全局异常处理器已初始化"); } catch (Exception ex) @@ -57,7 +58,7 @@ namespace NavisworksTransport LogManager.Error($"[全局异常] 初始化异常处理器失败: {ex.Message}"); } } - + /// /// 处理未捕获的异常 /// @@ -68,15 +69,15 @@ namespace NavisworksTransport Exception ex = e.ExceptionObject as Exception; string message = ex?.Message ?? "未知异常"; string stackTrace = ex?.StackTrace ?? "无堆栈信息"; - + LogManager.Error($"[全局异常] 未处理异常: {message}"); LogManager.Error($"[全局异常] 堆栈信息: {stackTrace}"); LogManager.Error($"[全局异常] 异常类型: {ex?.GetType().Name ?? "Unknown"}"); LogManager.Error($"[全局异常] 是否终止: {e.IsTerminating}"); - + // 显示用户友好的错误信息 ShowErrorDialog("程序发生未预期的错误", message, ex); - + // 尝试恢复关键组件 TryRecoverComponents(); } @@ -85,7 +86,7 @@ namespace NavisworksTransport // 异常处理中的异常,保持静默 } } - + /// /// 处理线程异常 /// @@ -97,10 +98,10 @@ namespace NavisworksTransport LogManager.Error($"[全局异常] 线程异常: {ex.Message}"); LogManager.Error($"[全局异常] 堆栈信息: {ex.StackTrace}"); LogManager.Error($"[全局异常] 异常类型: {ex.GetType().Name}"); - + // 显示用户友好的错误信息 ShowErrorDialog("程序发生线程错误", ex.Message, ex); - + // 尝试恢复关键组件 TryRecoverComponents(); } @@ -109,7 +110,7 @@ namespace NavisworksTransport // 异常处理中的异常,保持静默 } } - + /// /// 安全执行方法,捕获所有异常 /// @@ -124,17 +125,17 @@ namespace NavisworksTransport LogManager.Error($"[安全执行] {operationName}失败: {ex.Message}"); LogManager.Error($"[安全执行] 堆栈信息: {ex.StackTrace}"); LogManager.Error($"[安全执行] 异常类型: {ex.GetType().Name}"); - + // 对于用户操作,显示简短的错误提示 if (!string.IsNullOrEmpty(operationName) && operationName != "操作") { ShowErrorToast($"{operationName}失败: {ex.Message}"); } - + return defaultValue; } } - + /// /// 安全执行方法(无返回值) /// @@ -142,7 +143,7 @@ namespace NavisworksTransport { SafeExecute(() => { action(); return true; }, false, operationName); } - + /// /// 显示错误对话框 /// @@ -159,7 +160,7 @@ namespace NavisworksTransport detailMessage += $"\n错误信息: {ex.Message}"; } } - + MessageBox.Show(detailMessage, title, MessageBoxButtons.OK, MessageBoxIcon.Error); } catch @@ -167,7 +168,7 @@ namespace NavisworksTransport // 如果连错误对话框都显示不了,就保持静默 } } - + /// /// 显示简短的错误提示 /// @@ -182,7 +183,7 @@ namespace NavisworksTransport // 保持静默 } } - + /// /// 清理 Clash Detective 集成相关资源 /// @@ -191,10 +192,10 @@ namespace NavisworksTransport try { LogManager.Info("[清理] 开始清理 Clash Detective 集成..."); - + // 清理 Clash Detective 集成 ClashDetectiveIntegration.Instance.Cleanup(); - + LogManager.Info("[清理] Clash Detective 集成清理完成"); } catch (Exception ex) @@ -211,7 +212,7 @@ namespace NavisworksTransport try { LogManager.Info("[全局异常] 尝试恢复关键组件..."); - + // 尝试重置路径编辑状态 var activeManager = PathPlanningManager.GetActivePathManager(); if (activeManager != null) @@ -219,17 +220,17 @@ namespace NavisworksTransport activeManager.ResetPathEditState(); LogManager.Info("[全局异常] 已重置路径编辑状态"); } - + // 尝试清除临时高亮 if (NavisApplication.ActiveDocument?.Models != null) { NavisApplication.ActiveDocument.Models.ResetAllTemporaryMaterials(); LogManager.Info("[全局异常] 已清除临时材质"); } - + // 清理 Clash Detective 集成 CleanupClashDetectiveIntegration(); - + LogManager.Info("[全局异常] 组件恢复完成"); } catch (Exception ex) @@ -239,38 +240,40 @@ namespace NavisworksTransport } } - [PluginAttribute("Basic", "Tian", ToolTip = "Transport Plugin", DisplayName = "Transport Plugin")] - [AddInPlugin(AddInLocation.AddIn)] // 将插件显示在Navisworks的"附加模块"选项卡中 - public class Main : AddInPlugin + [Plugin("NavisworksTransport.Main", "Tian", + DisplayName = "物流路径规划", + ToolTip = "物流路径规划和动画仿真插件")] + [DockPanePlugin(420, 700, FixedSize = false, AutoScroll = true)] + public class Main : DockPanePlugin { /// /// 会话初始化标志,用于确保在同一Navisworks会话中只清空一次日志 /// private static bool _isSessionInitialized = false; - + // 静态变量保存控制面板实例,避免重复创建 private static Form _controlPanelForm = null; - + // 保存关键UI控件引用,用于实时更新 private static Label _instructionLabel = null; private static Label _selectedModelsLabel = null; private static ListView _logisticsListView = null; private static Label _statsLabel = null; - + // 动画管理器实例(类级别变量) private static PathAnimationManager _animationManager; - + // 路径规划管理器实例 private static PathPlanningManager _pathPlanningManager; - + // 路径编辑UI控件引用 private static ListView _pathListView = null; private static Label _currentPathStatusLabel = null; private static ListView _currentPathPointsListView = null; // 使用ListView替代ListBox - + // 物流属性变更事件 public static event EventHandler LogisticsAttributeChanged; - + // 选择状态保护标志 private static bool _isUpdatingLogisticsData = false; private static ModelItemCollection _savedDocumentSelection = null; @@ -281,10 +284,12 @@ namespace NavisworksTransport private Label _animationStatusLabel; private ProgressBar _animationProgressBar; - public override int Execute(params string[] parameters) + public override Control CreateControlPane() { return GlobalExceptionHandler.SafeExecute(() => { + LogManager.Info("开始创建DockPane控制面板"); + // 初始化会话(只执行一次) if (!_isSessionInitialized) { @@ -292,36 +297,49 @@ namespace NavisworksTransport _isSessionInitialized = true; } - // 显示控制面板 - ShowDockPane(); - return 0; + // 创建WPF用户控件 + var wpfControl = new UI.WPF.LogisticsControlPanel(); + + // 使用ElementHost托管WPF控件 + var elementHost = new ElementHost + { + Dock = DockStyle.Fill, + Child = wpfControl + }; - }, -1, "插件初始化"); + // 重要:调用CreateControl()方法 + elementHost.CreateControl(); + + LogManager.Info("DockPane控制面板创建成功"); + return elementHost; + + }, null, "创建控制面板"); } - /// - /// 显示左侧控制面板(无模态对话框) - /// - private void ShowDockPane() + public override void DestroyControlPane(Control pane) { GlobalExceptionHandler.SafeExecute(() => { - // 如果面板已存在,直接显示并置前 - if (_controlPanelForm != null && !_controlPanelForm.IsDisposed) + LogManager.Info("开始销毁DockPane控制面板"); + + if (pane is ElementHost elementHost && elementHost.Child is UI.WPF.LogisticsControlPanel wpfControl) { - _controlPanelForm.Show(); - _controlPanelForm.BringToFront(); - _controlPanelForm.Focus(); - LogManager.Info("控制面板已显示并置前"); - return; + // 清理WPF控件资源 + wpfControl.Cleanup(); + elementHost.Child = null; } - // 创建新的控制面板 - ShowCategorySelectionDialog(); - - }, "显示控制面板"); + // 释放控件资源 + pane?.Dispose(); + + LogManager.Info("DockPane控制面板销毁完成"); + + }, "销毁控制面板"); } + // 注意:ShowDockPane方法已被移除,因为DockPanePlugin使用CreateControlPane方法 + // 原有的Windows Forms界面创建逻辑已迁移到WPF实现中 + /// /// 初始化插件会话 /// @@ -330,15 +348,15 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Info("=== 物流路径规划插件初始化开始 ==="); - + // 初始化全局异常处理 GlobalExceptionHandler.Initialize(); - + // 初始化路径规划管理器 InitializePathPlanningManager(); - + LogManager.Info("插件会话初始化完成"); - + }, "初始化会话"); } @@ -350,25 +368,25 @@ namespace NavisworksTransport try { LogManager.Info("开始初始化PathPlanningManager"); - + // 创建或获取PathPlanningManager实例 _pathPlanningManager = PathPlanningManager.GetActivePathManager() ?? new PathPlanningManager(); - + // 订阅路径编辑状态变更事件 _pathPlanningManager.PathEditStateChanged += OnPathEditStateChanged; - + // 订阅路径点相关事件 _pathPlanningManager.PathPointAddedIn3D += OnPathPointAddedIn3D; _pathPlanningManager.PathPointRemovedFrom3D += OnPathPointRemovedFrom3D; _pathPlanningManager.PathPointsListUpdated += OnPathPointsListUpdated; - + // 订阅路径变更事件 _pathPlanningManager.CurrentRouteChanged += OnCurrentRouteChanged; - + // 订阅状态和错误事件 _pathPlanningManager.StatusChanged += OnPathManagerStatusChanged; _pathPlanningManager.ErrorOccurred += OnPathManagerErrorOccurred; - + LogManager.Info("PathPlanningManager初始化完成,事件订阅成功"); } catch (Exception ex) @@ -400,7 +418,7 @@ namespace NavisworksTransport // 添加选择变化事件监听 NavisApplication.ActiveDocument.CurrentSelection.Changed += OnSelectionChanged; - + // 订阅物流属性变更事件 LogisticsAttributeChanged += OnLogisticsAttributeChanged; @@ -465,26 +483,26 @@ namespace NavisworksTransport bottomPanel.Controls.Add(closeButton); // 关闭按钮事件处理 - closeButton.Click += (sender, e) => + closeButton.Click += (sender, e) => { _controlPanelForm.Close(); }; // 事件处理 - helpButton.Click += (sender, e) => + helpButton.Click += (sender, e) => { - MessageBox.Show("物流路径规划插件使用说明:\n\n1. 类别设置:设置对象类别和可见性\n2. 路径编辑:创建和管理3D路径\n3. 动画控制:创建路径动画\n4. 系统管理:插件设置和日志", + MessageBox.Show("物流路径规划插件使用说明:\n\n1. 类别设置:设置对象类别和可见性\n2. 路径编辑:创建和管理3D路径\n3. 动画控制:创建路径动画\n4. 系统管理:插件设置和日志", "帮助", MessageBoxButtons.OK, MessageBoxIcon.Information); }; - aboutButton.Click += (sender, e) => + aboutButton.Click += (sender, e) => { - MessageBox.Show("物流路径规划插件 v1.0\n\n适用于 Navisworks 2017\n\n功能:3D路径规划、动画创建、碰撞检测", + MessageBox.Show("物流路径规划插件 v1.0\n\n适用于 Navisworks 2017\n\n功能:3D路径规划、动画创建、碰撞检测", "关于插件", MessageBoxButtons.OK, MessageBoxIcon.Information); }; // 添加窗口关闭事件处理 - _controlPanelForm.FormClosed += (sender, e) => + _controlPanelForm.FormClosed += (sender, e) => { // 清理选择事件监听 try @@ -495,7 +513,7 @@ namespace NavisworksTransport { LogManager.Error($"清理选择事件监听失败: {ex.Message}"); } - + // 清理物流属性变更事件监听 try { @@ -505,7 +523,7 @@ namespace NavisworksTransport { LogManager.Error($"清理物流属性变更事件监听失败: {ex.Message}"); } - + // 清理 Clash Detective 集成 try { @@ -515,7 +533,7 @@ namespace NavisworksTransport { LogManager.Error($"清理 Clash Detective 集成失败: {ex.Message}"); } - + _controlPanelForm = null; // 清空引用 _instructionLabel = null; _selectedModelsLabel = null; @@ -537,12 +555,12 @@ namespace NavisworksTransport { TabPage modelTab = new TabPage("类别设置"); modelTab.Padding = new Padding(10); - + Panel scrollPanel = new Panel { Dock = DockStyle.Fill, - AutoScroll = true - }; + AutoScroll = true + }; modelTab.Controls.Add(scrollPanel); int currentY = 10; @@ -574,15 +592,15 @@ namespace NavisworksTransport currentY += 30; // 类别属性设置 - GroupBox categoryGroupBox = new GroupBox - { + GroupBox categoryGroupBox = new GroupBox + { Text = "类别属性设置", Location = new Point(10, currentY), Size = new Size(350, 70), Font = new Font("微软雅黑", 8, FontStyle.Bold) }; scrollPanel.Controls.Add(categoryGroupBox); - CreateCategoryDropdown(categoryGroupBox); + CreateCategoryDropdown(categoryGroupBox); currentY += 85; // 物流模型列表 @@ -611,15 +629,15 @@ namespace NavisworksTransport currentY += 30; // 可见性控制 - GroupBox visibilityGroupBox = new GroupBox - { - Text = "可见性控制", + GroupBox visibilityGroupBox = new GroupBox + { + Text = "可见性控制", Location = new Point(10, currentY), Size = new Size(350, 95), Font = new Font("微软雅黑", 8, FontStyle.Bold) }; scrollPanel.Controls.Add(visibilityGroupBox); - CreateVisibilityControls(visibilityGroupBox); + CreateVisibilityControls(visibilityGroupBox); tabControl.TabPages.Add(modelTab); @@ -634,7 +652,7 @@ namespace NavisworksTransport { TabPage pathTab = new TabPage("路径编辑"); pathTab.Padding = new Padding(10); - + Panel scrollPanel = new Panel { Dock = DockStyle.Fill, @@ -689,7 +707,7 @@ namespace NavisworksTransport { TabPage animationTab = new TabPage("检测动画"); animationTab.Padding = new Padding(10); - + Panel scrollPanel = new Panel { Dock = DockStyle.Fill, @@ -744,7 +762,7 @@ namespace NavisworksTransport { TabPage systemTab = new TabPage("系统管理"); systemTab.Padding = new Padding(10); - + Panel scrollPanel = new Panel { Dock = DockStyle.Fill, @@ -817,25 +835,26 @@ namespace NavisworksTransport Font = new Font("微软雅黑", 8) }; groupBox.Controls.Add(splitterButton); - - splitterButton.Click += (sender, e) => { + + splitterButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { // 检查是否有打开的模型 if (NavisApplication.ActiveDocument?.Models?.Count == 0) { - MessageBox.Show("请先打开一个Navisworks模型文件", "提示", + MessageBox.Show("请先打开一个Navisworks模型文件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - + // 显示模型分层对话框 var splitterDialog = new ModelSplitterDialog(); splitterDialog.ShowDialog(_controlPanelForm); - + }, "打开模型分层对话框"); }; - + Label infoLabel = new Label { Text = "将大型模型按楼层或属性拆分为多个文件", @@ -899,26 +918,27 @@ namespace NavisworksTransport GridLines = true, Font = new Font("微软雅黑", 8) }; - + _pathListView.Columns.Add("路径名称", 140); _pathListView.Columns.Add("创建时间", 100); _pathListView.Columns.Add("点数", 60); - + groupBox.Controls.Add(_pathListView); // 添加事件处理 - newButton.Click += (sender, e) => { + newButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { LogManager.Info("[新建按钮] ===== 新建按钮被点击 ====="); - + var pathManager = PathPlanningManager.GetActivePathManager() ?? new PathPlanningManager(); LogManager.Info($"[新建按钮] 获取到路径管理器: {pathManager != null}"); - + // 开始创建新路径 var newRoute = pathManager.StartCreatingNewRoute(); LogManager.Info($"[新建按钮] StartCreatingNewRoute调用完成,返回路径: {newRoute?.Name ?? "null"}"); - + if (newRoute != null) { // 更新UI状态显示 @@ -926,105 +946,108 @@ namespace NavisworksTransport UpdateCurrentPathPointsList(); UpdatePathList(); LogManager.Info("[新建按钮] UI状态更新完成"); - + LogManager.Info("开始创建新路径,已进入创建状态"); - MessageBox.Show("已进入新建路径模式!\n\n请在3D视图中点击物流通道来设置路径点。", + MessageBox.Show("已进入新建路径模式!\n\n请在3D视图中点击物流通道来设置路径点。", "新建路径", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { LogManager.Error("[新建按钮] 创建新路径失败"); - MessageBox.Show("创建新路径失败,请查看日志", "错误", + MessageBox.Show("创建新路径失败,请查看日志", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } - + LogManager.Info("[新建按钮] ===== 新建按钮事件处理完成 ====="); - + }, "新建路径"); }; - editButton.Click += (sender, e) => { + editButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { if (_pathListView.SelectedItems.Count == 0) { - MessageBox.Show("请先选择要编辑的路径", "提示", + MessageBox.Show("请先选择要编辑的路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } var selectedRouteName = _pathListView.SelectedItems[0].Text; - + // 查找对应的路径对象 var pathManager = PathPlanningManager.GetActivePathManager() ?? new PathPlanningManager(); var routeToEdit = pathManager.Routes.FirstOrDefault(r => r.Name == selectedRouteName); - + if (routeToEdit != null) { // 切换到编辑状态 pathManager.SwitchToEditingState(routeToEdit); - + // 更新UI状态显示 UpdateCurrentPathPointsList(); UpdatePathList(); - + LogManager.Info($"开始编辑路径: {selectedRouteName}"); - MessageBox.Show($"已进入编辑模式!\n\n正在编辑路径: {selectedRouteName}\n\n可以在3D视图中修改路径点。", + MessageBox.Show($"已进入编辑模式!\n\n正在编辑路径: {selectedRouteName}\n\n可以在3D视图中修改路径点。", "编辑路径", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { - MessageBox.Show($"找不到路径: {selectedRouteName}", "错误", + MessageBox.Show($"找不到路径: {selectedRouteName}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } - + }, "编辑路径"); }; - deleteButton.Click += (sender, e) => { + deleteButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { if (_pathListView.SelectedItems.Count == 0) { - MessageBox.Show("请先选择要删除的路径", "提示", + MessageBox.Show("请先选择要删除的路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - - var result = MessageBox.Show($"确定要删除路径 '{_pathListView.SelectedItems[0].Text}' 吗?", + + var result = MessageBox.Show($"确定要删除路径 '{_pathListView.SelectedItems[0].Text}' 吗?", "确认删除", MessageBoxButtons.YesNo, MessageBoxIcon.Question); - + if (result == DialogResult.Yes) { // TODO: 实现删除路径的逻辑 LogManager.Info($"删除路径: {_pathListView.SelectedItems[0].Text}"); _pathListView.SelectedItems[0].Remove(); - MessageBox.Show("路径已删除", "操作完成", + MessageBox.Show("路径已删除", "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } - + }, "删除路径"); }; - detailButton.Click += (sender, e) => { + detailButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { if (_pathListView.SelectedItems.Count == 0) { - MessageBox.Show("请先选择要查看的路径", "提示", + MessageBox.Show("请先选择要查看的路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - + var selectedPath = _pathListView.SelectedItems[0]; var detailInfo = $"路径名称: {selectedPath.SubItems[0].Text}\n" + $"创建时间: {selectedPath.SubItems[1].Text}\n" + $"路径点数: {selectedPath.SubItems[2].Text}"; - - MessageBox.Show(detailInfo, "路径详情", + + MessageBox.Show(detailInfo, "路径详情", MessageBoxButtons.OK, MessageBoxIcon.Information); LogManager.Info($"查看路径详情: {selectedPath.Text}"); - + }, "查看路径详情"); }; } @@ -1075,7 +1098,7 @@ namespace NavisworksTransport FullRowSelect = true, GridLines = true }; - + // 添加列 _currentPathPointsListView.Columns.Add("序号", 60, HorizontalAlignment.Left); _currentPathPointsListView.Columns.Add("名称", 80, HorizontalAlignment.Left); @@ -1106,24 +1129,25 @@ namespace NavisworksTransport groupBox.Controls.Add(cancelButton); // 添加事件处理 - finishButton.Click += (sender, e) => { + finishButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { var pathManager = PathPlanningManager.GetActivePathManager(); if (pathManager == null) { - MessageBox.Show("路径管理器未初始化", "错误", + MessageBox.Show("路径管理器未初始化", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - + if (!pathManager.IsInEditableState) { - MessageBox.Show("当前不在编辑状态", "提示", + MessageBox.Show("当前不在编辑状态", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - + // 完成编辑并保存 bool success = pathManager.FinishEditing(); if (success) @@ -1131,41 +1155,42 @@ namespace NavisworksTransport // 更新UI状态显示 UpdateCurrentPathPointsList(); UpdatePathList(); - + LogManager.Info("路径编辑已完成并保存"); - MessageBox.Show("路径编辑完成!", "操作完成", + MessageBox.Show("路径编辑完成!", "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { - MessageBox.Show("完成编辑时发生错误,请查看日志", "错误", + MessageBox.Show("完成编辑时发生错误,请查看日志", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } - + }, "完成编辑"); }; - cancelButton.Click += (sender, e) => { + cancelButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { var pathManager = PathPlanningManager.GetActivePathManager(); if (pathManager == null) { - MessageBox.Show("路径管理器未初始化", "错误", + MessageBox.Show("路径管理器未初始化", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - + if (!pathManager.IsInEditableState) { - MessageBox.Show("当前不在编辑状态", "提示", + MessageBox.Show("当前不在编辑状态", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - - var result = MessageBox.Show("确定要取消编辑吗?未保存的更改将丢失。", + + var result = MessageBox.Show("确定要取消编辑吗?未保存的更改将丢失。", "确认取消", MessageBoxButtons.YesNo, MessageBoxIcon.Question); - + if (result == DialogResult.Yes) { // 取消编辑并清理临时数据 @@ -1175,18 +1200,18 @@ namespace NavisworksTransport // 更新UI状态显示 UpdateCurrentPathPointsList(); UpdatePathList(); - + LogManager.Info("路径编辑已取消"); - MessageBox.Show("已取消编辑", "操作完成", + MessageBox.Show("已取消编辑", "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { - MessageBox.Show("取消编辑时发生错误,请查看日志", "错误", + MessageBox.Show("取消编辑时发生错误,请查看日志", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } - + }, "取消编辑"); }; } @@ -1233,17 +1258,18 @@ namespace NavisworksTransport groupBox.Controls.Add(historyButton); // 添加事件处理 - saveButton.Click += (sender, e) => { + saveButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { var pathManager = PathPlanningManager.GetActivePathManager(); if (pathManager?.CurrentRoute?.Points == null || pathManager.CurrentRoute.Points.Count == 0) { - MessageBox.Show("当前没有可保存的路径", "提示", + MessageBox.Show("当前没有可保存的路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - + // 显示保存对话框 SaveFileDialog saveDialog = new SaveFileDialog { @@ -1252,12 +1278,12 @@ namespace NavisworksTransport FilterIndex = 1, DefaultExt = "xml" }; - + if (saveDialog.ShowDialog(_controlPanelForm) == DialogResult.OK) { bool saveResult = false; var extension = System.IO.Path.GetExtension(saveDialog.FileName).ToLowerInvariant(); - + if (extension == ".json") { saveResult = PathFileSerializer.SaveToJson(pathManager.CurrentRoute, saveDialog.FileName); @@ -1266,28 +1292,29 @@ namespace NavisworksTransport { saveResult = PathFileSerializer.SaveToXml(pathManager.CurrentRoute, saveDialog.FileName); } - + if (saveResult) { // 保存到历史记录 pathManager.SaveCurrentRouteToHistory($"手动保存到文件: {System.IO.Path.GetFileName(saveDialog.FileName)}"); - + LogManager.Info($"路径成功保存到: {saveDialog.FileName}"); - MessageBox.Show($"路径已保存到: {saveDialog.FileName}", "保存成功", + MessageBox.Show($"路径已保存到: {saveDialog.FileName}", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { LogManager.Error($"保存路径失败: {saveDialog.FileName}"); - MessageBox.Show("保存路径失败,请检查文件路径和权限", "保存失败", + MessageBox.Show("保存路径失败,请检查文件路径和权限", "保存失败", MessageBoxButtons.OK, MessageBoxIcon.Error); - } + } } - + }, "保存当前路径"); }; - importButton.Click += (sender, e) => { + importButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { var pathManager = PathPlanningManager.GetActivePathManager(); @@ -1296,19 +1323,19 @@ namespace NavisworksTransport MessageBox.Show("路径管理器未初始化", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - + OpenFileDialog openDialog = new OpenFileDialog { Title = "导入路径文件", Filter = "路径文件 (*.xml;*.json)|*.xml;*.json|XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json|所有文件 (*.*)|*.*", FilterIndex = 1 }; - + if (openDialog.ShowDialog(_controlPanelForm) == DialogResult.OK) { PathRoute importedRoute = null; var fileFormat = PathFileSerializer.DetectFileFormat(openDialog.FileName); - + switch (fileFormat) { case "XML": @@ -1318,62 +1345,63 @@ namespace NavisworksTransport importedRoute = PathFileSerializer.LoadFromJson(openDialog.FileName); if (importedRoute == null) { - MessageBox.Show("JSON格式导入功能简化,建议使用XML格式", "提示", + MessageBox.Show("JSON格式导入功能简化,建议使用XML格式", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } break; default: - MessageBox.Show($"不支持的文件格式: {fileFormat}", "导入失败", + MessageBox.Show($"不支持的文件格式: {fileFormat}", "导入失败", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - + if (importedRoute != null && importedRoute.Points.Count > 0) { // 添加到路径管理器 pathManager.AddRoute(importedRoute); pathManager.CurrentRoute = importedRoute; - + LogManager.Info($"成功导入路径: {importedRoute.Name},包含 {importedRoute.Points.Count} 个路径点"); - MessageBox.Show($"路径 '{importedRoute.Name}' 已成功导入,包含 {importedRoute.Points.Count} 个路径点", "导入成功", + MessageBox.Show($"路径 '{importedRoute.Name}' 已成功导入,包含 {importedRoute.Points.Count} 个路径点", "导入成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { LogManager.Error($"导入路径失败: {openDialog.FileName}"); - MessageBox.Show("导入路径失败,文件格式可能不正确", "导入失败", + MessageBox.Show("导入路径失败,文件格式可能不正确", "导入失败", MessageBoxButtons.OK, MessageBoxIcon.Error); - } + } } - + }, "导入路径"); }; - exportButton.Click += (sender, e) => { + exportButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { var pathManager = PathPlanningManager.GetActivePathManager(); if (pathManager?.CurrentRoute?.Points == null || pathManager.CurrentRoute.Points.Count == 0) { - MessageBox.Show("当前没有可导出的路径", "提示", + MessageBox.Show("当前没有可导出的路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - + SaveFileDialog exportDialog = new SaveFileDialog { - Title = "导出路径文件", + Title = "导出路径文件", Filter = "XML文件 (*.xml)|*.xml|JSON文件 (*.json)|*.json", FilterIndex = 1, DefaultExt = "xml" }; - + if (exportDialog.ShowDialog(_controlPanelForm) == DialogResult.OK) { bool exportResult = false; var extension = System.IO.Path.GetExtension(exportDialog.FileName).ToLowerInvariant(); - + if (extension == ".json") { exportResult = PathFileSerializer.SaveToJson(pathManager.CurrentRoute, exportDialog.FileName); @@ -1382,46 +1410,47 @@ namespace NavisworksTransport { exportResult = PathFileSerializer.SaveToXml(pathManager.CurrentRoute, exportDialog.FileName); } - + if (exportResult) { LogManager.Info($"路径成功导出到: {exportDialog.FileName}"); - MessageBox.Show($"路径已导出到: {exportDialog.FileName}", "导出成功", + MessageBox.Show($"路径已导出到: {exportDialog.FileName}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { LogManager.Error($"导出路径失败: {exportDialog.FileName}"); - MessageBox.Show("导出路径失败,请检查文件路径和权限", "导出失败", + MessageBox.Show("导出路径失败,请检查文件路径和权限", "导出失败", MessageBoxButtons.OK, MessageBoxIcon.Error); } } - + }, "导出路径"); }; - historyButton.Click += (sender, e) => { + historyButton.Click += (sender, e) => + { GlobalExceptionHandler.SafeExecute(() => { var pathManager = PathPlanningManager.GetActivePathManager(); if (pathManager?.HistoryManager == null) { - MessageBox.Show("历史记录管理器未初始化", "错误", + MessageBox.Show("历史记录管理器未初始化", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } - + var historyEntries = pathManager.HistoryManager.GetAllHistoryEntries(); if (historyEntries.Count == 0) { - MessageBox.Show("暂无历史记录", "提示", + MessageBox.Show("暂无历史记录", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - + // 显示历史记录窗口 ShowPathHistoryDialog(historyEntries, pathManager.HistoryManager); - + }, "查看历史记录"); }; } @@ -1439,7 +1468,7 @@ namespace NavisworksTransport _startAnimationButton.Size = new Size(80, 30); groupBox.Controls.Add(_startAnimationButton); } - + // 停止按钮 if (_stopAnimationButton != null) { @@ -1486,7 +1515,7 @@ namespace NavisworksTransport int totalModels = 0; int logisticsModels = 0; - + // 统计所有模型和物流模型 foreach (ModelItem rootItem in document.Models.RootItems) { @@ -1513,7 +1542,7 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { listView.Items.Clear(); - + var document = NavisApplication.ActiveDocument; if (document?.Models == null) return; @@ -1555,23 +1584,23 @@ namespace NavisworksTransport var selectedItem = listView.SelectedItems[0]; var modelItem = selectedItem.Tag as ModelItem; - + if (modelItem != null) { // 获取现有的物流属性信息 var existingInfo = CategoryAttributeManager.GetLogisticsAttributeInfo(modelItem); - + // 打开属性编辑对话框 using (var editDialog = new LogisticsPropertyEditDialog(existingInfo)) { editDialog.Text = $"编辑模型属性 - {modelItem.DisplayName}"; - + if (editDialog.ShowDialog(_controlPanelForm) == DialogResult.OK) { // 创建包含该模型的集合 var items = new ModelItemCollection(); items.Add(modelItem); - + // 使用新的属性值更新物流属性 int updateCount = CategoryAttributeManager.UpdateLogisticsAttributes( items, @@ -1580,27 +1609,27 @@ namespace NavisworksTransport editDialog.Priority, editDialog.VehicleSize, editDialog.SpeedLimit); - + if (updateCount > 0) { // 保存当前文档选择状态 _savedDocumentSelection = new ModelItemCollection(); _savedDocumentSelection.AddRange(NavisApplication.ActiveDocument.CurrentSelection.SelectedItems); - + // 设置颜色标记(根据元素类型设置不同颜色) var color = GetElementTypeColor(editDialog.SelectedElementType); NavisApplication.ActiveDocument.Models.OverrideTemporaryColor(new ModelItem[] { modelItem }, color); - + LogManager.Info($"已成功修改模型 {modelItem.DisplayName} 的物流属性为 {editDialog.SelectedElementType}"); LogManager.Info($"[选择保存] 已保存文档选择状态: {_savedDocumentSelection.Count}个模型"); - + MessageBox.Show("属性修改成功!", "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information); - + // 强制刷新文档状态,确保属性更改立即生效 ForceDocumentRefresh(); - + // 触发物流属性变更事件,实现即时刷新(会自动恢复选择状态) - var eventArgs = new LogisticsAttributeChangedEventArgs("修改", updateCount, + var eventArgs = new LogisticsAttributeChangedEventArgs("修改", updateCount, $"已修改 {updateCount} 个模型的物流属性"); LogisticsAttributeChanged?.Invoke(null, eventArgs); } @@ -1661,29 +1690,29 @@ namespace NavisworksTransport var selectedItem = listView.SelectedItems[0]; var modelItem = selectedItem.Tag as ModelItem; - + if (modelItem != null) { // 检查模型是否有物流属性 if (!CategoryAttributeManager.HasLogisticsAttributes(modelItem)) { - MessageBox.Show($"模型 '{modelItem.DisplayName}' 没有物流属性可清除。", "提示", + MessageBox.Show($"模型 '{modelItem.DisplayName}' 没有物流属性可清除。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } - var result = MessageBox.Show($"确定要删除模型 '{modelItem.DisplayName}' 的所有物流属性吗?\n\n此操作将永久删除该模型的物流分类信息。", + var result = MessageBox.Show($"确定要删除模型 '{modelItem.DisplayName}' 的所有物流属性吗?\n\n此操作将永久删除该模型的物流分类信息。", "删除物流属性", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); - + if (result == DialogResult.Yes) { // 创建包含该模型的集合 var items = new ModelItemCollection(); items.Add(modelItem); - + // 删除物流属性 int removeCount = CategoryAttributeManager.RemoveLogisticsAttributes(items); - + if (removeCount > 0) { // 保存当前文档选择状态(排除将要删除的模型) @@ -1695,20 +1724,20 @@ namespace NavisworksTransport _savedDocumentSelection.Add(item); } } - + // 重置颜色显示 NavisApplication.ActiveDocument.Models.ResetAllTemporaryMaterials(); - + LogManager.Info($"已成功删除模型 {modelItem.DisplayName} 的物流属性"); LogManager.Info($"[选择保存] 已保存文档选择状态: {_savedDocumentSelection.Count}个模型"); - + MessageBox.Show("物流属性已成功删除!", "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information); - + // 强制刷新文档状态,确保属性删除立即生效 ForceDocumentRefresh(); - + // 触发物流属性变更事件,实现即时刷新(会自动恢复选择状态) - var eventArgs = new LogisticsAttributeChangedEventArgs("删除", removeCount, + var eventArgs = new LogisticsAttributeChangedEventArgs("删除", removeCount, $"已删除 {removeCount} 个模型的物流属性"); LogisticsAttributeChanged?.Invoke(null, eventArgs); } @@ -1738,20 +1767,20 @@ namespace NavisworksTransport var selectedItem = listView.SelectedItems[0]; var modelItem = selectedItem.Tag as ModelItem; - + if (modelItem != null) { // 先重置所有颜色 NavisApplication.ActiveDocument.Models.ResetAllTemporaryMaterials(); - + // 高亮选中的模型(黄色) var highlightColor = new Autodesk.Navisworks.Api.Color(1.0, 1.0, 0.0); // 黄色 NavisApplication.ActiveDocument.Models.OverrideTemporaryColor(new ModelItem[] { modelItem }, highlightColor); - + // 选中该模型 NavisApplication.ActiveDocument.CurrentSelection.Clear(); NavisApplication.ActiveDocument.CurrentSelection.Add(modelItem); - + LogManager.Info($"已高亮显示模型 {modelItem.DisplayName}"); } @@ -1899,28 +1928,28 @@ namespace NavisworksTransport }; groupBox.Controls.Add(logLevelLabel); - // 事件处理 - viewLogButton.Click += (sender, e) => - { - string logPath = LogManager.LogFilePath; - if (System.IO.File.Exists(logPath)) - { - System.Diagnostics.Process.Start("notepad.exe", logPath); - } - else - { - MessageBox.Show("日志文件不存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - }; + // 事件处理 + viewLogButton.Click += (sender, e) => + { + string logPath = LogManager.LogFilePath; + if (System.IO.File.Exists(logPath)) + { + System.Diagnostics.Process.Start("notepad.exe", logPath); + } + else + { + MessageBox.Show("日志文件不存在", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + }; - clearLogButton.Click += (sender, e) => - { - if (MessageBox.Show("确定要清空所有日志吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) - { - LogManager.ClearLog(); - MessageBox.Show("日志已清空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - }; + clearLogButton.Click += (sender, e) => + { + if (MessageBox.Show("确定要清空所有日志吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + { + LogManager.ClearLog(); + MessageBox.Show("日志已清空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + }; } /// @@ -2085,32 +2114,32 @@ namespace NavisworksTransport if (routes.Count > 0) { - MessageBox.Show($"成功导入 {routes.Count} 条路径", "导入成功", + MessageBox.Show($"成功导入 {routes.Count} 条路径", "导入成功", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { - MessageBox.Show("文件中未找到有效的路径数据", "导入提示", + MessageBox.Show("文件中未找到有效的路径数据", "导入提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } catch (Exception ex) { - MessageBox.Show($"导入路径失败: {ex.Message}", "错误", + MessageBox.Show($"导入路径失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }; exportPathButton.Click += (sender, e) => { - MessageBox.Show("导出功能正在开发中", "提示", + MessageBox.Show("导出功能正在开发中", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); }; pathHistoryButton.Click += (sender, e) => { - MessageBox.Show("路径历史功能正在开发中", "提示", + MessageBox.Show("路径历史功能正在开发中", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); }; @@ -2175,17 +2204,17 @@ namespace NavisworksTransport { if (categoryComboBox.SelectedIndex < 0) { - MessageBox.Show("请先选择一个类别", "提示", + MessageBox.Show("请先选择一个类别", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } // 获取当前选中的模型项 ModelItemCollection selectedItems = NavisApplication.ActiveDocument.CurrentSelection.SelectedItems; - + if (selectedItems.Count == 0) { - MessageBox.Show("请先选择要设置属性的模型项", "提示", + MessageBox.Show("请先选择要设置属性的模型项", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } @@ -2207,35 +2236,35 @@ namespace NavisworksTransport // 执行属性添加操作 int successCount = CategoryAttributeManager.AddLogisticsAttributes( - selectedItems, + selectedItems, elementType, elementType != CategoryAttributeManager.LogisticsElementType.障碍物, // 障碍物默认不可通行 5, // 默认优先级 "标准", // 默认车辆尺寸 10.0 // 默认速度限制 ); - + // 显示结果 if (successCount > 0) { // 设置为类别对应的颜色标记(视觉反馈) var navisColor = GetElementTypeColor(elementType); NavisApplication.ActiveDocument.Models.OverrideTemporaryColor(selectedItems.ToArray(), navisColor); - + // 自动刷新界面数据 RefreshInterfaceData(); - + LogManager.Info($"已为 {successCount} 个模型设置物流属性: {categoryComboBox.SelectedItem}"); } else { - MessageBox.Show("未能为任何模型项设置属性,请检查选择的模型项", "操作失败", + MessageBox.Show("未能为任何模型项设置属性,请检查选择的模型项", "操作失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception ex) { - MessageBox.Show($"设置属性时发生错误: {ex.Message}", "错误", + MessageBox.Show($"设置属性时发生错误: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); LogManager.Error($"设置物流属性失败: {ex.Message}"); } @@ -2329,7 +2358,7 @@ namespace NavisworksTransport { Text = "生成动画", Location = new Point(235, 112), - Size = new Size(100,30), + Size = new Size(100, 30), Font = new Font("微软雅黑", 8), Enabled = false }; @@ -2365,7 +2394,7 @@ namespace NavisworksTransport // 存储选中的部件 ModelItem selectedComponent = null; - + // 车辆选择事件处理 getVehicleButton.Click += (sender, e) => { @@ -2373,17 +2402,17 @@ namespace NavisworksTransport { var doc = NavisApplication.ActiveDocument; var selectedItems = doc.CurrentSelection.SelectedItems; - + if (selectedItems.Count == 0) { - MessageBox.Show("请先在选择树中选择一个部件", "提示", + MessageBox.Show("请先在选择树中选择一个部件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } if (selectedItems.Count > 1) { - MessageBox.Show("请只选择一个部件", "提示", + MessageBox.Show("请只选择一个部件", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } @@ -2391,13 +2420,13 @@ namespace NavisworksTransport selectedComponent = selectedItems.First; vehicleStatusLabel.Text = $"状态: 已选择 {selectedComponent.DisplayName}"; vehicleStatusLabel.ForeColor = System.Drawing.Color.Green; - + // 检查是否可以启用生成动画按钮 if (pathComboBox.SelectedItem != null) { createAnimationButton.Enabled = true; } - + }, "获取选中部件"); }; @@ -2412,7 +2441,7 @@ namespace NavisworksTransport if (selectedPath != null) { pathInfoLabel.Text = $"点数: {selectedPath.Points?.Count ?? 0}"; - + // 添加路径可视化功能 try { @@ -2422,7 +2451,7 @@ namespace NavisworksTransport { // 清空所有现有的路径点标记 renderPlugin.ClearAllMarkers(); - + // 绘制选中路径的所有路径点 var sortedPoints = selectedPath.GetSortedPoints(); for (int i = 0; i < sortedPoints.Count; i++) @@ -2430,7 +2459,7 @@ namespace NavisworksTransport var point = sortedPoints[i]; renderPlugin.AddCircleMarker(point.Position, point.Type, i + 1); } - + LogManager.Info($"已绘制路径: {selectedPath.Name},包含 {sortedPoints.Count} 个路径点和连线"); } else @@ -2442,7 +2471,7 @@ namespace NavisworksTransport { LogManager.Error($"路径可视化失败: {ex.Message}"); } - + // 检查是否可以启用生成动画按钮 if (selectedComponent != null) { @@ -2454,7 +2483,7 @@ namespace NavisworksTransport { pathInfoLabel.Text = "点数: 0"; createAnimationButton.Enabled = false; - + // 清空路径可视化 try { @@ -2494,7 +2523,7 @@ namespace NavisworksTransport } }, "刷新路径列表"); }); - + refreshPathList(); // 初始加载 // 监听路径生成事件,自动刷新路径列表 @@ -2518,7 +2547,7 @@ namespace NavisworksTransport { if (selectedComponent == null) { - MessageBox.Show("请先选择部件模型", "提示", + MessageBox.Show("请先选择部件模型", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } @@ -2526,7 +2555,7 @@ namespace NavisworksTransport var selectedPath = pathComboBox.SelectedItem as PathRoute; if (selectedPath?.Points == null || selectedPath.Points.Count < 2) { - MessageBox.Show("请选择有效的路径(至少2个点)", "提示", + MessageBox.Show("请选择有效的路径(至少2个点)", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } @@ -2535,7 +2564,7 @@ namespace NavisworksTransport if (_animationManager == null) { _animationManager = new PathAnimationManager(); - + // 订阅动画管理器的事件 _animationManager.StateChanged += (s, newState) => { @@ -2565,10 +2594,10 @@ namespace NavisworksTransport if (_animationManager != null) { _animationManager.StartAnimation(); - + _animationStatusLabel.Text = "状态: 动画播放中"; _animationStatusLabel.ForeColor = System.Drawing.Color.Blue; - + _startAnimationButton.Enabled = false; _stopAnimationButton.Enabled = true; createAnimationButton.Enabled = false; @@ -2583,14 +2612,14 @@ namespace NavisworksTransport if (_animationManager != null) { _animationManager.StopAnimation(); - + _animationStatusLabel.Text = "状态: 动画已停止"; _animationStatusLabel.ForeColor = System.Drawing.Color.Orange; - + _startAnimationButton.Enabled = true; _stopAnimationButton.Enabled = false; createAnimationButton.Enabled = true; - + // 清理 Clash Detective 集成 GlobalExceptionHandler.CleanupClashDetectiveIntegration(); } @@ -2705,9 +2734,9 @@ namespace NavisworksTransport logisticsOnlyCheckBox.Enabled = false; statusLabel.Text = "状态: 正在处理..."; statusLabel.ForeColor = System.Drawing.Color.Orange; - + VisibilityOperationResult result; - + if (logisticsOnlyCheckBox.Checked) { // 勾选时:隐藏非物流分类项目 @@ -2778,7 +2807,7 @@ namespace NavisworksTransport LogManager.Info("[选择保护] 正在更新物流数据,忽略选择变化事件"); return; } - + // 更新选择显示 UpdateSelectionDisplay(); }, "选择变化事件处理"); @@ -2809,10 +2838,10 @@ namespace NavisworksTransport { if (selectedCount > 0) { - string modelNames = string.Join(", ", selectedItems.Take(3).Select(item => + string modelNames = string.Join(", ", selectedItems.Take(3).Select(item => item.DisplayName.Length > 15 ? item.DisplayName.Substring(0, 15) + "..." : item.DisplayName)); if (selectedCount > 3) modelNames += $" 等{selectedCount}项"; - + _selectedModelsLabel.Text = $"选中模型: {modelNames}"; _selectedModelsLabel.Visible = true; } @@ -2855,7 +2884,7 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { listView.Items.Clear(); - + var document = NavisApplication.ActiveDocument; if (document?.Models == null) return; @@ -2901,7 +2930,7 @@ namespace NavisworksTransport int totalModels = 0; int logisticsModels = 0; - + // 统计所有模型和物流模型 foreach (ModelItem rootItem in document.Models.RootItems) { @@ -2919,9 +2948,9 @@ namespace NavisworksTransport }, "更新物流统计"); } - + #region 物流属性变更事件处理器 - + /// /// 强制刷新文档状态以确保属性更改生效(不影响选择状态) /// @@ -2934,18 +2963,18 @@ namespace NavisworksTransport { // 强制文档重新计算属性 - 使用温和的方法 document.Models.ResetAllTemporaryMaterials(); - + // 强制刷新视图以触发属性重新加载 // 通过刷新渲染状态来触发属性更新 document.Models.ResetAllTemporaryMaterials(); - + // 强制垃圾回收以清理缓存 System.GC.Collect(); System.GC.WaitForPendingFinalizers(); - + // 增加延迟确保操作完成 System.Threading.Thread.Sleep(100); - + LogManager.Info("[文档刷新] 已强制刷新文档状态(保持选择)"); } } @@ -2954,7 +2983,7 @@ namespace NavisworksTransport LogManager.Error($"[文档刷新] 强制刷新文档状态失败: {ex.Message}"); } } - + /// /// 处理物流属性变更事件 /// @@ -2966,7 +2995,7 @@ namespace NavisworksTransport LogManager.Info($"[UI同步] 操作类型: {e.OperationType}"); LogManager.Info($"[UI同步] 受影响模型数: {e.AffectedCount}"); LogManager.Info($"[UI同步] 当前线程: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); - + // 确保在UI线程上执行 if (_controlPanelForm != null && _controlPanelForm.InvokeRequired) { @@ -2974,21 +3003,21 @@ namespace NavisworksTransport _controlPanelForm.Invoke(new Action(() => OnLogisticsAttributeChanged(sender, e))); return; } - + LogManager.Info("[UI同步] 在UI线程上执行物流数据刷新"); - + // 设置更新标志,保护选择状态 _isUpdatingLogisticsData = true; - + try { // 首先强制刷新文档状态,确保属性更改生效 ForceDocumentRefresh(); - + // 强制清理属性缓存 System.GC.Collect(); System.GC.WaitForPendingFinalizers(); - + // 刷新物流模型列表(会自动恢复选择状态) if (_logisticsListView != null && !_logisticsListView.IsDisposed) { @@ -3002,7 +3031,7 @@ namespace NavisworksTransport UpdateLogisticsStatsStatic(_statsLabel); LogManager.Info("[UI同步] 统计信息已更新"); } - + // 恢复文档选择状态 if (_savedDocumentSelection != null && _savedDocumentSelection.Count > 0) { @@ -3019,20 +3048,20 @@ namespace NavisworksTransport { // 清除更新标志 _isUpdatingLogisticsData = false; - + // 清除保存的选择状态 _savedDocumentSelection = null; } LogManager.Info($"[UI同步] 物流属性{e.OperationType}事件处理完成"); - + }, "处理物流属性变更事件"); } - + #endregion - + #region PathPlanningManager 事件处理器 - + /// /// 处理路径编辑状态变更事件 /// @@ -3043,7 +3072,7 @@ namespace NavisworksTransport LogManager.Info($"[UI同步] ===== 路径编辑状态变更事件触发 ====="); LogManager.Info($"[UI同步] 新状态: {newState}"); LogManager.Info($"[UI同步] 当前线程: {System.Threading.Thread.CurrentThread.ManagedThreadId}"); - + // 确保在UI线程上执行 if (_controlPanelForm != null && _controlPanelForm.InvokeRequired) { @@ -3051,9 +3080,9 @@ namespace NavisworksTransport _controlPanelForm.Invoke(new Action(() => OnPathEditStateChanged(sender, newState))); return; } - + LogManager.Info("[UI同步] 在UI线程上执行状态更新"); - + // 更新当前路径状态标签 if (_currentPathStatusLabel != null) { @@ -3073,10 +3102,10 @@ namespace NavisworksTransport stateText = "未知状态"; break; } - + LogManager.Info($"[UI同步] 更新状态标签文本: {stateText}"); _currentPathStatusLabel.Text = $"当前状态: {stateText}"; - + System.Drawing.Color color; switch (newState) { @@ -3100,15 +3129,15 @@ namespace NavisworksTransport { LogManager.Warning("[UI同步] _currentPathStatusLabel为null,无法更新状态标签"); } - + // 更新按钮状态 UpdateButtonStates(newState); - + LogManager.Info("[UI同步] ===== 路径编辑状态变更事件处理完成 ====="); - + }, "处理路径编辑状态变更"); } - + /// /// 根据编辑状态更新按钮状态 /// @@ -3122,19 +3151,19 @@ namespace NavisworksTransport { var finishButton = FindButtonByText(_controlPanelForm, "完成编辑"); var cancelButton = FindButtonByText(_controlPanelForm, "取消编辑"); - + bool isEditable = editState == PathEditState.Creating || editState == PathEditState.Editing; - + if (finishButton != null) { finishButton.Enabled = isEditable; } - + if (cancelButton != null) { cancelButton.Enabled = isEditable; } - + LogManager.Info($"[UI同步] 按钮状态已更新,编辑状态: {editState}, 按钮启用: {isEditable}"); } } @@ -3143,7 +3172,7 @@ namespace NavisworksTransport LogManager.Error($"[UI同步] 更新按钮状态失败: {ex.Message}"); } } - + /// /// 根据文本查找按钮控件 /// @@ -3158,7 +3187,7 @@ namespace NavisworksTransport { return button; } - + // 递归查找子控件 var foundInChild = FindButtonByText(control, buttonText); if (foundInChild != null) @@ -3166,10 +3195,10 @@ namespace NavisworksTransport return foundInChild; } } - + return null; } - + /// /// 处理3D路径点添加事件 /// @@ -3178,13 +3207,13 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Info($"[UI同步] 3D路径点已添加: {pathPoint?.Name}"); - + // 更新当前路径点列表 UpdateCurrentPathPointsList(); - + }, "处理3D路径点添加"); } - + /// /// 处理3D路径点删除事件 /// @@ -3193,13 +3222,13 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Info($"[UI同步] 3D路径点已删除: {pathPoint?.Name}"); - + // 更新当前路径点列表 UpdateCurrentPathPointsList(); - + }, "处理3D路径点删除"); } - + /// /// 处理路径点列表更新事件 /// @@ -3208,16 +3237,16 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Info($"[UI同步] 路径点列表已更新: {updatedRoute?.Name ?? "空路径"}"); - + // 更新当前路径点列表 UpdateCurrentPathPointsList(); - + // 更新路径列表 UpdatePathList(); - + }, "处理路径点列表更新"); } - + /// /// 处理当前路径变更事件 /// @@ -3226,16 +3255,16 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Info($"[UI同步] 当前路径已变更: {newRoute?.Name ?? "无路径"}"); - + // 更新当前路径点列表 UpdateCurrentPathPointsList(); - + // 更新路径列表 UpdatePathList(); - + }, "处理当前路径变更"); } - + /// /// 处理路径管理器状态变更事件 /// @@ -3244,12 +3273,12 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Info($"[UI同步] 路径管理器状态: {status}"); - + // 可以在这里更新状态栏或其他UI元素 - + }, "处理路径管理器状态变更"); } - + /// /// 处理路径管理器错误事件 /// @@ -3258,13 +3287,13 @@ namespace NavisworksTransport GlobalExceptionHandler.SafeExecute(() => { LogManager.Error($"[UI同步] 路径管理器错误: {error}"); - + // 显示错误信息 MessageBox.Show($"路径管理器错误: {error}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Warning); - + }, "处理路径管理器错误"); } - + /// /// 更新当前路径点列表UI /// @@ -3276,9 +3305,9 @@ namespace NavisworksTransport return; var currentRoute = _pathPlanningManager.CurrentRoute; - + _currentPathPointsListView.Items.Clear(); - + if (currentRoute?.Points != null && currentRoute.Points.Count > 0) { for (int i = 0; i < currentRoute.Points.Count; i++) @@ -3317,12 +3346,12 @@ namespace NavisworksTransport placeholder.ForeColor = System.Drawing.Color.Gray; _currentPathPointsListView.Items.Add(placeholder); } - + LogManager.Info($"[UI同步] 当前路径点列表已更新,共{currentRoute?.Points?.Count ?? 0}个点"); - + }, "更新当前路径点列表"); } - + /// /// 更新路径列表UI /// @@ -3332,9 +3361,9 @@ namespace NavisworksTransport { if (_pathListView == null || _pathPlanningManager == null) return; - + _pathListView.Items.Clear(); - + var routes = _pathPlanningManager.Routes; if (routes != null && routes.Count > 0) { @@ -3344,16 +3373,16 @@ namespace NavisworksTransport item.SubItems.Add(route.CreatedTime.ToString("MM-dd HH:mm")); item.SubItems.Add(route.Points?.Count.ToString() ?? "0"); item.Tag = route; // 保存路径对象引用 - + _pathListView.Items.Add(item); } } - + LogManager.Info($"[UI同步] 路径列表已更新,共{routes?.Count ?? 0}条路径"); - + }, "更新路径列表"); } - + /// /// 显示路径历史记录对话框 /// @@ -3414,7 +3443,7 @@ namespace NavisworksTransport Size = new Size(80, 30), Font = new Font("微软雅黑", 8) }; - + var exportButton = new Button { Text = "导出历史", @@ -3433,8 +3462,9 @@ namespace NavisworksTransport }; // 事件处理 - clearButton.Click += (sender, e) => { - var result = MessageBox.Show("确定要清空所有历史记录吗?此操作不可撤销。", + clearButton.Click += (sender, e) => + { + var result = MessageBox.Show("确定要清空所有历史记录吗?此操作不可撤销。", "确认清空", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (result == DialogResult.Yes) { @@ -3449,22 +3479,23 @@ namespace NavisworksTransport historyManager.ClearRouteHistory(route.Id); } } - + historyListView.Items.Clear(); - MessageBox.Show("历史记录已清空", "操作完成", + MessageBox.Show("历史记录已清空", "操作完成", MessageBoxButtons.OK, MessageBoxIcon.Information); LogManager.Info("用户清空了路径历史记录"); } catch (Exception ex) { - MessageBox.Show($"清空历史记录失败: {ex.Message}", "错误", + MessageBox.Show($"清空历史记录失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); LogManager.Error($"清空历史记录失败: {ex.Message}"); } } }; - exportButton.Click += (sender, e) => { + exportButton.Click += (sender, e) => + { try { SaveFileDialog saveDialog = new SaveFileDialog @@ -3483,21 +3514,21 @@ namespace NavisworksTransport content.AppendLine($"总记录数: {historyEntries.Count}"); content.AppendLine(); content.AppendLine("操作时间\t路径ID\t操作类型\t描述"); - + foreach (var entry in historyEntries) { content.AppendLine($"{entry.OperationTime:yyyy-MM-dd HH:mm:ss}\t{entry.RouteId}\t{GetOperationTypeName(entry.OperationType)}\t{entry.Description}"); } - + File.WriteAllText(saveDialog.FileName, content.ToString(), Encoding.UTF8); - MessageBox.Show($"历史记录已导出到: {saveDialog.FileName}", "导出成功", + MessageBox.Show($"历史记录已导出到: {saveDialog.FileName}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information); LogManager.Info($"历史记录已导出到: {saveDialog.FileName}"); } } catch (Exception ex) { - MessageBox.Show($"导出历史记录失败: {ex.Message}", "错误", + MessageBox.Show($"导出历史记录失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); LogManager.Error($"导出历史记录失败: {ex.Message}"); } @@ -3509,7 +3540,7 @@ namespace NavisworksTransport historyForm.ShowDialog(_controlPanelForm); } - + /// /// 获取操作类型的显示名称 /// @@ -3535,7 +3566,7 @@ namespace NavisworksTransport return operationType.ToString(); } } - + #endregion } } diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index eb7d87f..61b63fb 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.cs @@ -121,6 +121,8 @@ namespace NavisworksTransport if (lastPoint.Type != PathPointType.StartPoint) // 起点不能是终点 { lastPoint.Type = PathPointType.EndPoint; + // 更新名称以匹配新的类型 + lastPoint.Name = GeneratePointName(PathPointType.EndPoint); LogManager.Info($"已自动设置最后一个点为终点: {lastPoint.Name}"); // 更新3D标记的外观 @@ -3631,18 +3633,18 @@ namespace NavisworksTransport LogManager.WriteLog($"[ToolPlugin事件] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})"); LogManager.WriteLog($"[ToolPlugin事件] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}"); - // 检查是否在选定的通道内 + // 检查是否在可通行的物流模型内 if (_selectedChannels != null && _selectedChannels.Any()) { - bool isInChannel = IsItemInSelectedChannels(pickResult.ModelItem) || + bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) || IsItemChildOfSelectedChannels(pickResult.ModelItem); - LogManager.WriteLog($"[ToolPlugin事件] 在选定通道内: {isInChannel}"); + LogManager.WriteLog($"[ToolPlugin事件] 在可通行的物流模型内: {isInTraversableLogisticsModel}"); - if (isInChannel) + if (isInTraversableLogisticsModel) { // 不再传递点类型,让AddPathPointIn3D方法内部自动判断 - LogManager.WriteLog($"[ToolPlugin事件] 调用AddPathPointIn3D添加路径点"); + LogManager.WriteLog("[ToolPlugin事件] 调用AddPathPointIn3D添加路径点"); var pathPoint = AddPathPointIn3D(pickResult.Point); if (pathPoint != null) @@ -3656,12 +3658,15 @@ namespace NavisworksTransport } else { - LogManager.WriteLog("[ToolPlugin事件] ✗ 点击位置不在选定通道内"); + LogManager.WriteLog("[ToolPlugin事件] ✗ 点击位置不在可通行的物流模型内"); + // 可以考虑给用户一个提示 + OnStatusChanged("点击位置不是可通行的物流模型,请在可通行的物流模型上点击"); } } else { - LogManager.WriteLog("[ToolPlugin事件] ✗ 未选择通道"); + LogManager.WriteLog("[ToolPlugin事件] ✗ 没有可通行的物流模型"); + OnStatusChanged("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性"); } } catch (Exception ex) @@ -3704,29 +3709,41 @@ namespace NavisworksTransport _editingRoute = newRoute; CurrentRoute = newRoute; - // 自动选择所有物流通道 + // 自动选择所有可通行的物流模型 AutoSelectLogisticsChannels(); + // 检查是否有可通行的物流模型 + if (_selectedChannels == null || _selectedChannels.Count == 0) + { + LogManager.Warning("没有找到任何可通行的物流模型"); + OnErrorOccurred("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性"); + // 重置状态 + SwitchToViewingState(); + return null; + } + // 智能管理ToolPlugin状态 ManageToolPluginForEditState(); - // 高亮通道以便用户点击 + // 高亮可通行的物流模型以便用户点击 HighlightLogisticsChannels(); LogManager.Info($"开始新建路径: {newRoute.Name}"); - OnStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中点击设置路径点"); + OnStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中可通行的物流模型上点击设置路径点"); return newRoute; } catch (Exception ex) { OnErrorOccurred($"开始新建路径失败: {ex.Message}"); + // 确保在出错时也重置状态 + SwitchToViewingState(); return null; } } /// - /// 自动选择所有物流通道 + /// 自动选择所有可通行的物流模型 /// private void AutoSelectLogisticsChannels() { @@ -3738,17 +3755,27 @@ namespace NavisworksTransport _selectedChannels.Clear(); // 收集所有物流标记的模型 + var allLogisticsItems = new ModelItemCollection(); foreach (ModelItem rootItem in document.Models.RootItems) { foreach (ModelItem item in rootItem.DescendantsAndSelf) { if (CategoryAttributeManager.HasLogisticsAttributes(item)) { - _selectedChannels.Add(item); + allLogisticsItems.Add(item); } } } + // 筛选出可通行的物流模型 + var traversableItems = CategoryAttributeManager.FilterTraversableItems(allLogisticsItems); + + // 将可通行的物流模型添加到_selectedChannels + foreach (ModelItem item in traversableItems) + { + _selectedChannels.Add(item); + } + if (_selectedChannels.Count > 0) { // 计算组合边界 @@ -3757,54 +3784,50 @@ namespace NavisworksTransport // 触发事件 ChannelsSelected?.Invoke(this, _selectedChannels); - LogManager.Info($"自动选择了 {_selectedChannels.Count} 个物流通道"); + LogManager.Info($"自动选择了 {_selectedChannels.Count} 个可通行的物流模型"); } else { - LogManager.Warning("未找到任何物流通道"); + LogManager.Warning("未找到任何可通行的物流模型"); } } catch (Exception ex) { - LogManager.WriteLog($"自动选择物流通道失败: {ex.Message}"); + LogManager.WriteLog($"自动选择可通行物流模型失败: {ex.Message}"); } } /// - /// 高亮所有物流通道 + /// 高亮所有可通行的物流模型 /// private void HighlightLogisticsChannels() { try { - var document = Application.ActiveDocument; - if (document?.Models == null) return; + // 直接使用已选择的可通行物流模型(_selectedChannels) + // _selectedChannels在AutoSelectLogisticsChannels()中已经被填充 - var logisticsChannels = new ModelItemCollection(); - - // 收集所有物流标记的模型 - foreach (ModelItem rootItem in document.Models.RootItems) + if (_selectedChannels != null && _selectedChannels.Count > 0) { - foreach (ModelItem item in rootItem.DescendantsAndSelf) + var logisticsChannels = new ModelItemCollection(); + foreach (var item in _selectedChannels) { - if (CategoryAttributeManager.HasLogisticsAttributes(item)) - { - logisticsChannels.Add(item); - } + logisticsChannels.Add(item); } - } - - // 高亮显示 - if (logisticsChannels.Count > 0) - { + + // 高亮显示 // 改为淡青色,避免与黄色连线冲突 HighlightChannels(logisticsChannels, System.Drawing.Color.Cyan); - LogManager.Info($"高亮显示 {logisticsChannels.Count} 个物流通道为青色"); + LogManager.Info($"高亮显示 {_selectedChannels.Count} 个可通行的物流模型为青色"); + } + else + { + LogManager.Info("没有可通行的物流模型需要高亮显示"); } } catch (Exception ex) { - LogManager.WriteLog($"高亮物流通道失败: {ex.Message}"); + LogManager.WriteLog($"高亮可通行物流模型失败: {ex.Message}"); } } diff --git a/src/Core/Properties/CategoryAttributeManager.cs b/src/Core/Properties/CategoryAttributeManager.cs index 9113555..13abeec 100644 --- a/src/Core/Properties/CategoryAttributeManager.cs +++ b/src/Core/Properties/CategoryAttributeManager.cs @@ -31,6 +31,7 @@ namespace NavisworksTransport public const string PRIORITY = "优先级"; public const string VEHICLE_SIZE = "适用车辆尺寸"; public const string SPEED_LIMIT = "速度限制"; + public const string WIDTH_LIMIT = "宽度限制"; } /// @@ -57,6 +58,7 @@ namespace NavisworksTransport /// 优先级(1-10) /// 适用车辆尺寸 /// 速度限制(km/h) + /// 宽度限制(米) /// 成功处理的项目数量 public static int AddLogisticsAttributes( ModelItemCollection items, @@ -64,7 +66,8 @@ namespace NavisworksTransport bool isTraversable = true, int priority = 5, string vehicleSize = "标准", - double speedLimit = 10.0) + double speedLimit = 10.0, + double widthLimit = 3.0) { if (items == null || items.Count == 0) { @@ -118,6 +121,10 @@ namespace NavisworksTransport AddProperty(state, propertyCategory, LogisticsProperties.SPEED_LIMIT, speedLimit.ToString("F1") + " km/h", "SpeedLimit_Internal"); + // 创建并添加宽度限制属性 + AddProperty(state, propertyCategory, LogisticsProperties.WIDTH_LIMIT, + widthLimit.ToString("F1") + " m", "WidthLimit_Internal"); + if (existingIndex >= 0) { // 如果存在现有属性,使用覆盖模式 (参数1) diff --git a/src/Core/TestPlugin.cs b/src/Core/TestPlugin.cs new file mode 100644 index 0000000..31e854f --- /dev/null +++ b/src/Core/TestPlugin.cs @@ -0,0 +1,30 @@ +using System.Windows.Forms; +using Autodesk.Navisworks.Api.Plugins; + +namespace NavisworksTransport +{ + [Plugin("NavisworksTransport.Test", "Tian", + DisplayName = "Test Plugin", + ToolTip = "Simple test plugin")] + [DockPanePlugin(300, 200)] + public class TestPlugin : DockPanePlugin + { + public override Control CreateControlPane() + { + var label = new Label + { + Text = "Test Plugin Loaded Successfully!", + Dock = DockStyle.Fill, + TextAlign = System.Drawing.ContentAlignment.MiddleCenter, + BackColor = System.Drawing.Color.LightGreen + }; + + return label; + } + + public override void DestroyControlPane(Control pane) + { + pane?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Resources/NavisworksTransport.Tian.name.txt b/src/Resources/NavisworksTransport.Tian.name.txt new file mode 100644 index 0000000..feda63c --- /dev/null +++ b/src/Resources/NavisworksTransport.Tian.name.txt @@ -0,0 +1,29 @@ +# Navisworks Transport Plugin Localization File +# This file contains localized strings for the plugin + +# Plugin Display Names +HelloWorldText=物流路径规划插件已加载 +PluginDisplayName=物流路径规划 +PluginToolTip=物流路径规划和动画仿真插件 + +# Tab Names +ModelSettingsTab=类别设置 +PathEditingTab=路径编辑 +AnimationControlTab=检测动画 +SystemManagementTab=系统管理 + +# Button Labels +ShowAllButton=显示全部 +HideAllButton=隐藏全部 +RefreshButton=刷新 +NewPathButton=新建路径 +StartEditButton=开始编辑 +StartAnimationButton=开始动画 +HelpButton=帮助 +AboutButton=关于 + +# Status Messages +InitializingPlugin=正在初始化插件... +PluginReady=插件就绪 +SelectModelsInstruction=请在主界面中点击选择模型 +OpenModelInstruction=请先打开一个Navisworks模型文件 \ No newline at end of file diff --git a/src/UI/Forms/SimpleDockPaneControl.cs b/src/UI/Forms/SimpleDockPaneControl.cs new file mode 100644 index 0000000..b3efe24 --- /dev/null +++ b/src/UI/Forms/SimpleDockPaneControl.cs @@ -0,0 +1,205 @@ +using System; +using System.Drawing; +using System.Windows.Forms; +using Autodesk.Navisworks.Api; +using NavisApplication = Autodesk.Navisworks.Api.Application; + +namespace NavisworksTransport.UI.Forms +{ + /// + /// 简单的DockPane控件,用于验证DockPanePlugin基础功能 + /// + public partial class SimpleDockPaneControl : UserControl + { + private Label _statusLabel; + private Button _testButton; + private ListView _infoListView; + + public SimpleDockPaneControl() + { + InitializeComponent(); + InitializeSession(); + } + + private void InitializeComponent() + { + this.SuspendLayout(); + + // 设置控件基本属性 + this.Size = new Size(400, 600); + this.BackColor = SystemColors.Control; + this.Padding = new Padding(10); + + // 创建状态标签 + _statusLabel = new Label + { + Text = "物流路径规划插件 - DockPane模式", + Font = new Font("微软雅黑", 10, FontStyle.Bold), + ForeColor = System.Drawing.Color.DarkBlue, + AutoSize = true, + Location = new Point(10, 10) + }; + this.Controls.Add(_statusLabel); + + // 创建测试按钮 + _testButton = new Button + { + Text = "测试功能", + Size = new Size(100, 30), + Location = new Point(10, 40), + Font = new Font("微软雅黑", 9) + }; + _testButton.Click += OnTestButtonClick; + this.Controls.Add(_testButton); + + // 创建信息列表 + _infoListView = new ListView + { + View = System.Windows.Forms.View.Details, + FullRowSelect = true, + GridLines = true, + Location = new Point(10, 80), + Size = new Size(380, 500), + Font = new Font("微软雅黑", 8) + }; + + // 添加列 + _infoListView.Columns.Add("时间", 120); + _infoListView.Columns.Add("事件", 100); + _infoListView.Columns.Add("描述", 160); + + this.Controls.Add(_infoListView); + + this.ResumeLayout(false); + } + + private void InitializeSession() + { + GlobalExceptionHandler.SafeExecute(() => + { + LogManager.Info("=== SimpleDockPaneControl 初始化开始 ==="); + + // 初始化全局异常处理 + GlobalExceptionHandler.Initialize(); + + // 订阅选择变化事件 + if (NavisApplication.ActiveDocument != null) + { + NavisApplication.ActiveDocument.CurrentSelection.Changed += OnSelectionChanged; + } + + // 添加初始化信息 + AddInfoItem("初始化", "插件已加载"); + + LogManager.Info("SimpleDockPaneControl 初始化完成"); + + }, "初始化SimpleDockPaneControl"); + } + + private void OnTestButtonClick(object sender, EventArgs e) + { + GlobalExceptionHandler.SafeExecute(() => + { + LogManager.Info("测试按钮被点击"); + + // 获取当前文档信息 + var doc = NavisApplication.ActiveDocument; + if (doc != null) + { + var modelCount = doc.Models?.Count ?? 0; + var selectedCount = doc.CurrentSelection?.SelectedItems?.Count ?? 0; + + AddInfoItem("测试", $"模型数量: {modelCount}, 选中: {selectedCount}"); + + MessageBox.Show($"当前文档信息:\n模型数量: {modelCount}\n选中项目: {selectedCount}", + "测试结果", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + AddInfoItem("测试", "没有打开的文档"); + MessageBox.Show("请先打开一个Navisworks文档", + "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + }, "测试按钮点击"); + } + + private void OnSelectionChanged(object sender, EventArgs e) + { + if (this.InvokeRequired) + { + this.BeginInvoke(new Action(() => OnSelectionChanged(sender, e))); + return; + } + + try + { + var selectedCount = NavisApplication.ActiveDocument?.CurrentSelection?.SelectedItems?.Count ?? 0; + AddInfoItem("选择变更", $"选中 {selectedCount} 个项目"); + } + catch (Exception ex) + { + LogManager.Error($"处理选择变更事件失败: {ex.Message}"); + } + } + + private void AddInfoItem(string eventType, string description) + { + try + { + if (this.InvokeRequired) + { + this.BeginInvoke(new Action(() => AddInfoItem(eventType, description))); + return; + } + + var item = new ListViewItem(DateTime.Now.ToString("HH:mm:ss")); + item.SubItems.Add(eventType); + item.SubItems.Add(description); + + _infoListView.Items.Insert(0, item); + + // 保持最多50条记录 + while (_infoListView.Items.Count > 50) + { + _infoListView.Items.RemoveAt(_infoListView.Items.Count - 1); + } + } + catch (Exception ex) + { + LogManager.Error($"添加信息项失败: {ex.Message}"); + } + } + + public void Cleanup() + { + try + { + LogManager.Info("开始清理SimpleDockPaneControl资源"); + + // 取消事件订阅 + if (NavisApplication.ActiveDocument != null) + { + NavisApplication.ActiveDocument.CurrentSelection.Changed -= OnSelectionChanged; + } + + AddInfoItem("清理", "资源已清理"); + + LogManager.Info("SimpleDockPaneControl资源清理完成"); + } + catch (Exception ex) + { + LogManager.Error($"清理SimpleDockPaneControl资源失败: {ex.Message}"); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Cleanup(); + } + base.Dispose(disposing); + } + } +} \ No newline at end of file diff --git a/src/UI/WPF/Commands/RelayCommand.cs b/src/UI/WPF/Commands/RelayCommand.cs new file mode 100644 index 0000000..efa6497 --- /dev/null +++ b/src/UI/WPF/Commands/RelayCommand.cs @@ -0,0 +1,102 @@ +using System; +using System.Windows.Input; + +namespace NavisworksTransport.UI.WPF.ViewModels +{ + /// + /// 通用命令实现,用于MVVM模式 + /// + public class RelayCommand : ICommand + { + private readonly Action _execute; + private readonly Func _canExecute; + + /// + /// 构造函数 + /// + /// 执行的操作 + /// 是否可以执行的判断 + public RelayCommand(Action execute, Func canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + + /// + /// 判断命令是否可以执行 + /// + /// 命令参数 + /// 是否可以执行 + public bool CanExecute(object parameter) + { + return _canExecute?.Invoke() ?? true; + } + + /// + /// 执行命令 + /// + /// 命令参数 + public void Execute(object parameter) + { + _execute(); + } + + /// + /// 可执行状态变更事件 + /// + public event EventHandler CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + } + + /// + /// 带参数的通用命令实现 + /// + /// 参数类型 + public class RelayCommand : ICommand + { + private readonly Action _execute; + private readonly Func _canExecute; + + /// + /// 构造函数 + /// + /// 执行的操作 + /// 是否可以执行的判断 + public RelayCommand(Action execute, Func canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + + /// + /// 判断命令是否可以执行 + /// + /// 命令参数 + /// 是否可以执行 + public bool CanExecute(object parameter) + { + return _canExecute?.Invoke((T)parameter) ?? true; + } + + /// + /// 执行命令 + /// + /// 命令参数 + public void Execute(object parameter) + { + _execute((T)parameter); + } + + /// + /// 可执行状态变更事件 + /// + public event EventHandler CanExecuteChanged + { + add { CommandManager.RequerySuggested += value; } + remove { CommandManager.RequerySuggested -= value; } + } + } +} \ No newline at end of file diff --git a/src/UI/WPF/LogisticsControlPanel.xaml b/src/UI/WPF/LogisticsControlPanel.xaml new file mode 100644 index 0000000..d0bb4c1 --- /dev/null +++ b/src/UI/WPF/LogisticsControlPanel.xaml @@ -0,0 +1,217 @@ + + + + + + + + + + + + +