CreoOtkPluging/ShellExportHandler.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

253 lines
9.2 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 "ShellExportHandler.h"
#include <algorithm>
#include <regex>
#include <stdexcept>
HttpResponse ShellExportHandler::HandleShrinkwrapShellRequest(const HttpRequest& request) {
std::string validation_error;
if (!ValidateHttpRequest(request, validation_error)) {
return FormatErrorResponse(400, validation_error);
}
try {
ShrinkwrapShellRequest shrinkwrap_request = ParseShrinkwrapRequest(request.body);
ShrinkwrapShellResult result = ShrinkwrapManager::Instance().ExecuteShrinkwrapShell(shrinkwrap_request);
if (result.success) {
return FormatSuccessResponse(result);
} else {
return FormatErrorResponse(500, result.error_message);
}
} catch (const std::exception& e) {
return FormatErrorResponse(500, "Request processing error: " + std::string(e.what()));
} catch (...) {
return FormatErrorResponse(500, "Unknown error during shrinkwrap shell export");
}
}
std::string ShellExportHandler::ExtractJsonValue(const std::string& json, const std::string& key) {
try {
std::string search_pattern = "\\\"" + key + "\\\"\\s*:\\s*\\\"([^\\\"]*?)\\\"";
std::regex pattern(search_pattern);
std::smatch match;
if (std::regex_search(json, match, pattern)) {
return match[1].str();
}
std::string search_pattern2 = "\\\"" + key + "\\\"\\s*:\\s*([^,}\\s]+)";
std::regex pattern2(search_pattern2);
if (std::regex_search(json, match, pattern2)) {
std::string value = match[1].str();
if (value.front() == '\"' && value.back() == '\"') {
value = value.substr(1, value.length() - 2);
}
return value;
}
} catch (...) {
// Error handling
}
return "";
}
bool ShellExportHandler::ExtractJsonBoolValue(const std::string& json, const std::string& key, bool default_value) {
std::string value = ExtractJsonValue(json, key);
if (value.empty()) {
return default_value;
}
std::string lower_value = value;
std::transform(lower_value.begin(), lower_value.end(), lower_value.begin(), ::tolower);
return (lower_value == "true" || lower_value == "1");
}
int ShellExportHandler::ExtractJsonIntValue(const std::string& json, const std::string& key, int default_value) {
std::string value = ExtractJsonValue(json, key);
if (value.empty()) {
return default_value;
}
try {
return std::stoi(value);
} catch (...) {
return default_value;
}
}
double ShellExportHandler::ExtractJsonDoubleValue(const std::string& json, const std::string& key, double default_value) {
std::string value = ExtractJsonValue(json, key);
if (value.empty()) {
return default_value;
}
try {
return std::stod(value);
} catch (...) {
return default_value;
}
}
std::string ShellExportHandler::EscapeJsonString(const std::string& input) {
std::string output;
output.reserve(input.length() * 2);
for (char c : input) {
switch (c) {
case '\"': output += "\\\""; break;
case '\\': output += "\\\\"; break;
case '\b': output += "\\b"; break;
case '\f': output += "\\f"; break;
case '\n': output += "\\n"; break;
case '\r': output += "\\r"; break;
case '\t': output += "\\t"; break;
default:
if (c < 0x20) {
char buffer[7];
snprintf(buffer, sizeof(buffer), "\\u%04x", static_cast<unsigned char>(c));
output += buffer;
} else {
output += c;
}
break;
}
}
return output;
}
bool ShellExportHandler::ValidateHttpRequest(const HttpRequest& request, std::string& error_message) {
if (request.method != "POST") {
error_message = "Method not allowed. Only POST requests are supported.";
return false;
}
if (request.body.empty()) {
error_message = "Request body is empty";
return false;
}
std::string trimmed_body = Trim(request.body);
if (!StartsWith(trimmed_body, "{") || !EndsWith(trimmed_body, "}")) {
error_message = "Invalid JSON format";
return false;
}
return true;
}
ShrinkwrapShellRequest ShellExportHandler::ParseShrinkwrapRequest(const std::string& json_body) {
ShrinkwrapShellRequest request;
request.software_type = ExtractJsonValue(json_body, "software_type");
if (request.software_type.empty()) {
request.software_type = "creo";
}
std::string project_name = ExtractJsonValue(json_body, "project_name");
if (!project_name.empty()) {
request.project_name = project_name;
}
std::string method = ExtractJsonValue(json_body, "method");
if (!method.empty()) {
request.method = method;
}
request.quality = ExtractJsonIntValue(json_body, "quality", 8);
request.chord_height = ExtractJsonDoubleValue(json_body, "chord_height", 0.1);
request.fill_holes = ExtractJsonBoolValue(json_body, "fill_holes", true);
request.ignore_small_surfaces = ExtractJsonBoolValue(json_body, "ignore_small_surfaces", true);
request.small_surface_percentage = ExtractJsonDoubleValue(json_body, "small_surface_percentage", 0.5);
request.ignore_quilts = ExtractJsonBoolValue(json_body, "ignore_quilts", true);
request.ignore_skeleton = ExtractJsonBoolValue(json_body, "ignore_skeleton", true);
request.assign_mass_properties = ExtractJsonBoolValue(json_body, "assign_mass_properties", false);
request.output_file_path = ExtractJsonValue(json_body, "output_file_path");
request.output_type = ExtractJsonValue(json_body, "output_type");
if (request.output_type.empty()) {
request.output_type = "solid_surface";
}
request.preset = ExtractJsonValue(json_body, "preset");
// 解析超时参数默认30秒最大300秒
request.timeout_seconds = ExtractJsonIntValue(json_body, "timeout_seconds", 30);
if (request.timeout_seconds < 10) {
request.timeout_seconds = 10; // 最小10秒
}
if (request.timeout_seconds > 300) {
request.timeout_seconds = 300; // 最大300秒
}
return request;
}
HttpResponse ShellExportHandler::FormatSuccessResponse(const ShrinkwrapShellResult& result) {
HttpResponse response;
response.status_code = 200;
std::ostringstream json;
json << "{"
<< "\"success\": true,"
<< "\"message\": \"" << EscapeJsonString(result.message) << "\","
<< "\"data\": {"
<< "\"output_file\": \"" << EscapeJsonString(result.parameters.output_file_path) << "\","
<< "\"file_size\": \"" << EscapeJsonString(result.parameters.output_file_size) << "\","
<< "\"export_time\": \"" << EscapeJsonString(result.parameters.shrinkwrap_time) << "\","
<< "\"parameters_used\": {"
<< "\"method\": \"" << EscapeJsonString(result.parameters.method) << "\","
<< "\"quality\": " << result.parameters.quality << ","
<< "\"fill_holes\": " << (result.parameters.fill_holes ? "true" : "false") << ","
<< "\"ignore_small_surfaces\": " << (result.parameters.ignore_small_surfaces ? "true" : "false") << ","
<< "\"small_surface_percentage\": " << result.parameters.small_surface_percentage << ","
<< "\"ignore_quilts\": " << (result.parameters.ignore_quilts ? "true" : "false") << ","
<< "\"ignore_skeleton\": " << (result.parameters.ignore_skeleton ? "true" : "false") << ","
<< "\"assign_mass_properties\": " << (result.parameters.assign_mass_properties ? "true" : "false")
<< "}"
<< "},"
<< "\"error\": null"
<< "}";
response.body = json.str();
return response;
}
HttpResponse ShellExportHandler::FormatErrorResponse(int status_code, const std::string& error_message) {
HttpResponse response;
response.status_code = status_code;
std::ostringstream json;
json << "{"
<< "\"success\": false,"
<< "\"message\": null,"
<< "\"data\": null,"
<< "\"error\": \"" << EscapeJsonString(error_message) << "\""
<< "}";
response.body = json.str();
return response;
}
std::string ShellExportHandler::Trim(const std::string& str) {
size_t start = str.find_first_not_of(" \t\n\r");
if (start == std::string::npos) {
return "";
}
size_t end = str.find_last_not_of(" \t\n\r");
return str.substr(start, end - start + 1);
}
bool ShellExportHandler::StartsWith(const std::string& str, const std::string& prefix) {
return str.length() >= prefix.length() &&
str.compare(0, prefix.length(), prefix) == 0;
}
bool ShellExportHandler::EndsWith(const std::string& str, const std::string& suffix) {
return str.length() >= suffix.length() &&
str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
}