CreoOtkPluging/PathDeleteManager.cpp
root 0e98285a74 实现路径删除功能并修复编译问题
## 新增功能
- 新增PathDeleteManager类,实现按路径批量删除装配体组件
- 支持绝对和相对路径格式的智能解析
- 采用按装配体分组的抑制策略,确保上下文正确匹配
- 完善的异常处理和错误原因追踪机制

## 修复的技术问题
- 解决路径删除"Unknown exception during suppression operation"错误
- 修复特征ID与owner_assembly上下文不匹配问题
- 转换文件为CRLF行尾符,解决Visual Studio编译错误
- 添加UTF-8 BOM确保编码一致性
- 移除C++11语法实现传统C++兼容性

## API端点
- POST /api/creo/path/delete - 路径组件删除接口

## 文件变更
- 新增: PathDeleteManager.h, PathDeleteManager.cpp
- 修改: MFCCreoDll.cpp (集成路径删除接口)
- 修改: 项目配置文件
- 更新: CLAUDE.md (技术文档)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-08 12:29:58 +08:00

466 lines
19 KiB
C++

#include "pch.h"
#include "PathDeleteManager.h"
#include <windows.h>
#include <sstream>
#include <algorithm>
#include <chrono>
#include <functional>
#include <map>
PathDeleteManager::SessionInfo PathDeleteManager::GetSessionInfo() {
SessionInfo info;
try {
info.session = pfcGetCurrentSession();
info.is_valid = (info.session != nullptr);
} catch (...) {
info.is_valid = false;
info.session = nullptr;
}
return info;
}
std::string PathDeleteManager::XStringToString(const xstring& xstr) {
try {
if (xstr.IsNull()) {
return "";
}
std::wstring wstr(xstr);
if (wstr.empty()) {
return "";
}
if (wstr.length() > 32767) {
return "";
}
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
} catch (...) {
return "";
}
}
xstring PathDeleteManager::StringToXString(const std::string& str) {
try {
if (str.empty()) {
return xstring();
}
if (str.length() > 65535) {
return xstring();
}
std::wstring wstr(str.begin(), str.end());
return xstring(wstr.c_str());
} catch (...) {
return xstringnil;
}
}
pfcModel_ptr PathDeleteManager::LoadComponentModel(pfcComponentFeat_ptr compFeat) {
try {
if (!compFeat) return nullptr;
pfcModelDescriptor_ptr modelDescr = compFeat->GetModelDescr();
if (!modelDescr) return nullptr;
SessionInfo sessionInfo = GetSessionInfo();
if (!sessionInfo.is_valid) return nullptr;
try {
pfcModel_ptr existingModel = sessionInfo.session->GetModelFromDescr(modelDescr);
if (existingModel) {
return existingModel;
}
} catch (...) {
// Model not in memory, try to load
}
try {
pfcModel_ptr loadedModel = sessionInfo.session->RetrieveModel(modelDescr);
return loadedModel;
} catch (...) {
return nullptr;
}
} catch (...) {
return nullptr;
}
}
PathDeleteManager::ParsedPath PathDeleteManager::ParseComponentPath(const std::string& full_path) {
ParsedPath parsed;
try {
if (full_path.empty()) {
return parsed;
}
std::string path = full_path;
std::string delimiter = "/";
size_t pos = 0;
std::string token;
while ((pos = path.find(delimiter)) != std::string::npos) {
token = path.substr(0, pos);
if (!token.empty()) {
parsed.path_segments.push_back(token);
}
path.erase(0, pos + delimiter.length());
}
if (!path.empty()) {
parsed.path_segments.push_back(path);
parsed.target_component = path;
}
if (parsed.path_segments.size() >= 1 && !parsed.target_component.empty()) {
parsed.is_valid = true;
}
} catch (...) {
parsed.is_valid = false;
}
return parsed;
}
bool PathDeleteManager::CaseInsensitiveCompare(const std::string& str1, const std::string& str2) {
if (str1.length() != str2.length()) {
return false;
}
for (size_t i = 0; i < str1.length(); ++i) {
if (std::tolower(str1[i]) != std::tolower(str2[i])) {
return false;
}
}
return true;
}
bool PathDeleteManager::RecursiveSearchComponent(wfcWAssembly_ptr currentAssembly,
const ParsedPath& target_path,
const std::string& pathSoFar,
int currentLevel,
std::vector<ComponentMatch>& matches) {
if (!currentAssembly || currentLevel >= (int)target_path.path_segments.size()) {
return false;
}
std::vector<std::string> found_components;
try {
pfcFeatures_ptr features = currentAssembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT);
if (!features) {
return false;
}
for (int i = 0; i < features->getarraysize(); i++) {
try {
pfcFeature_ptr feature = features->get(i);
if (!feature) continue;
pfcComponentFeat_ptr compFeat = pfcComponentFeat::cast(feature);
if (!compFeat) continue;
std::string comp_name = "";
try {
pfcModelDescriptor_ptr modelDescr = compFeat->GetModelDescr();
if (modelDescr) {
xstring filename_xstr = modelDescr->GetFileName();
if (filename_xstr != xstringnil && !filename_xstr.IsEmpty()) {
comp_name = XStringToString(filename_xstr);
}
}
} catch (...) {
continue;
}
if (comp_name.empty()) continue;
found_components.push_back(comp_name);
std::string newPath = pathSoFar.empty() ? comp_name : pathSoFar + "/" + comp_name;
std::string targetSegment = target_path.path_segments[currentLevel];
if (CaseInsensitiveCompare(comp_name, targetSegment)) {
if (currentLevel == (int)target_path.path_segments.size() - 1) {
ComponentMatch match;
match.feature = feature;
match.actual_path = newPath;
match.found = true;
match.owner_assembly = currentAssembly; // 记录组件所属装配体
matches.push_back(match);
} else {
pfcModel_ptr childModel = LoadComponentModel(compFeat);
if (childModel && childModel->GetType() == pfcMDL_ASSEMBLY) {
wfcWAssembly_ptr childAssembly = wfcWAssembly::cast(childModel);
if (childAssembly) {
RecursiveSearchComponent(childAssembly, target_path, newPath, currentLevel + 1, matches);
}
}
}
}
} catch (...) {
continue;
}
}
if (matches.empty() && currentLevel == 0) {
ComponentMatch debug_match;
debug_match.found = false;
debug_match.feature = nullptr;
debug_match.owner_assembly = nullptr;
std::string debug_info = "Level " + std::to_string(currentLevel) + " components found: ";
if (found_components.empty()) {
debug_info += "NONE";
} else {
for (size_t i = 0; i < found_components.size(); ++i) {
if (i > 0) debug_info += ", ";
debug_info += found_components[i];
}
}
if (currentLevel < (int)target_path.path_segments.size()) {
debug_info += " | Looking for: " + target_path.path_segments[currentLevel];
}
debug_match.actual_path = debug_info;
matches.push_back(debug_match);
}
} catch (...) {
return false;
}
return !matches.empty();
}
bool PathDeleteManager::FindComponentByPath(wfcWAssembly_ptr assembly,
const ParsedPath& target_path,
const std::string& current_path,
int target_depth,
int current_depth,
std::vector<ComponentMatch>& matches) {
if (!assembly || !target_path.is_valid) return false;
try {
return RecursiveSearchComponent(assembly, target_path, current_path, 0, matches);
} catch (...) {
return false;
}
}
PathDeleteManager::PathDeleteResult PathDeleteManager::DeleteComponentsByPaths(const PathDeleteRequest& request) {
PathDeleteResult result;
result.total_requested = (int)request.component_paths.size();
if (request.software_type != "creo") {
result.error_message = "Only 'creo' software_type is supported";
return result;
}
if (request.component_paths.empty()) {
result.error_message = "No component paths provided";
return result;
}
SessionInfo sessionInfo = GetSessionInfo();
if (!sessionInfo.is_valid) {
result.error_message = "Creo session not available";
return result;
}
try {
pfcModel_ptr current_model = sessionInfo.session->GetCurrentModel();
if (!current_model) {
result.error_message = "No current model loaded";
return result;
}
if (current_model->GetType() != pfcMDL_ASSEMBLY) {
result.error_message = "Current model is not an assembly";
return result;
}
wfcWAssembly_ptr currentAssembly = wfcWAssembly::cast(current_model);
if (!currentAssembly) {
result.error_message = "Failed to cast current model to assembly";
return result;
}
// Get root assembly name for path processing
std::string root_assembly_name = "";
try {
xstring filename_xstr = current_model->GetFileName();
if (filename_xstr != xstringnil && !filename_xstr.IsEmpty()) {
root_assembly_name = XStringToString(filename_xstr);
}
} catch (...) {
// Unable to get root assembly name, use default processing
}
// 按装配体分组收集feature ID和路径
std::map<wfcWAssembly_ptr, std::vector<std::pair<int, std::string>>> featuresGroupedByAssembly;
typedef std::vector<std::string> StringVector;
for (StringVector::const_iterator path_it = request.component_paths.begin();
path_it != request.component_paths.end(); ++path_it) {
const std::string& path = *path_it;
try {
ParsedPath parsed_path = ParseComponentPath(path);
if (!parsed_path.is_valid) {
result.failed_to_delete.push_back(path);
result.deletion_reasons[path] = "Invalid path format";
continue;
}
// Path processing: support absolute and relative paths
ParsedPath adjusted_path = parsed_path;
// If the first segment of the path is the root assembly name, skip it (convert to relative path)
if (!root_assembly_name.empty() &&
!parsed_path.path_segments.empty() &&
CaseInsensitiveCompare(parsed_path.path_segments[0], root_assembly_name)) {
adjusted_path.path_segments.erase(adjusted_path.path_segments.begin());
if (adjusted_path.path_segments.empty()) {
result.failed_to_delete.push_back(path);
result.deletion_reasons[path] = "Path only contains root assembly name";
continue;
}
adjusted_path.target_component = adjusted_path.path_segments.back();
}
std::vector<ComponentMatch> matches;
bool found = FindComponentByPath(currentAssembly, adjusted_path, "", 0, 0, matches);
if (!found || matches.empty()) {
result.failed_to_delete.push_back(path);
result.deletion_reasons[path] = "Component not found in assembly";
continue;
}
bool has_valid_match = false;
typedef std::vector<ComponentMatch> ComponentMatchVector;
for (ComponentMatchVector::const_iterator match_it = matches.begin();
match_it != matches.end(); ++match_it) {
if (match_it->found && match_it->feature && match_it->owner_assembly) {
try {
int featId = match_it->feature->GetId();
std::pair<int, std::string> featPair;
featPair.first = featId;
featPair.second = path;
featuresGroupedByAssembly[match_it->owner_assembly].push_back(featPair);
has_valid_match = true;
break;
} catch (...) {
continue;
}
}
}
if (!has_valid_match) {
result.failed_to_delete.push_back(path);
result.deletion_reasons[path] = "Component found but failed to get feature ID or owner assembly";
}
} catch (...) {
result.failed_to_delete.push_back(path);
result.deletion_reasons[path] = "Exception occurred while processing path";
}
}
// 按装配体分组执行抑制操作(参考层级删除的实现)
typedef std::map<wfcWAssembly_ptr, std::vector<std::pair<int, std::string>>> AssemblyFeatureMap;
for (AssemblyFeatureMap::iterator it = featuresGroupedByAssembly.begin();
it != featuresGroupedByAssembly.end(); ++it) {
wfcWAssembly_ptr targetAssembly = it->first;
const std::vector<std::pair<int, std::string>>& featuresAndPaths = it->second;
if (!targetAssembly || featuresAndPaths.empty()) continue;
try {
xintsequence_ptr featIds = xintsequence::create();
std::vector<std::string> pathsForThisAssembly;
// 为当前装配体收集feature ID
typedef std::vector<std::pair<int, std::string>> FeaturePairVector;
for (FeaturePairVector::const_iterator feat_it = featuresAndPaths.begin();
feat_it != featuresAndPaths.end(); ++feat_it) {
featIds->append(feat_it->first);
pathsForThisAssembly.push_back(feat_it->second);
}
// 执行抑制操作(使用与层级删除相同的逻辑)
if (featIds->getarraysize() > 0) {
wfcWSolid_ptr wsolid = wfcWSolid::cast(targetAssembly);
if (wsolid) {
wfcFeatSuppressOrDeleteOptions_ptr options = wfcFeatSuppressOrDeleteOptions::create();
options->append(wfcFEAT_SUPP_OR_DEL_NO_OPTS);
wfcWRegenInstructions_ptr regenInstr = wfcWRegenInstructions::Create();
// 执行抑制操作
wsolid->SuppressFeatures(featIds, options, regenInstr);
// 手动重生成模型
try {
targetAssembly->Regenerate(nullptr);
} catch (...) {
// 重生成失败不影响抑制操作
}
// 标记成功
for (std::vector<std::string>::const_iterator str_it = pathsForThisAssembly.begin();
str_it != pathsForThisAssembly.end(); ++str_it) {
result.successfully_deleted.push_back(*str_it);
result.deletion_reasons[*str_it] = "Component suppressed successfully (safer than deletion)";
}
} else {
// WSolid转换失败
for (std::vector<std::string>::const_iterator str_it = pathsForThisAssembly.begin();
str_it != pathsForThisAssembly.end(); ++str_it) {
result.failed_to_delete.push_back(*str_it);
result.deletion_reasons[*str_it] = "Failed to cast assembly to WSolid for suppression";
}
}
}
} catch (...) {
// 简化异常处理,参考层级删除的实现
typedef std::vector<std::pair<int, std::string>> FeaturePairVector;
for (FeaturePairVector::const_iterator feat_it = featuresAndPaths.begin();
feat_it != featuresAndPaths.end(); ++feat_it) {
result.failed_to_delete.push_back(feat_it->second);
result.deletion_reasons[feat_it->second] = "Exception during suppression operation";
}
}
}
result.successful = (int)result.successfully_deleted.size();
result.failed = (int)result.failed_to_delete.size();
result.success = (result.successful > 0);
if (result.success && result.failed > 0) {
result.error_message = "Partial success: " + std::to_string(result.successful) +
" components suppressed, " + std::to_string(result.failed) +
" components failed";
} else if (!result.success) {
result.error_message = "All deletion operations failed";
}
} catch (const std::exception& e) {
result.error_message = "Exception during path deletion: " + std::string(e.what());
} catch (...) {
result.error_message = "Unknown error during path deletion";
}
return result;
}