修改程序关闭崩溃的bug
This commit is contained in:
parent
3c1458245c
commit
c40e1219a7
@ -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:string,objectName:string(≤128),isChannel:bool,channelType:string(≤64),properties:object;
|
||||
- 用途:界面显示和后续路径规划使用;
|
||||
- 设置结果(内存):
|
||||
- 字段:successCount:int,failCount:int,totalCount:int,failReasons:string[];
|
||||
- 用途:操作结果反馈和错误处理;
|
||||
- 持久化属性(平台存储):
|
||||
- 位置:Navisworks模型属性系统,自定义分类"物流属性";
|
||||
- 字段:LogisticsCategory="通道",IsTraversable=true,Priority=3,WidthLimit,HeightLimit,SpeedLimit;
|
||||
- 编码:UTF-8;可被后续模块读取和筛选;
|
||||
- 操作日志(磁盘):
|
||||
- 字段:timestamp,user,action="setChannel",objectIds:string[],properties:json,result:string;
|
||||
- 格式:JSON行格式;编码=UTF-8;
|
||||
|
||||
3.3.3.1.2.5. 功能界面(界面元素详解与操作流程)
|
||||
|
||||
- 选择状态显示(SelectedModelsText,TextBlock)
|
||||
- 作用:实时显示当前选中的模型数量和简要信息。
|
||||
- 使用:自动更新,格式如"已选择 15 个模型项:通道_01, 走廊_A02..."。
|
||||
- 选择模式切换(SelectionModeOptions,RadioButton组)
|
||||
- 作用:选择"选择树模式"或"三维视图模式"或"组合模式"。
|
||||
- 使用:影响后续点选行为和快捷键响应。
|
||||
- 设置为通道(SetAsChannelCommand,Button)
|
||||
- 作用:将选中模型批量设置为通道类型并应用默认属性。
|
||||
- 使用:需要先选择模型;设置后在状态栏显示成功/失败数量。
|
||||
- 启用条件:HasSelectedItems && IsNotProcessing。
|
||||
- 通道属性配置面板(ChannelPropertiesGroup,GroupBox)
|
||||
- 作用:设置通道的默认物流属性(限宽、限高、限速、优先级)。
|
||||
- 使用:在"设置为通道"前配置;影响批量设置的默认值。
|
||||
- 高亮显示通道(HighlightChannelsCommand,Button)
|
||||
- 作用:在三维视图中高亮显示所有已设置为通道的模型。
|
||||
- 使用:用于验证通道设置结果;可与"仅显示通道"配合使用。
|
||||
- 清除通道设置(ClearChannelCommand,Button)
|
||||
- 作用:清除选中模型的通道属性,恢复为普通模型。
|
||||
- 使用:支持批量撤销;需要确认操作。
|
||||
- 通道筛选器(ChannelFilterOptions,ComboBox + CheckBox)
|
||||
- 作用:按通道类型、限制属性等条件筛选显示。
|
||||
- 使用:支持"显示所有"、"仅显示通道"、"按限宽筛选"等选项。
|
||||
- 进度指示器(ProgressIndicator,ProgressBar + 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:string,routeName:string(≤64),description:string(≤256),createdAt:datetime(ISO 8601),modifiedAt:datetime;
|
||||
- 路径点集合:points[]{index:int,name:string(≤32),type:enum,position:Point3D,orientation:Vector3D(可选)};
|
||||
- 状态标识:status:enum{Draft,Editing,Completed,Validated},isValid:bool;
|
||||
- 可视化数据(渲染层):
|
||||
- 路径线段:LineSegment[],用于3D视图中的路径连线显示;
|
||||
- 点位标识:PointMarker[],包含类型图标、名称标签、选中状态;
|
||||
- 渲染属性:lineColor:Color,lineWidth:double,markerSize:double;
|
||||
- 路径文件(磁盘):
|
||||
- 格式:JSON;编码=UTF-8;扩展名=.json;
|
||||
- 内容:完整路径对象序列化,包含版本号和元数据;
|
||||
- 命名规范:{项目名}_{路径名}_{yyyyMMdd_HHmm}.json;
|
||||
- 验证报告(内存):
|
||||
- 有效性检查结果:isValid:bool,issues[]{type:string,point:int,message:string,severity:enum};
|
||||
- 路径统计信息:totalLength:double(m),pointCount:int,segmentCount:int,estimatedTime:double(s);
|
||||
|
||||
3.3.3.1.3.5. 功能界面(界面元素详解与操作流程)
|
||||
|
||||
- 路径管理面板(PathManagementPanel,GroupBox)
|
||||
- 作用:显示当前项目的所有路径列表,支持选择、创建、删除操作。
|
||||
- 使用:列表显示路径名称、点数、状态、修改时间;单击选中,双击编辑。
|
||||
- 新建路径(CreateNewPathCommand,Button)
|
||||
- 作用:创建新的空路径对象并进入编辑模式。
|
||||
- 使用:弹出命名对话框→创建路径→自动激活编辑工具。
|
||||
- 流程:创建后立即可在3D视图中点击添加路径点。
|
||||
- 编辑模式切换(EditModeToggle,ToggleButton)
|
||||
- 作用:激活/关闭路径点编辑模式,控制3D交互工具状态。
|
||||
- 使用:开启时可在3D视图点击添加路径点;关闭时恢复正常视图操作。
|
||||
- 状态:显示当前是否处于编辑状态和活动路径名称。
|
||||
- 三维交互区域(3D Viewport Integration)
|
||||
- 作用:在Navisworks 3D视图中直接点击创建路径点。
|
||||
- 使用:编辑模式下,左键点击通道表面创建点位;右键完成当前段;
|
||||
- 反馈:实时显示路径连线、点位标识和鼠标悬停提示。
|
||||
- 路径点列表(PathPointsList,ListView + GridView)
|
||||
- 列:序号、名称、类型、坐标(X,Y,Z)、方向、操作(编辑/删除);
|
||||
- 作用:显示当前编辑路径的所有路径点,支持逐个编辑和调整。
|
||||
- 使用:单击选中点位(3D视图中高亮);双击编辑属性;拖拽调整顺序。
|
||||
- 路径点属性编辑器(PointPropertiesEditor,PropertyGrid)
|
||||
- 作用:精确编辑选中路径点的坐标、方向、名称等属性。
|
||||
- 使用:选中路径点后自动加载属性;支持数值输入和下拉选择;
|
||||
- 验证:输入时实时检查坐标范围和命名冲突。
|
||||
- 路径有效性检查(PathValidationPanel,StatusPanel)
|
||||
- 作用:显示当前路径的有效性状态和问题诊断。
|
||||
- 使用:自动检查路径连通性、点位合法性、通道覆盖等;
|
||||
- 反馈:显示警告图标、问题描述和建议修正方案。
|
||||
- 路径可视化控制(VisualizationControls,ToolBar)
|
||||
- 显示/隐藏路径线:CheckBox,控制路径连线的显示状态;
|
||||
- 显示/隐藏点标识:CheckBox,控制路径点标记的显示状态;
|
||||
- 路径颜色设置:ColorPicker,自定义当前路径的显示颜色;
|
||||
- 视图聚焦:Button,自动调整视角聚焦当前路径。
|
||||
- 路径操作工具栏(PathOperationsToolBar,ToolBar)
|
||||
- 保存路径:Button,保存当前路径到文件和内存;
|
||||
- 另存为:Button,复制当前路径并重命名保存;
|
||||
- 导入路径:Button,从文件导入路径数据;
|
||||
- 导出路径:Button,导出当前路径为JSON/XML/CSV格式;
|
||||
- 删除路径:Button,删除选中路径(需确认)。
|
||||
- 操作历史管理(UndoRedoManager,ButtonGroup)
|
||||
- 撤销:Button,撤销最后一次路径点操作;
|
||||
- 重做:Button,重做已撤销的操作;
|
||||
- 清空路径:Button,清除当前路径的所有点位;
|
||||
- 重新开始:Button,重置路径编辑状态。
|
||||
- 操作流程(截图占位):
|
||||
1) 点击"新建路径"→输入路径名称→激活编辑模式 → [截图占位:路径点规划_创建路径.png]
|
||||
2) 在3D视图中依次点击通道表面创建路径点→查看路径点列表 → [截图占位:路径点规划_点击创建.png]
|
||||
3) 选择路径点→在属性编辑器中调整坐标和方向→验证路径有效性 → [截图占位:路径点规划_属性编辑.png]
|
||||
4) 使用可视化控制调整显示效果→保存路径到管理列表 → [截图占位:路径点规划_保存管理.png]
|
||||
|
||||
3.3.3.2.1. 编辑保存和导入
|
||||
3.3.3.2.1.1. 功能要求
|
||||
|
||||
|
||||
100
doc/working/animation_collision_report_issue.md
Normal file
100
doc/working/animation_collision_report_issue.md
Normal 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(生成侧生命周期控制),在每次开播前清理“动画生成的测试”和“运行期缓存”,并完善事件订阅的生命周期管理,可使“碰撞报告”的动画测试数字与本轮真实结果一致,不再随历史递增。
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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默认设置保持一致
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -35,7 +35,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// 4. 所有业务方法都遵循统一的四步骤模式,确保线程安全和避免死锁
|
||||
/// 5. 特别针对PreviewSplitAsync方法进行了重点重构,解决了死锁问题的根源
|
||||
/// </summary>
|
||||
public class LayerManagementViewModel : ViewModelBase
|
||||
public class LayerManagementViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
#region 私有字段和依赖注入
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// 4. 支持批量属性设置和单个模型编辑
|
||||
/// 5. 使用MVVM架构和线程安全的UI更新
|
||||
/// </summary>
|
||||
public class ModelSettingsViewModel : ViewModelBase
|
||||
public class ModelSettingsViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
#region 私有字段和依赖注入
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user