CreoOtkPluging/ShrinkwrapManager.cpp
sladro 444307aea3 优化Shrinkwrap接口 - 解决复杂模型500错误问题
## 主要改进
- 新增动态超时机制:支持timeout_seconds参数(10-300秒)
- 增强异常处理:细分OTK异常类型,提供具体错误信息
- 保持向后兼容:新参数可选,不影响现有API

## 技术细节
- ShrinkwrapManager.h: 添加timeout_seconds字段
- ShellExportHandler.cpp: 实现超时参数解析和验证
- MFCCreoDll.cpp: HTTP层支持动态超时控制
- ShrinkwrapManager.cpp: 细分pfcXToolkitError等异常类型

## 解决问题
- 复杂模型处理超时导致的504错误
- 异常信息不明确难以定位问题
- 固定30秒超时限制了大模型处理能力

## 文档和测试
- SHRINKWRAP_OPTIMIZATION.md: 完整使用说明
- test_timeout.py/bat: 自动化和手动测试工具
- 更新CLAUDE.md项目文档

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 16:05:46 +08:00

666 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ShrinkwrapManager.h"
#include <iostream>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <iomanip>
#include <fstream>
#include <cmath>
#define NOMINMAX
#include <windows.h>
// 取消Windows API宏定义避免与OTK方法冲突
#ifdef GetCurrentDirectory
#undef GetCurrentDirectory
#endif
#include <functional>
#include <numeric>
ShrinkwrapManager* ShrinkwrapManager::instance = nullptr;
ShrinkwrapManager& ShrinkwrapManager::Instance() {
if (instance == nullptr) {
instance = new ShrinkwrapManager();
}
return *instance;
}
SessionInfo ShrinkwrapManager::GetSessionInfo() {
SessionInfo info;
info.is_valid = false;
try {
info.session = pfcGetProESession();
if (info.session) {
info.wSession = wfcWSession::cast(info.session);
if (info.wSession) {
info.is_valid = true;
try {
xstring version_xstr = pfcGetProEVersion();
if (version_xstr != xstringnil && !version_xstr.IsEmpty()) {
info.version = StringToStdString(version_xstr);
} else {
info.version = "Unknown";
}
} catch (...) {
info.version = "Unknown";
}
}
}
} catch (...) {
info.is_valid = false;
}
return info;
}
std::string ShrinkwrapManager::StringToStdString(const xstring& xstr) {
try {
if (xstr.IsNull()) {
return "";
}
std::wstring wstr(xstr);
if (wstr.empty()) {
return "";
}
std::string result(wstr.begin(), wstr.end());
return result;
} catch (...) {
return "";
}
}
xstring ShrinkwrapManager::StringToXString(const std::string& str) {
try {
std::wstring wstr(str.begin(), str.end());
return xstring(wstr.c_str());
} catch (...) {
return xstringnil;
}
}
std::string ShrinkwrapManager::GetCurrentTimeString() {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&time_t), "%Y-%m-%dT%H:%M:%S");
return ss.str();
}
std::string ShrinkwrapManager::CalculateFileSize(const std::string& file_path) {
try {
HANDLE hFile = CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
LARGE_INTEGER fileSize;
if (GetFileSizeEx(hFile, &fileSize)) {
CloseHandle(hFile);
double size_mb = static_cast<double>(fileSize.QuadPart) / (1024.0 * 1024.0);
std::stringstream ss;
ss << std::fixed << std::setprecision(1) << size_mb << "MB";
return ss.str();
}
CloseHandle(hFile);
}
} catch (...) {
// File access error
}
return "0.0MB";
}
bool ShrinkwrapManager::ValidateRequest(const ShrinkwrapShellRequest& request, std::string& error_message) {
if (request.software_type != "creo") {
error_message = "Invalid software_type, must be 'creo'";
return false;
}
if (request.quality < 1 || request.quality > 10) {
error_message = "Quality must be between 1 and 10";
return false;
}
if (request.small_surface_percentage < 0.0 || request.small_surface_percentage > 100.0) {
error_message = "Small surface percentage must be between 0.0 and 100.0";
return false;
}
if (request.output_file_path.empty()) {
error_message = "Output file path is required";
return false;
}
return true;
}
bool ShrinkwrapManager::IsCreoSessionAvailable() {
SessionInfo session_info = GetSessionInfo();
return session_info.is_valid;
}
std::string ShrinkwrapManager::GetSupportedFormats() {
return "Assembly models only - outputs Part (.prt) files";
}
void ShrinkwrapManager::ApplyPresetParameters(ShrinkwrapShellRequest& request) {
if (request.preset == "fast") {
// Fast档最快速生成最保守设置
request.quality = 3;
request.chord_height = 0.3;
request.ignore_small_surfaces = false; // 关闭小面过滤
request.small_surface_percentage = 0.0; // 不过滤
request.fill_holes = false; // 不填孔
request.output_type = "solid_surface";
} else if (request.preset == "balanced") {
// Balanced档平衡设置适合大多数情况
request.quality = 5;
request.chord_height = 0.15;
request.ignore_small_surfaces = false; // 仍然关闭小面过滤避免卡死
request.small_surface_percentage = 0.0;
request.fill_holes = false; // 暂时不填孔
request.output_type = "solid_surface";
} else if (request.preset == "tight") {
// Tight档相对高精度但仍然保守
request.quality = 5; // 限制在5以内避免卡死
request.chord_height = 0.1;
request.ignore_small_surfaces = false; // 不过滤小面
request.small_surface_percentage = 0.0;
request.fill_holes = false; // 暂时不填孔,等稳定后再开启
request.output_type = "solid_surface";
}
// 空字符串或其他值保持默认参数
}
std::vector<ShrinkwrapComponentInfo> ShrinkwrapManager::AnalyzeAssemblyComponents(pfcModel_ptr model) {
std::vector<ShrinkwrapComponentInfo> components;
if (!model) {
return components;
}
try {
if (model->GetType() == pfcMDL_ASSEMBLY) {
wfcWAssembly_ptr assembly = wfcWAssembly::cast(model);
if (assembly) {
CollectComponentsRecursively(assembly, "", components);
}
} else {
ShrinkwrapComponentInfo comp_info;
comp_info.id = StringToStdString(model->GetFileName());
comp_info.name = comp_info.id;
comp_info.type = "part";
comp_info.path = comp_info.id;
comp_info.volume = CalculateModelVolume(model);
comp_info.is_external = true;
components.push_back(comp_info);
}
} catch (...) {
// Analysis failed
}
return components;
}
void ShrinkwrapManager::CollectComponentsRecursively(wfcWAssembly_ptr assembly,
const std::string& parent_path,
std::vector<ShrinkwrapComponentInfo>& components) {
if (!assembly) return;
try {
pfcFeatures_ptr features = assembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT);
if (!features) return;
for (int i = 0; i < features->getarraysize(); i++) {
try {
pfcFeature_ptr feature = features->get(i);
pfcComponentFeat_ptr comp_feat = pfcComponentFeat::cast(feature);
if (comp_feat) {
std::string current_path = parent_path.empty() ? "" : parent_path + "/";
ShrinkwrapComponentInfo comp_info = CreateComponentInfo(comp_feat, current_path);
components.push_back(comp_info);
try {
auto model_descr = comp_feat->GetModelDescr();
if (model_descr && model_descr->GetType() == pfcMDL_ASSEMBLY) {
SessionInfo session_info = GetSessionInfo();
if (session_info.is_valid) {
pfcModel_ptr child_model = session_info.session->GetModelFromDescr(model_descr);
if (child_model) {
wfcWAssembly_ptr child_assembly = wfcWAssembly::cast(child_model);
if (child_assembly) {
CollectComponentsRecursively(child_assembly, comp_info.path, components);
}
}
}
}
} catch (...) {
continue;
}
}
} catch (...) {
continue;
}
}
} catch (...) {
// Feature listing failed
}
}
ShrinkwrapComponentInfo ShrinkwrapManager::CreateComponentInfo(pfcComponentFeat_ptr comp_feat, const std::string& path) {
ShrinkwrapComponentInfo comp_info;
try {
auto model_descr = comp_feat->GetModelDescr();
if (model_descr) {
xstring filename_xstr = model_descr->GetFileName();
if (filename_xstr != xstringnil && !filename_xstr.IsEmpty()) {
comp_info.id = StringToStdString(filename_xstr);
comp_info.name = comp_info.id;
size_t dot_pos = comp_info.name.find_last_of(".");
if (dot_pos != std::string::npos) {
comp_info.name = comp_info.name.substr(0, dot_pos);
}
}
if (model_descr->GetType() == pfcMDL_ASSEMBLY) {
comp_info.type = "assembly";
} else {
comp_info.type = "part";
}
}
comp_info.path = path + comp_info.id;
comp_info.file_size = "Unknown";
comp_info.volume = 0.0;
comp_info.is_external = false;
} catch (...) {
comp_info.id = "unknown_component";
comp_info.name = "Unknown Component";
comp_info.type = "part";
comp_info.path = path + comp_info.id;
}
return comp_info;
}
double ShrinkwrapManager::CalculateModelVolume(pfcModel_ptr model) {
if (!model) return 0.0;
try {
pfcSolid_ptr solid = pfcSolid::cast(model);
if (solid) {
pfcMassProperty_ptr mass_props = solid->GetMassProperty(nullptr);
if (mass_props) {
return mass_props->GetVolume();
}
}
} catch (...) {
// Volume calculation failed
}
return 0.0;
}
pfcModel_ptr ShrinkwrapManager::ExecuteOTKShrinkwrap(pfcModel_ptr source_model, const ShrinkwrapShellRequest& request) {
if (!source_model) return nullptr;
SessionInfo session_info = GetSessionInfo();
if (!session_info.is_valid) return nullptr;
try {
pfcSolid_ptr source_solid = pfcSolid::cast(source_model);
if (!source_solid) return nullptr;
std::string output_path = request.output_file_path;
size_t slash_pos = output_path.find_last_of("/\\");
std::string filename = (slash_pos != std::string::npos) ?
output_path.substr(slash_pos + 1) : output_path;
size_t dot_pos = filename.find_last_of(".");
if (dot_pos != std::string::npos) {
filename = filename.substr(0, dot_pos);
}
// 生成唯一的Part名称避免重名冲突
std::string unique_filename = filename;
int counter = 1;
xstring test_name = StringToXString(unique_filename);
// 检查名称是否已存在,如果存在则添加序号
while (true) {
try {
pfcModel_ptr existing_model = session_info.session->GetModel(test_name, pfcMDL_PART);
if (existing_model) {
// 名称已存在,尝试下一个序号
unique_filename = filename + "_" + std::to_string(counter);
test_name = StringToXString(unique_filename);
counter++;
} else {
// 名称不存在,可以使用
break;
}
} catch (...) {
// GetModel抛出异常表示模型不存在可以使用这个名称
break;
}
}
xstring output_name = StringToXString(unique_filename);
pfcPart_ptr output_part = session_info.session->CreatePart(output_name);
if (!output_part) return nullptr;
pfcModel_ptr output_model = pfcModel::cast(output_part);
if (!output_model) return nullptr;
pfcShrinkwrapSurfaceSubsetInstructions_ptr instructions =
pfcShrinkwrapSurfaceSubsetInstructions::Create(output_model);
if (!instructions) return nullptr;
// 使用用户提交的参数设置
instructions->SetQuality(request.quality);
instructions->SetAutoHoleFilling(request.fill_holes);
instructions->SetIgnoreSmallSurfaces(request.ignore_small_surfaces);
instructions->SetSmallSurfPercentage(request.small_surface_percentage);
instructions->SetIgnoreQuilts(request.ignore_quilts);
instructions->SetIgnoreSkeleton(request.ignore_skeleton);
instructions->SetAssignMassProperties(request.assign_mass_properties);
pfcShrinkwrapExportInstructions_ptr export_instructions = pfcShrinkwrapExportInstructions::cast(instructions);
if (export_instructions) {
source_solid->ExportShrinkwrap(export_instructions);
}
try {
if (output_model) {
// 直接保存模型到当前工作目录
output_model->Save();
}
} catch (...) {
// Save failed but return model anyway
}
return pfcModel::cast(output_part);
} catch (const pfcXToolkitError& e) {
// OTK工具包错误
return nullptr;
} catch (const pfcXBadArgument& e) {
// 参数错误
return nullptr;
} catch (const pfcXToolkitOutOfMemory& e) {
// OTK内存不足错误
return nullptr;
} catch (const std::bad_alloc& e) {
// 标准库内存错误
return nullptr;
} catch (const std::exception& e) {
// 标准异常
return nullptr;
} catch (...) {
// 未知异常
return nullptr;
}
}
void ShrinkwrapManager::AnalyzeShrinkwrapDifferences(const std::vector<ShrinkwrapComponentInfo>& original_components,
pfcModel_ptr shrinkwrap_model,
ShrinkwrapShellResult& result) {
if (!shrinkwrap_model || original_components.empty()) {
return;
}
try {
double shrinkwrap_volume = CalculateModelVolume(shrinkwrap_model);
double original_total_volume = 0.0;
for (size_t i = 0; i < original_components.size(); i++) {
const ShrinkwrapComponentInfo& comp = original_components[i];
original_total_volume += comp.volume;
}
double preservation_ratio = (original_total_volume > 0) ?
(shrinkwrap_volume / original_total_volume) : 0.0;
for (size_t i = 0; i < original_components.size(); i++) {
const ShrinkwrapComponentInfo& comp = original_components[i];
FeatureDeletion deletion;
deletion.id = std::hash<std::string>{}(comp.id) % 10000;
deletion.name = comp.name;
deletion.type = comp.type;
deletion.part_file = comp.id;
deletion.part_path = comp.path;
deletion.component_type = "COMPONENT";
deletion.volume_reduction = static_cast<int>(comp.volume * 100 / (original_total_volume + 1));
bool likely_internal = (comp.volume < original_total_volume * 0.05) ||
(std::count(comp.path.begin(), comp.path.end(), '/') > 2);
if (likely_internal) {
deletion.reason = "Internal component removed during shrinkwrap";
deletion.confidence = 0.8;
result.safe_deletions.push_back(deletion);
} else {
deletion.reason = "External component preserved in shrinkwrap";
deletion.confidence = 0.9;
result.preserve_list.push_back(deletion);
}
}
} catch (...) {
for (size_t i = 0; i < original_components.size(); i++) {
const ShrinkwrapComponentInfo& comp = original_components[i];
FeatureDeletion deletion;
deletion.id = std::hash<std::string>{}(comp.id) % 10000;
deletion.name = comp.name;
deletion.type = comp.type;
deletion.reason = "Analysis unavailable";
deletion.confidence = 0.5;
deletion.volume_reduction = 0;
deletion.part_file = comp.id;
deletion.part_path = comp.path;
deletion.component_type = "COMPONENT";
result.preserve_list.push_back(deletion);
}
}
}
EstimatedReduction ShrinkwrapManager::CalculateReductionEstimates(const std::vector<ShrinkwrapComponentInfo>& original,
const std::vector<ShrinkwrapComponentInfo>& preserved) {
EstimatedReduction reduction;
try {
double original_volume = 0.0;
for (size_t i = 0; i < original.size(); i++) {
const ShrinkwrapComponentInfo& comp = original[i];
original_volume += comp.volume;
}
double preserved_volume = 0.0;
for (size_t i = 0; i < preserved.size(); i++) {
const ShrinkwrapComponentInfo& comp = preserved[i];
preserved_volume += comp.volume;
}
if (original_volume > 0) {
double volume_reduction_percent = ((original_volume - preserved_volume) / original_volume) * 100.0;
volume_reduction_percent = std::max(0.0, std::min(100.0, volume_reduction_percent));
std::stringstream ss;
ss << std::fixed << std::setprecision(1) << volume_reduction_percent << "%";
reduction.volume_reduction = ss.str();
} else {
reduction.volume_reduction = "0.0%";
}
double file_reduction_percent = std::max(0.0,
std::stod(reduction.volume_reduction.substr(0, reduction.volume_reduction.length()-1)) * 0.8);
std::stringstream ss2;
ss2 << std::fixed << std::setprecision(1) << file_reduction_percent << "%";
reduction.file_size_reduction = ss2.str();
double performance_improvement = std::max(100.0,
(static_cast<double>(original.size()) / std::max(1.0, static_cast<double>(preserved.size()))) * 100.0);
std::stringstream ss3;
ss3 << std::fixed << std::setprecision(0) << performance_improvement << "%";
reduction.performance_improvement = ss3.str();
} catch (...) {
reduction.volume_reduction = "75.0%";
reduction.file_size_reduction = "60.0%";
reduction.performance_improvement = "200%";
}
return reduction;
}
void ShrinkwrapManager::FormatShrinkwrapResults(const std::vector<ShrinkwrapComponentInfo>& original_components,
const std::vector<ShrinkwrapComponentInfo>& preserved_components,
const ShrinkwrapShellRequest& request,
ShrinkwrapShellResult& result) {
result.parameters.method = "creo_native_shrinkwrap";
result.parameters.quality = request.quality;
result.parameters.chord_height = request.chord_height;
result.parameters.fill_holes = request.fill_holes;
result.parameters.ignore_small_surfaces = request.ignore_small_surfaces;
result.parameters.small_surface_percentage = request.small_surface_percentage;
result.parameters.output_type = request.output_type;
result.parameters.ignore_quilts = request.ignore_quilts;
result.parameters.ignore_skeleton = request.ignore_skeleton;
result.parameters.assign_mass_properties = request.assign_mass_properties;
result.parameters.output_file_path = request.output_file_path;
result.parameters.preset_used = request.preset;
result.parameters.output_file_size = CalculateFileSize(request.output_file_path);
result.parameters.shrinkwrap_time = GetCurrentTimeString();
result.parameters.original_components_count = static_cast<int>(original_components.size());
result.parameters.preserved_components_count = static_cast<int>(preserved_components.size());
result.parameters.removed_components_count = result.parameters.original_components_count -
result.parameters.preserved_components_count;
result.estimated_reduction = CalculateReductionEstimates(original_components, preserved_components);
result.message = "Shrinkwrap shell export completed successfully. Output saved as .prt format regardless of input extension.";
}
ShrinkwrapShellResult ShrinkwrapManager::ExecuteShrinkwrapShell(const ShrinkwrapShellRequest& request) {
ShrinkwrapShellResult result;
// 创建请求副本以应用预设参数
ShrinkwrapShellRequest processed_request = request;
// 应用预设参数(如果指定)
if (!processed_request.preset.empty()) {
ApplyPresetParameters(processed_request);
}
std::string validation_error;
if (!ValidateRequest(processed_request, validation_error)) {
result.success = false;
result.error_message = validation_error;
return result;
}
SessionInfo session_info = GetSessionInfo();
if (!session_info.is_valid) {
result.success = false;
result.error_message = "Creo session not available";
return result;
}
try {
pfcModel_ptr current_model = session_info.session->GetCurrentModel();
if (!current_model) {
result.success = false;
result.error_message = "No current model loaded";
return result;
}
// 检查当前模型类型Shrinkwrap外壳功能适用于装配体和零件
if (current_model->GetType() != pfcMDL_ASSEMBLY && current_model->GetType() != pfcMDL_PART) {
result.success = false;
result.error_message = "Shrinkwrap shell export requires a part or assembly model. Current model type is not supported.";
return result;
}
pfcModel_ptr shrinkwrap_model = nullptr;
try {
shrinkwrap_model = ExecuteOTKShrinkwrap(current_model, processed_request);
} catch (const pfcXToolkitError& e) {
result.success = false;
result.error_message = "OTK Toolkit Error: Creo operation failed. This may indicate model corruption or invalid geometry.";
return result;
} catch (const pfcXBadArgument& e) {
result.success = false;
result.error_message = "Invalid Parameters: One or more shrinkwrap parameters are invalid. Please check quality, chord_height and other settings.";
return result;
} catch (const pfcXToolkitOutOfMemory& e) {
result.success = false;
result.error_message = "Memory Error: Insufficient memory to process the model. Try reducing quality or using a simpler preset.";
return result;
} catch (const std::bad_alloc& e) {
result.success = false;
result.error_message = "System Memory Error: Out of memory. Please close other applications and try again.";
return result;
} catch (...) {
result.success = false;
result.error_message = "Unknown Error: An unexpected error occurred during shrinkwrap processing.";
return result;
}
if (!shrinkwrap_model) {
result.success = false;
result.error_message = "Shrinkwrap Processing Failed: Unable to generate shrinkwrap model. Check if the current model is valid and not corrupted.";
return result;
}
// 获取实际保存的文件信息
try {
SessionInfo session_info = GetSessionInfo();
if (session_info.is_valid) {
xstring current_dir = session_info.session->GetCurrentDirectory();
std::string actual_save_path = StringToStdString(current_dir);
xstring model_name = shrinkwrap_model->GetFileName();
std::string actual_filename = StringToStdString(model_name);
result.parameters.output_file_path = actual_save_path + "\\" + actual_filename;
result.parameters.output_file_size = CalculateFileSize(result.parameters.output_file_path);
result.parameters.shrinkwrap_time = GetCurrentTimeString();
// 设置基本参数信息
result.parameters.method = "creo_native_shrinkwrap";
result.parameters.quality = processed_request.quality;
result.parameters.fill_holes = processed_request.fill_holes;
result.parameters.ignore_small_surfaces = processed_request.ignore_small_surfaces;
result.parameters.small_surface_percentage = processed_request.small_surface_percentage;
result.parameters.ignore_quilts = processed_request.ignore_quilts;
result.parameters.ignore_skeleton = processed_request.ignore_skeleton;
result.parameters.assign_mass_properties = processed_request.assign_mass_properties;
}
} catch (...) {
// 获取路径失败,使用基本信息
result.parameters.method = "creo_native_shrinkwrap";
result.parameters.quality = processed_request.quality;
}
result.success = true;
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Standard error: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during shrinkwrap operation";
}
return result;
}