修改程序关闭崩溃的bug

This commit is contained in:
tian 2025-09-02 18:49:28 +08:00
parent 3c1458245c
commit c40e1219a7
10 changed files with 392 additions and 556 deletions

View File

@ -95,6 +95,214 @@
4) [截图占位分层管理_策略与深度.png] [截图占位分层管理_预览列表.png] [截图占位分层管理_视图验证.png] [截图占位分层管理_保存与选项.png]
3.3.3.2. 通道选择路径点规划
3.3.3.1.2. 通道选择
3.3.3.1.2.1. 功能要求
- 满足用户需求文档"通道选择":支持选择通道模型功能,可通过选择树或三维视图点选的方式选择模型并制定为通道类型;
- 支持批量选择多个模型元素,统一设置为通道类型;
- 与物流属性系统集成,自动设置通道的物流分类属性(如可通行性、限宽限高等);
- 提供清晰的选择状态反馈,显示当前选中的通道模型数量和名称;
- 支持通道模型的筛选、高亮显示和快速定位功能;
- 支持通道选择的撤销和重新设置操作。
3.3.3.1.2.2. 实现方案
- 设计思路:采用"选择-设置-验证"的交互模式,确保通道选择的准确性和易用性;
- 业务流程:
1) 用户通过选择树或三维视图点选模型元素支持单选、框选、Ctrl多选
2) 系统实时显示当前选中的模型数量和基本信息;
3) 用户点击"设置为通道",系统批量设置物流分类为"通道"
4) 自动应用通道默认属性(可通行=true优先级=3推荐限速=0.8m/s
5) 提供"高亮显示通道"功能验证设置结果;
6) 支持"清除通道设置"进行撤销操作;
- 集成策略:与"类别设置"模块深度集成,通道选择后自动触发物流属性设置;
- 性能优化:大批量选择时提供进度指示,支持取消操作防止界面卡顿。
3.3.3.1.2.3. 输入数据(详细规格)
- 选择对象(必需):
- 类型ModelItem[]来源选择树NodeSelection或三维视图RaycastHit
- 约束数量≥1模型元素可读且未损坏
- 选择方式(必需):
- 类型enum{SelectionTree, 3DView, Mixed}默认Mixed
- 说明:支持选择树点选、三维视图框选、组合选择;
- 通道属性配置(可选):
- 物流类型string默认="通道"
- 可通行性bool默认=true
- 优先级int(1-5),默认=3
- 推荐限宽double(m),默认=3.0范围0.5-10.0
- 推荐限高double(m),默认=3.0范围1.5-10.0
- 推荐限速double(m/s),默认=0.8范围0.1-2.0
- 批处理选项(可选):
- 覆盖现有属性bool默认=false
- 忽略错误继续bool默认=true
3.3.3.1.2.4. 输出数据(详细规格)
- 通道列表(内存):
- 字段objectId:stringobjectName:string(≤128)isChannel:boolchannelType:string(≤64)properties:object
- 用途:界面显示和后续路径规划使用;
- 设置结果(内存):
- 字段successCount:intfailCount:inttotalCount:intfailReasons:string[]
- 用途:操作结果反馈和错误处理;
- 持久化属性(平台存储):
- 位置Navisworks模型属性系统自定义分类"物流属性"
- 字段LogisticsCategory="通道"IsTraversable=truePriority=3WidthLimitHeightLimitSpeedLimit
- 编码UTF-8可被后续模块读取和筛选
- 操作日志(磁盘):
- 字段timestampuseraction="setChannel"objectIds:string[]properties:jsonresult:string
- 格式JSON行格式编码=UTF-8
3.3.3.1.2.5. 功能界面(界面元素详解与操作流程)
- 选择状态显示SelectedModelsTextTextBlock
- 作用:实时显示当前选中的模型数量和简要信息。
- 使用:自动更新,格式如"已选择 15 个模型项通道_01, 走廊_A02..."。
- 选择模式切换SelectionModeOptionsRadioButton组
- 作用:选择"选择树模式"或"三维视图模式"或"组合模式"。
- 使用:影响后续点选行为和快捷键响应。
- 设置为通道SetAsChannelCommandButton
- 作用:将选中模型批量设置为通道类型并应用默认属性。
- 使用:需要先选择模型;设置后在状态栏显示成功/失败数量。
- 启用条件HasSelectedItems && IsNotProcessing。
- 通道属性配置面板ChannelPropertiesGroupGroupBox
- 作用:设置通道的默认物流属性(限宽、限高、限速、优先级)。
- 使用:在"设置为通道"前配置;影响批量设置的默认值。
- 高亮显示通道HighlightChannelsCommandButton
- 作用:在三维视图中高亮显示所有已设置为通道的模型。
- 使用:用于验证通道设置结果;可与"仅显示通道"配合使用。
- 清除通道设置ClearChannelCommandButton
- 作用:清除选中模型的通道属性,恢复为普通模型。
- 使用:支持批量撤销;需要确认操作。
- 通道筛选器ChannelFilterOptionsComboBox + CheckBox
- 作用:按通道类型、限制属性等条件筛选显示。
- 使用:支持"显示所有"、"仅显示通道"、"按限宽筛选"等选项。
- 进度指示器ProgressIndicatorProgressBar + StatusText
- 作用:大批量操作时显示处理进度和当前状态。
- 使用:自动显示/隐藏;支持取消长耗时操作。
- 操作流程(截图占位):
1) 切换选择模式→在界面中选择模型→查看选择状态 → [截图占位通道选择_选择模式.png]
2) 配置通道属性→点击"设置为通道"→查看设置结果 → [截图占位通道选择_属性配置.png]
3) 使用"高亮显示通道"验证→必要时使用筛选器聚焦 → [截图占位通道选择_结果验证.png]
3.3.3.1.3. 路径点规划
3.3.3.1.3.1. 功能要求
- 依据用户需求"路径点规划":针对较为复杂的环境,支持路径点功能,在三维视图中,在通道上点击指定起点、路径点、终点的位置及方向;
- 以三维可视化的方式显示路径,路径点之间通过直线连接形成完整路径;
- 支持多条路径的创建、保存、选择和编辑管理;
- 支持路径点的位置和方向调整,包括手动拖拽和精确数值输入;
- 提供路径有效性验证,确保路径点位于可通行的通道表面;
- 支持路径点的类型标识(起点/路径点/终点)和自定义命名;
- 提供路径预览和实时编辑反馈,支持撤销/重做操作。
3.3.3.1.3.2. 实现方案
- 设计思路:采用"创建-编辑-验证-保存"的路径管理流程,结合三维交互和属性面板的双重编辑模式;
- 业务流程:
1) 新建路径→进入路径编辑模式,激活三维交互工具;
2) 在三维视图中依次点击通道表面,创建起点、路径点、终点;
3) 系统实时显示路径连线和点位标识,提供可视化反馈;
4) 支持选中路径点进行位置调整、方向设置和类型修改;
5) 提供路径有效性检查,标识问题点位和建议修正方案;
6) 完成编辑后保存路径,加入路径管理列表;
7) 支持路径的重新编辑、复制、删除和导出操作;
- 交互设计结合PathClickToolPlugin的3D交互能力和路径编辑面板的精确控制
- 数据管理路径数据采用JSON格式存储支持版本控制和历史记录
- 可视化渲染使用PathPointRenderPlugin在3D视图中实时渲染路径线和标识点。
3.3.3.1.3.3. 输入数据(详细规格)
- 路径基本信息(必需):
- 路径名称string(1-64),唯一标识;默认格式="路径_{序号}_{时间戳}"
- 路径描述string(≤256),可选;用于备注和说明;
- 三维交互输入(必需):
- 点击事件RaycastHit包含世界坐标(x,y,z)、法向量、命中对象;
- 坐标系Navisworks世界坐标系单位=米精度≤0.001m
- 路径点属性(必需):
- 点类型enum{Start, Waypoint, End}每条路径Start和End各唯一
- 位置坐标Point3D{x,y,z:double(m)}范围±1e6米
- 方向朝向Vector3D{yaw,pitch,roll:double(度)},可选;范围-180~+180
- 点名称string(≤32),可选;默认按类型和序号生成;
- 通道验证数据(可选但建议):
- 通道模型集合ModelItem[],来源于通道选择功能的结果;
- 可通行性检查基于IsTraversable属性和几何约束
- 路径配置参数(可选):
- 自动贴合bool默认=true是否自动贴合到通道表面
- 贴合容差double(m),默认=0.05范围0.01-0.5
- 最小点间距double(m),默认=0.5;防止点位过密;
3.3.3.1.3.4. 输出数据(详细规格)
- 路径对象(内存):
- 基本信息routeId:stringrouteName:string(≤64)description:string(≤256)createdAt:datetime(ISO 8601)modifiedAt:datetime
- 路径点集合points[]{index:intname:string(≤32)type:enumposition:Point3Dorientation:Vector3D(可选)}
- 状态标识status:enum{Draft,Editing,Completed,Validated}isValid:bool
- 可视化数据(渲染层):
- 路径线段LineSegment[]用于3D视图中的路径连线显示
- 点位标识PointMarker[],包含类型图标、名称标签、选中状态;
- 渲染属性lineColor:ColorlineWidth:doublemarkerSize:double
- 路径文件(磁盘):
- 格式JSON编码=UTF-8扩展名=.json
- 内容:完整路径对象序列化,包含版本号和元数据;
- 命名规范:{项目名}_{路径名}_{yyyyMMdd_HHmm}.json
- 验证报告(内存):
- 有效性检查结果isValid:boolissues[]{type:stringpoint:intmessage:stringseverity:enum}
- 路径统计信息totalLength:double(m)pointCount:intsegmentCount:intestimatedTime:double(s)
3.3.3.1.3.5. 功能界面(界面元素详解与操作流程)
- 路径管理面板PathManagementPanelGroupBox
- 作用:显示当前项目的所有路径列表,支持选择、创建、删除操作。
- 使用:列表显示路径名称、点数、状态、修改时间;单击选中,双击编辑。
- 新建路径CreateNewPathCommandButton
- 作用:创建新的空路径对象并进入编辑模式。
- 使用:弹出命名对话框→创建路径→自动激活编辑工具。
- 流程创建后立即可在3D视图中点击添加路径点。
- 编辑模式切换EditModeToggleToggleButton
- 作用:激活/关闭路径点编辑模式控制3D交互工具状态。
- 使用开启时可在3D视图点击添加路径点关闭时恢复正常视图操作。
- 状态:显示当前是否处于编辑状态和活动路径名称。
- 三维交互区域3D Viewport Integration
- 作用在Navisworks 3D视图中直接点击创建路径点。
- 使用:编辑模式下,左键点击通道表面创建点位;右键完成当前段;
- 反馈:实时显示路径连线、点位标识和鼠标悬停提示。
- 路径点列表PathPointsListListView + GridView
- 列:序号、名称、类型、坐标(X,Y,Z)、方向、操作(编辑/删除);
- 作用:显示当前编辑路径的所有路径点,支持逐个编辑和调整。
- 使用单击选中点位3D视图中高亮双击编辑属性拖拽调整顺序。
- 路径点属性编辑器PointPropertiesEditorPropertyGrid
- 作用:精确编辑选中路径点的坐标、方向、名称等属性。
- 使用:选中路径点后自动加载属性;支持数值输入和下拉选择;
- 验证:输入时实时检查坐标范围和命名冲突。
- 路径有效性检查PathValidationPanelStatusPanel
- 作用:显示当前路径的有效性状态和问题诊断。
- 使用:自动检查路径连通性、点位合法性、通道覆盖等;
- 反馈:显示警告图标、问题描述和建议修正方案。
- 路径可视化控制VisualizationControlsToolBar
- 显示/隐藏路径线CheckBox控制路径连线的显示状态
- 显示/隐藏点标识CheckBox控制路径点标记的显示状态
- 路径颜色设置ColorPicker自定义当前路径的显示颜色
- 视图聚焦Button自动调整视角聚焦当前路径。
- 路径操作工具栏PathOperationsToolBarToolBar
- 保存路径Button保存当前路径到文件和内存
- 另存为Button复制当前路径并重命名保存
- 导入路径Button从文件导入路径数据
- 导出路径Button导出当前路径为JSON/XML/CSV格式
- 删除路径Button删除选中路径需确认
- 操作历史管理UndoRedoManagerButtonGroup
- 撤销Button撤销最后一次路径点操作
- 重做Button重做已撤销的操作
- 清空路径Button清除当前路径的所有点位
- 重新开始Button重置路径编辑状态。
- 操作流程(截图占位):
1) 点击"新建路径"→输入路径名称→激活编辑模式 → [截图占位路径点规划_创建路径.png]
2) 在3D视图中依次点击通道表面创建路径点→查看路径点列表 → [截图占位路径点规划_点击创建.png]
3) 选择路径点→在属性编辑器中调整坐标和方向→验证路径有效性 → [截图占位路径点规划_属性编辑.png]
4) 使用可视化控制调整显示效果→保存路径到管理列表 → [截图占位路径点规划_保存管理.png]
3.3.3.2.1. 编辑保存和导入
3.3.3.2.1.1. 功能要求

View File

@ -0,0 +1,100 @@
# 动画碰撞报告计数逐次递增问题分析与建议(归档)
更新时间2025-09-02
状态已定位问题根因建议采取“生成侧生命周期控制方案B暂不改代码仅归档待实施。
## 1. 问题概述
在“碰撞报告”中,“动画测试”的数字会随着每次动画运行不断增加,而其他(权威/静态碰撞数字保持不变。这与用户观察一致Clash Detective 的既有测试结果不变,唯独“动画相关”的计数递增。
## 2. 复现与现象
- 连续播放同一动画多次后,“碰撞报告”中“动画测试数量”与“动画相关碰撞汇总”呈单调递增。
- 打开 Clash Detective 面板,可看到多组名称包含“动画路径碰撞”或“动画碰撞”的测试,越跑越多。
- 若在一次或多次“停止(未完成)→再次开始→完成”的使用路径下,最终一次完成后计数更大。
## 3. 根因分析(直接原因)
1) 报告统计口径累积(历史所有动画测试合并统计)
- 文件src/Commands/ViewCollisionReportCommand.cs
- 方法CollectAllCollisionData()
- 逻辑:把 DisplayName 包含“动画路径碰撞”的所有测试计入统计(历史全量),而不是仅统计“本次运行”。因此报告端数字随历史累加而递增。
2) 生成侧每次动画结束都会“新增”一批动画测试,未清理旧测试
- 文件src/Core/Collision/ClashDetectiveIntegration.cs
- 方法CreateAllAnimationCollisionTests(...)
- 逻辑去重后的碰撞对逐一创建新测试测试命名如“动画路径碰撞_{编号}_{时间}”。旧测试不清理,文档内逐轮叠加;报告端再合并统计,自然越来越多。
3) 辅助触发(条件):停止不清缓存导致跨次累计
- 文件src/Core/Animation/PathAnimationManager.cs
- StopAnimation():只停定时器,不清 ClashDetectiveIntegration 的 _cachedResults 等运行期缓存。
- FinishAnimation():完成时才调用 CreateAllAnimationCollisionTests(...) 并最终 _cachedResults.Clear()。
- 若多次“停止→重播→完成”,最后一次会把之前若干次积累的缓存一并写入测试,放大一次性计数。
4) 次要风险:动态碰撞事件重复订阅
- 文件src/Core/Animation/PathAnimationManager.cs
- 方法SetupDynamicClashDetection()
- 每次 StartAnimation 都执行 `CollisionDetected += ...`,未见对称 `-=` 解绑。会造成事件回调倍增,放大 UI “发现N个碰撞”的提示频率与日志量虽对最终权威计数影响有限但体验上加剧“变多”的观感
## 4. 影响范围
- 仅影响“动画相关”的碰撞测试与报告统计;既有的固定/权威测试不受影响(名称筛选不同)。
- 主要是展示与统计口径问题,非算法错误;静态 Clash Detective 的数量维持不变。
## 5. 临时规避建议(无需改代码)
- 每次生成报告前,手动删除 Clash Detective 列表中名称包含“动画路径碰撞/动画碰撞”的历史测试,只保留本次运行生成的测试,再生成报告。
## 6. 解决思路选择B生成侧生命周期控制
目标:每次动画“开始前”清理历史动画测试与运行期缓存,确保“碰撞报告”只统计“本次运行”生成的动画测试。
建议在不改变对外功能的前提下,最小增量实现:
A) 在 ClashDetectiveIntegration 中新增两个方法(仅归档待实现)
- RemoveAnimationGeneratedTests(params string[] nameKeywords)
- 遍历 `_documentClash.TestsData.Tests`,删除 DisplayName 含任一关键字(如“动画路径碰撞”“动画碰撞”)的测试。
- ClearAnimationRuntimeCache()
- 清空 `_cachedResults`、`_currentCollisions`、`_animationCollisionCount` 等“动画运行期”缓存(不动 `_documentClash/_dynamicClashTest` 等重对象)。
B) 在 PathAnimationManager.StartAnimation() 的最前面调用上述清理
- 开播前执行:
- `ClashDetectiveIntegration.Instance.RemoveAnimationGeneratedTests("动画路径碰撞", "动画碰撞");`
- `ClashDetectiveIntegration.Instance.ClearAnimationRuntimeCache();`
- 确保每次新一轮动画从“干净环境”起步。
C) 事件订阅去重与解绑(易实施,强烈建议纳入)
- 在 PathAnimationManager 中增加 `_isClashSubscribed` 标志,避免重复 `+=`;在 StopAnimation()/Dispose 中成对 `-=` 解绑。
D) 可选增强(非必须,后续考虑)
- 为本次运行引入 `runId` 或统一前缀如“动画路径碰撞_ACTIVE”以覆盖而非追加或仅保留最近 N 轮测试,按需清理历史。
## 7. 验证清单(实施后建议回归)
- 连续完整播放同一动画两次,报告中的“动画测试数量/碰撞总数”应一致,不随历史递增。
- 执行一次“停止→再开始→完成”,最终结果不应包含停止阶段产生的历史缓存。
- 日志中在 StartAnimation 之前应出现“已清理历史动画测试/已清理动画运行期缓存”的记录。
- 检查回调是否重复:多次 Start/Stop 后,碰撞事件回调触发次数不应递增。
## 8. 风险与注意事项
- 清理逻辑只应作用于“动画生成的测试”,避免误删用户手工维护的权威测试。通过名称关键字白名单控制(例如:仅匹配“动画路径碰撞”“动画碰撞”前缀)。
- 如果项目需要保留历史动画测试用于比对应在实现中提供开关或“保留最近N轮”的策略。
- 清缓存应仅限运行期缓存,不应重置文档级对象或用户手工结果。
## 9. 相关代码位置(便于后续实现/评审)
- 报告端统计:
- src/Commands/ViewCollisionReportCommand.cs → CollectAllCollisionData()(基于 DisplayName 包含“动画路径碰撞”的测试统计)
- 生成端创建动画测试:
- src/Core/Collision/ClashDetectiveIntegration.cs → CreateAllAnimationCollisionTests(...)
- 动画控制与订阅:
- src/Core/Animation/PathAnimationManager.cs → StartAnimation()/StopAnimation()/SetupDynamicClashDetection()
## 10. 结论
问题本质是“统计口径=历史累积”叠加“生成侧每轮追加不清理”。采用方案B生成侧生命周期控制在每次开播前清理“动画生成的测试”和“运行期缓存”并完善事件订阅的生命周期管理可使“碰撞报告”的动画测试数字与本轮真实结果一致不再随历史递增。

View File

@ -1031,25 +1031,9 @@ namespace NavisworksTransport.Core.Animation
LogManager.Warning($"停止动画时出现警告: {stopEx.Message},继续清理其他资源");
}
// 2. 尝试重置动画如果Navisworks对象仍然可用
// ResetAnimation内部已经有状态检查不会重复停止动画
try
{
// 检查Navisworks应用程序是否仍然可用只有在可用时才进行重置
var activeDoc = NavisApplication.ActiveDocument;
if (activeDoc != null && activeDoc.Models != null)
{
ResetAnimation();
}
else
{
LogManager.Info("Navisworks文档已不可用跳过动画重置操作");
}
}
catch (Exception resetEx)
{
LogManager.Warning($"重置动画时出现警告: {resetEx.Message},继续清理其他资源");
}
// 2. 在Dispose中不进行重置操作避免访问已释放的对象
// 程序关闭时,对象位置重置是不必要的
LogManager.Info("跳过动画重置操作(程序关闭时不需要恢复对象位置)");
// 3. 清理 TimeLiner 资源
if (_timeLinerManager != null)

View File

@ -1,513 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NavisApplication = Autodesk.Navisworks.Api.Application;
namespace NavisworksTransport
{
/// <summary>
/// Clash Detective 集成测试管理器
/// 用于测试和验证碰撞检测功能
/// </summary>
public class ClashDetectiveIntegrationTest
{
/// <summary>
/// 测试结果数据结构
/// </summary>
public class TestResult
{
public bool IsSuccess { get; set; }
public string TestName { get; set; }
public string Message { get; set; }
public TimeSpan ExecutionTime { get; set; }
public int CollisionCount { get; set; }
public Exception Exception { get; set; }
}
/// <summary>
/// 执行完整的集成测试
/// </summary>
/// <returns>测试结果列表</returns>
public static List<TestResult> RunFullIntegrationTest()
{
var results = new List<TestResult>();
LogManager.Info("开始执行 Clash Detective 集成测试...");
// 测试1: 初始化集成
results.Add(TestInitialization());
// 测试2: 插件检测
results.Add(TestPluginDetection());
// 测试3: 简单碰撞检测
results.Add(TestSimpleCollisionDetection());
// 测试4: 动态碰撞检测
results.Add(TestDynamicCollisionDetection());
// 测试5: 高亮显示功能
results.Add(TestHighlightingFeature());
// 测试6: 窗口同步功能
results.Add(TestWindowSynchronization());
// 测试7: 清理功能
results.Add(TestCleanupFunction());
// 输出测试报告
GenerateTestReport(results);
return results;
}
/// <summary>
/// 测试初始化功能
/// </summary>
private static TestResult TestInitialization()
{
var result = new TestResult { TestName = "初始化集成测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 测试集成初始化
var instance = ClashDetectiveIntegration.Instance;
instance.Initialize();
result.IsSuccess = true;
result.Message = "集成初始化成功";
LogManager.Info("[测试] 初始化集成测试 - 通过");
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"初始化失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 初始化集成测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 测试插件检测功能
/// </summary>
private static TestResult TestPluginDetection()
{
var result = new TestResult { TestName = "插件检测测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 检查COM API状态
var state = Autodesk.Navisworks.Api.ComApi.ComApiBridge.State;
if (state == null)
{
throw new Exception("COM API 状态为空");
}
// 检查插件数量
var plugins = state.Plugins();
if (plugins == null || plugins.Count == 0)
{
throw new Exception("未找到任何插件");
}
result.IsSuccess = true;
result.Message = $"找到 {plugins.Count} 个插件";
LogManager.Info($"[测试] 插件检测测试 - 通过: {plugins.Count} 个插件");
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"插件检测失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 插件检测测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 测试简单碰撞检测
/// </summary>
private static TestResult TestSimpleCollisionDetection()
{
var result = new TestResult { TestName = "简单碰撞检测测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 获取文档中的模型项
var doc = NavisApplication.ActiveDocument;
if (doc?.Models?.RootItemDescendantsAndSelf == null)
{
throw new Exception("文档或模型为空");
}
var items = doc.Models.RootItemDescendantsAndSelf
.Where(item => item.HasGeometry)
.Take(2)
.ToList();
if (items.Count < 2)
{
throw new Exception("可用于测试的模型元素不足");
}
// 执行碰撞检测
var collisions = ClashDetectiveIntegration.Instance.DetectCollisions(items[0]);
result.IsSuccess = true;
result.CollisionCount = collisions.Count;
result.Message = $"检测到 {collisions.Count} 个碰撞";
LogManager.Info($"[测试] 简单碰撞检测测试 - 通过: {collisions.Count} 个碰撞");
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"简单碰撞检测失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 简单碰撞检测测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 测试动态碰撞检测
/// </summary>
private static TestResult TestDynamicCollisionDetection()
{
var result = new TestResult { TestName = "动态碰撞检测测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 测试动态检测功能
var instance = ClashDetectiveIntegration.Instance;
var eventFired = false;
// 订阅事件
instance.CollisionDetected += (sender, e) =>
{
eventFired = true;
result.CollisionCount = e.CollisionCount;
};
// 模拟动态检测
var doc = NavisApplication.ActiveDocument;
var testItems = doc.Models.RootItemDescendantsAndSelf
.Where(item => item.HasGeometry)
.Take(1)
.ToList();
if (testItems.Count > 0)
{
var collisions = instance.DetectCollisions(testItems[0]);
result.IsSuccess = true;
result.Message = $"动态检测完成,事件触发: {eventFired}";
LogManager.Info($"[测试] 动态碰撞检测测试 - 通过: 事件触发={eventFired}");
}
else
{
throw new Exception("没有可用的测试对象");
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"动态碰撞检测失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 动态碰撞检测测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 测试高亮显示功能
/// </summary>
private static TestResult TestHighlightingFeature()
{
var result = new TestResult { TestName = "高亮显示功能测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 创建测试碰撞结果
var testResults = new List<CollisionResult>();
var doc = NavisApplication.ActiveDocument;
var testItems = doc.Models.RootItemDescendantsAndSelf
.Where(item => item.HasGeometry)
.Take(2)
.ToList();
if (testItems.Count >= 2)
{
testResults.Add(new CollisionResult
{
ClashGuid = Guid.NewGuid(),
DisplayName = "测试碰撞",
Item1 = testItems[0],
Item2 = testItems[1],
CreatedTime = DateTime.Now
});
}
// 测试高亮显示
ClashDetectiveIntegration.Instance.HighlightCollisions(testResults);
result.IsSuccess = true;
result.Message = $"高亮显示 {testResults.Count} 个碰撞对象";
LogManager.Info($"[测试] 高亮显示功能测试 - 通过: {testResults.Count} 个对象");
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"高亮显示功能失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 高亮显示功能测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 测试窗口同步功能
/// </summary>
private static TestResult TestWindowSynchronization()
{
var result = new TestResult { TestName = "窗口同步功能测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 测试窗口同步
// 这里只是验证不抛出异常
var instance = ClashDetectiveIntegration.Instance;
// 尝试同步内部方法会处理Clash Detective不可用的情况
var doc = NavisApplication.ActiveDocument;
var testItems = doc.Models.RootItemDescendantsAndSelf
.Where(item => item.HasGeometry)
.Take(1)
.ToList();
if (testItems.Count > 0)
{
var collisions = instance.DetectCollisions(testItems[0]);
result.IsSuccess = true;
result.Message = "窗口同步测试完成(无异常)";
LogManager.Info("[测试] 窗口同步功能测试 - 通过");
}
else
{
result.IsSuccess = true;
result.Message = "窗口同步测试完成(无测试对象)";
}
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"窗口同步功能失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 窗口同步功能测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 测试清理功能
/// </summary>
private static TestResult TestCleanupFunction()
{
var result = new TestResult { TestName = "清理功能测试" };
var stopwatch = Stopwatch.StartNew();
try
{
// 测试清理功能
ClashDetectiveIntegration.Instance.Cleanup();
result.IsSuccess = true;
result.Message = "清理功能测试完成";
LogManager.Info("[测试] 清理功能测试 - 通过");
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"清理功能失败: {ex.Message}";
result.Exception = ex;
LogManager.Error($"[测试] 清理功能测试 - 失败: {ex.Message}");
}
finally
{
stopwatch.Stop();
result.ExecutionTime = stopwatch.Elapsed;
}
return result;
}
/// <summary>
/// 生成测试报告
/// </summary>
private static void GenerateTestReport(List<TestResult> results)
{
try
{
var passedCount = results.Count(r => r.IsSuccess);
var failedCount = results.Count(r => !r.IsSuccess);
var totalTime = TimeSpan.FromMilliseconds(results.Sum(r => r.ExecutionTime.TotalMilliseconds));
LogManager.Info("=== Clash Detective 集成测试报告 ===");
LogManager.Info($"总测试数: {results.Count}");
LogManager.Info($"通过测试: {passedCount}");
LogManager.Info($"失败测试: {failedCount}");
LogManager.Info($"总执行时间: {totalTime.TotalSeconds:F2} 秒");
LogManager.Info("");
foreach (var result in results)
{
var status = result.IsSuccess ? "✓" : "✗";
LogManager.Info($"{status} {result.TestName}: {result.Message} ({result.ExecutionTime.TotalMilliseconds:F0}ms)");
if (!result.IsSuccess && result.Exception != null)
{
LogManager.Error($" 错误详情: {result.Exception.Message}");
}
}
LogManager.Info("=== 测试报告结束 ===");
}
catch (Exception ex)
{
LogManager.Error($"生成测试报告失败: {ex.Message}");
}
}
/// <summary>
/// 快速验证集成功能(修复版 - 避免UI阻塞
/// </summary>
/// <returns>验证是否成功</returns>
public static bool QuickValidation()
{
try
{
LogManager.Info("执行快速验证...");
// 1. 检查实例创建
var instance = ClashDetectiveIntegration.Instance;
if (instance == null)
{
LogManager.Error("快速验证失败: 无法创建实例");
return false;
}
// 2. 检查初始化
instance.Initialize();
// 3. 检查基本功能(使用异步方式)
var doc = NavisApplication.ActiveDocument;
if (doc?.Models?.RootItemDescendantsAndSelf != null)
{
var testItem = doc.Models.RootItemDescendantsAndSelf
.Where(item => item != null && item.HasGeometry)
.FirstOrDefault();
if (testItem != null)
{
try
{
var collisions = instance.DetectCollisions(testItem);
LogManager.Info($"快速验证成功: 检测到 {collisions.Count} 个碰撞");
}
catch (ObjectDisposedException)
{
LogManager.Warning("快速验证时对象已释放,跳过碰撞检测");
}
}
}
// 4. 清理(异步执行)
System.Threading.Tasks.Task.Run(() =>
{
try
{
System.Threading.Thread.Sleep(50); // 给UI线程喘息时间
instance.Cleanup();
LogManager.Debug("快速验证清理完成");
}
catch (Exception cleanupEx)
{
LogManager.Warning($"快速验证清理失败: {cleanupEx.Message}");
}
});
LogManager.Info("快速验证完成");
return true;
}
catch (ObjectDisposedException ex)
{
LogManager.Warning($"快速验证时对象已释放: {ex.Message}");
return false;
}
catch (Exception ex)
{
LogManager.Error($"快速验证失败: {ex.Message}");
return false;
}
}
}
}

View File

@ -2782,12 +2782,11 @@ namespace NavisworksTransport
// 基于模型大小智能选择网格大小
if (maxDimension > 1000) return 5.0; // 大型模型5米
if (maxDimension > 500) return 2.0; // 中型模型2米
if (maxDimension > 100) return 1.0; // 小型模型1米
return 1.0; // 微型模型1.0米
return 0.5; // 小型模型0.5米
}
catch
{
return 1.0; // 默认值改为1.0米与UI默认设置保持一致
return 0.5; // 默认值改为0.5与UI默认设置保持一致
}
}

View File

@ -1527,23 +1527,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
// 3. 直接清理动画管理器PathAnimationManager.Dispose内部已经优化了重复调用
// 不需要在这里再次调用StopAnimation让PathAnimationManager自己处理
// 3. 清理动画管理器PathAnimationManager.Dispose内部已经优化了安全清理
if (_pathAnimationManager != null)
{
try
{
// 检查Navisworks应用程序是否仍然可用
var app = Autodesk.Navisworks.Api.Application.ActiveDocument;
if (app != null && app.Models != null)
{
_pathAnimationManager.Dispose();
LogManager.Debug("PathAnimationManager清理完成");
}
else
{
LogManager.Info("Navisworks文档已不可用跳过PathAnimationManager清理操作");
}
_pathAnimationManager.Dispose();
LogManager.Debug("PathAnimationManager清理完成");
}
catch (Exception disposeEx)
{

View File

@ -35,7 +35,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// 4. 所有业务方法都遵循统一的四步骤模式,确保线程安全和避免死锁
/// 5. 特别针对PreviewSplitAsync方法进行了重点重构解决了死锁问题的根源
/// </summary>
public class LayerManagementViewModel : ViewModelBase
public class LayerManagementViewModel : ViewModelBase, IDisposable
{
#region

View File

@ -27,7 +27,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// 4. 支持批量属性设置和单个模型编辑
/// 5. 使用MVVM架构和线程安全的UI更新
/// </summary>
public class ModelSettingsViewModel : ViewModelBase
public class ModelSettingsViewModel : ViewModelBase, IDisposable
{
#region

View File

@ -23,7 +23,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// 暂时使用空实现避免与主LogisticsControlViewModel功能重复
/// TODO: 后续将从LogisticsControlViewModelcopy.cs迁移完整业务逻辑
/// </summary>
public class PathEditingViewModel : ViewModelBase
public class PathEditingViewModel : ViewModelBase, IDisposable
{
#region
@ -66,6 +66,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 路径文件管理
private string _pathFileStatus = "未保存";
// 资源清理管理
private bool _disposed = false;
#endregion
#region
@ -2468,6 +2471,40 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// 释放资源 (IDisposable implementation)
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 释放托管和非托管资源
/// </summary>
/// <param name="disposing">是否释放托管资源</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
try
{
// 调用现有的清理逻辑
Cleanup();
LogManager.Info("PathEditingViewModel已正确释放资源 (IDisposable)");
}
catch (Exception ex)
{
LogManager.Error($"PathEditingViewModel释放资源时发生异常: {ex.Message}", ex);
}
}
_disposed = true;
}
}
#endregion
}

View File

@ -13,7 +13,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// <summary>
/// 系统管理ViewModel - 处理插件系统管理功能
/// </summary>
public class SystemManagementViewModel : ViewModelBase
public class SystemManagementViewModel : ViewModelBase, IDisposable
{
#region
@ -697,7 +697,41 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#endregion
#region
#region IDisposable实现
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// 释放资源的具体实现
/// </summary>
/// <param name="disposing">是否正在释放托管资源</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
try
{
// 调用现有的清理逻辑
Cleanup();
LogManager.Info("SystemManagementViewModel已正确释放资源 (IDisposable)");
}
catch (Exception ex)
{
LogManager.Error($"SystemManagementViewModel释放资源时发生异常: {ex.Message}", ex);
}
}
_disposed = true;
}
}
public void Cleanup()
{
@ -705,9 +739,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
LogManager.Info("开始清理SystemManagementViewModel资源");
// 🔧 修复:标记为已释放
_disposed = true;
// 🔧 修复:停止性能监控定时器
if (_performanceTimer != null)
{