基本定义
- 什么是模版测量
- 要做什么事
- 怎么做,每个步骤是什么
模版测量的两种可能的方式
- 完整点云,通过手动或者自动方式传递到测量软件
- 实时点云,通过自动化传递到测量软件
什么是模版测量
模版测量主要是通过定义一个模版,能够自动化处理模版中的所有步骤,包括:
- 导入参考
- 导入扫描
- 对齐(自动对齐)
- 创建特征(球、圆、圆柱、平面、直线)等
- 测量基础测量项(整体体积、特征体积、距离、角度)
- 测量GDT项(直线度、圆度、平行度、倾斜度、位置度、全跳动、平面度、圆柱度、垂直度、同心度、圆跳动)
- 测量整体偏差
- 测量点偏差
这些测量过程,没有人为的干预或者较少的人为干预
那么什么是没有人为干预
模版测量的主要框架
主要采用 命令模式 + 责任链模式 + *状态机模式 *的组合架构
┌────────────────────────────────────────────────────────┐
│ Template Engine │
│ ┌────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │ Template │ │ Workflow │ │ State │ │
│ │ Manager │→ │ Executor │→ │ Machine │ │
│ └────────────┘ └──────────────┘ └─────────────┘ │
└────────────────────────────────────────────────────────┘
↓
┌───────────────────────────────────────────────────────┐
│ Command Pipeline │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Command │→ │ Command │→ │ Command │→ ... │
│ │ Queue │ │ Factory │ │ Executor │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└───────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Concrete Commands │
│ ImportScan │ ImportCAD │ Align │ Feature │ Measure ... │
└─────────────────────────────────────────────────────────┘
- 模版管理模块
- workflow模块
- 命令模块,将所有的步骤命令话
命令引擎
┌─────────────────────────────────────────────────────────────┐
│ 应用层 (软件主体) │
│ C++ 代码直接调用 Command 对象 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第1层: Command Core (核心命令层) │
│ │
│ 职责: 命令的核心业务逻辑 │
│ 特点: 纯 C++,无序列化,性能最优 │
│ 接口: ICommand (C++ 接口) │
│ 实现: 所有具体命令类 │
│ │
│ 调用方式: 直接 new/delete 或智能指针 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第2层: Command Facade (门面/调度层) │
│ │
│ 职责: 统一命令调用入口,屏蔽创建细节 │
│ 特点: 仍然是 C++,但提供统一接口 │
│ 核心类: CommandInvoker, CommandFactory │
│ │
│ 提供两种调用模式: │
│ ├── 模式1: C++ 原生调用 (类型安全,零序列化开销) │
│ └── 模式2: 字符串调用 (支持序列化,用于跨语言) │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第3层: C API Bridge (C 接口桥接层) - 可选 │
│ │
│ 职责: 提供 C 风格接口,供其他语言 FFI 调用 │
│ 特点: extern "C",纯指针和基础类型 │
│ 接口: command_invoke(), command_get_result() │
│ │
│ 仅在需要跨语言调用时启用 │
└──────────────────────┬──────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第4层: Language Bindings (语言绑定层) - 可选 │
│ │
│ Python: pybind11 或 ctypes 封装 │
│ Java: JNI 封装 │
│ RPC: gRPC/Thrift 服务 │
│ REST: HTTP API 服务 │
└─────────────────────────────────────────────────────────────┘
命令核心层 Command Core:
设计理念:
- 纯粹业务逻辑,不关心调用方式
- 不包含序列化
- 不依赖网络、RPC外部通信
总结:命令的具体业务逻辑实现,输入参数以及上下文,输出执行结果只做三件事:
- 定义命令接口规范
- 提供命令基类模版
- 实现具体命令的业务逻辑
这里对命令说明一下:
什么是具体业务逻辑?
如:导入参考模型、创建特征、测量距离,这些都是具体业务,也就是说要对这些具体业务命令化,把这些基础的、原子的业务进行命令封装。
这样做为了什么?
主要是方便我们后期其他地方调用,如提供给第三方API调用,想象一下场景:用户想要用python实现创建某个圆柱特征,这时如果封装了创建圆柱特征这个命令,只需要规范用户输入参数,直接调用我们封装的平台层(专为python预语言封装),统一调用对应的命令即可;
再有场景,我们的AI调用功能,通过AI自然语言处理,获取用户想要创建某个特征,这个时候我们MCP服务封装创建特征需要的参数,调用创建特征命令,最终执行对应的C++代码。
即统一的命令(稳定不变层)、多样的调用方式。
具体接口设计:
├── ICommand: 命令接口
├── BaseCommand: 命令基类(模板方法)
├── 具体命令: ImportScanCommand, AlignCommand 等
└── 数据对象: 使用 C++ 原生类型(Vector3D, PointCloud 等)
参数传递策略、返回结构策略:
- 命令自定义参数结构体
- 命令参数结构体序列化
- 或者通用结构容器方式,支持序列化
struct ImportScanParams {
std::string filePath;
PointCloudFormat format;
};
ImportScanCommand::Execute(ImportScanParams params);
// 通用结构容器方式,使用key-value容器,说白了就是能够用json这种通用格式提供给其他地方调用
CommandParams params;
params.Set("filePath", "/path/to/file.stl");
command->Execute(params);
ICommand 接口定义
class ICommand {
public:
virtual ~ICommand() = default;
// ===== 核心执行接口 =====
// 执行命令的主入口
// 参数: params - 命令参数(强类型或通用容器)
// 返回: 执行结果
virtual CommandResult Execute(const CommandParams& params) = 0;
// ===== 验证接口 =====
// 在执行前验证参数和前置条件
// 返回: true-验证通过, false-验证失败
virtual bool Validate(const CommandParams& params) = 0;
// ===== 状态查询接口 =====
virtual CommandState GetState() const = 0;
virtual float GetProgress() const = 0;
// ===== 控制接口 =====
// 这些方法由外部调用(WorkflowEngine)调用,用于流程控制
virtual void Pause() = 0;
virtual void Resume() = 0;
virtual void Cancel() = 0;
virtual CommandMetadata GetMetadata() const = 0;
};
CommandParams 参数容器接口定义
// CommandParams.h
// 通用参数容器 - 支持类型安全访问
class CommandParams {
private:
std::map<std::string, std::any> data_;
public:
// ===== 类型安全的参数访问 =====
template<typename T>
T Get(const std::string& key) const {
auto it = data_.find(key);
if (it == data_.end()) {
throw ParamNotFoundException(key);
}
try {
return std::any_cast<T>(it->second);
} catch (const std::bad_any_cast&) {
throw ParamTypeMismatchException(key, typeid(T).name());
}
}
// 带默认值的获取
template<typename T>
T GetOr(const std::string& key, const T& defaultValue) const {
auto it = data_.find(key);
if (it == data_.end()) {
return defaultValue;
}
try {
return std::any_cast<T>(it->second);
} catch (const std::bad_any_cast&) {
return defaultValue;
}
}
// 尝试获取(不抛异常)
template<typename T>
bool TryGet(const std::string& key, T& out) const {
auto it = data_.find(key);
if (it == data_.end()) {
return false;
}
try {
out = std::any_cast<T>(it->second);
return true;
} catch (const std::bad_any_cast&) {
return false;
}
}
// ===== 参数设置 =====
template<typename T>
void Set(const std::string& key, const T& value) {
data_[key] = value;
}
// ===== 参数检查 =====
bool Has(const std::string& key) const {
return data_.find(key) != data_.end();
}
// 检查类型
template<typename T>
bool IsType(const std::string& key) const {
auto it = data_.find(key);
if (it == data_.end()) return false;
return it->second.type() == typeid(T);
}
// ===== 工具方法 =====
std::vector<std::string> GetKeys() const {
std::vector<std::string> keys;
for (const auto& [key, _] : data_) {
keys.push_back(key);
}
return keys;
}
size_t Size() const { return data_.size(); }
void Clear() { data_.clear(); }
// ===== 调试支持 =====
std::string ToString() const; // 返回可读的参数列表
};
CommandResult 结果对象
// CommandResult.h
// 命令执行结果
struct CommandResult {
// ===== 基本结果 =====
bool isSuccess; // 是否成功
std::string message; // 结果消息(成功或失败原因)
// ===== 结果数据 =====
std::any data; // 结果数据(类型由具体命令定义)
// ===== 执行信息 =====
double executionTime; // 执行耗时(秒)
std::vector<std::string> warnings; // 警告信息
// ===== 类型安全的数据访问 =====
template<typename T>
T GetData() const {
return std::any_cast<T>(data);
}
template<typename T>
bool TryGetData(T& out) const {
try {
out = std::any_cast<T>(data);
return true;
} catch (...) {
return false;
}
}
// ===== 便捷构造函数 =====
static CommandResult Success(const std::string& msg, const std::any& resultData = {}) {
return {true, msg, resultData, 0.0, {}};
}
static CommandResult Failure(const std::string& msg) {
return {false, msg, {}, 0.0, {}};
}
static CommandResult SuccessWithWarnings(
const std::string& msg,
const std::any& resultData,
const std::vector<std::string>& warnings
) {
return {true, msg, resultData, 0.0, warnings};
}
};
CommandMetadata 元数据
// CommandMetadata.h
// 命令元数据定义
// 参数定义
struct ParamDefinition {
std::string name; // 参数名
std::string type; // 类型描述("string", "double", "Vector3D" 等)
bool required; // 是否必需
std::string description; // 参数说明
std::any defaultValue; // 默认值(可选)
// 约束条件(可选)
std::optional<double> minValue;
std::optional<double> maxValue;
std::vector<std::string> enumValues; // 枚举值(如果是枚举类型)
};
// 命令元数据
struct CommandMetadata {
std::string name; // 命令名称
std::string description; // 命令描述
std::string category; // 分类(Import/Align/Feature/Measure)
std::vector<ParamDefinition> parameters; // 参数定义列表
std::string returnType; // 返回值类型描述
// 依赖信息
std::vector<std::string> requiredContextData; // 需要的上下文数据
// 例如: ["scanPointCloud", "referencePointCloud"]
// 副作用说明
std::vector<std::string> modifiesContextData; // 修改的上下文数据
// 例如: ["scanPointCloud"] (ImportScan 会设置这个)
};
BaseCommand 类结构
// BaseCommand.h
// 命令基类 - 实现通用逻辑
class BaseCommand : public ICommand {
protected:
// ===== 核心成员 =====
ExecutionContext* context_; // 执行上下文(不拥有所有权)
CommandMetadata metadata_; // 命令元数据
// ===== 状态成员 =====
CommandState state_; // 当前状态
std::atomic<float> progress_; // 执行进度 (0.0 - 1.0)
CommandResult result_; // 执行结果
// ===== 控制标志 =====
std::atomic<bool> isPaused_; // 暂停标志
std::atomic<bool> isCancelled_; // 取消标志
// ===== 时间统计 =====
std::chrono::steady_clock::time_point startTime_;
public:
// ===== 构造函数 =====
BaseCommand(ExecutionContext* context, CommandMetadata metadata)
: context_(context)
, metadata_(std::move(metadata))
, state_(CommandState::Idle)
, progress_(0.0f)
, isPaused_(false)
, isCancelled_(false)
{}
virtual ~BaseCommand() = default;
// ===== 实现 ICommand 接口 =====
// 模板方法 - 定义执行流程
CommandResult Execute(const CommandParams& params) override final {
try {
// 1. 记录开始时间
startTime_ = std::chrono::steady_clock::now();
// 2. 验证阶段
state_ = CommandState::Validating;
if (!Validate(params)) {
return CommandResult::Failure("参数验证失败");
}
// 3. 前置钩子
state_ = CommandState::Running;
OnBeforeExecute(params);
// 4. 执行核心逻辑(子类实现)
result_ = DoExecute(params);
// 5. 后置钩子
OnAfterExecute(result_);
// 6. 更新状态
state_ = result_.isSuccess ? CommandState::Completed : CommandState::Failed;
// 7. 填充执行时间
auto endTime = std::chrono::steady_clock::now();
result_.executionTime = std::chrono::duration<double>(endTime - startTime_).count();
return result_;
} catch (const CancelledException&) {
state_ = CommandState::Cancelled;
return CommandResult::Failure("命令已取消");
} catch (const std::exception& e) {
state_ = CommandState::Failed;
return CommandResult::Failure(
std::string("执行异常: ") + e.what()
);
}
}
// 验证方法 - 基于元数据自动验证 + 子类扩展
bool Validate(const CommandParams& params) override {
// 1. 自动验证必需参数
for (const auto& paramDef : metadata_.parameters) {
if (paramDef.required && !params.Has(paramDef.name)) {
// 记录错误
return false;
}
}
// 2. 验证上下文依赖
for (const auto& requiredData : metadata_.requiredContextData) {
if (!context_->Has(requiredData)) {
// 记录错误: 缺少必需的上下文数据
return false;
}
}
// 3. 调用子类的自定义验证
return DoValidate(params);
}
// ===== 状态查询 =====
CommandState GetState() const override { return state_; }
float GetProgress() const override { return progress_.load(); }
CommandMetadata GetMetadata() const override { return metadata_; }
// ===== 控制方法 =====
void Pause() override { isPaused_ = true; }
void Resume() override { isPaused_ = false; }
void Cancel() override {
isCancelled_ = true;
Resume(); // 唤醒可能在等待的线程
}
protected:
// ===== 子类必须实现的方法 =====
// 核心执行逻辑
virtual CommandResult DoExecute(const CommandParams& params) = 0;
// ===== 子类可选重写的钩子方法 =====
// 自定义验证逻辑(在自动验证之后)
virtual bool DoValidate(const CommandParams& params) {
return true;
}
// 执行前钩子
virtual void OnBeforeExecute(const CommandParams& params) {}
// 执行后钩子
virtual void OnAfterExecute(const CommandResult& result) {}
// ===== 工具方法(供子类使用)=====
// 更新进度
void SetProgress(float progress) {
progress_.store(std::clamp(progress, 0.0f, 1.0f));
// 可以在这里触发进度回调
if (context_->progressCallback) {
context_->progressCallback(metadata_.name, progress_.load());
}
}
// 检查暂停点
void CheckPausePoint() {
// 检查取消
if (isCancelled_.load()) {
throw CancelledException();
}
// 检查暂停
while (isPaused_.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// 暂停期间也要检查取消
if (isCancelled_.load()) {
throw CancelledException();
}
}
}
// 添加警告
void AddWarning(const std::string& warning) {
result_.warnings.push_back(warning);
}
// 访问上下文的便捷方法
template<typename T>
T* GetContextData(const std::string& key) {
return context_->Get<T>(key);
}
template<typename T>
void SetContextData(const std::string& key, T* data) {
context_->Set(key, data);
}
};
创建圆柱特征示例
// CreateCylinderCommand.h
// 创建圆柱特征命令
class CreateCylinderCommand : public BaseCommand {
public:
CreateCylinderCommand(ExecutionContext* context)
: BaseCommand(context, CreateMetadata())
{}
private:
static CommandMetadata CreateMetadata() {
CommandMetadata meta;
meta.name = "CreateCylinder";
meta.description = "从点云中拟合圆柱特征";
meta.category = "Feature";
meta.parameters = {
ParamDefinition{
"featureId",
"string",
true,
"特征ID(唯一标识)"
},
ParamDefinition{
"nominalPosition",
"Vector3D",
false,
"名义位置",
Vector3D(0, 0, 0)
},
ParamDefinition{
"nominalAxis",
"Vector3D",
false,
"名义轴向",
Vector3D(0, 0, 1)
},
ParamDefinition{
"nominalRadius",
"double",
false,
"名义半径(mm)",
0.0
},
ParamDefinition{
"nominalHeight",
"double",
false,
"名义高度(mm)",
0.0
},
ParamDefinition{
"reuseIfExists",
"bool",
false,
"如果特征已存在则复用",
true
}
};
meta.returnType = "CylinderFeature*";
// 依赖的上下文数据
meta.requiredContextData = {"scanPointCloud", "featureRegistry"};
// 修改的上下文数据
meta.modifiesContextData = {"featureRegistry"};
return meta;
}
bool DoValidate(const CommandParams& params) override {
std::string featureId = params.Get<std::string>("featureId");
// 检查 featureId 是否为空
if (featureId.empty()) {
return false;
}
// 检查名义参数的合理性
double nominalRadius = params.GetOr<double>("nominalRadius", 0.0);
if (nominalRadius < 0) {
return false;
}
double nominalHeight = params.GetOr<double>("nominalHeight", 0.0);
if (nominalHeight < 0) {
return false;
}
return true;
}
CommandResult DoExecute(const CommandParams& params) override {
// 1. 获取参数
std::string featureId = params.Get<std::string>("featureId");
Vector3D nominalPos = params.GetOr("nominalPosition", Vector3D(0, 0, 0));
Vector3D nominalAxis = params.GetOr("nominalAxis", Vector3D(0, 0, 1));
double nominalRadius = params.GetOr("nominalRadius", 0.0);
double nominalHeight = params.GetOr("nominalHeight", 0.0);
bool reuseIfExists = params.GetOr("reuseIfExists", true);
SetProgress(0.0f);
// 2. 获取特征注册表
FeatureRegistry* registry = GetContextData<FeatureRegistry>("featureRegistry");
// 3. 检查是否已存在
if (reuseIfExists) {
Feature* existing = registry->FindFeatureById(featureId);
if (existing) {
// 复用已存在的特征
SetProgress(1.0f);
return CommandResult::SuccessWithWarnings(
"复用已存在的特征: " + featureId,
existing,
{"特征已存在,未重新创建"}
);
}
}
CheckPausePoint();
SetProgress(0.2f);
// 4. 从点云中提取候选点
PointCloud* scanCloud = GetContextData<PointCloud>("scanPointCloud");
PointCloud* candidatePoints = ExtractCylinderCandidates(
scanCloud,
nominalPos,
nominalAxis,
nominalRadius,
nominalHeight
);
if (!candidatePoints || candidatePoints->GetPointCount() < 10) {
delete candidatePoints;
return CommandResult::Failure("未找到足够的候选点");
}
CheckPausePoint();
SetProgress(0.5f);
// 5. 拟合圆柱
CylinderFitter fitter;
CylinderFeature* cylinder = fitter.Fit(
candidatePoints,
nominalPos,
nominalAxis,
nominalRadius
);
delete candidatePoints;
if (!cylinder) {
return CommandResult::Failure("圆柱拟合失败");
}
CheckPausePoint();
SetProgress(0.8f);
// 6. 设置特征属性
cylinder->featureId = featureId;
cylinder->nominalRadius = nominalRadius;
cylinder->nominalHeight = nominalHeight;
// 7. 注册特征
bool registered = registry->RegisterFeature(cylinder);
if (!registered) {
delete cylinder;
return CommandResult::Failure("特征注册失败(ID冲突)");
}
SetProgress(1.0f);
// 8. 返回结果
return CommandResult::Success(
"成功创建圆柱特征: " + featureId,
cylinder
);
}
// 辅助方法
PointCloud* ExtractCylinderCandidates(
PointCloud* cloud,
const Vector3D& nominalPos,
const Vector3D& nominalAxis,
double nominalRadius,
double nominalHeight
) {
// 实现点云提取逻辑
// 根据名义参数在点云中搜索候选点
// ...
return nullptr; // 实际实现
}
};
作者:admin 创建时间:2025-12-09 11:47
最后编辑:admin 更新时间:2025-12-09 17:25
最后编辑:admin 更新时间:2025-12-09 17:25