feat: implement component children API with Creo Feature ID support
- Add ComponentChildrenManager class for retrieving first-level child components - Support path-based component lookup with intelligent top-level assembly handling - Return Creo Feature IDs instead of filenames for precise component identification - Add comprehensive path resolution logic for nested assemblies - Implement new API endpoint /api/creo/component/children 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6aaead4c6e
commit
8fd63500f2
502
ComponentChildrenManager.cpp
Normal file
502
ComponentChildrenManager.cpp
Normal file
@ -0,0 +1,502 @@
|
||||
#include "pch.h"
|
||||
#include "ComponentChildrenManager.h"
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
// Main interface method
|
||||
ComponentChildrenManager::ComponentChildrenResult ComponentChildrenManager::GetComponentFirstLevelChildren(const ComponentChildrenRequest& request) {
|
||||
ComponentChildrenResult result;
|
||||
|
||||
if (request.component_path.empty()) {
|
||||
result.error_message = "Component path is required";
|
||||
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;
|
||||
}
|
||||
|
||||
// Check if component_path refers to the current top-level assembly
|
||||
std::string currentModelName = "";
|
||||
try {
|
||||
xstring filename_xstr = current_model->GetFileName();
|
||||
currentModelName = XStringToString(filename_xstr);
|
||||
} catch (...) {
|
||||
// If can't get filename, try origin
|
||||
try {
|
||||
xstring origin = current_model->GetOrigin();
|
||||
std::string origin_str = XStringToString(origin);
|
||||
size_t pos = origin_str.find_last_of("/\\");
|
||||
if (pos != std::string::npos) {
|
||||
currentModelName = origin_str.substr(pos + 1);
|
||||
} else {
|
||||
currentModelName = origin_str;
|
||||
}
|
||||
} catch (...) {
|
||||
currentModelName = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the component path
|
||||
std::string actualPath = request.component_path;
|
||||
ParsedPath targetPath;
|
||||
|
||||
if (actualPath.empty()) {
|
||||
// Empty path is valid (represents top-level query)
|
||||
targetPath.is_valid = true;
|
||||
} else {
|
||||
targetPath = ParseComponentPath(actualPath);
|
||||
if (!targetPath.is_valid) {
|
||||
result.error_message = "Invalid component path format";
|
||||
return result;
|
||||
}
|
||||
|
||||
// If path starts with current model name, skip the first segment
|
||||
if (!targetPath.path_segments.empty() &&
|
||||
!currentModelName.empty() &&
|
||||
CaseInsensitiveCompare(targetPath.path_segments[0], currentModelName)) {
|
||||
// Remove first segment as it refers to current assembly itself
|
||||
targetPath.path_segments.erase(targetPath.path_segments.begin());
|
||||
if (!targetPath.path_segments.empty()) {
|
||||
targetPath.target_component = targetPath.path_segments.back();
|
||||
} else {
|
||||
// Only top-level name provided, treat as top-level query
|
||||
actualPath = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle top-level assembly case (empty path after conversion)
|
||||
ComponentMatch match;
|
||||
bool found = false;
|
||||
|
||||
if (actualPath.empty() || targetPath.path_segments.empty()) {
|
||||
// Direct query of top-level assembly, no need to search
|
||||
match.found = true;
|
||||
match.owner_assembly = currentAssembly;
|
||||
found = true;
|
||||
} else {
|
||||
// Search for the component in the assembly
|
||||
found = FindComponentByPath(currentAssembly, targetPath, match);
|
||||
if (!found || !match.found) {
|
||||
result.error_message = "Component not found at path: " + request.component_path;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate component ID if provided
|
||||
if (request.component_id != -1) {
|
||||
int actualFeatureId = -1;
|
||||
try {
|
||||
if (match.feature) {
|
||||
actualFeatureId = match.feature->GetId();
|
||||
}
|
||||
} catch (...) {
|
||||
actualFeatureId = -1;
|
||||
}
|
||||
|
||||
if (actualFeatureId != request.component_id) {
|
||||
result.error_message = "Component Feature ID mismatch. Expected: " + std::to_string(request.component_id) +
|
||||
", Found: " + std::to_string(actualFeatureId);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Set parent component info
|
||||
result.parent_component_path = request.component_path;
|
||||
|
||||
pfcModel_ptr componentModel = nullptr;
|
||||
wfcWAssembly_ptr componentAssembly = nullptr;
|
||||
|
||||
if (actualPath.empty()) {
|
||||
// Top-level assembly case
|
||||
result.parent_component_id = -1; // No Feature ID for top-level
|
||||
result.parent_component_filename = currentModelName;
|
||||
componentModel = current_model;
|
||||
componentAssembly = currentAssembly;
|
||||
} else {
|
||||
// Sub-component case
|
||||
try {
|
||||
if (match.feature) {
|
||||
result.parent_component_id = match.feature->GetId();
|
||||
}
|
||||
|
||||
auto modelDescr = match.component_feature->GetModelDescr();
|
||||
if (modelDescr) {
|
||||
xstring filename_xstr = modelDescr->GetFileName();
|
||||
result.parent_component_filename = XStringToString(filename_xstr);
|
||||
}
|
||||
} catch (...) {
|
||||
result.parent_component_id = -1;
|
||||
result.parent_component_filename = "unknown";
|
||||
}
|
||||
|
||||
// Load the component model to get its children
|
||||
componentModel = LoadComponentModel(match.component_feature);
|
||||
if (!componentModel) {
|
||||
result.error_message = "Failed to load component model";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if the component is an assembly (has children)
|
||||
if (componentModel->GetType() != pfcMDL_ASSEMBLY) {
|
||||
// Part has no children, return empty list
|
||||
result.success = true;
|
||||
result.message = "Component is a part (no children)";
|
||||
result.children_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Cast to assembly and get first level children
|
||||
componentAssembly = wfcWAssembly::cast(componentModel);
|
||||
}
|
||||
if (!componentAssembly) {
|
||||
result.error_message = "Failed to cast component to assembly";
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get all component features at the first level
|
||||
pfcFeatures_ptr features = componentAssembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT);
|
||||
if (!features) {
|
||||
result.success = true;
|
||||
result.message = "Assembly has no components";
|
||||
result.children_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int features_count = features->getarraysize();
|
||||
if (features_count <= 0) {
|
||||
result.success = true;
|
||||
result.message = "Assembly has no components";
|
||||
result.children_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Collect all first-level children
|
||||
for (int i = 0; i < features_count; i++) {
|
||||
try {
|
||||
pfcFeature_ptr feature = features->get(i);
|
||||
if (!feature) continue;
|
||||
|
||||
pfcComponentFeat_ptr compFeat = pfcComponentFeat::cast(feature);
|
||||
if (!compFeat) continue;
|
||||
|
||||
ChildComponentInfo childInfo = CreateChildComponentFromFeature(compFeat, 1, request.component_path);
|
||||
result.children.push_back(childInfo);
|
||||
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result.children_count = result.children.size();
|
||||
result.success = true;
|
||||
result.message = "Successfully retrieved " + std::to_string(result.children_count) + " child components";
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
result.error_message = "Exception: " + std::string(e.what());
|
||||
} catch (...) {
|
||||
result.error_message = "Unknown exception occurred";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper method implementations
|
||||
ComponentChildrenManager::SessionInfo ComponentChildrenManager::GetSessionInfo() {
|
||||
SessionInfo info;
|
||||
try {
|
||||
info.session = pfcGetCurrentSessionWithCompatibility(pfcC4Compatible);
|
||||
info.is_valid = (info.session != nullptr);
|
||||
} catch (...) {
|
||||
info.is_valid = false;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
ComponentChildrenManager::ParsedPath ComponentChildrenManager::ParseComponentPath(const std::string& full_path) {
|
||||
ParsedPath result;
|
||||
|
||||
if (full_path.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Split path by '/' or '\'
|
||||
std::string path = full_path;
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
|
||||
std::istringstream iss(path);
|
||||
std::string segment;
|
||||
|
||||
while (std::getline(iss, segment, '/')) {
|
||||
if (!segment.empty()) {
|
||||
result.path_segments.push_back(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.path_segments.empty()) {
|
||||
result.target_component = result.path_segments.back();
|
||||
result.is_valid = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ComponentChildrenManager::FindComponentByPath(wfcWAssembly_ptr assembly,
|
||||
const ParsedPath& target_path,
|
||||
ComponentMatch& match) {
|
||||
if (!assembly || !target_path.is_valid) return false;
|
||||
|
||||
try {
|
||||
return RecursiveSearchComponent(assembly, target_path, "", 0, match);
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ComponentChildrenManager::RecursiveSearchComponent(wfcWAssembly_ptr currentAssembly,
|
||||
const ParsedPath& target_path,
|
||||
const std::string& pathSoFar,
|
||||
int currentLevel,
|
||||
ComponentMatch& match) {
|
||||
if (!currentAssembly || currentLevel >= (int)target_path.path_segments.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
std::string currentTarget = target_path.path_segments[currentLevel];
|
||||
|
||||
pfcFeatures_ptr features = currentAssembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT);
|
||||
if (!features) return false;
|
||||
|
||||
int features_count = features->getarraysize();
|
||||
|
||||
for (int i = 0; i < features_count; i++) {
|
||||
try {
|
||||
pfcFeature_ptr feature = features->get(i);
|
||||
if (!feature) continue;
|
||||
|
||||
pfcComponentFeat_ptr compFeat = pfcComponentFeat::cast(feature);
|
||||
if (!compFeat) continue;
|
||||
|
||||
auto modelDescr = compFeat->GetModelDescr();
|
||||
if (!modelDescr) continue;
|
||||
|
||||
xstring filename_xstr = modelDescr->GetFileName();
|
||||
std::string componentName = XStringToString(filename_xstr);
|
||||
|
||||
std::string newPath = pathSoFar.empty() ? componentName : pathSoFar + "/" + componentName;
|
||||
|
||||
if (CaseInsensitiveCompare(componentName, currentTarget)) {
|
||||
// Found the target at this level
|
||||
if (currentLevel == (int)target_path.path_segments.size() - 1) {
|
||||
// This is the final target
|
||||
match.feature = feature;
|
||||
match.component_feature = compFeat;
|
||||
match.owner_assembly = currentAssembly;
|
||||
match.actual_path = newPath;
|
||||
match.found = true;
|
||||
return true;
|
||||
} else {
|
||||
// Need to go deeper
|
||||
pfcModel_ptr childModel = LoadComponentModel(compFeat);
|
||||
if (childModel && childModel->GetType() == pfcMDL_ASSEMBLY) {
|
||||
wfcWAssembly_ptr childAssembly = wfcWAssembly::cast(childModel);
|
||||
if (childAssembly) {
|
||||
if (RecursiveSearchComponent(childAssembly, target_path, newPath, currentLevel + 1, match)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ComponentChildrenManager::ChildComponentInfo ComponentChildrenManager::CreateChildComponentFromFeature(pfcComponentFeat_ptr compFeat,
|
||||
int level,
|
||||
const std::string& parentPath) {
|
||||
ChildComponentInfo component;
|
||||
component.level = level;
|
||||
component.children_count = 0;
|
||||
component.is_visible = true;
|
||||
component.file_size = "0.0MB";
|
||||
component.id = -1; // Default Feature ID
|
||||
|
||||
try {
|
||||
// Get Feature ID first
|
||||
pfcFeature_ptr feature = pfcFeature::cast(compFeat);
|
||||
if (feature) {
|
||||
component.id = feature->GetId();
|
||||
}
|
||||
|
||||
auto modelDescr = compFeat->GetModelDescr();
|
||||
if (modelDescr) {
|
||||
xstring filename_xstr = modelDescr->GetFileName();
|
||||
component.filename = XStringToString(filename_xstr);
|
||||
|
||||
component.name = component.filename;
|
||||
size_t ext_pos = component.name.find_last_of(".");
|
||||
if (ext_pos != std::string::npos) {
|
||||
component.name = component.name.substr(0, ext_pos);
|
||||
}
|
||||
|
||||
if (!component.name.empty()) {
|
||||
component.name[0] = std::toupper(component.name[0]);
|
||||
}
|
||||
|
||||
// Build paths
|
||||
if (parentPath.empty()) {
|
||||
component.path = component.filename;
|
||||
component.full_path = component.filename;
|
||||
} else {
|
||||
component.path = parentPath + "/" + component.filename;
|
||||
component.full_path = component.path;
|
||||
}
|
||||
|
||||
// Determine component type
|
||||
std::string ext = "";
|
||||
if (ext_pos != std::string::npos) {
|
||||
ext = component.filename.substr(ext_pos + 1);
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||
}
|
||||
|
||||
if (ext == "asm") {
|
||||
component.type = "assembly";
|
||||
component.model_type = "MDL_ASSEMBLY";
|
||||
} else {
|
||||
component.type = "part";
|
||||
component.model_type = "MDL_PART";
|
||||
}
|
||||
|
||||
// Try to load model and get additional info
|
||||
pfcModel_ptr childModel = LoadComponentModel(compFeat);
|
||||
if (childModel) {
|
||||
component.file_size = GetModelFileSize(childModel);
|
||||
|
||||
if (childModel->GetType() == pfcMDL_ASSEMBLY) {
|
||||
wfcWAssembly_ptr childAssembly = wfcWAssembly::cast(childModel);
|
||||
if (childAssembly) {
|
||||
try {
|
||||
pfcFeatures_ptr childFeatures = childAssembly->ListFeaturesByType(xfalse, pfcFEATTYPE_COMPONENT);
|
||||
if (childFeatures) {
|
||||
component.children_count = childFeatures->getarraysize();
|
||||
}
|
||||
} catch (...) {
|
||||
component.children_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
component.id = -1;
|
||||
component.filename = "unknown_component";
|
||||
component.name = "Unknown";
|
||||
component.type = "unknown";
|
||||
component.model_type = "UNKNOWN";
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
pfcModel_ptr ComponentChildrenManager::LoadComponentModel(pfcComponentFeat_ptr compFeat) {
|
||||
if (!compFeat) return nullptr;
|
||||
|
||||
try {
|
||||
auto modelDescr = compFeat->GetModelDescr();
|
||||
if (!modelDescr) return nullptr;
|
||||
|
||||
SessionInfo sessionInfo = GetSessionInfo();
|
||||
if (!sessionInfo.is_valid) return nullptr;
|
||||
|
||||
pfcModel_ptr model = sessionInfo.session->GetModelFromDescr(modelDescr);
|
||||
if (!model) {
|
||||
model = sessionInfo.session->RetrieveModel(modelDescr);
|
||||
}
|
||||
|
||||
return model;
|
||||
} catch (...) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string ComponentChildrenManager::GetModelFileSize(pfcModel_ptr model) {
|
||||
try {
|
||||
return "N/A";
|
||||
} catch (...) {
|
||||
return "0.0MB";
|
||||
}
|
||||
}
|
||||
|
||||
std::string ComponentChildrenManager::XStringToString(const xstring& xstr) {
|
||||
try {
|
||||
const wchar_t* wstr = xstr;
|
||||
if (!wstr) return "";
|
||||
|
||||
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
|
||||
if (bufferSize <= 0) return "";
|
||||
|
||||
std::string result(bufferSize - 1, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, &result[0], bufferSize, nullptr, nullptr);
|
||||
|
||||
return result;
|
||||
} catch (...) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
xstring ComponentChildrenManager::StringToXString(const std::string& str) {
|
||||
try {
|
||||
int bufferSize = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
|
||||
if (bufferSize <= 0) return xstring();
|
||||
|
||||
std::vector<wchar_t> wstr(bufferSize);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], bufferSize);
|
||||
|
||||
return xstring(&wstr[0]);
|
||||
} catch (...) {
|
||||
return xstring();
|
||||
}
|
||||
}
|
||||
|
||||
bool ComponentChildrenManager::CaseInsensitiveCompare(const std::string& str1, const std::string& str2) {
|
||||
if (str1.length() != str2.length()) return false;
|
||||
|
||||
return std::equal(str1.begin(), str1.end(), str2.begin(),
|
||||
[](char a, char b) {
|
||||
return std::tolower(a) == std::tolower(b);
|
||||
});
|
||||
}
|
||||
118
ComponentChildrenManager.h
Normal file
118
ComponentChildrenManager.h
Normal file
@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
// OTK headers
|
||||
#include <pfcGlobal.h>
|
||||
#include <pfcSession.h>
|
||||
#include <wfcSession.h>
|
||||
#include <wfcGlobal.h>
|
||||
#include <pfcModel.h>
|
||||
#include <pfcExceptions.h>
|
||||
#include <pfcFeature.h>
|
||||
#include <pfcComponentFeat.h>
|
||||
#include <pfcFeature_s.h>
|
||||
#include <pfcSolid.h>
|
||||
#include <wfcSolid.h>
|
||||
#include <wfcAssembly.h>
|
||||
#include <stdcols.h>
|
||||
|
||||
class ComponentChildrenManager {
|
||||
public:
|
||||
// Request data structure
|
||||
struct ComponentChildrenRequest {
|
||||
std::string component_path; // Required: component path like "Assembly.asm/SubAsm.asm/Part.prt"
|
||||
int component_id = -1; // Optional: Creo Feature ID for additional validation (-1 means not specified)
|
||||
};
|
||||
|
||||
// Child component info structure
|
||||
struct ChildComponentInfo {
|
||||
int id; // Creo Feature ID
|
||||
std::string filename; // Component filename (e.g., "part.prt")
|
||||
std::string name;
|
||||
std::string type; // "part", "assembly"
|
||||
int level;
|
||||
std::string path;
|
||||
std::string full_path;
|
||||
std::string file_size;
|
||||
bool is_visible;
|
||||
std::string model_type; // "MDL_PART", "MDL_ASSEMBLY"
|
||||
int children_count;
|
||||
};
|
||||
|
||||
// Response data structure
|
||||
struct ComponentChildrenResult {
|
||||
bool success = false;
|
||||
std::string message;
|
||||
int parent_component_id = -1; // Creo Feature ID
|
||||
std::string parent_component_filename; // Component filename
|
||||
std::string parent_component_path;
|
||||
int children_count = 0;
|
||||
std::vector<ChildComponentInfo> children;
|
||||
std::string error_message;
|
||||
};
|
||||
|
||||
private:
|
||||
// Session info structure
|
||||
struct SessionInfo {
|
||||
pfcSession_ptr session;
|
||||
bool is_valid = false;
|
||||
};
|
||||
|
||||
// Path parsing structure
|
||||
struct ParsedPath {
|
||||
std::vector<std::string> path_segments;
|
||||
std::string target_component;
|
||||
bool is_valid = false;
|
||||
};
|
||||
|
||||
// Component match result
|
||||
struct ComponentMatch {
|
||||
pfcFeature_ptr feature;
|
||||
pfcComponentFeat_ptr component_feature;
|
||||
wfcWAssembly_ptr owner_assembly;
|
||||
std::string actual_path;
|
||||
bool found = false;
|
||||
};
|
||||
|
||||
private:
|
||||
// Helper methods
|
||||
SessionInfo GetSessionInfo();
|
||||
ParsedPath ParseComponentPath(const std::string& full_path);
|
||||
bool FindComponentByPath(wfcWAssembly_ptr assembly,
|
||||
const ParsedPath& target_path,
|
||||
ComponentMatch& match);
|
||||
bool RecursiveSearchComponent(wfcWAssembly_ptr currentAssembly,
|
||||
const ParsedPath& target_path,
|
||||
const std::string& pathSoFar,
|
||||
int currentLevel,
|
||||
ComponentMatch& match);
|
||||
ChildComponentInfo CreateChildComponentFromFeature(pfcComponentFeat_ptr compFeat,
|
||||
int level,
|
||||
const std::string& parentPath);
|
||||
pfcModel_ptr LoadComponentModel(pfcComponentFeat_ptr compFeat);
|
||||
std::string GetModelFileSize(pfcModel_ptr model);
|
||||
|
||||
// String utility methods
|
||||
std::string XStringToString(const xstring& xstr);
|
||||
xstring StringToXString(const std::string& str);
|
||||
bool CaseInsensitiveCompare(const std::string& str1, const std::string& str2);
|
||||
|
||||
public:
|
||||
// Main interface method
|
||||
ComponentChildrenResult GetComponentFirstLevelChildren(const ComponentChildrenRequest& request);
|
||||
|
||||
// Singleton pattern
|
||||
static ComponentChildrenManager& Instance() {
|
||||
static ComponentChildrenManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
private:
|
||||
// Private constructor for singleton
|
||||
ComponentChildrenManager() = default;
|
||||
ComponentChildrenManager(const ComponentChildrenManager&) = delete;
|
||||
ComponentChildrenManager& operator=(const ComponentChildrenManager&) = delete;
|
||||
};
|
||||
104
MFCCreoDll.cpp
104
MFCCreoDll.cpp
@ -11,6 +11,7 @@
|
||||
#include "GeometryAnalyzer.h"
|
||||
#include "ModelSearchHandler.h"
|
||||
#include "HierarchyStatisticsAnalyzer.h"
|
||||
#include "ComponentChildrenManager.h"
|
||||
#include <wfcSession.h>
|
||||
#include <wfcGlobal.h>
|
||||
#include <string>
|
||||
@ -1648,6 +1649,108 @@ HttpResponse HierarchyStatisticsHandler(const HttpRequest& request) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Component Children Handler
|
||||
HttpResponse ComponentChildrenHandler(const HttpRequest& request) {
|
||||
HttpResponse response;
|
||||
response.headers["Content-Type"] = "application/json";
|
||||
response.headers["Access-Control-Allow-Origin"] = "*";
|
||||
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS";
|
||||
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization";
|
||||
|
||||
if (request.method != "POST") {
|
||||
response.status_code = 405;
|
||||
response.body = "{\"success\": false, \"error\": \"Method not allowed\"}";
|
||||
return response;
|
||||
}
|
||||
|
||||
try {
|
||||
// Parse JSON request
|
||||
ComponentChildrenManager::ComponentChildrenRequest children_request;
|
||||
|
||||
children_request.component_path = ExtractJsonValue(request.body, "component_path");
|
||||
|
||||
// Parse component_id as integer (Creo Feature ID)
|
||||
std::string component_id_str = ExtractJsonValue(request.body, "component_id");
|
||||
if (!component_id_str.empty()) {
|
||||
try {
|
||||
children_request.component_id = std::stoi(component_id_str);
|
||||
} catch (...) {
|
||||
response.status_code = 400;
|
||||
response.body = "{\"success\": false, \"error\": \"Invalid component_id: must be a number\"}";
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
if (children_request.component_path.empty()) {
|
||||
response.status_code = 400;
|
||||
response.body = "{\"success\": false, \"error\": \"Missing required parameter: component_path\"}";
|
||||
return response;
|
||||
}
|
||||
|
||||
// Call ComponentChildrenManager
|
||||
ComponentChildrenManager::ComponentChildrenResult result =
|
||||
ComponentChildrenManager::Instance().GetComponentFirstLevelChildren(children_request);
|
||||
|
||||
if (result.success) {
|
||||
// Build success response
|
||||
std::ostringstream json;
|
||||
json << "{"
|
||||
<< "\"success\": true,"
|
||||
<< "\"message\": \"" << EscapeJsonString(result.message) << "\","
|
||||
<< "\"data\": {"
|
||||
<< "\"parent_component_id\": " << result.parent_component_id << ","
|
||||
<< "\"parent_component_filename\": \"" << EscapeJsonString(result.parent_component_filename) << "\","
|
||||
<< "\"parent_component_path\": \"" << EscapeJsonString(result.parent_component_path) << "\","
|
||||
<< "\"children_count\": " << result.children_count << ","
|
||||
<< "\"children\": [";
|
||||
|
||||
// Build children array
|
||||
for (size_t i = 0; i < result.children.size(); ++i) {
|
||||
if (i > 0) json << ",";
|
||||
const auto& child = result.children[i];
|
||||
json << "{"
|
||||
<< "\"id\": " << child.id << ","
|
||||
<< "\"filename\": \"" << EscapeJsonString(child.filename) << "\","
|
||||
<< "\"name\": \"" << EscapeJsonString(child.name) << "\","
|
||||
<< "\"type\": \"" << child.type << "\","
|
||||
<< "\"level\": " << child.level << ","
|
||||
<< "\"path\": \"" << EscapeJsonString(child.path) << "\","
|
||||
<< "\"full_path\": \"" << EscapeJsonString(child.full_path) << "\","
|
||||
<< "\"file_size\": \"" << child.file_size << "\","
|
||||
<< "\"is_visible\": " << (child.is_visible ? "true" : "false") << ","
|
||||
<< "\"model_type\": \"" << child.model_type << "\","
|
||||
<< "\"children_count\": " << child.children_count
|
||||
<< "}";
|
||||
}
|
||||
|
||||
json << "]"
|
||||
<< "}"
|
||||
<< "}";
|
||||
|
||||
response.body = json.str();
|
||||
} else {
|
||||
// Build error response
|
||||
std::ostringstream json;
|
||||
json << "{"
|
||||
<< "\"success\": false,"
|
||||
<< "\"data\": null,"
|
||||
<< "\"error\": \"" << EscapeJsonString(result.error_message) << "\""
|
||||
<< "}";
|
||||
response.body = json.str();
|
||||
response.status_code = 500;
|
||||
}
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
response.status_code = 500;
|
||||
response.body = "{\"success\": false, \"error\": \"Exception: " + std::string(e.what()) + "\"}";
|
||||
} catch (...) {
|
||||
response.status_code = 500;
|
||||
response.body = "{\"success\": false, \"error\": \"Unknown error during component children retrieval\"}";
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
extern "C" int user_initialize(
|
||||
int argc,
|
||||
@ -1684,6 +1787,7 @@ extern "C" int user_initialize(
|
||||
g_http_server->SetRouteHandler("/api/creo/component/delete-by-path", PathDeleteHandler);
|
||||
g_http_server->SetRouteHandler("/api/search/models", ModelSearchHandler::HandleModelSearchRequest);
|
||||
g_http_server->SetRouteHandler("/api/analysis/hierarchy-statistics", HierarchyStatisticsHandler);
|
||||
g_http_server->SetRouteHandler("/api/creo/component/children", ComponentChildrenHandler);
|
||||
|
||||
|
||||
if (g_http_server->Start()) {
|
||||
|
||||
@ -207,6 +207,7 @@ ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AuthManager.cpp" />
|
||||
<ClCompile Include="ComponentChildrenManager.cpp" />
|
||||
<ClCompile Include="CreoManager.cpp" />
|
||||
<ClCompile Include="GeometryAnalyzer.cpp" />
|
||||
<ClCompile Include="HierarchyStatisticsAnalyzer.cpp" />
|
||||
@ -236,6 +237,7 @@ ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AuthManager.h" />
|
||||
<ClInclude Include="ComponentChildrenManager.h" />
|
||||
<ClInclude Include="Config.h" />
|
||||
<ClInclude Include="CreoManager.h" />
|
||||
<ClInclude Include="framework.h" />
|
||||
|
||||
@ -90,6 +90,9 @@
|
||||
<ClCompile Include="HierarchyStatisticsAnalyzer.cpp">
|
||||
<Filter>源文件\src\creo</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ComponentChildrenManager.cpp">
|
||||
<Filter>源文件\src\creo</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="MFCCreoDll.def">
|
||||
@ -172,6 +175,9 @@
|
||||
<ClInclude Include="HierarchyStatisticsAnalyzer.h">
|
||||
<Filter>源文件\src\creo</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ComponentChildrenManager.h">
|
||||
<Filter>源文件\src\creo</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="MFCCreoDll.rc">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user