CreoOtkPluging/BatchOperationManager.cpp
sladro a1e0237c9e feat: implement batch operations API for sequential multi-operation execution
- Add BatchOperationManager class with singleton pattern
- Support 7 operation types: save, export, delete_by_path, hierarchy_delete, shrinkwrap, close, open
- Implement sequential execution with individual error isolation
- Add JSON parsing helper for operations array
- Register /api/creo/batch-operations endpoint
- Fix namespace declaration errors in header file

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 17:42:54 +08:00

587 lines
21 KiB
C++

#include "pch.h"
#include "BatchOperationManager.h"
#include "CreoManager.h"
#include "PathDeleteManager.h"
#include "ShrinkwrapManager.h"
#include <sstream>
#include <chrono>
#include <iomanip>
// Helper: Get current time as string
std::string BatchOperationManager::GetCurrentTimeString() {
auto now = std::chrono::system_clock::now();
auto now_time = std::chrono::system_clock::to_time_t(now);
std::tm local_time;
localtime_s(&local_time, &now_time);
std::ostringstream oss;
oss << std::put_time(&local_time, "%Y-%m-%d %H:%M:%S");
return oss.str();
}
// Helper: Escape JSON string
std::string BatchOperationManager::EscapeJsonString(const std::string& str) {
std::string escaped = str;
// Replace backslashes
size_t pos = 0;
while ((pos = escaped.find("\\", pos)) != std::string::npos) {
escaped.replace(pos, 1, "\\\\");
pos += 2;
}
// Replace double quotes
pos = 0;
while ((pos = escaped.find("\"", pos)) != std::string::npos) {
escaped.replace(pos, 1, "\\\"");
pos += 2;
}
// Replace newlines
pos = 0;
while ((pos = escaped.find("\n", pos)) != std::string::npos) {
escaped.replace(pos, 1, "\\n");
pos += 2;
}
// Replace tabs
pos = 0;
while ((pos = escaped.find("\t", pos)) != std::string::npos) {
escaped.replace(pos, 1, "\\t");
pos += 2;
}
return escaped;
}
// Build JSON result for SaveModel
std::string BatchOperationManager::BuildSaveResultJson(const SaveResult& result) {
std::ostringstream json;
json << "{"
<< "\"file_size\":\"" << EscapeJsonString(result.file_size) << "\","
<< "\"save_time\":\"" << EscapeJsonString(result.save_time) << "\","
<< "\"software\":\"" << EscapeJsonString(result.software) << "\","
<< "\"original_file\":\"" << EscapeJsonString(result.original_file) << "\""
<< "}";
return json.str();
}
// Build JSON result for ExportModel
std::string BatchOperationManager::BuildExportResultJson(const ExportResult& result) {
std::ostringstream json;
json << "{"
<< "\"export_path\":\"" << EscapeJsonString(result.export_path) << "\","
<< "\"file_size\":\"" << EscapeJsonString(result.file_size) << "\","
<< "\"format\":\"" << EscapeJsonString(result.format) << "\","
<< "\"export_time\":\"" << EscapeJsonString(result.export_time) << "\","
<< "\"software\":\"" << EscapeJsonString(result.software) << "\","
<< "\"original_file\":\"" << EscapeJsonString(result.original_file) << "\","
<< "\"dirname\":\"" << EscapeJsonString(result.dirname) << "\","
<< "\"filename\":\"" << EscapeJsonString(result.filename) << "\""
<< "}";
return json.str();
}
// Build JSON result for DeleteByPath
std::string BatchOperationManager::BuildDeleteByPathResultJson(const PathDeleteManager::PathDeleteResult& result) {
std::ostringstream json;
json << "{"
<< "\"successfully_deleted\":[";
for (size_t i = 0; i < result.successfully_deleted.size(); i++) {
if (i > 0) json << ",";
json << "\"" << EscapeJsonString(result.successfully_deleted[i]) << "\"";
}
json << "],\"failed_to_delete\":[";
for (size_t i = 0; i < result.failed_to_delete.size(); i++) {
if (i > 0) json << ",";
json << "\"" << EscapeJsonString(result.failed_to_delete[i]) << "\"";
}
json << "],\"total_requested\":" << result.total_requested
<< ",\"successful\":" << result.successful
<< ",\"failed\":" << result.failed
<< "}";
return json.str();
}
// Build JSON result for HierarchyDelete
std::string BatchOperationManager::BuildHierarchyDeleteResultJson(const CreoManager::HierarchyDeleteResult& result) {
std::ostringstream json;
json << "{"
<< "\"message\":\"" << EscapeJsonString(result.message) << "\","
<< "\"original_levels\":" << result.original_levels << ","
<< "\"target_level\":" << result.target_level << ","
<< "\"final_levels\":" << result.final_levels << ","
<< "\"total_deleted\":" << result.total_deleted << ","
<< "\"successful\":" << result.successful << ","
<< "\"failed\":" << result.failed
<< "}";
return json.str();
}
// Build JSON result for Shrinkwrap
std::string BatchOperationManager::BuildShrinkwrapResultJson(const ShrinkwrapShellResult& result) {
std::ostringstream json;
json << "{"
<< "\"message\":\"" << EscapeJsonString(result.message) << "\","
<< "\"output_file_path\":\"" << EscapeJsonString(result.parameters.output_file_path) << "\","
<< "\"output_file_size\":\"" << EscapeJsonString(result.parameters.output_file_size) << "\","
<< "\"shrinkwrap_time\":\"" << EscapeJsonString(result.parameters.shrinkwrap_time) << "\","
<< "\"quality\":" << result.parameters.quality << ","
<< "\"chord_height\":" << result.parameters.chord_height
<< "}";
return json.str();
}
// Build JSON result for CloseModel
std::string BatchOperationManager::BuildCloseResultJson(const CloseResult& result) {
std::ostringstream json;
json << "{"
<< "\"model_name\":\"" << EscapeJsonString(result.model_name) << "\","
<< "\"was_modified\":" << (result.was_modified ? "true" : "false") << ","
<< "\"close_time\":\"" << EscapeJsonString(result.close_time) << "\""
<< "}";
return json.str();
}
// Build JSON result for OpenModel
std::string BatchOperationManager::BuildOpenResultJson(const OpenResult& result) {
std::ostringstream json;
json << "{"
<< "\"model_name\":\"" << EscapeJsonString(result.model_name) << "\","
<< "\"model_type\":\"" << EscapeJsonString(result.model_type) << "\","
<< "\"file_path\":\"" << EscapeJsonString(result.file_path) << "\","
<< "\"file_size\":\"" << EscapeJsonString(result.file_size) << "\","
<< "\"open_time\":\"" << EscapeJsonString(result.open_time) << "\","
<< "\"is_assembly\":" << (result.is_assembly ? "true" : "false") << ","
<< "\"total_parts\":" << result.total_parts
<< "}";
return json.str();
}
// Execute save_model operation
BatchOperationResult BatchOperationManager::ExecuteSaveModel(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "save_model";
auto start = std::chrono::high_resolution_clock::now();
try {
SaveResult save_result = CreoManager::Instance().SaveModel();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = save_result.success;
if (save_result.success) {
result.result_json = BuildSaveResultJson(save_result);
} else {
result.error_message = save_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during save operation";
}
return result;
}
// Execute export_model operation
BatchOperationResult BatchOperationManager::ExecuteExportModel(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "export_model";
auto start = std::chrono::high_resolution_clock::now();
try {
// Extract parameters
std::string export_path;
std::string geom_flags = "solids";
auto 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;
}
if (export_path.empty()) {
result.success = false;
result.error_message = "Missing required parameter: export_path";
return result;
}
ExportResult export_result = CreoManager::Instance().ExportModelToSTEP(export_path, geom_flags);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = export_result.success;
if (export_result.success) {
result.result_json = BuildExportResultJson(export_result);
} else {
result.error_message = export_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during export operation";
}
return result;
}
// Execute delete_by_path operation
BatchOperationResult BatchOperationManager::ExecuteDeleteByPath(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "delete_by_path";
auto start = std::chrono::high_resolution_clock::now();
try {
PathDeleteManager::PathDeleteRequest delete_request;
delete_request.software_type = "creo";
delete_request.component_paths = operation.array_parameters;
auto it = operation.parameters.find("force_delete");
if (it != operation.parameters.end()) {
delete_request.force_delete = (it->second == "true");
}
if (delete_request.component_paths.empty()) {
result.success = false;
result.error_message = "Missing required parameter: component_paths";
return result;
}
PathDeleteManager::PathDeleteResult delete_result =
PathDeleteManager::Instance().DeleteComponentsByPaths(delete_request);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = delete_result.success;
if (delete_result.success) {
result.result_json = BuildDeleteByPathResultJson(delete_result);
} else {
result.error_message = delete_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during delete by path operation";
}
return result;
}
// Execute hierarchy_delete operation
BatchOperationResult BatchOperationManager::ExecuteHierarchyDelete(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "hierarchy_delete";
auto start = std::chrono::high_resolution_clock::now();
try {
std::string project_name;
int target_level = 0;
auto 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()) {
try {
target_level = std::stoi(it->second);
} catch (...) {
result.success = false;
result.error_message = "Invalid target_level parameter";
return result;
}
}
if (project_name.empty()) {
result.success = false;
result.error_message = "Missing required parameter: project_name";
return result;
}
CreoManager::HierarchyDeleteResult delete_result =
CreoManager::Instance().DeleteHierarchyComponents(project_name, target_level);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = delete_result.success;
if (delete_result.success) {
result.result_json = BuildHierarchyDeleteResultJson(delete_result);
} else {
result.error_message = delete_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during hierarchy delete operation";
}
return result;
}
// Execute shrinkwrap_shell operation
BatchOperationResult BatchOperationManager::ExecuteShrinkwrapShell(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "shrinkwrap_shell";
auto start = std::chrono::high_resolution_clock::now();
try {
ShrinkwrapShellRequest shrink_request;
// Extract parameters
auto it = operation.parameters.find("output_file_path");
if (it != operation.parameters.end()) {
shrink_request.output_file_path = it->second;
}
it = operation.parameters.find("quality");
if (it != operation.parameters.end()) {
try {
shrink_request.quality = std::stoi(it->second);
} catch (...) {}
}
it = operation.parameters.find("chord_height");
if (it != operation.parameters.end()) {
try {
shrink_request.chord_height = std::stod(it->second);
} catch (...) {}
}
if (shrink_request.output_file_path.empty()) {
result.success = false;
result.error_message = "Missing required parameter: output_file_path";
return result;
}
ShrinkwrapShellResult shrink_result =
ShrinkwrapManager::Instance().ExecuteShrinkwrapShell(shrink_request);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = shrink_result.success;
if (shrink_result.success) {
result.result_json = BuildShrinkwrapResultJson(shrink_result);
} else {
result.error_message = shrink_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during shrinkwrap operation";
}
return result;
}
// Execute close_model operation
BatchOperationResult BatchOperationManager::ExecuteCloseModel(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "close_model";
auto start = std::chrono::high_resolution_clock::now();
try {
bool force_close = false;
auto it = operation.parameters.find("force_close");
if (it != operation.parameters.end()) {
force_close = (it->second == "true");
}
CloseResult close_result = CreoManager::Instance().CloseModel(force_close);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = close_result.success;
if (close_result.success) {
result.result_json = BuildCloseResultJson(close_result);
} else {
result.error_message = close_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during close operation";
}
return result;
}
// Execute open_model operation
BatchOperationResult BatchOperationManager::ExecuteOpenModel(int index, const BatchOperation& operation) {
BatchOperationResult result;
result.operation_index = index;
result.operation_type = "open_model";
auto start = std::chrono::high_resolution_clock::now();
try {
std::string file_path;
std::string open_mode = "active";
auto it = operation.parameters.find("file_path");
if (it != operation.parameters.end()) {
file_path = it->second;
}
it = operation.parameters.find("open_mode");
if (it != operation.parameters.end()) {
open_mode = it->second;
}
if (file_path.empty()) {
result.success = false;
result.error_message = "Missing required parameter: file_path";
return result;
}
OpenResult open_result = CreoManager::Instance().OpenModel(file_path, open_mode);
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
result.execution_time = std::to_string(duration.count()) + "ms";
result.success = open_result.success;
if (open_result.success) {
result.result_json = BuildOpenResultJson(open_result);
} else {
result.error_message = open_result.error_message;
}
} catch (const std::exception& e) {
result.success = false;
result.error_message = "Exception: " + std::string(e.what());
} catch (...) {
result.success = false;
result.error_message = "Unknown error during open operation";
}
return result;
}
// Main entry point: Execute batch operations
BatchOperationsResponse BatchOperationManager::ExecuteBatchOperations(const BatchOperationsRequest& request) {
BatchOperationsResponse response;
response.total_operations = static_cast<int>(request.operations.size());
auto start = std::chrono::high_resolution_clock::now();
try {
// Validate request
if (request.software_type != "creo") {
response.success = false;
response.error_message = "Invalid software_type, must be 'creo'";
return response;
}
if (request.operations.empty()) {
response.success = false;
response.error_message = "No operations specified";
return response;
}
// Execute each operation sequentially
for (size_t i = 0; i < request.operations.size(); i++) {
const BatchOperation& operation = request.operations[i];
BatchOperationResult op_result;
if (operation.operation_type == "save_model") {
op_result = ExecuteSaveModel(static_cast<int>(i), operation);
} else if (operation.operation_type == "export_model") {
op_result = ExecuteExportModel(static_cast<int>(i), operation);
} else if (operation.operation_type == "delete_by_path") {
op_result = ExecuteDeleteByPath(static_cast<int>(i), operation);
} else if (operation.operation_type == "hierarchy_delete") {
op_result = ExecuteHierarchyDelete(static_cast<int>(i), operation);
} else if (operation.operation_type == "shrinkwrap_shell") {
op_result = ExecuteShrinkwrapShell(static_cast<int>(i), operation);
} else if (operation.operation_type == "close_model") {
op_result = ExecuteCloseModel(static_cast<int>(i), operation);
} else if (operation.operation_type == "open_model") {
op_result = ExecuteOpenModel(static_cast<int>(i), operation);
} else {
// Unknown operation type
op_result.operation_index = static_cast<int>(i);
op_result.operation_type = operation.operation_type;
op_result.success = false;
op_result.error_message = "Unknown operation type: " + operation.operation_type;
}
response.results.push_back(op_result);
if (op_result.success) {
response.successful_operations++;
} else {
response.failed_operations++;
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
response.execution_time = std::to_string(duration.count()) + "ms";
// Overall success if at least one operation succeeded
response.success = (response.successful_operations > 0);
if (response.success) {
response.message = "Batch operations completed";
} else {
response.message = "All batch operations failed";
response.error_message = "All operations failed to execute";
}
} catch (const std::exception& e) {
response.success = false;
response.error_message = "Exception during batch operations: " + std::string(e.what());
} catch (...) {
response.success = false;
response.error_message = "Unknown error during batch operations";
}
return response;
}