主要功能: - 新增Shrinkwrap外壳导出API (/api/creo/shrinkwrap/shell) - 使用OTK SurfaceSubsetInstructions实现真正的外壳导出 - 智能重名处理,自动生成唯一文件名 - 完全使用用户参数配置,无硬编码限制 性能优化: - 移除耗时的装配体分析和差异计算 - 简化文件保存逻辑,统一保存到工作目录 - 精简API响应格式,专注核心导出功能 - 大幅提升导出速度和系统稳定性 技术突破: - 解决Windows API宏冲突问题 (GetCurrentDirectory) - 实现SurfaceSubset vs MergedSolid性能差异优化 - 建立稳定的跨线程OTK操作机制 - 支持装配体和零件的统一外壳导出 文件变更: + ShrinkwrapManager.h/cpp - 核心Shrinkwrap功能实现 + ShellExportHandler.h/cpp - HTTP API处理逻辑 * MFCCreoDll.cpp - 集成新的消息处理和路由 * CLAUDE.md - 更新项目文档 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
244 lines
8.9 KiB
C++
244 lines
8.9 KiB
C++
#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");
|
|
|
|
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;
|
|
} |