feat(v1.0.1): tangent_pull + error codes + version + license management
Remaining P3 item: - tangent_pull: maintain G1 tangency when pulling faces System infrastructure: - Unified ErrorCode (0-7xxx): ErrorInfo with Chinese messages - Version system: VDE_VERSION_MAJOR/MINOR/PATCH macros, vde_version() → VersionInfo, vde_version_string() - License management: LicenseTier (Community/Professional/Enterprise), LicenseKey (VDE-XXXX-XXXX, Base32 + HMAC-SHA256 signature), LicenseManager singleton with trial support - Convenience header: vde/vde_version.h Tests: 43 total (26 license + 17 version)
This commit is contained in:
@@ -12,6 +12,7 @@ add_library(vde_foundation STATIC
|
||||
foundation/io_3mf.cpp
|
||||
foundation/industrial_formats.cpp
|
||||
foundation/vde_format.cpp
|
||||
foundation/error_codes.cpp
|
||||
)
|
||||
target_include_directories(vde_foundation
|
||||
PUBLIC ${CMAKE_SOURCE_DIR}/include
|
||||
@@ -38,9 +39,11 @@ add_library(vde_core STATIC
|
||||
core/concurrent_data.cpp
|
||||
core/transaction.cpp
|
||||
core/topology_compression.cpp
|
||||
core/license_manager.cpp
|
||||
)
|
||||
target_include_directories(vde_core
|
||||
PUBLIC ${CMAKE_SOURCE_DIR}/include
|
||||
PUBLIC ${CMAKE_BINARY_DIR}/generated
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_link_libraries(vde_core
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "vde/brep/direct_modeling.h"
|
||||
#include "vde/brep/brep_validate.h"
|
||||
#include "vde/brep/trimmed_surface.h"
|
||||
#include "vde/curves/surface_continuity.h"
|
||||
#include <cmath>
|
||||
#include <set>
|
||||
#include <map>
|
||||
@@ -1147,4 +1148,161 @@ DirectModelingResult pull_up_to_face(const BrepModel& body,
|
||||
return result;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// tangent_pull — 相切联动拉伸
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
/// 检测两面是否 G1 相切连续
|
||||
/// 优先通过法向量平行性快速判定(适用于平面),
|
||||
/// 非平面或判定不明确时回退到 surface_continuity
|
||||
static bool is_g1_tangent(const BrepModel& body, int fa, int fb) {
|
||||
Vector3D na = compute_face_normal(body, fa);
|
||||
Vector3D nb = compute_face_normal(body, fb);
|
||||
|
||||
// 法向量平行(同向)→ G1 tangent
|
||||
double dot_abs = std::abs(na.dot(nb));
|
||||
if (dot_abs > 0.9999) return true;
|
||||
|
||||
// 法向量平行(反向,reversed face)→ G1 tangent
|
||||
// 已由 dot_abs 覆盖
|
||||
|
||||
// 通过曲面连续性检测(曲线/曲面需要)
|
||||
const auto& face_a = body.face(fa);
|
||||
const auto& face_b = body.face(fb);
|
||||
|
||||
if (face_a.surface_id >= 0 && face_b.surface_id >= 0) {
|
||||
const auto& surf_a = body.surface(face_a.surface_id);
|
||||
const auto& surf_b = body.surface(face_b.surface_id);
|
||||
auto cont = curves::surface_continuity(surf_a, surf_b, 20, 1e-6);
|
||||
if (cont >= curves::ContinuityLevel::G1) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// 收集非共享顶点:face_id 的所有顶点中,排除与 ref_face 共享的顶点
|
||||
static std::set<int> collect_non_shared_vertices(const BrepModel& body,
|
||||
int face_id, int ref_face_id) {
|
||||
auto face_verts = collect_face_vertices(body, face_id);
|
||||
auto ref_verts = collect_face_vertices(body, ref_face_id);
|
||||
|
||||
std::set<int> result;
|
||||
for (int vi : face_verts) {
|
||||
if (ref_verts.find(vi) == ref_verts.end()) {
|
||||
result.insert(vi);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DirectModelingResult tangent_pull(const BrepModel& body,
|
||||
int face_id, double distance) {
|
||||
DirectModelingResult result;
|
||||
result.operation = "tangent_pull";
|
||||
|
||||
// ── 1. 参数验证 ──
|
||||
if (face_id < 0 || face_id >= static_cast<int>(body.num_faces())) {
|
||||
result.errors.push_back("tangent_pull: face_id " + std::to_string(face_id) +
|
||||
" out of range [0, " + std::to_string(body.num_faces()) + ")");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (std::abs(distance) < 1e-15) {
|
||||
result.success = true;
|
||||
result.body = body;
|
||||
result.warnings.push_back("tangent_pull: distance is zero, no change made");
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── 2. 检测相邻面 ──
|
||||
std::vector<int> adjacent_faces;
|
||||
for (size_t fi = 0; fi < body.num_faces(); ++fi) {
|
||||
int cur_fi = static_cast<int>(fi);
|
||||
if (cur_fi != face_id && faces_share_edge(body, face_id, cur_fi)) {
|
||||
adjacent_faces.push_back(cur_fi);
|
||||
}
|
||||
}
|
||||
|
||||
if (adjacent_faces.empty()) {
|
||||
result.errors.push_back("tangent_pull: face " + std::to_string(face_id) +
|
||||
" has no adjacent faces (isolated)");
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── 3. 检测每个相邻面是否 G1 连续 ──
|
||||
std::set<int> g1_adjacent;
|
||||
for (int af : adjacent_faces) {
|
||||
if (is_g1_tangent(body, face_id, af)) {
|
||||
g1_adjacent.insert(af);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 4. 计算拉伸面法向 ──
|
||||
Vector3D normal = compute_face_normal(body, face_id);
|
||||
|
||||
// ── 5. 构建顶点偏移 ──
|
||||
// 拉伸面顶点 → 偏移 distance
|
||||
// G1 相邻面非共享顶点 → 也偏移 distance(保持相切)
|
||||
|
||||
// 拉伸面顶点偏移
|
||||
auto face_verts = collect_face_vertices(body, face_id);
|
||||
std::map<int, Point3D> offsets;
|
||||
for (int vi : face_verts) {
|
||||
offsets[vi] = body.vertex(vi).point + normal * distance;
|
||||
}
|
||||
|
||||
// G1 相邻面非共享顶点偏移
|
||||
for (int af : g1_adjacent) {
|
||||
auto non_shared = collect_non_shared_vertices(body, af, face_id);
|
||||
for (int vi : non_shared) {
|
||||
// 避免重复添加(一个顶点可能属于多个 G1 相邻面)
|
||||
if (offsets.find(vi) == offsets.end()) {
|
||||
offsets[vi] = body.vertex(vi).point + normal * distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int g1_count = static_cast<int>(g1_adjacent.size());
|
||||
int total_adjacent = static_cast<int>(adjacent_faces.size());
|
||||
|
||||
// ── 6. 重建模型(创建侧面 = push_pull 行为) ──
|
||||
bool create_sides = (std::abs(distance) > 1e-15);
|
||||
result.body = rebuild_with_offset(body, face_id, offsets, create_sides);
|
||||
|
||||
// ── 7. 统计侧面数 ──
|
||||
if (create_sides) {
|
||||
const auto& src_face = body.face(face_id);
|
||||
for (int li : src_face.loops) {
|
||||
const auto& loop = body.loop_by_id(li);
|
||||
if (loop.is_outer) {
|
||||
result.new_faces = static_cast<int>(loop.edges.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 8. 验证 ──
|
||||
auto val_result = validate(result.body);
|
||||
result.success = val_result.valid;
|
||||
result.affected_faces = 1 + g1_count;
|
||||
|
||||
if (!val_result.valid) {
|
||||
for (const auto& e : val_result.errors)
|
||||
result.warnings.push_back("Validation: " + e);
|
||||
}
|
||||
|
||||
// 诊断信息
|
||||
if (g1_count > 0) {
|
||||
result.warnings.push_back("tangent_pull: " + std::to_string(g1_count) +
|
||||
" / " + std::to_string(total_adjacent) +
|
||||
" adjacent faces adjusted for G1 tangency");
|
||||
} else {
|
||||
result.warnings.push_back("tangent_pull: no G1-adjacent faces found " +
|
||||
std::string("(all ") + std::to_string(total_adjacent) +
|
||||
" adjacent faces are non-tangent)");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace vde::brep
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
/**
|
||||
* @file license_manager.cpp
|
||||
* @brief 许可证管理实现
|
||||
*
|
||||
* License Key 格式:VDE-XXXX-XXXX-XXXX
|
||||
*
|
||||
* 二进制布局(17字节 payload):
|
||||
* - byte 0: version (1)
|
||||
* - byte 1: tier (0=Community, 1=Professional, 2=Enterprise)
|
||||
* - byte 2-9: expiry (uint64_t, Unix timestamp, big-endian)
|
||||
* - byte 10-16: reserved (0)
|
||||
*
|
||||
* 签名:HMAC-SHA256(使用固定种子密钥)前 4 字节附加到 payload。
|
||||
* 完整编码:payload(17B) + signature(4B) → Base32 → VDE-XXXX-XXXX-XXXX
|
||||
*/
|
||||
|
||||
#include "vde/core/license_manager.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vde::core {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// 内部常量
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
static constexpr int PAYLOAD_SIZE = 17; // 载荷字节数
|
||||
static constexpr int SIGNATURE_SIZE = 4; // 签名截断字节数
|
||||
static constexpr int ENCODED_SIZE = 21; // payload + signature
|
||||
static constexpr char KEY_PREFIX[] = "VDE-";
|
||||
static constexpr size_t KEY_PREFIX_LEN = 4;
|
||||
|
||||
// Base32 字母表(RFC 4648,大写)
|
||||
static const char BASE32_ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
||||
// HMAC 种子密钥(固定,用于简单验证;生产环境应从安全存储加载)
|
||||
static const uint8_t HMAC_KEY[] = {
|
||||
0x56, 0x44, 0x45, 0x4E, 0x47, 0x49, 0x4E, 0x45, // "VDENGINE"
|
||||
0x4C, 0x49, 0x43, 0x45, 0x4E, 0x53, 0x45, 0x00, // "LICENSE\0"
|
||||
0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x4B, 0x45, // "SECRETKE"
|
||||
0x59, 0x32, 0x30, 0x32, 0x35, 0x21, 0x21, 0x21, // "Y2025!!!"
|
||||
};
|
||||
static constexpr size_t HMAC_KEY_SIZE = sizeof(HMAC_KEY);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// parse_license_tier / license_tier_name (已在头文件 inline)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
int parse_license_tier(const std::string& name) {
|
||||
if (name == "Community" || name == "community") return 0;
|
||||
if (name == "Professional" || name == "professional") return 1;
|
||||
if (name == "Enterprise" || name == "enterprise") return 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Feature 辅助函数
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
const char* feature_name(Feature f) {
|
||||
switch (f) {
|
||||
case Feature::BASIC_BREP: return "BASIC_BREP";
|
||||
case Feature::STEP_IMPORT_EXPORT: return "STEP_IMPORT_EXPORT";
|
||||
case Feature::IGES_IMPORT_EXPORT: return "IGES_IMPORT_EXPORT";
|
||||
case Feature::BASE_IO_FORMATS: return "BASE_IO_FORMATS";
|
||||
case Feature::MESH_OPERATIONS: return "MESH_OPERATIONS";
|
||||
case Feature::SKETCH_SOLVER: return "SKETCH_SOLVER";
|
||||
case Feature::CAM_5AXIS: return "CAM_5AXIS";
|
||||
case Feature::ADVANCED_SURFACING: return "ADVANCED_SURFACING";
|
||||
case Feature::GD_T: return "GD_T";
|
||||
case Feature::PMI_MBD: return "PMI_MBD";
|
||||
case Feature::ASSEMBLY_DESIGN: return "ASSEMBLY_DESIGN";
|
||||
case Feature::SHEET_METAL: return "SHEET_METAL";
|
||||
case Feature::ADVANCED_HEALING: return "ADVANCED_HEALING";
|
||||
case Feature::FEA_MESH: return "FEA_MESH";
|
||||
case Feature::IGA_ANALYSIS: return "IGA_ANALYSIS";
|
||||
case Feature::COLLISION_DETECTION: return "COLLISION_DETECTION";
|
||||
case Feature::DIRECT_MODELING: return "DIRECT_MODELING";
|
||||
case Feature::SURFACE_FAIRING: return "SURFACE_FAIRING";
|
||||
case Feature::TECHNICAL_SUPPORT: return "TECHNICAL_SUPPORT";
|
||||
case Feature::CUSTOM_FORMATS: return "CUSTOM_FORMATS";
|
||||
case Feature::SCRIPTING_API: return "SCRIPTING_API";
|
||||
case Feature::GPU_ACCELERATION: return "GPU_ACCELERATION";
|
||||
case Feature::CLOUD_COLLABORATION: return "CLOUD_COLLABORATION";
|
||||
case Feature::SDF_MODELING: return "SDF_MODELING";
|
||||
case Feature::LICENSE_MANAGER_SDK: return "LICENSE_MANAGER_SDK";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
int parse_feature(const std::string& name) {
|
||||
static const std::unordered_map<std::string, int> map = {
|
||||
{"BASIC_BREP", 0}, {"STEP_IMPORT_EXPORT", 1},
|
||||
{"IGES_IMPORT_EXPORT", 2}, {"BASE_IO_FORMATS", 3},
|
||||
{"MESH_OPERATIONS", 4}, {"SKETCH_SOLVER", 5},
|
||||
{"CAM_5AXIS", 10}, {"ADVANCED_SURFACING", 11},
|
||||
{"GD_T", 12}, {"PMI_MBD", 13},
|
||||
{"ASSEMBLY_DESIGN", 14}, {"SHEET_METAL", 15},
|
||||
{"ADVANCED_HEALING", 16}, {"FEA_MESH", 17},
|
||||
{"IGA_ANALYSIS", 18}, {"COLLISION_DETECTION",19},
|
||||
{"DIRECT_MODELING", 20}, {"SURFACE_FAIRING", 21},
|
||||
{"TECHNICAL_SUPPORT", 30}, {"CUSTOM_FORMATS", 31},
|
||||
{"SCRIPTING_API", 32}, {"GPU_ACCELERATION", 33},
|
||||
{"CLOUD_COLLABORATION",34}, {"SDF_MODELING", 35},
|
||||
{"LICENSE_MANAGER_SDK",36},
|
||||
};
|
||||
auto it = map.find(name);
|
||||
return (it != map.end()) ? it->second : -1;
|
||||
}
|
||||
|
||||
std::set<Feature> features_for_tier(LicenseTier tier) {
|
||||
std::set<Feature> feats;
|
||||
|
||||
// Community
|
||||
feats.insert(Feature::BASIC_BREP);
|
||||
feats.insert(Feature::STEP_IMPORT_EXPORT);
|
||||
feats.insert(Feature::IGES_IMPORT_EXPORT);
|
||||
feats.insert(Feature::BASE_IO_FORMATS);
|
||||
feats.insert(Feature::MESH_OPERATIONS);
|
||||
feats.insert(Feature::SKETCH_SOLVER);
|
||||
|
||||
if (tier == LicenseTier::Community) return feats;
|
||||
|
||||
// Professional
|
||||
feats.insert(Feature::CAM_5AXIS);
|
||||
feats.insert(Feature::ADVANCED_SURFACING);
|
||||
feats.insert(Feature::GD_T);
|
||||
feats.insert(Feature::PMI_MBD);
|
||||
feats.insert(Feature::ASSEMBLY_DESIGN);
|
||||
feats.insert(Feature::SHEET_METAL);
|
||||
feats.insert(Feature::ADVANCED_HEALING);
|
||||
feats.insert(Feature::FEA_MESH);
|
||||
feats.insert(Feature::IGA_ANALYSIS);
|
||||
feats.insert(Feature::COLLISION_DETECTION);
|
||||
feats.insert(Feature::DIRECT_MODELING);
|
||||
feats.insert(Feature::SURFACE_FAIRING);
|
||||
|
||||
if (tier == LicenseTier::Professional) return feats;
|
||||
|
||||
// Enterprise
|
||||
feats.insert(Feature::TECHNICAL_SUPPORT);
|
||||
feats.insert(Feature::CUSTOM_FORMATS);
|
||||
feats.insert(Feature::SCRIPTING_API);
|
||||
feats.insert(Feature::GPU_ACCELERATION);
|
||||
feats.insert(Feature::CLOUD_COLLABORATION);
|
||||
feats.insert(Feature::SDF_MODELING);
|
||||
feats.insert(Feature::LICENSE_MANAGER_SDK);
|
||||
|
||||
return feats;
|
||||
}
|
||||
|
||||
bool feature_available_in_tier(Feature f, LicenseTier tier) {
|
||||
auto feats = features_for_tier(tier);
|
||||
return feats.find(f) != feats.end();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// LicenseInfo 成员
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
bool LicenseInfo::is_expired() const {
|
||||
if (expiry == 0) return false; // 永久
|
||||
return std::time(nullptr) > expiry;
|
||||
}
|
||||
|
||||
int LicenseInfo::days_until_expiry() const {
|
||||
if (expiry == 0) return -1; // 永久
|
||||
auto now = std::time(nullptr);
|
||||
if (now >= expiry) return 0;
|
||||
double diff = std::difftime(expiry, now);
|
||||
return static_cast<int>(diff / 86400.0); // 秒转天
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Base32 RFC 4648 编解码
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
static int base32_char_to_value(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a';
|
||||
if (c >= '2' && c <= '7') return c - '2' + 26;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string LicenseKey::base32_encode(const std::vector<uint8_t>& data) {
|
||||
std::string result;
|
||||
result.reserve((data.size() * 8 + 4) / 5);
|
||||
|
||||
size_t bit_buf = 0;
|
||||
int bit_count = 0;
|
||||
|
||||
for (uint8_t byte : data) {
|
||||
bit_buf = (bit_buf << 8) | byte;
|
||||
bit_count += 8;
|
||||
|
||||
while (bit_count >= 5) {
|
||||
bit_count -= 5;
|
||||
result.push_back(BASE32_ALPHABET[(bit_buf >> bit_count) & 0x1F]);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理剩余位
|
||||
if (bit_count > 0) {
|
||||
result.push_back(BASE32_ALPHABET[(bit_buf << (5 - bit_count)) & 0x1F]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> LicenseKey::base32_decode(const std::string& encoded) {
|
||||
std::vector<uint8_t> result;
|
||||
result.reserve(encoded.size() * 5 / 8);
|
||||
|
||||
size_t bit_buf = 0;
|
||||
int bit_count = 0;
|
||||
|
||||
for (char c : encoded) {
|
||||
int val = base32_char_to_value(c);
|
||||
if (val < 0) continue; // 忽略无效字符(如连字符)
|
||||
|
||||
bit_buf = (bit_buf << 5) | static_cast<size_t>(val);
|
||||
bit_count += 5;
|
||||
|
||||
while (bit_count >= 8) {
|
||||
bit_count -= 8;
|
||||
result.push_back(static_cast<uint8_t>((bit_buf >> bit_count) & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// HMAC-SHA256(简化版,使用固定密钥)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
// 简化版 SHA256 — 完整实现但内联在此文件中
|
||||
namespace {
|
||||
|
||||
struct Sha256Ctx {
|
||||
uint32_t state[8];
|
||||
uint64_t bit_count;
|
||||
uint8_t buffer[64];
|
||||
size_t buffer_len;
|
||||
};
|
||||
|
||||
static const uint32_t SHA256_K[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
||||
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
||||
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
inline uint32_t rotr32(uint32_t x, uint32_t n) {
|
||||
return (x >> n) | (x << (32 - n));
|
||||
}
|
||||
|
||||
static void sha256_transform(Sha256Ctx* ctx, const uint8_t data[64]) {
|
||||
uint32_t w[64];
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
w[i] = (static_cast<uint32_t>(data[i * 4]) << 24) |
|
||||
(static_cast<uint32_t>(data[i * 4 + 1]) << 16) |
|
||||
(static_cast<uint32_t>(data[i * 4 + 2]) << 8) |
|
||||
(static_cast<uint32_t>(data[i * 4 + 3]));
|
||||
}
|
||||
for (int i = 16; i < 64; ++i) {
|
||||
uint32_t s0 = rotr32(w[i - 15], 7) ^ rotr32(w[i - 15], 18) ^ (w[i - 15] >> 3);
|
||||
uint32_t s1 = rotr32(w[i - 2], 17) ^ rotr32(w[i - 2], 19) ^ (w[i - 2] >> 10);
|
||||
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
|
||||
}
|
||||
|
||||
uint32_t a = ctx->state[0], b = ctx->state[1], c = ctx->state[2], d = ctx->state[3];
|
||||
uint32_t e = ctx->state[4], f = ctx->state[5], g = ctx->state[6], h = ctx->state[7];
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
uint32_t S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
|
||||
uint32_t ch = (e & f) ^ (~e & g);
|
||||
uint32_t temp1 = h + S1 + ch + SHA256_K[i] + w[i];
|
||||
uint32_t S0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22);
|
||||
uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
|
||||
uint32_t temp2 = S0 + maj;
|
||||
|
||||
h = g; g = f; f = e; e = d + temp1;
|
||||
d = c; c = b; b = a; a = temp1 + temp2;
|
||||
}
|
||||
|
||||
ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d;
|
||||
ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h;
|
||||
}
|
||||
|
||||
static void sha256_init(Sha256Ctx* ctx) {
|
||||
ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85;
|
||||
ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a;
|
||||
ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c;
|
||||
ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19;
|
||||
ctx->bit_count = 0;
|
||||
ctx->buffer_len = 0;
|
||||
}
|
||||
|
||||
static void sha256_update(Sha256Ctx* ctx, const uint8_t* data, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
ctx->buffer[ctx->buffer_len++] = data[i];
|
||||
if (ctx->buffer_len == 64) {
|
||||
sha256_transform(ctx, ctx->buffer);
|
||||
ctx->bit_count += 512;
|
||||
ctx->buffer_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sha256_final(Sha256Ctx* ctx, uint8_t hash[32]) {
|
||||
uint64_t total_bits = ctx->bit_count + ctx->buffer_len * 8;
|
||||
|
||||
// 填充:0x80 后跟 0x00,然后是 64-bit 长度
|
||||
ctx->buffer[ctx->buffer_len++] = 0x80;
|
||||
if (ctx->buffer_len > 56) {
|
||||
while (ctx->buffer_len < 64) ctx->buffer[ctx->buffer_len++] = 0;
|
||||
sha256_transform(ctx, ctx->buffer);
|
||||
ctx->buffer_len = 0;
|
||||
}
|
||||
while (ctx->buffer_len < 56) ctx->buffer[ctx->buffer_len++] = 0;
|
||||
|
||||
// 大端序写入长度
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
ctx->buffer[56 + i] = static_cast<uint8_t>(total_bits & 0xFF);
|
||||
total_bits >>= 8;
|
||||
}
|
||||
sha256_transform(ctx, ctx->buffer);
|
||||
|
||||
// 输出
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
hash[i * 4] = static_cast<uint8_t>((ctx->state[i] >> 24) & 0xFF);
|
||||
hash[i * 4 + 1] = static_cast<uint8_t>((ctx->state[i] >> 16) & 0xFF);
|
||||
hash[i * 4 + 2] = static_cast<uint8_t>((ctx->state[i] >> 8) & 0xFF);
|
||||
hash[i * 4 + 3] = static_cast<uint8_t>(ctx->state[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::vector<uint8_t> LicenseKey::compute_hmac(const std::vector<uint8_t>& data) {
|
||||
constexpr size_t BLOCK_SIZE = 64; // SHA256 block size
|
||||
|
||||
// HMAC: H((K ^ opad) || H((K ^ ipad) || message))
|
||||
uint8_t key_block[BLOCK_SIZE] = {};
|
||||
|
||||
// 如果 key 超过 block size,先哈希
|
||||
if (HMAC_KEY_SIZE > BLOCK_SIZE) {
|
||||
Sha256Ctx key_ctx;
|
||||
sha256_init(&key_ctx);
|
||||
sha256_update(&key_ctx, HMAC_KEY, HMAC_KEY_SIZE);
|
||||
uint8_t key_hash[32];
|
||||
sha256_final(&key_ctx, key_hash);
|
||||
std::memcpy(key_block, key_hash, 32);
|
||||
} else {
|
||||
std::memcpy(key_block, HMAC_KEY, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
// ipad
|
||||
uint8_t ipad[BLOCK_SIZE];
|
||||
for (size_t i = 0; i < BLOCK_SIZE; ++i) ipad[i] = key_block[i] ^ 0x36;
|
||||
|
||||
// opad
|
||||
uint8_t opad[BLOCK_SIZE];
|
||||
for (size_t i = 0; i < BLOCK_SIZE; ++i) opad[i] = key_block[i] ^ 0x5C;
|
||||
|
||||
// 内层哈希:H(ipad || data)
|
||||
Sha256Ctx inner;
|
||||
sha256_init(&inner);
|
||||
sha256_update(&inner, ipad, BLOCK_SIZE);
|
||||
sha256_update(&inner, data.data(), data.size());
|
||||
uint8_t inner_hash[32];
|
||||
sha256_final(&inner, inner_hash);
|
||||
|
||||
// 外层哈希:H(opad || inner_hash)
|
||||
Sha256Ctx outer;
|
||||
sha256_init(&outer);
|
||||
sha256_update(&outer, opad, BLOCK_SIZE);
|
||||
sha256_update(&outer, inner_hash, 32);
|
||||
uint8_t outer_hash[32];
|
||||
sha256_final(&outer, outer_hash);
|
||||
|
||||
// 返回完整 32 字节
|
||||
return std::vector<uint8_t>(outer_hash, outer_hash + 32);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// LicenseKey 实现
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* @brief 构造 payload 字节序列
|
||||
*
|
||||
* 布局(17字节):
|
||||
* [0]: version
|
||||
* [1]: tier
|
||||
* [2-9]: expiry (uint64_t big-endian)
|
||||
* [10-16]: reserved (0)
|
||||
*/
|
||||
static std::vector<uint8_t> build_payload(int version, LicenseTier tier,
|
||||
std::time_t expiry) {
|
||||
std::vector<uint8_t> payload(PAYLOAD_SIZE, 0);
|
||||
payload[0] = static_cast<uint8_t>(version);
|
||||
payload[1] = static_cast<uint8_t>(static_cast<int>(tier));
|
||||
|
||||
// expiry 大端序写入 byte 2-9
|
||||
uint64_t exp_val = static_cast<uint64_t>(expiry);
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
payload[2 + i] = static_cast<uint8_t>(exp_val & 0xFF);
|
||||
exp_val >>= 8;
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从 payload 解析 LicenseInfo
|
||||
*/
|
||||
static LicenseInfo parse_payload(const std::vector<uint8_t>& data) {
|
||||
LicenseInfo info;
|
||||
if (data.size() < PAYLOAD_SIZE) return info;
|
||||
|
||||
info.version = data[0];
|
||||
info.tier = static_cast<LicenseTier>(std::min(static_cast<int>(data[1]), 2));
|
||||
|
||||
uint64_t exp_val = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
exp_val = (exp_val << 8) | data[2 + i];
|
||||
}
|
||||
info.expiry = static_cast<std::time_t>(exp_val);
|
||||
info.licensee = "Licensee"; // 未嵌入在 payload 中
|
||||
info.features = features_for_tier(info.tier);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
bool LicenseKey::parse(const std::string& key_str) {
|
||||
key_string_ = key_str;
|
||||
sig_valid_ = false;
|
||||
|
||||
// 检查前缀
|
||||
if (key_str.size() < KEY_PREFIX_LEN + 3 ||
|
||||
key_str.substr(0, KEY_PREFIX_LEN) != KEY_PREFIX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 去掉 "VDE-" 前缀
|
||||
std::string encoded = key_str.substr(KEY_PREFIX_LEN);
|
||||
|
||||
// 移除连字符
|
||||
encoded.erase(std::remove(encoded.begin(), encoded.end(), '-'), encoded.end());
|
||||
|
||||
// Base32 解码
|
||||
auto raw = base32_decode(encoded);
|
||||
if (raw.size() < static_cast<size_t>(PAYLOAD_SIZE + SIGNATURE_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 分离 payload 和签名
|
||||
std::vector<uint8_t> payload(raw.begin(), raw.begin() + PAYLOAD_SIZE);
|
||||
std::vector<uint8_t> sig(raw.begin() + PAYLOAD_SIZE,
|
||||
raw.begin() + PAYLOAD_SIZE + SIGNATURE_SIZE);
|
||||
|
||||
// 验证签名
|
||||
auto expected_hmac = compute_hmac(payload);
|
||||
sig_valid_ = (sig.size() == SIGNATURE_SIZE &&
|
||||
std::equal(sig.begin(), sig.end(), expected_hmac.begin()));
|
||||
|
||||
// 解析 payload
|
||||
info_ = parse_payload(payload);
|
||||
info_.key_string = key_str;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string LicenseKey::generate(LicenseTier tier, std::time_t expiry,
|
||||
const std::string& /*licensee*/) {
|
||||
auto payload = build_payload(1, tier, expiry);
|
||||
|
||||
// 计算 HMAC 签名
|
||||
auto hmac = compute_hmac(payload);
|
||||
|
||||
// 合并 payload + signature[0:4]
|
||||
std::vector<uint8_t> combined(payload);
|
||||
combined.insert(combined.end(), hmac.begin(), hmac.begin() + SIGNATURE_SIZE);
|
||||
|
||||
// Base32 编码
|
||||
std::string encoded = base32_encode(combined);
|
||||
|
||||
// 格式化为 VDE-XXXX-XXXX-XXXX
|
||||
std::ostringstream oss;
|
||||
oss << KEY_PREFIX;
|
||||
size_t pos = 0;
|
||||
while (pos < encoded.size()) {
|
||||
if (pos > 0) oss << '-';
|
||||
size_t len = std::min<size_t>(4, encoded.size() - pos);
|
||||
oss << encoded.substr(pos, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool LicenseKey::validate(const std::string& key_str) {
|
||||
LicenseKey lk;
|
||||
if (!lk.parse(key_str)) return false;
|
||||
return lk.signature_valid() && !lk.info().is_expired();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// LicenseManager 单例
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
LicenseManager& LicenseManager::instance() {
|
||||
static LicenseManager mgr;
|
||||
return mgr;
|
||||
}
|
||||
|
||||
LicenseManager::LicenseManager() {
|
||||
// 默认:Community 社区版
|
||||
current_.tier = LicenseTier::Community;
|
||||
current_.licensee = "Community User";
|
||||
current_.expiry = 0; // 永久
|
||||
current_.features = features_for_tier(LicenseTier::Community);
|
||||
current_.version = 1;
|
||||
}
|
||||
|
||||
bool LicenseManager::set_license(const std::string& key) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
LicenseKey lk;
|
||||
if (!lk.parse(key)) return false;
|
||||
if (!lk.signature_valid()) return false;
|
||||
|
||||
const auto& info = lk.info();
|
||||
if (info.is_expired()) return false;
|
||||
|
||||
current_ = info;
|
||||
trial_mode_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LicenseManager::start_trial(int days) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
trial_mode_ = true;
|
||||
trial_start_ = std::time(nullptr);
|
||||
trial_days_ = std::max(1, days);
|
||||
|
||||
current_.tier = LicenseTier::Professional; // 试用Professional
|
||||
current_.licensee = "Trial User";
|
||||
current_.expiry = trial_start_ + static_cast<std::time_t>(trial_days_) * 86400;
|
||||
current_.features = features_for_tier(LicenseTier::Professional);
|
||||
current_.version = 1;
|
||||
}
|
||||
|
||||
LicenseTier LicenseManager::current_tier() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return current_.tier;
|
||||
}
|
||||
|
||||
bool LicenseManager::has_feature(const std::string& feature_name) const {
|
||||
int fid = parse_feature(feature_name);
|
||||
if (fid < 0) return false;
|
||||
return has_feature(static_cast<Feature>(fid));
|
||||
}
|
||||
|
||||
bool LicenseManager::has_feature(Feature f) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
// 检查是否过期
|
||||
if (is_expired()) return false;
|
||||
|
||||
return current_.features.find(f) != current_.features.end();
|
||||
}
|
||||
|
||||
bool LicenseManager::is_expired() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (current_.expiry == 0) return false; // 永久
|
||||
return std::time(nullptr) > current_.expiry;
|
||||
}
|
||||
|
||||
int LicenseManager::trial_days_left() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (!trial_mode_) return -1;
|
||||
|
||||
auto now = std::time(nullptr);
|
||||
auto end = trial_start_ + static_cast<std::time_t>(trial_days_) * 86400;
|
||||
if (now >= end) return 0;
|
||||
|
||||
double diff = std::difftime(end, now);
|
||||
return static_cast<int>(diff / 86400.0);
|
||||
}
|
||||
|
||||
const LicenseInfo& LicenseManager::current_license() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return current_;
|
||||
}
|
||||
|
||||
bool LicenseManager::is_trial() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return trial_mode_;
|
||||
}
|
||||
|
||||
} // namespace vde::core
|
||||
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* @file error_codes.cpp
|
||||
* @brief 统一错误码实现
|
||||
*/
|
||||
|
||||
#include "vde/foundation/error_codes.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vde::foundation {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// error_message — 中文描述
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
std::string error_message(ErrorCode code) {
|
||||
static const std::unordered_map<ErrorCode, const char*> kMessages = {
|
||||
// 成功
|
||||
{ErrorCode::SUCCESS, "成功"},
|
||||
|
||||
// 1xxx:foundation 格式IO错误
|
||||
{ErrorCode::ERR_FILE_NOT_FOUND, "文件未找到"},
|
||||
{ErrorCode::ERR_FILE_OPEN_FAILED, "文件打开失败"},
|
||||
{ErrorCode::ERR_FILE_WRITE_FAILED, "文件写入失败"},
|
||||
{ErrorCode::ERR_FILE_READ_FAILED, "文件读取失败"},
|
||||
{ErrorCode::ERR_FILE_PERMISSION_DENIED, "文件权限不足"},
|
||||
{ErrorCode::ERR_FILE_TOO_LARGE, "文件过大"},
|
||||
{ErrorCode::ERR_FILE_EMPTY, "文件为空"},
|
||||
{ErrorCode::ERR_FILE_ALREADY_EXISTS, "文件已存在"},
|
||||
|
||||
{ErrorCode::ERR_FORMAT_INVALID, "格式无效"},
|
||||
{ErrorCode::ERR_FORMAT_UNSUPPORTED, "格式不支持"},
|
||||
{ErrorCode::ERR_FORMAT_VERSION_MISMATCH,"格式版本不匹配"},
|
||||
{ErrorCode::ERR_FORMAT_CORRUPTED, "格式数据损坏"},
|
||||
{ErrorCode::ERR_FORMAT_TRUNCATED, "格式数据截断"},
|
||||
{ErrorCode::ERR_FORMAT_MAGIC_MISMATCH, "格式魔术字不匹配"},
|
||||
|
||||
{ErrorCode::ERR_VDE_HEADER_INVALID, "VDE文件头无效"},
|
||||
{ErrorCode::ERR_VDE_SECTION_MISSING, "VDE段缺失"},
|
||||
{ErrorCode::ERR_VDE_SECTION_CORRUPTED, "VDE段损坏"},
|
||||
{ErrorCode::ERR_VDE_TOPOLOGY_INVALID, "VDE拓扑数据无效"},
|
||||
{ErrorCode::ERR_VDE_GEOMETRY_INVALID, "VDE几何数据无效"},
|
||||
{ErrorCode::ERR_VDE_SERIALIZE_FAILED, "VDE序列化失败"},
|
||||
{ErrorCode::ERR_VDE_DESERIALIZE_FAILED, "VDE反序列化失败"},
|
||||
|
||||
{ErrorCode::ERR_STEP_PARSE, "STEP解析错误"},
|
||||
{ErrorCode::ERR_STEP_SCHEMA_MISMATCH, "STEP模式不匹配"},
|
||||
{ErrorCode::ERR_STEP_ENTITY_UNKNOWN, "STEP未知实体"},
|
||||
{ErrorCode::ERR_STEP_TOPOLOGY, "STEP拓扑构建失败"},
|
||||
{ErrorCode::ERR_STEP_GEOMETRY, "STEP几何构建失败"},
|
||||
{ErrorCode::ERR_IGES_PARSE, "IGES解析错误"},
|
||||
{ErrorCode::ERR_IGES_ENTITY_UNSUPPORTED,"IGES不支持的实体"},
|
||||
{ErrorCode::ERR_OBJ_PARSE, "OBJ解析错误"},
|
||||
{ErrorCode::ERR_STL_PARSE, "STL解析错误"},
|
||||
{ErrorCode::ERR_STL_BINARY_INVALID, "STL二进制格式无效"},
|
||||
{ErrorCode::ERR_PLY_PARSE, "PLY解析错误"},
|
||||
{ErrorCode::ERR_GLTF_PARSE, "glTF解析错误"},
|
||||
{ErrorCode::ERR_3MF_PARSE, "3MF解析错误"},
|
||||
{ErrorCode::ERR_DXF_PARSE, "DXF解析错误"},
|
||||
|
||||
// 2xxx:core 几何计算错误
|
||||
{ErrorCode::ERR_DEGENERATE_GEOMETRY, "退化几何"},
|
||||
{ErrorCode::ERR_NUMERIC_INSTABILITY, "数值不稳定"},
|
||||
{ErrorCode::ERR_DIVISION_BY_ZERO, "除零错误"},
|
||||
{ErrorCode::ERR_SINGULAR_MATRIX, "奇异矩阵"},
|
||||
{ErrorCode::ERR_CONVERGENCE_FAILURE, "收敛失败"},
|
||||
{ErrorCode::ERR_OUT_OF_MEMORY, "内存不足"},
|
||||
{ErrorCode::ERR_OVERFLOW, "数值溢出"},
|
||||
{ErrorCode::ERR_PRECISION_LOSS, "精度丢失"},
|
||||
{ErrorCode::ERR_BUFFER_TOO_SMALL, "缓冲区太小"},
|
||||
{ErrorCode::ERR_INDEX_OUT_OF_BOUNDS, "索引越界"},
|
||||
|
||||
{ErrorCode::ERR_INTERSECTION_FAILED, "求交失败"},
|
||||
{ErrorCode::ERR_PROJECTION_FAILED, "投影失败"},
|
||||
{ErrorCode::ERR_DISTANCE_COMPUTE_FAILED,"距离计算失败"},
|
||||
{ErrorCode::ERR_CONVEX_HULL_FAILED, "凸包计算失败"},
|
||||
{ErrorCode::ERR_VORONOI_FAILED, "Voronoi计算失败"},
|
||||
{ErrorCode::ERR_TRIANGULATION_FAILED, "三角剖分失败"},
|
||||
{ErrorCode::ERR_SIMPLIFY_FAILED, "简化失败"},
|
||||
|
||||
{ErrorCode::ERR_CAM_TOOLPATH_GENERATION,"刀路生成失败"},
|
||||
{ErrorCode::ERR_CAM_COLLISION_DETECTED, "刀具碰撞检测到"},
|
||||
{ErrorCode::ERR_CAM_INVALID_STRATEGY, "无效的加工策略"},
|
||||
{ErrorCode::ERR_CAM_5AXIS_LIMIT, "五轴极限超限"},
|
||||
{ErrorCode::ERR_CAM_OPTIMIZATION_FAILED,"CAM优化失败"},
|
||||
|
||||
{ErrorCode::ERR_THREAD_CREATE_FAILED, "线程创建失败"},
|
||||
{ErrorCode::ERR_LOCK_TIMEOUT, "锁超时"},
|
||||
{ErrorCode::ERR_TASK_CANCELLED, "任务已取消"},
|
||||
{ErrorCode::ERR_TRANSACTION_CONFLICT, "事务冲突"},
|
||||
{ErrorCode::ERR_TRANSACTION_ROLLBACK, "事务回滚"},
|
||||
|
||||
// 3xxx:curves 曲线曲面错误
|
||||
{ErrorCode::ERR_CURVE_DEGREE_INVALID, "曲线阶数无效"},
|
||||
{ErrorCode::ERR_CURVE_KNOT_INVALID, "节点向量无效"},
|
||||
{ErrorCode::ERR_CURVE_CP_COUNT_MISMATCH,"控制点数量不匹配"},
|
||||
{ErrorCode::ERR_CURVE_EVALUATION_FAILED,"曲线求值失败"},
|
||||
{ErrorCode::ERR_CURVE_DERIVATIVE_FAILED,"曲线求导失败"},
|
||||
{ErrorCode::ERR_CURVE_PARAM_OUT_OF_RANGE,"参数超出范围"},
|
||||
{ErrorCode::ERR_CURVE_SELF_INTERSECT, "曲线自交"},
|
||||
{ErrorCode::ERR_CURVE_DISCONTINUITY, "曲线不连续"},
|
||||
|
||||
{ErrorCode::ERR_SURFACE_INVALID, "曲面无效"},
|
||||
{ErrorCode::ERR_SURFACE_PARAM_RANGE, "曲面参数范围无效"},
|
||||
{ErrorCode::ERR_SURFACE_DEGENERATE, "退化曲面"},
|
||||
{ErrorCode::ERR_SURFACE_SINGULARITY, "曲面奇点"},
|
||||
{ErrorCode::ERR_SURFACE_INTERSECTION, "曲面求交失败"},
|
||||
{ErrorCode::ERR_SURFACE_CONTINUITY, "曲面连续性不满足"},
|
||||
{ErrorCode::ERR_SURFACE_TRIMMING, "曲面裁剪失败"},
|
||||
{ErrorCode::ERR_SURFACE_EXTENSION, "曲面延伸失败"},
|
||||
{ErrorCode::ERR_SURFACE_OFFSET, "曲面偏置失败"},
|
||||
{ErrorCode::ERR_SURFACE_FAIRING, "曲面光顺失败"},
|
||||
|
||||
{ErrorCode::ERR_IGA_MESH_INVALID, "IGA网格无效"},
|
||||
{ErrorCode::ERR_IGA_REFINEMENT_FAILED, "IGA细化失败"},
|
||||
|
||||
// 4xxx:mesh 网格错误
|
||||
{ErrorCode::ERR_MESH_EMPTY, "网格为空"},
|
||||
{ErrorCode::ERR_MESH_NON_MANIFOLD, "非流形网格"},
|
||||
{ErrorCode::ERR_MESH_DEGENERATE_FACE, "退化面"},
|
||||
{ErrorCode::ERR_MESH_SELF_INTERSECTION, "网格自交"},
|
||||
{ErrorCode::ERR_MESH_INVERTED_NORMAL, "法线翻转"},
|
||||
{ErrorCode::ERR_MESH_HOLE_DETECTED, "检测到孔洞"},
|
||||
{ErrorCode::ERR_MESH_DISCONNECTED, "网格不连通"},
|
||||
|
||||
{ErrorCode::ERR_MESH_BOOLEAN_FAILED, "网格布尔失败"},
|
||||
{ErrorCode::ERR_MESH_SIMPLIFY_FAILED, "网格简化失败"},
|
||||
{ErrorCode::ERR_MESH_SMOOTH_FAILED, "网格平滑失败"},
|
||||
{ErrorCode::ERR_MESH_REPAIR_FAILED, "网格修复失败"},
|
||||
{ErrorCode::ERR_MESH_PARAMETERIZE_FAILED,"网格参数化失败"},
|
||||
{ErrorCode::ERR_MESH_REMESH_FAILED, "网格重划分失败"},
|
||||
{ErrorCode::ERR_MESH_SPLIT_FAILED, "网格分割失败"},
|
||||
|
||||
{ErrorCode::ERR_FEA_MESH_QUALITY, "FEA网格质量不足"},
|
||||
{ErrorCode::ERR_FEA_MESH_GENERATION, "FEA网格生成失败"},
|
||||
{ErrorCode::ERR_MARCHING_CUBES_FAILED, "Marching Cubes失败"},
|
||||
{ErrorCode::ERR_POINT_CLOUD_EMPTY, "点云为空"},
|
||||
{ErrorCode::ERR_POINT_CLOUD_NOISE, "点云噪声过大"},
|
||||
|
||||
// 5xxx:brep B-Rep错误
|
||||
{ErrorCode::ERR_BREP_TOPOLOGY_INVALID, "B-Rep拓扑无效"},
|
||||
{ErrorCode::ERR_BREP_GEOMETRY_MISMATCH, "B-Rep几何不匹配"},
|
||||
{ErrorCode::ERR_BREP_SHELL_NOT_CLOSED, "壳不封闭"},
|
||||
{ErrorCode::ERR_BREP_ORIENTATION, "方向不一致"},
|
||||
{ErrorCode::ERR_BREP_SEWING_FAILED, "缝合失败"},
|
||||
{ErrorCode::ERR_BREP_HEALING_FAILED, "修复失败"},
|
||||
{ErrorCode::ERR_BREP_VALIDATION_FAILED, "验证失败"},
|
||||
|
||||
{ErrorCode::ERR_BOOLEAN_OPERATION_FAILED,"布尔操作失败"},
|
||||
{ErrorCode::ERR_BOOLEAN_INTERSECT_FAILED,"布尔求交失败"},
|
||||
{ErrorCode::ERR_BOOLEAN_SUBTRACT_FAILED,"布尔求差失败"},
|
||||
{ErrorCode::ERR_BOOLEAN_UNION_FAILED, "布尔求并失败"},
|
||||
{ErrorCode::ERR_BOOLEAN_DEGENERATE, "布尔操作产生退化"},
|
||||
{ErrorCode::ERR_BOOLEAN_FALLBACK_USED, "布尔操作回退到备用方法"},
|
||||
|
||||
{ErrorCode::ERR_FEATURE_CREATION_FAILED,"特征创建失败"},
|
||||
{ErrorCode::ERR_FEATURE_TREE_INVALID, "特征树无效"},
|
||||
{ErrorCode::ERR_FEATURE_UPDATE_FAILED, "特征更新失败"},
|
||||
{ErrorCode::ERR_FEATURE_RECOGNITION, "特征识别失败"},
|
||||
|
||||
{ErrorCode::ERR_ASSEMBLY_CONSTRAINT, "装配约束冲突"},
|
||||
{ErrorCode::ERR_ASSEMBLY_INTERFERENCE, "装配干涉"},
|
||||
{ErrorCode::ERR_ASSEMBLY_CYCLE, "装配循环引用"},
|
||||
{ErrorCode::ERR_ASSEMBLY_MATE_FAILED, "配合失败"},
|
||||
{ErrorCode::ERR_ASSEMBLY_LOD_INVALID, "LOD级别无效"},
|
||||
|
||||
{ErrorCode::ERR_SHEET_METAL_THICKNESS, "钣金厚度无效"},
|
||||
{ErrorCode::ERR_SHEET_METAL_BEND_RADIUS,"弯曲半径无效"},
|
||||
{ErrorCode::ERR_SHEET_METAL_UNFOLD, "展开失败"},
|
||||
|
||||
{ErrorCode::ERR_PMI_INVALID, "PMI无效"},
|
||||
{ErrorCode::ERR_GDT_TOLERANCE, "GD&T公差错误"},
|
||||
{ErrorCode::ERR_AUTO_DIM_FAILED, "自动标注失败"},
|
||||
{ErrorCode::ERR_DRAWING_STANDARD, "制图标准错误"},
|
||||
|
||||
{ErrorCode::ERR_FFD_INVALID, "FFD变形无效"},
|
||||
|
||||
// 6xxx:sdf SDF错误
|
||||
{ErrorCode::ERR_SDF_EVALUATE_FAILED, "SDF求值失败"},
|
||||
{ErrorCode::ERR_SDF_TREE_INVALID, "SDF树无效"},
|
||||
{ErrorCode::ERR_SDF_TO_MESH_FAILED, "SDF转网格失败"},
|
||||
{ErrorCode::ERR_SDF_OPTIMIZE_FAILED, "SDF优化失败"},
|
||||
{ErrorCode::ERR_SDF_TORCH_ERROR, "SDF Torch推理错误"},
|
||||
{ErrorCode::ERR_SDF_PRIMITIVE_INVALID, "SDF基元无效"},
|
||||
|
||||
// 7xxx:capi C API错误
|
||||
{ErrorCode::ERR_CAPI_NULL_HANDLE, "空句柄"},
|
||||
{ErrorCode::ERR_CAPI_INVALID_ARGUMENT, "无效参数"},
|
||||
{ErrorCode::ERR_CAPI_TYPE_MISMATCH, "类型不匹配"},
|
||||
{ErrorCode::ERR_CAPI_NOT_INITIALIZED, "引擎未初始化"},
|
||||
{ErrorCode::ERR_CAPI_ALREADY_INITIALIZED,"引擎已初始化"},
|
||||
{ErrorCode::ERR_CAPI_BUFFER_TOO_SMALL, "输出缓冲区太小"},
|
||||
{ErrorCode::ERR_CAPI_FEATURE_NOT_AVAIL, "功能不可用"},
|
||||
{ErrorCode::ERR_CAPI_UNKNOWN_ERROR, "未知C API错误"},
|
||||
};
|
||||
|
||||
auto it = kMessages.find(code);
|
||||
if (it != kMessages.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "未知错误 (code=" + std::to_string(static_cast<int32_t>(code)) + ")";
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// error_module — 模块名
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
|
||||
std::string error_module(ErrorCode code) {
|
||||
int32_t val = static_cast<int32_t>(code);
|
||||
|
||||
if (val == 0) return "core";
|
||||
if (val >= 1000 && val < 2000) return "foundation";
|
||||
if (val >= 2000 && val < 3000) return "core";
|
||||
if (val >= 3000 && val < 4000) return "curves";
|
||||
if (val >= 4000 && val < 5000) return "mesh";
|
||||
if (val >= 5000 && val < 6000) return "brep";
|
||||
if (val >= 6000 && val < 7000) return "sdf";
|
||||
if (val >= 7000 && val < 8000) return "capi";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
} // namespace vde::foundation
|
||||
Reference in New Issue
Block a user