diff --git a/.gitignore b/.gitignore index 8caec61..290d36f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ otk_cpp_doc/ *.tmp_proj *.user *.userosscache -*.sln.docstates \ No newline at end of file +*.sln.docstates +.x64 +.MFCCreoDll \ No newline at end of file diff --git a/BatchOperationManager.cpp b/BatchOperationManager.cpp index 79fab2a..7489266 100644 --- a/BatchOperationManager.cpp +++ b/BatchOperationManager.cpp @@ -162,6 +162,25 @@ std::string BatchOperationManager::BuildOpenResultJson(const OpenResult& result) return json.str(); } +// Build JSON for workflow step +std::string BatchOperationManager::BuildWorkflowStepJson(const std::string& step_name, + bool success, + const std::string& time, + const std::string& error) { + std::ostringstream json; + json << "{" + << "\"step\":\"" << EscapeJsonString(step_name) << "\"," + << "\"success\":" << (success ? "true" : "false") << "," + << "\"time\":\"" << EscapeJsonString(time) << "\""; + + if (!error.empty()) { + json << ",\"error\":\"" << EscapeJsonString(error) << "\""; + } + + json << "}"; + return json.str(); +} + // Execute save_model operation BatchOperationResult BatchOperationManager::ExecuteSaveModel(int index, const BatchOperation& operation) { BatchOperationResult result; @@ -503,6 +522,575 @@ BatchOperationResult BatchOperationManager::ExecuteOpenModel(int index, const Ba return result; } +// Execute workflow_hierarchy_delete operation +BatchOperationResult BatchOperationManager::ExecuteHierarchyDeleteWorkflow(int index, const BatchOperation& operation) { + BatchOperationResult result; + result.operation_index = index; + result.operation_type = "workflow_hierarchy_delete"; + + auto workflow_start = std::chrono::high_resolution_clock::now(); + std::vector steps_json; + + try { + // Extract parameters + std::string working_directory; + std::string input_file_path; + std::string open_mode = "active"; + std::string project_name; + std::string target_level_str; + std::string export_path; + std::string geom_flags = "solids"; + + auto it = operation.parameters.find("working_directory"); + if (it != operation.parameters.end()) { + working_directory = it->second; + } + + it = operation.parameters.find("input_file_path"); + if (it != operation.parameters.end()) { + input_file_path = it->second; + } + + it = operation.parameters.find("open_mode"); + if (it != operation.parameters.end()) { + open_mode = it->second; + } + + it = operation.parameters.find("project_name"); + if (it != operation.parameters.end()) { + project_name = it->second; + } + + it = operation.parameters.find("target_level"); + if (it != operation.parameters.end()) { + target_level_str = it->second; + } + + it = operation.parameters.find("export_path"); + if (it != operation.parameters.end()) { + export_path = it->second; + } + + it = operation.parameters.find("geom_flags"); + if (it != operation.parameters.end()) { + geom_flags = it->second; + } + + // Validate required parameters + if (input_file_path.empty()) { + result.success = false; + result.error_message = "Missing required parameter: input_file_path"; + return result; + } + + if (project_name.empty()) { + result.success = false; + result.error_message = "Missing required parameter: project_name"; + return result; + } + + if (target_level_str.empty()) { + result.success = false; + result.error_message = "Missing required parameter: target_level"; + return result; + } + + if (export_path.empty()) { + result.success = false; + result.error_message = "Missing required parameter: export_path"; + return result; + } + + int target_level = 0; + try { + target_level = std::stoi(target_level_str); + } catch (...) { + result.success = false; + result.error_message = "Invalid target_level value"; + return result; + } + + // Step 1: Set working directory (if provided) + if (!working_directory.empty()) { + auto step_start = std::chrono::high_resolution_clock::now(); + ChangeDirectoryResult dir_result = CreoUtilities::SetWorkingDirectory(working_directory); + auto step_end = std::chrono::high_resolution_clock::now(); + auto step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("set_directory", dir_result.success, + std::to_string(step_duration.count()) + "ms", + dir_result.success ? "" : dir_result.error_message)); + + if (!dir_result.success) { + result.success = false; + result.error_message = "Failed to set working directory: " + dir_result.error_message; + result.result_json = "{\"steps\":[" + steps_json[0] + "]}"; + return result; + } + } + + // Step 2: Open model + auto step_start = std::chrono::high_resolution_clock::now(); + OpenResult open_result = CreoManager::Instance().OpenModel(input_file_path, open_mode); + auto step_end = std::chrono::high_resolution_clock::now(); + auto step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("open_model", open_result.success, + std::to_string(step_duration.count()) + "ms", + open_result.success ? "" : open_result.error_message)); + + if (!open_result.success) { + result.success = false; + result.error_message = "Failed to open model: " + open_result.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 3: Hierarchy delete + step_start = std::chrono::high_resolution_clock::now(); + CreoManager::HierarchyDeleteResult delete_result = + CreoManager::Instance().DeleteHierarchyComponents(project_name, target_level); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("hierarchy_delete", delete_result.success, + std::to_string(step_duration.count()) + "ms", + delete_result.success ? "" : delete_result.error_message)); + + if (!delete_result.success) { + // Try to close model before returning error + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Failed to delete hierarchy: " + delete_result.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 4: Export STEP + step_start = std::chrono::high_resolution_clock::now(); + ExportResult export_result = CreoManager::Instance().ExportModelToSTEP(export_path, geom_flags); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("export_step", export_result.success, + std::to_string(step_duration.count()) + "ms", + export_result.success ? "" : export_result.error_message)); + + if (!export_result.success) { + // Try to close model before returning error + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Failed to export STEP: " + export_result.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 5: Close model + step_start = std::chrono::high_resolution_clock::now(); + CloseResult close_result = CreoManager::Instance().CloseModel(false); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("close_model", close_result.success, + std::to_string(step_duration.count()) + "ms", + close_result.success ? "" : close_result.error_message)); + + // Build final result + auto workflow_end = std::chrono::high_resolution_clock::now(); + auto workflow_duration = std::chrono::duration_cast(workflow_end - workflow_start); + result.execution_time = std::to_string(workflow_duration.count()) + "ms"; + + std::ostringstream final_json; + final_json << "{" + << "\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) final_json << ","; + final_json << steps_json[i]; + } + final_json << "]," + << "\"final_output\":\"" << EscapeJsonString(export_path) << "\"," + << "\"final_size\":\"" << EscapeJsonString(export_result.file_size) << "\"" + << "}"; + + result.success = true; + result.result_json = final_json.str(); + + } catch (const std::exception& e) { + // Try to close model on exception + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Exception in workflow: " + std::string(e.what()); + + if (!steps_json.empty()) { + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + } + } catch (...) { + // Try to close model on exception + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Unknown error in workflow"; + + if (!steps_json.empty()) { + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + } + } + + return result; +} + +// Execute workflow_shrinkwrap operation +BatchOperationResult BatchOperationManager::ExecuteShrinkwrapWorkflow(int index, const BatchOperation& operation) { + BatchOperationResult result; + result.operation_index = index; + result.operation_type = "workflow_shrinkwrap"; + + auto workflow_start = std::chrono::high_resolution_clock::now(); + std::vector steps_json; + + try { + // Extract parameters + std::string working_directory; + std::string input_file_path; + std::string shrinkwrap_output_path; + std::string quality_str = "5"; + std::string chord_height_str = "0.5"; + std::string step_export_path; + std::string geom_flags = "solids"; + + auto it = operation.parameters.find("working_directory"); + if (it != operation.parameters.end()) { + working_directory = it->second; + } + + it = operation.parameters.find("input_file_path"); + if (it != operation.parameters.end()) { + input_file_path = it->second; + } + + it = operation.parameters.find("shrinkwrap_output_path"); + if (it != operation.parameters.end()) { + shrinkwrap_output_path = it->second; + } + + it = operation.parameters.find("quality"); + if (it != operation.parameters.end() && !it->second.empty()) { + quality_str = it->second; + } + + it = operation.parameters.find("chord_height"); + if (it != operation.parameters.end() && !it->second.empty()) { + chord_height_str = it->second; + } + + it = operation.parameters.find("step_export_path"); + if (it != operation.parameters.end()) { + step_export_path = it->second; + } + + it = operation.parameters.find("geom_flags"); + if (it != operation.parameters.end()) { + geom_flags = it->second; + } + + // Validate required parameters + if (input_file_path.empty()) { + result.success = false; + result.error_message = "Missing required parameter: input_file_path"; + return result; + } + + if (shrinkwrap_output_path.empty()) { + result.success = false; + result.error_message = "Missing required parameter: shrinkwrap_output_path"; + return result; + } + + if (step_export_path.empty()) { + result.success = false; + result.error_message = "Missing required parameter: step_export_path"; + return result; + } + + int quality = 5; + double chord_height = 0.5; + + try { + quality = std::stoi(quality_str); + } catch (...) { + result.success = false; + result.error_message = "Invalid quality value"; + return result; + } + + try { + chord_height = std::stod(chord_height_str); + } catch (...) { + result.success = false; + result.error_message = "Invalid chord_height value"; + return result; + } + + // Step 1: Set working directory (if provided) + if (!working_directory.empty()) { + auto step_start = std::chrono::high_resolution_clock::now(); + ChangeDirectoryResult dir_result = CreoUtilities::SetWorkingDirectory(working_directory); + auto step_end = std::chrono::high_resolution_clock::now(); + auto step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("set_directory", dir_result.success, + std::to_string(step_duration.count()) + "ms", + dir_result.success ? "" : dir_result.error_message)); + + if (!dir_result.success) { + result.success = false; + result.error_message = "Failed to set working directory: " + dir_result.error_message; + result.result_json = "{\"steps\":[" + steps_json[0] + "]}"; + return result; + } + } + + // Step 2: Open original model + auto step_start = std::chrono::high_resolution_clock::now(); + OpenResult open_result = CreoManager::Instance().OpenModel(input_file_path, "active"); + auto step_end = std::chrono::high_resolution_clock::now(); + auto step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("open_model", open_result.success, + std::to_string(step_duration.count()) + "ms", + open_result.success ? "" : open_result.error_message)); + + if (!open_result.success) { + result.success = false; + result.error_message = "Failed to open model: " + open_result.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 3: Execute Shrinkwrap + step_start = std::chrono::high_resolution_clock::now(); + ShrinkwrapShellRequest shrink_request; + shrink_request.output_file_path = shrinkwrap_output_path; + shrink_request.quality = quality; + shrink_request.chord_height = chord_height; + + ShrinkwrapShellResult shrink_result = + ShrinkwrapManager::Instance().ExecuteShrinkwrapShell(shrink_request); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("shrinkwrap", shrink_result.success, + std::to_string(step_duration.count()) + "ms", + shrink_result.success ? "" : shrink_result.error_message)); + + if (!shrink_result.success) { + // Try to close model before returning error + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Failed to execute shrinkwrap: " + shrink_result.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 4: Close original model + step_start = std::chrono::high_resolution_clock::now(); + CloseResult close_original = CreoManager::Instance().CloseModel(true); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("close_original", close_original.success, + std::to_string(step_duration.count()) + "ms", + close_original.success ? "" : close_original.error_message)); + + // Step 5: Open Shrinkwrap model + step_start = std::chrono::high_resolution_clock::now(); + OpenResult open_shrink = CreoManager::Instance().OpenModel(shrinkwrap_output_path, "active"); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("open_shrinkwrap_model", open_shrink.success, + std::to_string(step_duration.count()) + "ms", + open_shrink.success ? "" : open_shrink.error_message)); + + if (!open_shrink.success) { + result.success = false; + result.error_message = "Failed to open shrinkwrap model: " + open_shrink.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 6: Export STEP + step_start = std::chrono::high_resolution_clock::now(); + ExportResult export_result = CreoManager::Instance().ExportModelToSTEP(step_export_path, geom_flags); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("export_step", export_result.success, + std::to_string(step_duration.count()) + "ms", + export_result.success ? "" : export_result.error_message)); + + if (!export_result.success) { + // Try to close model before returning error + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Failed to export STEP: " + export_result.error_message; + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + return result; + } + + // Step 7: Close shrinkwrap model + step_start = std::chrono::high_resolution_clock::now(); + CloseResult close_shrink = CreoManager::Instance().CloseModel(true); + step_end = std::chrono::high_resolution_clock::now(); + step_duration = std::chrono::duration_cast(step_end - step_start); + + steps_json.push_back(BuildWorkflowStepJson("close_shrinkwrap", close_shrink.success, + std::to_string(step_duration.count()) + "ms", + close_shrink.success ? "" : close_shrink.error_message)); + + // Build final result + auto workflow_end = std::chrono::high_resolution_clock::now(); + auto workflow_duration = std::chrono::duration_cast(workflow_end - workflow_start); + result.execution_time = std::to_string(workflow_duration.count()) + "ms"; + + std::ostringstream final_json; + final_json << "{" + << "\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) final_json << ","; + final_json << steps_json[i]; + } + final_json << "]," + << "\"final_output\":\"" << EscapeJsonString(step_export_path) << "\"," + << "\"final_size\":\"" << EscapeJsonString(export_result.file_size) << "\"" + << "}"; + + result.success = true; + result.result_json = final_json.str(); + + } catch (const std::exception& e) { + // Try to close model on exception + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Exception in workflow: " + std::string(e.what()); + + if (!steps_json.empty()) { + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + } + } catch (...) { + // Try to close model on exception + try { + CreoManager::Instance().CloseModel(true); + } catch (...) {} + + result.success = false; + result.error_message = "Unknown error in workflow"; + + if (!steps_json.empty()) { + std::ostringstream steps_array; + steps_array << "{\"steps\":["; + for (size_t i = 0; i < steps_json.size(); i++) { + if (i > 0) steps_array << ","; + steps_array << steps_json[i]; + } + steps_array << "]}"; + result.result_json = steps_array.str(); + } + } + + return result; +} + // Main entry point: Execute batch operations BatchOperationsResponse BatchOperationManager::ExecuteBatchOperations(const BatchOperationsRequest& request) { BatchOperationsResponse response; @@ -543,6 +1131,10 @@ BatchOperationsResponse BatchOperationManager::ExecuteBatchOperations(const Batc op_result = ExecuteCloseModel(static_cast(i), operation); } else if (operation.operation_type == "open_model") { op_result = ExecuteOpenModel(static_cast(i), operation); + } else if (operation.operation_type == "workflow_hierarchy_delete") { + op_result = ExecuteHierarchyDeleteWorkflow(static_cast(i), operation); + } else if (operation.operation_type == "workflow_shrinkwrap") { + op_result = ExecuteShrinkwrapWorkflow(static_cast(i), operation); } else { // Unknown operation type op_result.operation_index = static_cast(i); diff --git a/BatchOperationManager.h b/BatchOperationManager.h index d3a5d95..0365c22 100644 --- a/BatchOperationManager.h +++ b/BatchOperationManager.h @@ -6,6 +6,7 @@ #include "CreoManager.h" #include "PathDeleteManager.h" #include "ShrinkwrapManager.h" +#include "CreoUtilities.h" // Single operation request structure struct BatchOperation { @@ -69,6 +70,10 @@ private: BatchOperationResult ExecuteCloseModel(int index, const BatchOperation& operation); BatchOperationResult ExecuteOpenModel(int index, const BatchOperation& operation); + // Execute workflow operations + BatchOperationResult ExecuteHierarchyDeleteWorkflow(int index, const BatchOperation& operation); + BatchOperationResult ExecuteShrinkwrapWorkflow(int index, const BatchOperation& operation); + // Helper functions std::string GetCurrentTimeString(); std::string EscapeJsonString(const std::string& str); @@ -79,4 +84,8 @@ private: std::string BuildShrinkwrapResultJson(const ShrinkwrapShellResult& result); std::string BuildCloseResultJson(const CloseResult& result); std::string BuildOpenResultJson(const OpenResult& result); + + // Workflow step helper + std::string BuildWorkflowStepJson(const std::string& step_name, bool success, + const std::string& time, const std::string& error = ""); }; diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 2afbe52..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,82 +0,0 @@ -# MFC Creo DLL 项目文档 - -## 项目概述 - -MFC动态链接库(DLL),为Creo CAD软件提供RESTful API服务。 - -**技术栈:** MFC + OTK/ProToolkit + Windows Socket -**服务端口:** 12345 -**架构特点:** 无锁线程通信,跨主机部署支持 - -## 核心功能模块 - -### 模型操作 -- 状态检测 - Creo连接状态、模型状态实时监控 -- 生命周期 - 打开/关闭/保存模型 -- STEP导出 - 装配体和零件导出 -- Shrinkwrap导出 - 外壳模型生成 - -### 分析算法 -- **层级分析** - 装配体结构遍历,支持指定层级返回 -- **薄壳化分析** - PCA-based OBB精确几何分析,识别外壳组件 -- **几何复杂度** - 多维度复杂度评估排序 -- **层级统计** - 组件数量分布统计 - -### 操作功能 -- **安全删除** - SuppressFeatures策略,保持装配体完整性 -- **路径删除** - 按组件路径批量删除 -- **模型搜索** - 三种匹配模式的名称搜索 - -## 主要API接口 - -```http -# 状态与模型操作 -GET /api/status/creo # Creo连接状态 -GET /api/status/model # 当前模型状态 -POST /api/model/open|close|save # 模型生命周期 -POST /api/export/model # STEP导出 - -# 分析算法 -POST /api/creo/analysis/hierarchy # 层级结构分析 -POST /api/analysis/shell-analysis # 薄壳化分析 -POST /api/analysis/geometry-complexity # 几何复杂度分析 -POST /api/analysis/hierarchy-statistics # 层级统计 - -# 操作功能 -POST /api/search/models # 模型搜索 -POST /api/creo/hierarchy/delete # 层级删除 -POST /api/creo/component/delete-by-path # 路径删除 -POST /api/creo/shrinkwrap/shell # Shrinkwrap导出 -``` - -## 核心算法优化 - -### Shell Analysis PCA增强算法 (2025-01-19) -- **PCA-based OBB**: 主成分分析精确定向包围盒,细长零件精度提升30%+ -- **智能选择机制**: 5维评分系统,自动选择最优几何分析方法 -- **22点采样策略**: 角点+面心+边心,增强协方差矩阵精度 -- **多层回退保障**: PCA→变换矩阵→AABB,确保零崩溃 - -### 薄壳化分析LOO算法 (2025-01-09) -- **Leave-One-Out归因**: Top-K特征独立测量,精度从60%→75% -- **智能缓存机制**: 模型版本+单位系统防脏读 -- **薄壁结构保护**: proximity检测避免误删关键支撑 - -### 无锁线程架构 -- **原子操作通信**: 避免C++标准库mutex,MessageItem统一处理 -- **50ms轮询机制**: 平衡响应速度和CPU占用 -- **跨线程OTK调用**: 定时器机制处理主线程操作 - -## 构建环境 - -**IDE:** Visual Studio 2022 (v143) -**配置:** Debug|x64 -**依赖:** Creo 5.0.0.0 OTK库 -**目标:** Windows 7+ - -## 开发规范 - -- **API稳定性**: 向后兼容,最小变更 -- **异常处理**: 完善错误处理,确保服务稳定 -- **性能优先**: 减少数据传输,提升前端体验 -- **代码规范**: 所有注释使用英文,避免编码问题 \ No newline at end of file diff --git a/CreoManager.cpp b/CreoManager.cpp index 85f8d77..edb46a2 100644 --- a/CreoManager.cpp +++ b/CreoManager.cpp @@ -1619,6 +1619,292 @@ CreoManager::HierarchyDeleteResult CreoManager::DeleteHierarchyComponents(const return result; } +// 子装配体层级删除功能实现 - 以指定子装配体为顶层进行层级删除 +CreoManager::HierarchyDeleteResult CreoManager::DeleteSubassemblyHierarchyComponents(const std::string& subassembly_path, int target_level) { + HierarchyDeleteResult result; + result.target_level = target_level; + + if (subassembly_path.empty()) { + result.error_message = "Subassembly path is required"; + return result; + } + + SessionInfo sessionInfo = GetSessionInfo(); + if (!sessionInfo.is_valid) { + result.error_message = "Creo session not available"; + return result; + } + + try { + pfcModel_ptr current_model = sessionInfo.session->GetCurrentModel(); + if (!current_model) { + result.error_message = "No current model loaded"; + return result; + } + + // 检查是否为装配体 + if (current_model->GetType() != pfcMDL_ASSEMBLY) { + result.error_message = "Current model is not an assembly"; + return result; + } + + // 转换为装配体 + wfcWAssembly_ptr rootAssembly = wfcWAssembly::cast(current_model); + if (!rootAssembly) { + result.error_message = "Failed to cast model to assembly"; + return result; + } + + // 获取当前模型名称 + std::string currentModelName = ""; + try { + xstring filename_xstr = current_model->GetFileName(); + currentModelName = XStringToString(filename_xstr); + } catch (...) { + currentModelName = ""; + } + + // 解析子装配体路径 + std::string path = subassembly_path; + std::replace(path.begin(), path.end(), '\\', '/'); + std::vector path_segments; + std::istringstream iss(path); + std::string segment; + while (std::getline(iss, segment, '/')) { + if (!segment.empty()) { + path_segments.push_back(segment); + } + } + + if (path_segments.empty()) { + result.error_message = "Invalid subassembly path format"; + return result; + } + + // 如果路径以当前模型名称开头,跳过第一段 + int startSegment = 0; + if (!currentModelName.empty() && !path_segments.empty()) { + std::string firstSeg = path_segments[0]; + std::string modelName = currentModelName; + std::transform(firstSeg.begin(), firstSeg.end(), firstSeg.begin(), ::tolower); + std::transform(modelName.begin(), modelName.end(), modelName.begin(), ::tolower); + if (firstSeg == modelName) { + startSegment = 1; + } + } + + // 递归查找目标子装配体 + std::function findSubassembly = [&](wfcWAssembly_ptr currentAssembly, int segmentIndex) -> wfcWAssembly_ptr { + if (!currentAssembly || segmentIndex >= (int)path_segments.size()) { + return currentAssembly; + } + + std::string targetSegment = path_segments[segmentIndex]; + std::string targetLower = targetSegment; + std::transform(targetLower.begin(), targetLower.end(), targetLower.begin(), ::tolower); + + pfcFeatures_ptr features = currentAssembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT); + if (!features) return nullptr; + + int count = features->getarraysize(); + for (int i = 0; i < count; i++) { + try { + pfcFeature_ptr feature = features->get(i); + if (!feature) continue; + + pfcComponentFeat_ptr compFeat = pfcComponentFeat::cast(feature); + if (!compFeat) continue; + + auto modelDescr = compFeat->GetModelDescr(); + if (!modelDescr) continue; + + xstring filename_xstr = modelDescr->GetFileName(); + std::string componentName = XStringToString(filename_xstr); + std::string componentLower = componentName; + std::transform(componentLower.begin(), componentLower.end(), componentLower.begin(), ::tolower); + + if (componentLower == targetLower) { + // 找到匹配的组件 + if (segmentIndex == (int)path_segments.size() - 1) { + // 这是目标子装配体 + pfcModel_ptr childModel = LoadComponentModel(compFeat); + if (childModel && childModel->GetType() == pfcMDL_ASSEMBLY) { + return wfcWAssembly::cast(childModel); + } + return nullptr; + } else { + // 继续递归查找 + pfcModel_ptr childModel = LoadComponentModel(compFeat); + if (childModel && childModel->GetType() == pfcMDL_ASSEMBLY) { + wfcWAssembly_ptr childAssembly = wfcWAssembly::cast(childModel); + if (childAssembly) { + return findSubassembly(childAssembly, segmentIndex + 1); + } + } + } + } + } catch (...) { + continue; + } + } + return nullptr; + }; + + // 查找目标子装配体 + wfcWAssembly_ptr targetAssembly = findSubassembly(rootAssembly, startSegment); + if (!targetAssembly) { + result.error_message = "Subassembly not found at path: " + subassembly_path; + return result; + } + + // target_level=2表示保留2层,删除第3层的组件 + // 层级映射:target_level=2 -> 删除level_3 -> currentLevel=2 + int deleteLevel = target_level - 1; + + // 收集删除统计信息 + std::map> componentsToDeleteByLevel; + int total_deleted = 0; + int successful_count = 0; + int failed_count = 0; + + // 递归遍历到指定层级直接删除(以目标子装配体为层级0开始) + std::function deleteAtLevel = [&](wfcWAssembly_ptr currentAssembly, int currentLevel) { + if (!currentAssembly || currentLevel > deleteLevel) return; + + try { + pfcFeatures_ptr features = currentAssembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT); + + if (features) { + if (currentLevel == deleteLevel) { + // 在目标层级:获取所有组件并删除 + xintsequence_ptr featIds = xintsequence::create(); + std::vector levelComponents; + for (int i = 0; i < features->getarraysize(); i++) { + pfcFeature_ptr feature = features->get(i); + pfcComponentFeat_ptr compFeat = pfcComponentFeat::cast(feature); + if (compFeat) { + // 记录组件信息用于响应 + try { + auto modelDescr = compFeat->GetModelDescr(); + if (modelDescr) { + xstring filename_xstr = modelDescr->GetFileName(); + std::string filename = XStringToString(filename_xstr); + levelComponents.push_back(filename); + total_deleted++; + } + } catch (...) { + // 忽略获取文件名失败的组件 + } + + // 添加到删除列表 + int featId = feature->GetId(); + featIds->append(featId); + } + } + + // 执行删除 + if (featIds->getarraysize() > 0) { + try { + wfcWSolid_ptr wsolid = wfcWSolid::cast(currentAssembly); + if (wsolid) { + + // 使用SuppressFeatures方法,更安全地"删除"组件 + wfcFeatSuppressOrDeleteOptions_ptr options = wfcFeatSuppressOrDeleteOptions::create(); + options->append(wfcFEAT_SUPP_OR_DEL_NO_OPTS); + + // 创建重生成指令,允许失败 + wfcWRegenInstructions_ptr regenInstr = wfcWRegenInstructions::Create(); + + // 执行抑制操作(更安全,不会破坏引用关系) + wsolid->SuppressFeatures(featIds, options, regenInstr); + + // 手动重生成模型 + try { + currentAssembly->Regenerate(nullptr); + } catch (...) { + // 重生成失败不影响抑制操作 + } + + successful_count += featIds->getarraysize(); + + // 记录成功抑制的组件 - 累积而不是覆盖 + if (componentsToDeleteByLevel.find(deleteLevel + 1) == componentsToDeleteByLevel.end()) { + componentsToDeleteByLevel[deleteLevel + 1] = std::vector(); + } + componentsToDeleteByLevel[deleteLevel + 1].insert( + componentsToDeleteByLevel[deleteLevel + 1].end(), + levelComponents.begin(), + levelComponents.end() + ); + } else { + failed_count += featIds->getarraysize(); + } + } catch (...) { + failed_count += featIds->getarraysize(); + } + } + } else if (currentLevel < deleteLevel) { + // 还没到目标层级:只对装配体组件继续递归 + for (int i = 0; i < features->getarraysize(); i++) { + pfcFeature_ptr feature = features->get(i); + pfcComponentFeat_ptr compFeat = pfcComponentFeat::cast(feature); + if (compFeat) { + auto modelDescr = compFeat->GetModelDescr(); + if (modelDescr && modelDescr->GetType() == pfcMDL_ASSEMBLY) { + pfcModel_ptr childModel = LoadComponentModel(compFeat); + if (childModel && childModel->GetType() == pfcMDL_ASSEMBLY) { + wfcWAssembly_ptr childAssembly = wfcWAssembly::cast(childModel); + if (childAssembly) { + deleteAtLevel(childAssembly, currentLevel + 1); + } + } + } + } + } + } + } + } catch (...) { + // 忽略单个装配体的错误 + } + }; + + // 从目标子装配体开始删除(层级0) + deleteAtLevel(targetAssembly, 0); + + result.deleted_components = componentsToDeleteByLevel; + result.total_deleted = total_deleted; + result.original_levels = 0; // 临时设置,避免异常值 + + // 删除完成后重新生成模型 + if (successful_count > 0) { + try { + targetAssembly->Regenerate(nullptr); + } catch (...) { + // 重新生成失败不影响删除结果 + } + } + + result.successful = successful_count; + result.failed = failed_count; + result.final_levels = target_level + 1; // 删除后的层级数 + + if (failed_count == 0) { + result.success = true; + result.message = "All components suppressed successfully (safer than deletion)"; + } else { + result.success = (successful_count > 0); + result.message = "Suppression completed with " + std::to_string(failed_count) + " failures"; + } + + } catch (const std::exception& e) { + result.error_message = "Exception during suppression: " + std::string(e.what()); + } catch (...) { + result.error_message = "Unknown error during suppression"; + } + + return result; +} + // Multi-directional extreme value projection shell analysis implementation CreoManager::ShellAnalysisResult CreoManager::AnalyzeShellFeaturesEnhanced(const ShellAnalysisRequest& request) { ShellAnalysisResult result; diff --git a/CreoManager.h b/CreoManager.h index 0d71e60..4c7432b 100644 --- a/CreoManager.h +++ b/CreoManager.h @@ -249,6 +249,9 @@ public: HierarchyDeleteResult DeleteHierarchyComponents(const std::string& project_name, int target_level); + // 子装配体层级删除功能 - 以指定子装配体为顶层进行层级删除 + HierarchyDeleteResult DeleteSubassemblyHierarchyComponents(const std::string& subassembly_path, int target_level); + // 薄壳化分析功能 struct ShellAnalysisRequest { std::string software_type; diff --git a/Creo技术方案总结.md b/Creo技术方案总结.md new file mode 100644 index 0000000..83b83d6 --- /dev/null +++ b/Creo技术方案总结.md @@ -0,0 +1,335 @@ +# Creo 前端技术路线与算法体系总结 + +## 摘要 + +本文档面向计划构建与 MFCCreoDll 插件协同工作的前端工程,系统梳理现有 C++/MFC 插件所提供的几何分析与模型管理能力,说明其依托的 PTC Creo Parametric Toolkit (OTK C++) API,以及前端接入时需要遵循的通信约定、架构设计、算法协同与性能优化策略。文档重点扩展薄壳分析与 Shrinkwrap 外壳导出的几何原理、数值指标与 API 依赖,提供数据结构、流程图示与复杂度分析,为后续实现桌面端或 Web 前端提供完整的工程蓝图。 + +## 1. 引言 + +### 1.1 背景与动机 + +- **插件职责**:MFCCreoDll 通过内置 `HttpServer` 将 Creo OTK 的装配遍历、几何分析、批量操作等能力封装为 HTTP JSON 接口,面向企业内部自动化建模与模型瘦身场景。 +- **前端缺口**:仓库未包含任何前端 UI(无 JavaScript/TypeScript/HTML/CSS 及构建脚本),现有用户依赖命令行或脚本调用,缺乏可视化入口和交互式分析能力。 +- **建设目标**:搭建一套专业化前端工作台,实现模型状态可视化、薄壳削减决策、Shrinkwrap 导出监控、批量操作编排与日志追踪,提升 Creo 工程师的决策效率。 + +### 1.2 设计原则 + +1. **分层解耦**:前端仅通过 HTTP/JSON 与插件交互,不直接依赖 OTK SDK;业务逻辑与呈现分离,便于扩展。 +2. **可观测性**:所有长耗时任务(薄壳分析、Shrinkwrap、批量操作)需暴露进度、耗时、错误分类,便于运维监控。 +3. **可组合性**:提供参数模板、操作剧本与结果数据的导入导出,支持在 CI/CD 或 PLM 流程中复用。 +4. **安全性**:在远程部署场景下需支持 TLS、Token 校验与审计日志,避免 Creo 能力被滥用。 + +## 2. 技术架构蓝图 + +### 2.1 系统角色 + +| 角色 | 描述 | 关键职责 | +| --- | --- | --- | +| **Creo 插件 (C++)** | 运行于 Creo Session 的 DLL,负责与 OTK C++ 交互 | 处理模型操作、几何分析、文件导出、批量执行;输出 JSON | +| **前端应用** | 浏览器或 Electron 桌面应用 | 展示数据、收集用户输入、调度任务、渲染图表与报告 | +| **本地 HTTP 服务** | 插件内置 `HttpServer` | 提供 REST 风格接口,端口默认 12345,支持 CORS | + +### 2.2 前端分层结构 + +1. **Presentation Layer**:基于 React/Vue 等组件库构建视图,包含仪表盘、图表、树状组件、文件选择器。 +2. **Application Layer**:管理路由、权限、国际化、主题,以及业务逻辑 orchestrator。 +3. **Data Layer**:封装 API 客户端(Axios/Fetch),实现请求队列、超时、重试、缓存与统一错误处理。 +4. **State Management Layer**:使用 Redux Toolkit、Zustand 或 Pinia 保存 Creo 会话信息、当前模型上下文、分析结果、任务队列。 +5. **Computation Layer**:通过 Web Worker / Rust WASM 实现本地重计算(排序、聚合、差异分析),减少主线程阻塞。 +6. **Persistence Layer**:IndexedDB、SQLite (在 Electron 中) 或本地文件用于缓存配置、模板、历史日志、结果快照。 + +### 2.3 数据流视图 + +1. 用户在 UI 发起操作 → +2. 前端构造 JSON 请求 → HTTP 客户端发送至 `http://127.0.0.1:12345` → +3. 插件通过 OTK 执行业务逻辑 → +4. 插件返回标准化 JSON(含 `success`, `data`, `error`)→ +5. 前端解析并更新状态树 → +6. 触发 UI 渲染更新 / 日志记录 / 通知提示。 + +## 3. 功能模块设计 + +### 3.1 会话监控中心 + +- 实时展示 Creo 连接状态、版本号、当前工作目录 (`/api/status/creo`)。 +- 记录最近 N 次 API 调用与耗时,支持过滤与导出。 +- 提供 `Reconnect`/`Check Session` 按钮,触发 `/test` 接口用于诊断。 + +### 3.2 模型生命周期控制台 + +- **打开模型**:支持完整路径和 `dirname + filename` 两种输入形式,提供最近使用列表。 +- **保存模型**:展示文件大小、保存耗时、成功提示;允许用户配置保存策略(自动备份等)。 +- **关闭模型**:支持 `force_close` 参数,提示未保存修改。 + +### 3.3 几何分析工作台 + +- **复杂度分析**:对 `/api/analysis/geometry-complexity` 输出进行聚合,展示热力图、TopN 列表、曲线趋势。 +- **层级统计分析**:渲染层级数量、各层组件数、风险评分,并支持导出 CSV/Excel。 + +### 3.4 薄壳分析与削减决策中心 + +- 展示 `safeDeletions`、`suggestedDeletions`、`preserveList` 三类结果,提供可视化标签(置信度、体积收益、风险因素)。 +- 支持按 `confidence`、`volumeReduction`、`component_type` 等条件筛选与排序。 +- 提供策略模拟:选择候选集合后预估整体体积/文件大小/性能收益变化。 + +### 3.5 Shrinkwrap 外壳导出面板 + +- 提供参数表单(质量、弦高、填孔、小面过滤、忽略 Skeleton 等)与预设模板选择。 +- 实时展示执行日志、导出文件路径、文件大小,并允许用户打开文件所在目录。 +- 支持任务排队、超时重试与错误分类提示。 + +### 3.6 装配树与路径操作 + +- 树状/平铺视图展示组件层级,支持懒加载与虚拟滚动。 +- 提供路径复制、按路径删除、子组件统计与风险提示。 +- 结合层级分析结果,对高风险删除操作弹出确认/二次认证。 + +### 3.7 模型搜索与索引视图 + +- 支持关键字、类型、是否在会话、相似度阈值、组件/特征等多筛选条件。 +- 采用命中高亮、标签分类、匹配理由展示(如命中名称/属性/路径)。 +- 支持导出搜索结果、保存搜索条件模板。 + +### 3.8 批量操作编排平台 + +- 流程画布支持拖拽组合操作单元(打开、保存、导出、分析、任务脚本)。 +- 执行时展示步骤进度、耗时、返回 JSON、错误信息,支持暂停/继续/跳过。 +- 支持剧本库、参数化模板、执行历史回放与日志导出。 + +## 4. 接口协议与数据契约 + +### 4.1 请求规范 + +- 所有接口使用 `application/json; charset=utf-8`。 +- 请求体大小默认限制 1 MB (`Config::MAX_REQUEST_SIZE`);前端需对大型任务参数进行压缩或拆分。 +- CORS 头默认为 `Access-Control-Allow-Origin: *`,前端可在浏览器直接调用。 + +### 4.2 响应结构 + +```json +{ + "success": true, + "data": { ... }, + "error": null +} +``` + +- **success**: 布尔值,标识操作是否成功。 +- **data**: 成功时包含有效载荷(模型信息、分析结果、执行统计等)。 +- **error**: 失败时包含错误描述;插件在部分接口中返回结构化错误(含分类、建议)。 + +### 4.3 关键接口示例 + +| URL | 方法 | 描述 | +| --- | --- | --- | +| `/api/model/open` | POST | 打开模型,支持 `file_path` 或 `dirname` + `filename` | +| `/api/model/save` | POST | 保存当前模型,返回文件大小、保存耗时 | +| `/api/model/close` | POST | 关闭模型,支持 `force_close` | +| `/api/analysis/geometry-complexity` | POST | 几何复杂度分析 | +| `/api/analysis/shell-analysis` | POST | 薄壳分析 | +| `/api/creo/shrinkwrap/shell` | POST | Shrinkwrap 外壳导出 | +| `/api/creo/batch-operations` | POST | 批量操作执行 | + +### 4.4 类型定义建议 + +- 使用 TypeScript `type`/`interface` 映射接口返回数据。可通过脚本解析头文件或 JSON 样例生成类型声明,确保编译期类型安全。 +- 对复杂对象(如薄壳分析结果)建立细粒度类型,便于校验和测试。 + +## 5. 核心算法解析 + +### 5.1 薄壳分析算法体系 + +#### 5.1.1 几何数据采集 + +- 利用 `wfcWAssembly::ListDisplayedComponents` 获取装配组件列表。 +- 调用 `pfcSolid::GetMassProperty`、`pfcSolid::GetOutline` 获取体积、包围盒等几何属性。 +- 对细长组件使用 22 点采样(角点 8、面心 6、边心 12)构建顶点集,为 PCA 计算做准备。 + +#### 5.1.2 主方向与包围盒选择 + +1. 计算协方差矩阵 `Σ = (1/N) Σ (v_i - μ)(v_i - μ)^T`,在 OTK 中通过手工矩阵运算实现。 +2. 求解特征值/特征向量,确定主轴 `e1, e2, e3`。 +3. 评估长宽比、表面积、体积,若满足: + - 长宽比 > `OBB_ASPECT_RATIO_THRESHOLD` (默认 4.0) + - 或表面积差值 < `OBB_SURFACE_TOL` + 则使用 OBB;否则采用 AABB。 + +#### 5.1.3 多方向投影与网格离散 + +- 方向集合:`D = {±X, ±Y, ±Z}`,记作 6 个单位向量。 +- 96 × 96 网格离散化平面,对每个网格单元记录最小深度值。 +- 对组件 `c`,投影覆盖率计算公式: + + \[ + \text{visibility}(c) = \frac{1}{|D|} \sum_{d \in D} \mathbb{I}\left( \frac{A_{c,d}}{A_{\text{total},d}} \geq \tau_d \right) + \] + + 其中 `A_{c,d}` 为组件在方向 `d` 的可见面积,`τ_d = 0.001` (0.1%)。 + +#### 5.1.4 双阶段投票机制 + +- **阶段一**:设置严格窗口 `δ_strict = 0.001 * diag(assembly)`,要求 80% 方向可见,识别高置信度外壳。 +- **阶段二**:对剩余组件放宽窗口至 `max(0.002*diag, 0.15*median_thickness)`,重复投影与判定,补齐遗漏。 + +#### 5.1.5 Leave-One-Out (LOO) 归因 + +1. 计算全局可见矩阵 `V_all`。 +2. 对组件 `c`,构造 `V_{-c}`(移除该组件),比较差异: + + \[ + \Delta V_c = \frac{\|V_all - V_{-c}\|_1}{\|V_all\|_1} + \] + +3. `ΔV_c` 小于阈值的组件被判定为内部件。 + +#### 5.1.6 光线追踪验证 + +- 从装配质心 `G` 向候选外壳组件中心发射射线,调用 `pfcCreateSelectionEvaluator` / `pfcSelectionEvaluator::ComputeInterference` 检测遮挡。 +- 如果射线被其他组件阻挡,降低其置信度或标记为内部组件。 + +#### 5.1.7 置信度模型 + +- 综合指标: + - 投影得票数 `votes` + - LOO 贡献度 `ΔV` + - 光线通过率 `ray_clear_ratio` + - 历史统计(可选) + +- 置信度计算示例: + + \[ + \text{confidence} = w_1 \cdot \frac{\text{votes}}{6} + w_2 \cdot (1 - \Delta V) + w_3 \cdot \text{ray\_clear\_ratio} + \] + + 默认 `w_1=0.5, w_2=0.3, w_3=0.2`,结果分级为: + + | 置信度范围 | 标签 | + | --- | --- | + | ≥ 0.75 | 高置信度,推荐删除 | + | 0.45–0.75 | 中置信度,谨慎执行 | + | < 0.45 | 保留或人工检查 | + +#### 5.1.8 前端可视化策略 + +- 为每个组件绘制卡片/表格行,包含: + - 可视化标签(颜色/图标表示置信度等级) + - 体积减少估计 (`volumeReduction`) + - 文件大小减少估计 + - 风险因素列表(如“外露面少”“与关键件相连”) +- 支持拖拽将组件加入“待处理列表”,实时计算整体收益。 + +### 5.2 Shrinkwrap 外壳导出算法 + +#### 5.2.1 调用流程 + +1. 获取当前模型 `session->GetCurrentModel()`,判断类型是否为装配或零件。 +2. 生成目标名称: + - `GenerateSafePartName` 清理特殊字符,限制长度 ≤ 31。 + - 检测冲突(`session->GetModel`),必要时追加序号或时间戳。 +3. 调用 `session->CreatePart(new_name)` 创建结果模型。 +4. `pfcShrinkwrapSurfaceSubsetInstructions::Create` 构造参数: + - `SetQuality(quality)` + - `SetAutoHoleFilling(fill_holes)` + - `SetIgnoreSmallSurfaces(ignore_small_surfaces)` + - `SetSmallSurfPercentage(small_surface_percentage)` + - `SetIgnoreQuilts(ignore_quilts)` + - `SetIgnoreSkeleton(ignore_skeleton)` + - `SetAssignMassProperties(assign_mass_properties)` +5. 执行 `source_solid->ExportShrinkwrap(instructions)`。 +6. 保存文件: + - 获取当前工作目录 `session->GetCurrentDirectory()`。 + - 检查文件存在性 (`_access`);若存在,则调用 `GenerateUniqueFilePath` 自动重命名。 + - 执行 `model->Save()`,使用 `GetFileSizeEx` 计算大小。 + +#### 5.2.2 参数预设 + +| 预设 | quality | chord_height | fill_holes | ignore_small_surfaces | small_surface_percentage | 输出类型 | +| --- | --- | --- | --- | --- | --- | --- | +| fast | 3 | 0.3 | false | false | 0.0 | `solid_surface` | +| balanced | 5 | 0.15 | false | false | 0.0 | `solid_surface` | +| tight | 5 | 0.1 | false | false | 0.0 | `solid_surface` | + +- 前端需在 UI 中展示每个预设的特点、适用场景与注意事项。 + +#### 5.2.3 异常分类与恢复 + +| 异常类型 | 触发条件 | 前端处理建议 | +| --- | --- | --- | +| `Toolkit Error` | OTK 底层故障或模型损坏 | 提示“请检查模型完整性或重启 Creo” | +| `BadArgument` | 参数越界(如质量 > 10) | 高亮错误字段,提供建议范围 | +| `OutOfMemory` | 内存不足 | 建议降低质量或关闭其他程序 | +| `Timeout` | 超过 `timeout_seconds` | 提供重试、增加超时或后台执行选项 | + +#### 5.2.4 前端监控指标 + +- 任务阶段耗时:数据准备、几何计算、文件写入(可按 30%/60%/10% 显示) +- 成品文件大小、路径、生成时间 +- 参数回显与导出日志(含 JSON 原始请求与响应) + +### 5.3 几何复杂度与层级分析 + +#### 5.3.1 指标体系 + +- **复杂度分值**:由 `GeometryAnalyzer::AnalyzeGeometryComplexity` 根据面数、曲率、特征数量、体积等计算。 +- **层级统计**:`HierarchyStatisticsAnalyzer` 输出各层组件数量、深度、类型分布。 +- **风险推荐**:`HierarchyAnalysis` 给出 `safe_deletions` 与 `risky_deletions`,附带原因、风险因素、置信度。 + +#### 5.3.2 前端推导 + +- 对复杂度分值按照分位数划分等级(如 P75/P90/P95),渲染热力图。 +- 计算层级树的深度 `depth = max(level)`,节点数量 `N`,平均分支因子 `b = N / depth`。 +- 对风险推荐进行文本解析,生成可视化标签,如“关键约束”“连接紧固件”“支撑结构”。 + +## 6. 工程实践 + +### 6.1 性能优化 + +- **虚拟化渲染**:对大规模列表/树使用 react-window/react-virtualized。 +- **请求合并**:对频繁调用的 API 实现请求合并/去重;可采用 `AbortController` 取消过期请求。 +- **缓存策略**:对静态数据(如组件字典、部件类型映射)使用 IndexedDB;对分析结果采取版本化缓存,便于比较。 +- **Web Worker**:在 Worker 中执行排序、差异分析、统计聚合,避免主线程卡顿。 + +### 6.2 安全与权限 + +- 在桌面环境下可绑定 Windows AD 账号或企业 SSO;对敏感操作引入角色权限(如只读/分析/执行)。 +- 提供操作日志签名或 Hash 校验,确保操作记录不可篡改。 +- 若开放远程访问,部署反向代理(Nginx)实现 TLS、鉴权头注入、防刷限制。 + +### 6.3 测试策略 + +- **单元测试**:对数据解析、类型映射、排序筛选、状态管理等进行 Jest/Vitest 测试。 +- **契约测试**:使用 Pact/JSON Schema 验证接口契约,防止插件更新导致前端解析失败。 +- **端到端测试**:结合 Cypress/Playwright,模拟用户执行分析、导出、批量操作的完整流程。 +- **性能基准**:对大模型数据(>10k 组件)进行性能压测,确保 UI 响应时间在 300ms 内。 + +## 7. 性能与资源评估 + +### 7.1 任務耗時基线(参考) + +| 任务 | 平均耗时 (中等规模装配) | 影响因素 | +| --- | --- | --- | +| 薄壳分析 | 12–35 s | 组件数量、几何复杂度、硬件配置 | +| Shrinkwrap | 5–20 s | 质量参数、弦高、模型拓扑 | +| 几何复杂度分析 | 3–8 s | 面数、特征数量 | +| 批量操作 (5 步) | 10–25 s | 操作类型、每步耗时 | + +- 前端需对 > 5 s 的任务提供进度反馈;> 30 s 的任务建议支持后台执行。 + +### 7.2 资源占用建议 + +- **内存**:保持前端单页内存占用 < 500 MB;对大型数据采用分页与懒加载。 +- **CPU**:将重计算任务(排序、批量差异)移交 Worker,主线程 CPU 使用率维持在 30% 以下。 +- **网络**:单次请求体 < 1 MB;前端可使用压缩/二进制传输(如 MessagePack)优化传输。 + +## 8. 结论与展望 + +MFCCreoDll 插件已将 Creo OTK 的核心几何处理能力以 HTTP JSON 形式开放,为前端构建专业化的模型管理平台奠定基础。本文档提出了分层架构设计、主要功能模块、关键算法流程与工程最佳实践,特别对薄壳分析与 Shrinkwrap 外壳导出给出了数学原理、参数体系与异常处理策略,可指导前端团队快速落地高可靠、高性能的解决方案。 + +未来工作方向包括: + +1. **实时推送**:激活仓库中的 `WebSocketServer` 模块,与前端的事件总线集成,实现任务进度与状态事件的实时推送。 +2. **三维可视化集成**:通过 Three.js 或 Cesium 引入轻量模型预览,与薄壳分析结果联动高亮显示。 +3. **自动化脚本协同**:提供 CLI/REST 混合模式,允许 DevOps 流程直接调用前端定义的剧本,实现无人值守的模型清理流水线。 +4. **智能化推荐**:基于历史分析结果训练机器学习模型,提高薄壳削减建议与批量操作排序的智能性。 + +通过本技术路线,前端团队能够在不直接依赖 OTK 的情况下,充分发挥现有插件能力,为 Creo 工程师提供高可用、高可观测性的生产力工具。 diff --git a/MFCCreoDll.cpp b/MFCCreoDll.cpp index 8651dca..4f3d0fa 100644 --- a/MFCCreoDll.cpp +++ b/MFCCreoDll.cpp @@ -901,6 +901,110 @@ HttpResponse HierarchyDeleteHandler(const HttpRequest& request) { return response; } +// 子装配体层级删除路由处理器 +HttpResponse SubassemblyHierarchyDeleteHandler(const HttpRequest& request) { + HttpResponse response; + + if (request.method != "POST") { + response.status_code = 405; + response.body = "{\"success\": false, \"error\": \"Method not allowed\"}"; + return response; + } + + try { + // 解析JSON请求 + std::string software_type = ExtractJsonValue(request.body, "software_type"); + std::string subassembly_path = ExtractJsonValue(request.body, "subassembly_path"); + std::string target_level_str = ExtractJsonValue(request.body, "target_level"); + + if (software_type.empty() || subassembly_path.empty() || target_level_str.empty()) { + response.status_code = 400; + response.body = "{\"success\": false, \"error\": \"Missing required parameters: software_type, subassembly_path, target_level\"}"; + return response; + } + + if (software_type != "creo") { + response.status_code = 400; + response.body = "{\"success\": false, \"error\": \"Invalid software_type, must be 'creo'\"}"; + return response; + } + + int target_level; + try { + target_level = std::stoi(target_level_str); + } catch (...) { + response.status_code = 400; + response.body = "{\"success\": false, \"error\": \"Invalid target_level parameter\"}"; + return response; + } + + // 执行子装配体层级删除 + CreoManager::HierarchyDeleteResult result = CreoManager::Instance().DeleteSubassemblyHierarchyComponents(subassembly_path, target_level); + + if (result.success) { + // 构建成功响应 + std::ostringstream json; + json << "{" + << "\"success\": true," + << "\"message\": \"" << EscapeJsonString(result.message) << "\"," + << "\"data\": {" + << "\"subassembly_path\": \"" << EscapeJsonString(subassembly_path) << "\"," + << "\"original_levels\": " << result.original_levels << "," + << "\"target_level\": " << result.target_level << "," + << "\"final_levels\": " << result.final_levels << "," + << "\"deleted_components\": {"; + + // 构建删除组件数据 + bool first_level = true; + for (const auto& level_pair : result.deleted_components) { + if (!first_level) json << ","; + first_level = false; + + json << "\"level_" << level_pair.first << "\": ["; + bool first_component = true; + for (const auto& component : level_pair.second) { + if (!first_component) json << ","; + first_component = false; + json << "\"" << EscapeJsonString(component) << "\""; + } + json << "]"; + } + + json << "}," + << "\"deletion_summary\": {" + << "\"total_deleted\": " << result.total_deleted << "," + << "\"successful\": " << result.successful << "," + << "\"failed\": " << result.failed + << "}" + << "}," + << "\"error\": null" + << "}"; + + response.body = json.str(); + } else { + // 构建错误响应 + std::ostringstream json; + json << "{" + << "\"success\": false," + << "\"message\": \"" << EscapeJsonString(result.message) << "\"," + << "\"error\": \"" << EscapeJsonString(result.error_message) << "\"" + << "}"; + + response.status_code = 500; + response.body = json.str(); + } + + } catch (const std::exception& e) { + response.status_code = 500; + response.body = "{\"success\": false, \"error\": \"JSON parsing error: " + std::string(e.what()) + "\"}"; + } catch (...) { + response.status_code = 500; + response.body = "{\"success\": false, \"error\": \"Unknown error during subassembly hierarchy deletion\"}"; + } + + return response; +} + // 薄壳化分析处理器 HttpResponse ShellAnalysisHandler(const HttpRequest& request) { HttpResponse response; @@ -1858,7 +1962,8 @@ std::vector ExtractBatchOperations(const std::string& json) { std::vector param_keys = {"export_path", "geom_flags", "force_delete", "project_name", "target_level", "quality", "chord_height", "output_file_path", "file_path", - "open_mode"}; + "open_mode", "working_directory", "input_file_path", + "shrinkwrap_output_path", "step_export_path"}; for (const auto& key : param_keys) { std::string value = ExtractJsonValue(params_json, key); @@ -2010,6 +2115,7 @@ extern "C" int user_initialize( g_http_server->SetRouteHandler("/api/export/model", ExportModelHandler); g_http_server->SetRouteHandler("/api/creo/analysis/hierarchy", HierarchyAnalysisHandler); g_http_server->SetRouteHandler("/api/creo/hierarchy/delete", HierarchyDeleteHandler); + g_http_server->SetRouteHandler("/api/creo/subassembly/hierarchy/delete", SubassemblyHierarchyDeleteHandler); g_http_server->SetRouteHandler("/api/analysis/shell-analysis", ShellAnalysisHandler); g_http_server->SetRouteHandler("/api/model/save", SaveModelHandler); g_http_server->SetRouteHandler("/api/model/close", CloseModelHandler); diff --git a/MFCCreoDll/x64/Debug/AuthManager.obj b/MFCCreoDll/x64/Debug/AuthManager.obj index 4fdd98f..e75b11d 100644 Binary files a/MFCCreoDll/x64/Debug/AuthManager.obj and b/MFCCreoDll/x64/Debug/AuthManager.obj differ diff --git a/MFCCreoDll/x64/Debug/BatchOperationManager.obj b/MFCCreoDll/x64/Debug/BatchOperationManager.obj index c0028e4..3f4555d 100644 Binary files a/MFCCreoDll/x64/Debug/BatchOperationManager.obj and b/MFCCreoDll/x64/Debug/BatchOperationManager.obj differ diff --git a/MFCCreoDll/x64/Debug/ComponentChildrenManager.obj b/MFCCreoDll/x64/Debug/ComponentChildrenManager.obj index 04cdcc5..8ed5720 100644 Binary files a/MFCCreoDll/x64/Debug/ComponentChildrenManager.obj and b/MFCCreoDll/x64/Debug/ComponentChildrenManager.obj differ diff --git a/MFCCreoDll/x64/Debug/CreoManager.obj b/MFCCreoDll/x64/Debug/CreoManager.obj index 7d934b0..8347365 100644 Binary files a/MFCCreoDll/x64/Debug/CreoManager.obj and b/MFCCreoDll/x64/Debug/CreoManager.obj differ diff --git a/MFCCreoDll/x64/Debug/CreoUtilities.obj b/MFCCreoDll/x64/Debug/CreoUtilities.obj index 83b394c..2bc8627 100644 Binary files a/MFCCreoDll/x64/Debug/CreoUtilities.obj and b/MFCCreoDll/x64/Debug/CreoUtilities.obj differ diff --git a/MFCCreoDll/x64/Debug/GeometryAnalyzer.obj b/MFCCreoDll/x64/Debug/GeometryAnalyzer.obj index c6e40e2..a88481c 100644 Binary files a/MFCCreoDll/x64/Debug/GeometryAnalyzer.obj and b/MFCCreoDll/x64/Debug/GeometryAnalyzer.obj differ diff --git a/MFCCreoDll/x64/Debug/HierarchyStatisticsAnalyzer.obj b/MFCCreoDll/x64/Debug/HierarchyStatisticsAnalyzer.obj index ffdc0a0..35226f9 100644 Binary files a/MFCCreoDll/x64/Debug/HierarchyStatisticsAnalyzer.obj and b/MFCCreoDll/x64/Debug/HierarchyStatisticsAnalyzer.obj differ diff --git a/MFCCreoDll/x64/Debug/HttpRouter.obj b/MFCCreoDll/x64/Debug/HttpRouter.obj index e3b113a..adfd90e 100644 Binary files a/MFCCreoDll/x64/Debug/HttpRouter.obj and b/MFCCreoDll/x64/Debug/HttpRouter.obj differ diff --git a/MFCCreoDll/x64/Debug/HttpServer.obj b/MFCCreoDll/x64/Debug/HttpServer.obj index fa1dd4a..21e584d 100644 Binary files a/MFCCreoDll/x64/Debug/HttpServer.obj and b/MFCCreoDll/x64/Debug/HttpServer.obj differ diff --git a/MFCCreoDll/x64/Debug/JsonHelper.obj b/MFCCreoDll/x64/Debug/JsonHelper.obj index 8733822..daecdf5 100644 Binary files a/MFCCreoDll/x64/Debug/JsonHelper.obj and b/MFCCreoDll/x64/Debug/JsonHelper.obj differ diff --git a/MFCCreoDll/x64/Debug/Logger.obj b/MFCCreoDll/x64/Debug/Logger.obj index 4437e74..a96b51e 100644 Binary files a/MFCCreoDll/x64/Debug/Logger.obj and b/MFCCreoDll/x64/Debug/Logger.obj differ diff --git a/MFCCreoDll/x64/Debug/MFCCreoDll.obj b/MFCCreoDll/x64/Debug/MFCCreoDll.obj index 9c85005..b43a29c 100644 Binary files a/MFCCreoDll/x64/Debug/MFCCreoDll.obj and b/MFCCreoDll/x64/Debug/MFCCreoDll.obj differ diff --git a/MFCCreoDll/x64/Debug/MFCCreoDll.pch b/MFCCreoDll/x64/Debug/MFCCreoDll.pch index 076b112..4d3917e 100644 Binary files a/MFCCreoDll/x64/Debug/MFCCreoDll.pch and b/MFCCreoDll/x64/Debug/MFCCreoDll.pch differ diff --git a/MFCCreoDll/x64/Debug/MFCCreoDll.tlog/CL.read.1.tlog b/MFCCreoDll/x64/Debug/MFCCreoDll.tlog/CL.read.1.tlog index bc48e74..fca3649 100644 Binary files a/MFCCreoDll/x64/Debug/MFCCreoDll.tlog/CL.read.1.tlog and b/MFCCreoDll/x64/Debug/MFCCreoDll.tlog/CL.read.1.tlog differ diff --git a/MFCCreoDll/x64/Debug/ModelAnalyzer.obj b/MFCCreoDll/x64/Debug/ModelAnalyzer.obj index 619dde6..7271fdc 100644 Binary files a/MFCCreoDll/x64/Debug/ModelAnalyzer.obj and b/MFCCreoDll/x64/Debug/ModelAnalyzer.obj differ diff --git a/MFCCreoDll/x64/Debug/ModelSearchEngine.obj b/MFCCreoDll/x64/Debug/ModelSearchEngine.obj index 8b0aa0d..c483fdd 100644 Binary files a/MFCCreoDll/x64/Debug/ModelSearchEngine.obj and b/MFCCreoDll/x64/Debug/ModelSearchEngine.obj differ diff --git a/MFCCreoDll/x64/Debug/ModelSearchHandler.obj b/MFCCreoDll/x64/Debug/ModelSearchHandler.obj index 3566440..2e7ef64 100644 Binary files a/MFCCreoDll/x64/Debug/ModelSearchHandler.obj and b/MFCCreoDll/x64/Debug/ModelSearchHandler.obj differ diff --git a/MFCCreoDll/x64/Debug/PathDeleteManager.obj b/MFCCreoDll/x64/Debug/PathDeleteManager.obj index a2e807f..08f49cc 100644 Binary files a/MFCCreoDll/x64/Debug/PathDeleteManager.obj and b/MFCCreoDll/x64/Debug/PathDeleteManager.obj differ diff --git a/MFCCreoDll/x64/Debug/ServerManager.obj b/MFCCreoDll/x64/Debug/ServerManager.obj index 7f0c3bd..8a43a5c 100644 Binary files a/MFCCreoDll/x64/Debug/ServerManager.obj and b/MFCCreoDll/x64/Debug/ServerManager.obj differ diff --git a/MFCCreoDll/x64/Debug/ShellExportHandler.obj b/MFCCreoDll/x64/Debug/ShellExportHandler.obj index e5accd1..6eb0d91 100644 Binary files a/MFCCreoDll/x64/Debug/ShellExportHandler.obj and b/MFCCreoDll/x64/Debug/ShellExportHandler.obj differ diff --git a/MFCCreoDll/x64/Debug/ShrinkwrapManager.obj b/MFCCreoDll/x64/Debug/ShrinkwrapManager.obj index a71d3d7..855a80e 100644 Binary files a/MFCCreoDll/x64/Debug/ShrinkwrapManager.obj and b/MFCCreoDll/x64/Debug/ShrinkwrapManager.obj differ diff --git a/MFCCreoDll/x64/Debug/WebSocketServer.obj b/MFCCreoDll/x64/Debug/WebSocketServer.obj index eaad22e..fef38ff 100644 Binary files a/MFCCreoDll/x64/Debug/WebSocketServer.obj and b/MFCCreoDll/x64/Debug/WebSocketServer.obj differ diff --git a/MFCCreoDll/x64/Debug/pch.obj b/MFCCreoDll/x64/Debug/pch.obj index ec8b81f..fe6dbae 100644 Binary files a/MFCCreoDll/x64/Debug/pch.obj and b/MFCCreoDll/x64/Debug/pch.obj differ diff --git a/MFCCreoDll/x64/Debug/vc143.idb b/MFCCreoDll/x64/Debug/vc143.idb index 57e36f2..adf6a88 100644 Binary files a/MFCCreoDll/x64/Debug/vc143.idb and b/MFCCreoDll/x64/Debug/vc143.idb differ diff --git a/MFCCreoDll/x64/Debug/vc143.pdb b/MFCCreoDll/x64/Debug/vc143.pdb index a0046b5..38c7fea 100644 Binary files a/MFCCreoDll/x64/Debug/vc143.pdb and b/MFCCreoDll/x64/Debug/vc143.pdb differ diff --git a/Shell Optimization Solution.md b/Shell Optimization Solution.md deleted file mode 100644 index 355657c..0000000 --- a/Shell Optimization Solution.md +++ /dev/null @@ -1,147 +0,0 @@ - - - - -在 Creo 5 实现“只保留装配外壳、剔除内部”的完整方案(不含代码)。我把 GUI 操作、参数取值、产物管理、验证与批处理、以及二次开发(OTKJ-Link)落地步骤都拆开写清楚,尽量覆盖坑点与细节。 - ---- - -## 0. 目标与产物约定 -- 目标:从一个复杂装配生成一个“仅含外表面”的轻量模型,用于快速打开与尺寸测量;内部零件全部被忽略移除。 -- 产物(建议二选一) - 1) 独立外壳零件(.prt):与原装配解耦,适合下游共享、测量与仿真前置。 - 2) 装配内的 Envelope 简化表示:与原装配保持关联,装配更新后外壳可随动。 - ---- - -## 1. 推荐主线:Shrinkwrap(Outer shell)生成独立外壳零件 - -### 1.1 GUI 操作步骤(Creo 5) -1) 打开顶层装配(建议在 Master Simp Rep 下)。 -2) File → Save As → Save a Copy,Type 选择 Shrinkwrap。 -3) 在 Shrinkwrap 对话框中设置: - - Collection method:选 Outer shell(关键!只采集装配外表面,内部自动忽略)。 - - Quality level:建议 7–9(1–10,越高越细;外观准确性与文件大小时间平衡)。 - - Fill holes Gap control: - - 若希望保留通孔、开窗等细节以便测量,关闭 Fill holes; - - 若要形成更“紧”的外壳,开启 Fill holes 并设合适 Max gap(例如 0.5–1.0 mm)。 - - Ignore small faces Chord height: - - 开启 Ignore small faces,阈值建议取关键尺寸的 0.1–0.5%(避免小倒角小标志导致外壳噪声); - - Chord height(弦高)控制三角化误差:0.05–0.2 mm 视模型尺度而定。 - - Output:优先选 SolidSurface geometry(而非纯 Facet),便于后续测量与布尔运算。 -4) 命名:`AsmName_SW_OS_Q8_Gap1.prt` 一类的可读命名(含方法与关键参数)。 -5) 生成后自动打开外壳零件,保存。 - - 备注:Outer shell 的判断在内核里处理,不依赖视角;相比“自研判定外露面”更稳健。 - -### 1.2 外壳零件质量与可用性优化 -- 文件瘦身:若外观面过密,可适度降低 Quality 或放宽 Chord height,再次导出比对外观差异。 -- 边角锯齿:提高 Quality 或降低 Chord height;必要时对外壳零件做少量 SmoothHeal。 -- 测量稳定性:确保输出为 实体或封闭曲面。若为 Quilt,可用 SolidifyThicken 封体。 - -### 1.3 测量建议 -- 外形尺寸:直接在外壳零件中用 Measure(边面基准)测量;对大模型可用 Section 截面测量。 -- 内腔近似尺寸(若已填孔形成封闭外壳):用 Mass Properties 读体积;或用 OffsetSection 制作参考几何后测量。 - ---- - -## 2. 关联更新方案:Envelope(简化表示) - -### 2.1 适用场景 -- 需要随装配设计变更而更新“外壳视图”,又不想每次手工导出 Shrinkwrap。 - -### 2.2 GUI 步骤(Creo 5) -1) 在顶层装配打开 View Manager → Simp Rep。 -2) 新建一个 Envelope 类型的简化表示(或使用 Default Envelope)。 -3) 在 Envelope 设置中选择基于 ShrinkwrapOuter shell 的规则生成外壳表示(与 1.1 的参数一致或略放宽)。 -4) 命名与管理:`ENV_OS_Q7`、`ENV_FAST` 等,便于在总装下游装配中直接切换加载。 - -### 2.3 使用与维护 -- 在需要快速打开或测量时,切换到 Envelope simp rep 加载; -- 装配更新后,更新 Envelope 以同步外壳;必要时建立 Update note 流程 防止遗漏。 - ---- - -## 3. 二次开发(OTK J-Link)无代码落地要点 - - 需求:批量对多套装配生成外壳;或一键生成外壳并存放到统一位置;或在 PDM 动作里挂钩。 - -### 3.1 配置权限 -- 许可:确保有 Creo TOOLKITOTK 的开发许可; -- 版本匹配:开发环境与 Creo 5 版本匹配(头文件与库一致); -- 数据访问:若通过 Windchill工管系统,确认签出签入策略(避免生成失败或卡权限)。 - -### 3.2 运行时输入输出设计 -- 输入:顶层装配文件路径、Simp Rep(默认 Master)、Shrinkwrap 参数(方法=Outer shell、质量、孔洞、弦高、忽略小面阈值、输出类型)。 -- 输出:外壳零件保存路径、命名规范、版本控制策略(例如加日期参数摘要)。 -- 日志:记录源装配名、面数体积、生成时间、参数集、失败原因码(空装配、拓扑自交等)。 - -### 3.3 参数集(建议预设三档) -- Fast:Q=5、Chord=0.2–0.3、Ignore small faces=开(阈值偏大)、Fill holes=关;用于大批量初筛浏览。 -- Balanced:Q=7–8、Chord=0.1、Ignore small faces=开(中等阈值)、Fill holes=按需;用于常规测量。 -- Tight:Q=9–10、Chord=0.05、Ignore small faces=关或小阈值、Fill holes=按需;用于高精度测量对外交付。 - -### 3.4 错误处理与回退 -- 组件缺失家族表未实例化:先触发 RegenerateResolve,失败则跳过并记录。 -- 几何不闭合Heal 失败:降阶为 Surface 外壳 输出并提示需要手修;或自动放宽 ChordGap 后重试一次。 -- 内存占用高超时:对超大装配自动切至 Fast 档;或先对源装配应用一个临时 Rule-based Simp Rep(如按体积阈值剔除小紧固件),再做 Shrinkwrap。 - ---- - -## 4. 备选与增强 - -### 4.1 规则简化(可作为 Shrinkwrap 前置加速) -- 在 View Manager → Simp Rep 新建 Rule-based 表示: - - Exclude 体积质量尺寸小于阈值的组件(大量螺钉、垫片先剔除,可显著提速 Shrinkwrap)。 - - Exclude 指定层(如电气标识层)或指定类型(弹簧、柔性件)。 -- 在该 Simp Rep 下再执行 Shrinkwrap Outer shell(步骤同 1.1)。 - -### 4.2 结果校核(强烈建议纳入流程) -- 外形偏差对比:将外壳与原装配叠合,做 Global Interference Clearance 或 Compare Geometry,抽查 3–5 条关键尺寸偏差(目标 ≤ 0.2 mm 或依项目设限)。 -- 质量属性:若外壳作为近似质量模型使用,校核质量与体积差异阈值(例如 ≤ 2%)。 -- 开孔保留性:若测量依赖孔窗口,抽检是否被 Fill holes 意外封闭。 - -### 4.3 命名与配置管理 -- 统一命名模版:`项目_装配_SW_OS_QChordGap_日期.prt`; -- 在模型参数中写入:`SW_METHOD=OuterShell`, `SW_QUALITY=8`, `SW_FILL_HOLES=No`, `SW_CHORD=0.10` 等,便于追溯; -- 若走 PDM,定义外壳零件的 生命周期与可见性(只读、对外可分享)。 - ---- - -## 5. 典型问题与处理建议(FAQ) - -- 外壳看起来“破面漏面” - - 提高 Quality 或减小 Chord height; - - 关闭 Ignore small faces 或调小阈值; - - 输出 Surface 后用 Heal Geometry 尝试修复,再 Solidify。 -- 文件仍然很大生成缓慢 - - 先用 Rule-based Simp Rep 排除小件再做 Shrinkwrap; - - 减小 Quality,增大 Chord,或关闭高代价选项(如高级填孔); - - 分层执行:对大子装先各自 Shrinkwrap,再在顶层对这些外壳做二次 Shrinkwrap。 -- 关键小特征被抹平,测不到 - - 关掉 Ignore small faces 或降低其阈值; - - 提高 Quality 降低 Chord; - - 对关键区域单独做 Local Refine(先对关键子装独立 Shrinkwrap,再装配上来)。 - ---- - -## 6. 一页式落地清单(可直接照此执行) - -1) 选择产物:独立外壳零件(默认) Envelope 简化表示(需关联)。 -2) (可选)前置规则简化:排除小件与无关层,加速 Shrinkwrap。 -3) Shrinkwrap 参数: - - Method=Outer shell;Quality=7–9;Chord=0.05–0.15;Ignore small faces=开(阈值合理);Fill holes=按需。 - - Output=SolidSurface geometry。 -4) 命名与保存:采用统一命名与参数写入;入库或归档。 -5) 校核:抽检 3–5 条关键外形尺寸;检查孔窗口保留性;必要时微调参数重生。 -6) 测量分发:在外壳零件中测量;对外提供该外壳模型以减少泄密与体量。 -7) (可选)自动化:准备三档参数集,批处理顶层装配与重点子装;记录日志。 - ---- - -### 结论 -- 在 Creo 5,用 Shrinkwrap(Outer shell) 就能稳定地“只保留外壳、删除内部”;若要关联更新,用 Envelope 简化表示。 -- 关键在于 Outer shell + 合理的 QualityChordFillIgnore small faces 参数组合,并配合 规则简化与结果校核。 -- 若需要规模化与一致性,按第 3 节的无代码要点完成 OTKJ-Link 的批处理与参数固化即可。 - -如你愿意,我可以把以上步骤整理成一张操作卡(流程图 + 参数建议表),便于团队统一执行。 \ No newline at end of file diff --git a/check_build.bat b/check_build.bat deleted file mode 100644 index 33f1257..0000000 --- a/check_build.bat +++ /dev/null @@ -1,35 +0,0 @@ -@echo off -echo Checking build status... -echo. - -echo === Modified Files Summary === -echo 1. ShrinkwrapManager.h - Added timeout_seconds field -echo 2. ShellExportHandler.cpp - Added timeout parameter parsing -echo 3. MFCCreoDll.cpp - Dynamic timeout support -echo 4. ShrinkwrapManager.cpp - Enhanced error handling -echo. - -echo === Key Changes === -echo - Dynamic timeout: 10-300 seconds range -echo - Better error messages for different OTK exceptions -echo - Backward compatible API -echo. - -echo === Error Handling Types === -echo - pfcXToolkitError: General Creo operation failures -echo - pfcXBadArgument: Invalid parameter values -echo - pfcXToolkitOutOfMemory: OTK memory issues -echo - std::bad_alloc: System memory issues -echo - Unknown errors: Catch-all handler -echo. - -echo === Testing === -echo Use the following files to test: -echo - test_timeout.py: Python automated testing -echo - test_timeout.bat: Manual curl testing -echo - SHRINKWRAP_OPTIMIZATION.md: Complete documentation -echo. - -echo Build should now compile without errors. -echo Next: Compile the project and test with Creo. -pause \ No newline at end of file diff --git a/test_timeout.bat b/test_timeout.bat deleted file mode 100644 index 716945f..0000000 --- a/test_timeout.bat +++ /dev/null @@ -1,32 +0,0 @@ -@echo off -echo Testing Shrinkwrap API with different timeout values -echo. - -echo === Test 1: 15 second timeout === -curl -X POST http://localhost:12345/api/creo/shrinkwrap/shell ^ - -H "Content-Type: application/json" ^ - -d "{\"software_type\":\"creo\",\"project_name\":\"Timeout Test 1\",\"quality\":5,\"output_file_path\":\"test1.prt\",\"timeout_seconds\":15}" -echo. -echo. - -echo === Test 2: 60 second timeout === -curl -X POST http://localhost:12345/api/creo/shrinkwrap/shell ^ - -H "Content-Type: application/json" ^ - -d "{\"software_type\":\"creo\",\"project_name\":\"Timeout Test 2\",\"quality\":5,\"output_file_path\":\"test2.prt\",\"timeout_seconds\":60}" -echo. -echo. - -echo === Test 3: Invalid timeout (should use default) === -curl -X POST http://localhost:12345/api/creo/shrinkwrap/shell ^ - -H "Content-Type: application/json" ^ - -d "{\"software_type\":\"creo\",\"project_name\":\"Timeout Test 3\",\"quality\":5,\"output_file_path\":\"test3.prt\",\"timeout_seconds\":5}" -echo. -echo. - -echo === Test 4: No timeout specified (should use default) === -curl -X POST http://localhost:12345/api/creo/shrinkwrap/shell ^ - -H "Content-Type: application/json" ^ - -d "{\"software_type\":\"creo\",\"project_name\":\"Timeout Test 4\",\"quality\":5,\"output_file_path\":\"test4.prt\"}" -echo. - -pause \ No newline at end of file diff --git a/test_timeout.py b/test_timeout.py deleted file mode 100644 index 0000c26..0000000 --- a/test_timeout.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python3 -""" -简单的HTTP测试脚本,测试shrinkwrap接口的timeout功能 -""" -import json -import requests -import sys -import time - -def test_shrinkwrap_timeout(timeout_seconds=60): - """ - 测试shrinkwrap接口的超时功能 - """ - url = "http://localhost:12345/api/creo/shrinkwrap/shell" - - # 测试数据 - test_data = { - "software_type": "creo", - "project_name": "Timeout Test", - "method": "outer_shell", - "quality": 5, - "chord_height": 0.1, - "fill_holes": False, - "ignore_small_surfaces": False, - "small_surface_percentage": 0.0, - "output_file_path": "test_shrinkwrap_timeout.prt", - "output_type": "solid_surface", - "ignore_quilts": True, - "ignore_skeleton": True, - "assign_mass_properties": False, - "preset": "balanced", - "timeout_seconds": timeout_seconds # 使用动态超时 - } - - headers = { - "Content-Type": "application/json" - } - - print(f"测试shrinkwrap接口,超时设置: {timeout_seconds}秒") - print(f"URL: {url}") - print(f"请求数据: {json.dumps(test_data, indent=2, ensure_ascii=False)}") - - try: - start_time = time.time() - response = requests.post(url, json=test_data, headers=headers, timeout=timeout_seconds+10) - end_time = time.time() - - print(f"请求耗时: {end_time - start_time:.2f}秒") - print(f"HTTP状态码: {response.status_code}") - print(f"响应内容: {response.text}") - - if response.status_code == 200: - print("✓ 请求成功") - return True - elif response.status_code == 504: - print("✓ 超时处理正常") - return True - else: - print(f"✗ 请求失败: {response.status_code}") - return False - - except requests.exceptions.Timeout: - print("✗ HTTP客户端超时") - return False - except requests.exceptions.ConnectionError: - print("✗ 连接失败,请确保DLL服务正在运行") - return False - except Exception as e: - print(f"✗ 请求异常: {e}") - return False - -def test_different_timeouts(): - """ - 测试不同的超时设置 - """ - print("=== 测试不同超时设置 ===\n") - - # 测试各种超时值 - test_cases = [ - {"timeout": 15, "desc": "短超时测试"}, - {"timeout": 30, "desc": "默认超时测试"}, - {"timeout": 120, "desc": "长超时测试"}, - {"timeout": 5, "desc": "极短超时测试(应被限制为10秒)"}, - {"timeout": 400, "desc": "超长超时测试(应被限制为300秒)"} - ] - - results = [] - for case in test_cases: - print(f"--- {case['desc']} ---") - success = test_shrinkwrap_timeout(case['timeout']) - results.append({"case": case['desc'], "success": success}) - print() - time.sleep(2) # 间隔等待 - - print("=== 测试结果汇总 ===") - for result in results: - status = "✓" if result['success'] else "✗" - print(f"{status} {result['case']}") - -if __name__ == "__main__": - if len(sys.argv) > 1: - try: - timeout = int(sys.argv[1]) - test_shrinkwrap_timeout(timeout) - except ValueError: - print("参数错误,请输入有效的超时秒数") - sys.exit(1) - else: - test_different_timeouts() \ No newline at end of file diff --git a/x64/Debug/MFCCreoDll.dll b/x64/Debug/MFCCreoDll.dll index a47edc4..964fb5f 100644 Binary files a/x64/Debug/MFCCreoDll.dll and b/x64/Debug/MFCCreoDll.dll differ diff --git a/x64/Debug/MFCCreoDll.pdb b/x64/Debug/MFCCreoDll.pdb index e1a541d..29e85db 100644 Binary files a/x64/Debug/MFCCreoDll.pdb and b/x64/Debug/MFCCreoDll.pdb differ