基本定义

  • 什么是模版测量
  • 要做什么事
  • 怎么做,每个步骤是什么

模版测量的两种可能的方式

  • 完整点云,通过手动或者自动方式传递到测量软件
  • 实时点云,通过自动化传递到测量软件
什么是模版测量

模版测量主要是通过定义一个模版,能够自动化处理模版中的所有步骤,包括:

  • 导入参考
  • 导入扫描
  • 对齐(自动对齐)
  • 创建特征(球、圆、圆柱、平面、直线)等
  • 测量基础测量项(整体体积、特征体积、距离、角度)
  • 测量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外部通信

总结:命令的具体业务逻辑实现,输入参数以及上下文,输出执行结果只做三件事:

  1. 定义命令接口规范
  2. 提供命令基类模版
  3. 实现具体命令的业务逻辑

这里对命令说明一下:
什么是具体业务逻辑?
如:导入参考模型、创建特征、测量距离,这些都是具体业务,也就是说要对这些具体业务命令化,把这些基础的、原子的业务进行命令封装。
这样做为了什么?
主要是方便我们后期其他地方调用,如提供给第三方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