- 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>
587 lines
21 KiB
C++
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;
|
|
}
|