mc::app 自发现服务(Discovery Service)

Header: #include <mc/app.h>   Namespace: mc::app / mc::app::discovery

概述

自发现(Discovery)子系统是 openUBMC 中实现跨服务对象自动发现与编排的核心机制。硬件拓扑和传感器资源由专门的发现服务(如 bmc.kepler.hwdiscovery)探测,业务服务通过自发现机制被动接收并实例化对象,无需硬编码对象路径。

数据流:hwdiscovery → D-Bus 信号 → object_group_source(数据源)→ dispatcher(编排器)→ 业务 service

核心组件:

组件 职责
discovery_service 业务 service 基类,持有 dispatcher 和 source,管理两者生命周期
object_group_source 数据源抽象接口,定义 set_group_callback / set_removed_callback / start / stop
service_object_group_source 基于 D-Bus 信号的具体实现,订阅 ObjectGroup 路径并拉取对象
dispatcher 编排器:owner 过滤 → per-source lcid 去重 → 解码属性 → 创建对象 → 依赖管理 → 注册
object_group_proxy D-Bus bmc.kepler.ObjectGroup 接口代理,GetBinaryObjects 异步拉取
life_cycle_tracker 基于 LifeCycleId 的全局版本跟踪:should_accept / advance(仅维护诊断状态,去重已改用 per-source lcid)
position_manager begin_round → note → end_round 三轮协议,diff 计算增删
expression_evaluator ${name} 模板替换,dict/array 递归求值
serialize_decoder blob → dict 解码器抽象,工厂提供 JSON/mcdbus/raw/null
owner_filter sender 白名单/黑名单过滤
object_factory_hook 自发现对象创建挂钩,支持自定义父对象查找

主要特性

  • 被动发现,解耦拓扑:业务服务无需硬编码对象名或路径,dispatcher 自动实例化
  • LCID 版本管理:每轮全量发现递增 LifeCycleId,自动淘汰上一轮消失的对象
  • Position Diff 机制:begin_round → note → end_round 三轮协议,自动计算增删
  • 延迟注册 + 依赖等待:#/ 远程引用可配置 required 语义,依赖就绪后再注册
  • Owner 过滤:白名单/黑名单机制,按 sender 过滤派发来源
  • 可扩展解码器 + 表达式替换:JSON/mcdbus/raw 解码器 + ${variable} 模板替换
  • 结构化失败记录:ring buffer + 回调双通道,记录单对象派发失败现场

基本用法

示例 1:继承 discovery_service 接入自发现

#include <mc/app.h>
#include <iostream>

class SensorDiscoveryService : public mc::app::discovery_service {
public:
    explicit SensorDiscoveryService(mc::string name)
        : mc::app::discovery_service(std::move(name)) {}

protected:
    bool on_start() override {
        if (!mc::app::discovery_service::on_start()) return false;
        std::cout << "Started, known: "
                  << dispatcher().known_object_count() << std::endl;
        return true;
    }
    bool on_stop() override {
        return mc::app::discovery_service::on_stop();
    }
    std::vector<mc::string> allowed_owners() const override {
        return {"bmc.kepler.hwdiscovery"};
    }
    std::shared_ptr<mc::app::discovery::serialize_decoder>
    object_info_decoder() const override {
        return mc::app::discovery::make_mcdbus_serialize_decoder();
    }
};

MC_APP_REGISTER_GLOBAL_SERVICE(SensorDiscoveryService, "SensorDiscoveryService")

示例 2:手动配置 service_object_group_source

不继承 discovery_service,直接在自定义 service 中创建数据源:

#include <mc/app.h>
#include <mc/app/discovery/service_object_group_source.h>
#include <iostream>

class CustomService : public mc::app::service {
public:
    explicit CustomService(mc::string name) : mc::app::service(std::move(name)) {}

protected:
    bool on_start() override {
        mc::app::discovery::service_object_group_source::config cfg;
        cfg.host_service          = this;
        cfg.target_service_name   = "bmc.kepler.hwdiscovery";
        cfg.target_path_namespace = "/bmc/kepler/ObjectGroup";

        m_source = mc::make_unique<
            mc::app::discovery::service_object_group_source>(cfg);
        m_source->set_group_callback([](const auto& group) {
            std::cout << "Received " << group.objects.size()
                      << " objects, lcid=" << group.life_cycle_id << std::endl;
        });
        m_source->set_removed_callback([](mc::string_view key) {
            std::cout << "Removed: " << key << std::endl;
        });
        return m_source->start();
    }
    bool on_stop() override {
        if (m_source) m_source->stop();
        return true;
    }

private:
    mc::unique_ptr<mc::app::discovery::service_object_group_source> m_source;
};

示例 3:使用 expression_evaluator 进行模板替换

#include <mc/app.h>
#include <mc/app/discovery/expression_evaluator.h>
#include <iostream>

int main() {
    struct my_evaluator : mc::app::discovery::expression_evaluator {
        mc::string substitute_template(mc::string_view text,
                                       const mc::dict& vars) const override {
            mc::string result(text);
            for (const auto& [key, val] : vars) {
                mc::string ph = mc::string("${") + key + "}";
                mc::string rp = val.to_string();
                auto pos = result.find(ph);
                while (pos != mc::string::npos) {
                    result.replace(pos, ph.size(), rp);
                    pos = result.find(ph, pos + rp.size());
                }
            }
            return result;
        }
    };

    my_evaluator eval;
    mc::dict vars;
    vars["slot"]  = mc::string("Slot1");
    vars["index"] = int32_t{0};

    auto result = eval.substitute_template("/bmc/${slot}/Sensor_${index}", vars);
    std::cout << result << std::endl; // "/bmc/Slot1/Sensor_0"

    mc::dict props;
    props["path"] = mc::string("${slot}/temp");
    mc::variant evaluated = eval.evaluate_value(mc::variant(props), vars);
    std::cout << evaluated.as<mc::dict>()["path"].as<mc::string>() << std::endl;
    return 0;
}

API 详细说明

discovery_service 类

// discovery_service.h:39
class MC_API discovery_service : public mc::app::service {
public:
    enum class source_kind { remote, inline_group };

    struct options {
        source_kind   source{source_kind::remote};
        ref_semantics ref_policy{ref_semantics::optional};
        // remote 源定制(空串 = 维持默认;owner 默认取本 service 的 short_name)
        mc::string    target_service_name{};
        mc::string    target_path_namespace{};
        mc::string    get_binary_objects_owner{};
    };

    explicit discovery_service(mc::string name);
    discovery_service(mc::string name, options opts);
    ~discovery_service() override;
    discovery::dispatcher& dispatcher();
    discovery::object_group_source& source();
    std::shared_ptr<discovery::inline_object_group_source> inline_source();
    bool on_start() override;
    bool on_stop() override;
};

options 字段(数据驱动配置面,无需子类化):

字段 默认值 描述
source source_kind::remote 数据源选择:remote 订阅远端自发现服务;inline_group 进程内可编程源(载荷 JSON 编码)
ref_policy ref_semantics::optional #/ 引用语义:optional 不阻塞注册、required 依赖未到位前 hold 注册
target_service_name ""(bmc.kepler.hwdiscovery) remote 源目标自发现服务名
target_path_namespace ""(/bmc/kepler/ObjectGroup) remote 源 ObjectGroup 路径命名空间
get_binary_objects_owner ""(本 service short_name()) GetBinaryObjects 的 owner 过滤(组件短名)
方法 描述 异常
explicit discovery_service(mc::string name) 构造,用 once_flag 延迟构建 dispatcher std::bad_alloc
discovery_service(mc::string name, options opts) 按 options 构造(数据源/引用语义/remote 源定制) std::bad_alloc
~discovery_service() 析构,释放 dispatcher、source、persist_bridge 不抛出
discovery::dispatcher& dispatcher() 首次调用用 std::call_once 构建 dispatcher std::bad_alloc
discovery::object_group_source& source() 获取数据源引用 std::bad_alloc
bool on_start() override 构建 dispatcher + source,调用 source().start() std::bad_alloc;启动失败返回 false
bool on_stop() override dispatcher 停止接收更新 → source 排空在途回调 → dispatcher 释放已管理对象 source 异常时返回 false

受保护方法(子类覆盖点):

方法 默认值 描述 异常
allowed_owners() {}(全部接受) 返回 sender 白名单 std::bad_alloc
default_binary_decoder() make_mcdbus_serialize_decoder() 默认二进制解码器 std::bad_alloc
object_info_decoder() / extra_info_decoder() 委托 default_binary_decoder() 对象属性/扩展信息解码器 std::bad_alloc
expr_evaluator() make_null_evaluator() 表达式求值器 std::bad_alloc
ref_policy() ref_semantics::optional #/ 引用策略 不抛出
on_create_dispatched_object(args) 内置 object_factory_hook::create 对象「创建期」扩展点;返回 future<shared_ptr<abstract_object>>,解析为空表示失败/跳过 std::bad_alloc
on_dispatch_group_complete(position) 空实现 组完成屏障扩展点;该组每个 position 调一次(dispatcher 线程) 不抛出
on_remove_dispatched_object(object_name) 返回 false 对象移除扩展点;返回 true 表示移除已完整处理(dispatcher 不再默认反注册) 不抛出
make_object_group_source() 创建 service_object_group_source 数据源工厂 std::bad_alloc
make_service_source_config() host_service=this,目标 bmc.kepler.hwdiscovery;options 的 remote 三字段非空时覆盖对应项 数据源默认配置 std::bad_alloc
attach_dispatcher_to_signals() 连接 source 回调到 dispatcher 信号连接 未初始化时行为未定义

object_group_source / service_object_group_source 类

// object_group_source.h:28 — 抽象数据源
class MC_API object_group_source {
public:
    using group_callback   = std::function<void(const get_binary_objects_response&)>;
    using removed_callback = std::function<void(mc::string_view)>;
    virtual ~object_group_source() = default;
    virtual void set_group_callback(group_callback cb)   = 0;
    virtual void set_removed_callback(removed_callback cb) = 0;
    virtual bool start() = 0;
    virtual void stop()  = 0;
};

// service_object_group_source.h:31 — D-Bus 信号数据源
class MC_API service_object_group_source : public object_group_source {
public:
    struct config {
        mc::engine::service* host_service{nullptr};
        mc::string           target_service_name{"bmc.kepler.hwdiscovery"};
        mc::string           target_path_namespace{"/bmc/kepler/ObjectGroup"};
        mc::string           get_binary_objects_owner{};
        std::uint32_t        bootstrap_retry_count{50};       // 启动期 GetManagedObjects 重试次数
        std::uint32_t        bootstrap_retry_interval_ms{1000}; // 重试间隔(毫秒)
    };
    explicit service_object_group_source(config cfg);
    ~service_object_group_source() override;
    bool is_started() const noexcept;
    std::size_t match_count() const noexcept;
};

// inline_object_group_source.h:36 — 进程内可编程数据源
class MC_API inline_object_group_source : public object_group_source {
public:
    bool start() override;
    void stop() override;
    bool is_started() const noexcept;
    bool push(const get_binary_objects_response& group);
    bool push_removal(mc::string_view object_or_group_key);
};
类 方法 描述 异常
object_group_source set_group_callback / set_removed_callback 注册对象组/移除回调 std::bad_alloc
start() / stop() 启停数据源;start() 以返回值报告普通启动失败,stop() 无返回值;具体实现的 stop() 返回后不再有此前进入的回调执行 见具体实现
service_object_group_source 构造 config → start() 订阅 D-Bus 信号 + 拉取现有对象;目标服务未就绪时按 bootstrap_retry_* 重试 std::bad_alloc;host_service==nullptr 时 start() 返回 false
stop() 取消信号订阅并等待在途回调;并发调用共享同一完成屏障 不抛出
is_started() / match_count() 启动状态/信号匹配数(诊断) 不抛出
inline_object_group_source push() / push_removal() 在调用线程同步执行回调;不同线程的投递回调串行执行 回调异常原样传播
stop() 拒绝新投递并等待在途回调;允许在当前回调内调用,停止状态在该回调退出时完成 不抛出

dispatcher 类

// dispatcher.h:68
class MC_API dispatcher {
public:
    struct config {
        mc::engine::service*                  service{nullptr};
        std::shared_ptr<serialize_decoder>    object_info_decoder;
        std::shared_ptr<serialize_decoder>    extra_info_decoder;
        std::shared_ptr<expression_evaluator> expr_evaluator;
        std::vector<mc::string>               allowed_owners;
        ref_semantics                         ref_policy{ref_semantics::optional};
        object_failure_callback               on_object_failure{};
        // 扩展点(不设置即保持默认行为):
        std::function<mc::future<mc::shared_ptr<mc::engine::abstract_object>>(const dispatch_creation_args&)>
            object_creator{};                                         // 创建期委托(代替内置工厂)
        std::function<void(mc::string_view position)> group_complete_handler{};  // 组完成屏障
        std::function<bool(mc::string_view object_name)> object_removed_handler{}; // 移除委托
        std::size_t                           failure_history_capacity{64};
        std::size_t                           max_concurrent_creates{50};  // 外部创建并发上限(0=不限流)
        mc::runtime::any_executor              coordination_executor{};
        mc::runtime::any_executor              creation_executor{};
    };
    explicit dispatcher(config cfg);
    ~dispatcher();
    dispatcher(const dispatcher&)            = delete;
    dispatcher& operator=(const dispatcher&) = delete;
};
方法 描述 异常
explicit dispatcher(config cfg) 构造;service 为目标 service std::bad_alloc
~dispatcher() 自动执行幂等 finalize,撤销订阅并释放所有管理对象 不抛出;内部异常记录日志
config::object_creator 创建期委托:设置后代替内置 object_factory_hook::create,返回的对象须实现 mc::engine::object_impl;同步调用期间自动激活 creation_context,若把 init 延后到其他 executor,须在实际 init 线程再次 activate(),并在解析 future 前调用 try_complete(obj) 仲裁抢占;dispatch_aware 仅用于可选的主动 wait_for/defer_registration 能力 委托内异常计入失败记录
config::group_complete_handler 组完成屏障:一个 ObjectGroup 全部对象处理完后,对该组每个 position 各调用一次(dispatcher 线程) 不抛出(实现责任)
config::object_removed_handler 移除委托:返回 true 表示移除已完整处理,dispatcher 不再做默认 unready + unregister 不抛出(实现责任)
config::max_concurrent_creates 外部 object_creator 路径的同时在途创建数上限(默认 50,0=不限流);超限的创建在协调执行器排队,待已完成创建归还槽位后再发起,防止大组/高频事件下一股脑 fork。仅约束 object_creator,内置同步工厂不计入 不抛出
config::coordination_executor 串行化 dispatcher 内部状态;未设置时使用 immediate executor,仅适用于调用方已保证单线程/外部串行的场景,不提供跨线程互斥 不抛出
config::creation_executor 执行对象 creator;未设置时复用 coordination executor 不抛出
void on_object_group(group) 接收对象组:owner 过滤 → per-source lcid 去重(同 source 同/更旧 lcid 整组丢弃)→ 解码 → 创建 → 注册 std::bad_alloc;单对象失败走回调
void on_object_disappear(key) 注销并释放对象;与组处理同链串行执行(见下文时序语义) key 不存在时静默忽略
void notify_ref_satisfied(ref) / notify_ref_unsatisfied(ref) 依赖就绪/不再满足通知 不抛出
void prepare_shutdown() / void finalize_shutdown() 两阶段关闭:停止接收 → 释放对象;均幂等,析构会兜底调用 finalize 不抛出
known_object_count() / pending_dependency_count() 已管理/等待依赖的对象数量 std::bad_alloc
recent_failures() / clear_recent_failures() 获取/清空失败记录 std::bad_alloc / 不抛出
get_owner_filter() / get_life_cycle_tracker() / get_position_manager() 获取可变子组件引用;仅在无在途任务或 coordination executor 上下文中访问 不抛出
get_dependency_tracker() / get_object_factory_hook() 获取依赖跟踪器/工厂挂钩引用;仅在无在途任务或 coordination executor 上下文中访问 不抛出

时序语义

维度 语义
前段去重 按 source 维护已处理的 lcid(source_key → life_cycle_id)。同 source 收到 lcid <= 已处理 的组(重复 InterfacesAdded 的相同 lcid,或乱序过期组)整组丢弃,避免重复解码/派发整组(大组场景的内存 churn)。source 被移除(InterfacesRemoved/对象清空)时清除记录,重新出现可正常重载。对齐旧 libmc do_add_objects 的 per-path lcid 跳过;按 source 维护以规避全局单调 life_cycle_tracker 的跨 source 误丢
不同 ObjectGroup 之间 per-source 串行:每个 source(group 节点路径)一条独立串行链,不同 source 的链并发推进、互不阻塞。某 source 的用户创建阻塞只卡该源自身,不会 head-of-line 堵死其它源、也不会让其它源的整组载荷在单链上堆积。对齐旧 libmc 的 per-path 事件队列;跨源父子顺序由「同组父子 / 父 position 等待」保证,不依赖全局单链
同一 ObjectGroup 内对象 并发创建:逐对象经 object_creator(或内置工厂)并行发起,全部就绪后统一注册收尾;对象间完成顺序无保证。外部创建路径受 max_concurrent_creates 限流(默认 50)
属性校验失败拒收 对象 init 后、注册前(register_or_defer 网关):任一属性初始化失败(类型不匹配/越界/枚举不合法等,per-field 隔离记录在 failed_fields())→ 整对象不上树:首次投递逐字段 elog + abandon_creation() + record_object_failure(经 recent_failures()/失败回调可观测),重复投递静默。对齐 Lua add_object 语义(pcall 捕获 validator error() → register_mdb_objects 不执行)。已在外部注册的对象(is_registered() 分支)由创建方自担其责,不撤销其注册,但属性校验失败同样补结构化失败记录,保证可观测性无盲区。两条拒收路径均对重复投递幂等去重——bootstrap GetManagedObjects 与 InterfacesAdded 两条路径 source_key 相同(都设 source_group_path),双路径重复投递被前段 lcid/同源去重拦截,去重标记实际主要对 inline source(source_group_path 空、source_key=sender)的双路径重复投递生效:is_registered() 分支靠 managed_object::failure_recorded 标记、普通拒收路径靠 m_rejected_failures_reported(key→source_key 映射,成功纳管、从 m_managed 移除或对象消失时清除)保证同一次失败只记录一次,不重复进 recent_failures()/失败回调;对象消失后重新出现携带新失败会重新记录。绑定/表达式解析失败(如 $func: 函数表中找不到函数)只打 elog,不触发 failed_fields()/拒收;格式错误的 $func:{...} 抛异常并被 per-field 记录
同组父子 position 子对象等待父 position 完成再创建;但父 position 若与子同属本组,则不等待(父的完成屏障要等本组收尾才触发,等待会自死锁),扁平模型下一组只一层 position 本不应出现该情形
add 与 disappear 同 source 同链串行:on_object_disappear 链入该对象所属 source 的串行链,不会插进同源某组「异步创建中 → 收尾」之间(对齐旧框架同源 add/remove 互斥语义)。group 节点整体移除时按其 source_key 路由
组完成屏障 一组全部对象注册完成后,对该组出现过的每个 position 各触发一次 group_complete_handler
同源批次抢占 每个 source 维护批次号(generation)。同源新组到达、同源消失或 prepare_shutdown 时批次号递增:在途创建的 future 收到 cancel 通知(业务可感知则优雅丢弃半创建对象),被抢占组立即以空成员收敛、整组作废(不注册、不进快照、不触发组完成屏障),串行链立即推进到新组
过期交付清理 被抢占批次的创建结果晚到时,dispatcher 识别为过期残留并级联清理(已外部注册的对象走 object_removed_handler / 默认反注册),不会泄漏进托管表

扩展点契约

三个扩展点都是通用的外部对象系统接入机制,不绑定任何特定实现:

扩展点 调用时机 契约
object_creator 每个对象创建期 入参 dispatch_creation_args 已完成标准解析并携带可复制的 creation_context;回调同步栈自动激活上下文,跨 executor 延后 init 时须在目标线程用 auto scope = args.creation_context.activate() 恢复;解析 future 前调用 try_complete(obj),若返回 false 则对象已由上下文执行 abandon_creation(),应返回空结果
group_complete_handler 组完成屏障 每 position 一次,此时同 position 对象已全部创建/注册
object_removed_handler 对象移除期 返回 true 表示移除已完整处理(含反注册),dispatcher 不再做默认 unready + unregister

异常处理契约

discovery 调度全程不向调用方传播异常,按三层统一处理:

层次 适用场景 处理方式
扩展点/业务回调边界 object_creator、group_complete_handler、object_removed_handler、on_object_failure、对象 init/on_group_complete、register_or_defer 捕获 std::exception → elog;对象级失败同时 record_object_failure(可经 recent_failures() 与失败回调观测)。单对象/单回调异常不影响同组其余对象
串行链兜底 组处理链、disappear 链 catch_error + elog,异常组被丢弃但链保持就绪,后续组继续串行处理
预期失败的试探解析/转换 ref 表达式多形式试探解析、variant 类型转换 优先使用非抛接口(variant::try_as<T>() / as<T>(default));确需异常试探时收窄为 catch (const std::exception&) 并 dlog 留痕。禁止 catch (...) 空体静默

life_cycle_tracker / position_manager 类

// life_cycle_tracker.h:31 — LCID 版本跟踪
class MC_API life_cycle_tracker {
public:
    life_cycle_tracker() = default;
    bool should_accept(std::uint32_t lcid) const;  // lcid >= current → true
    bool advance(std::uint32_t lcid);               // 升级(仅 lcid > current),返回 true=淘汰旧对象
    std::uint32_t current() const;
    void reset();                                   // 重置(测试/故障恢复用)
    bool initialized() const;
};

// position_manager.h:35 — 对象集合增删 diff
class MC_API position_manager {
public:
    using object_key = mc::string; // "ClassName@Position"
    void begin_round();                           // 清空 next
    void note(mc::string_view object_key_text);   // 记录到 next
    std::vector<object_key> end_round();          // 返回 prev-next(消失对象),prev←next
    std::size_t known_size() const;               // prev 集合大小
    void clear();                                 // 完整重置
};
类 方法 描述 异常
life_cycle_tracker should_accept(lcid) lcid >= current;未初始化时首次任意接受。注意:自 per-source lcid 去重落地后,dispatcher 不再用本方法做组级丢弃(避免全局单调跨 source 误丢),去重见「时序语义/前段去重」 不抛出
advance(lcid) 升级全局 LCID(仅 lcid > current),返回 true;现仅维护诊断状态 不抛出
current() / reset() / initialized() 查询/重置/初始化状态 不抛出
position_manager begin_round() / note(key) / end_round() 三轮协议:清空 → 累积 → diff std::bad_alloc
known_size() / clear() 查询/重置 不抛出

expression_evaluator / serialize_decoder / object_group_proxy 类

类(头文件) 签名 描述
expression_evaluator substitute_template(text, vars) → mc::string ${name} → 字符串化值;evaluate_value 递归 dict/array
(expression_evaluator.h) make_null_evaluator() → shared_ptr 工厂:不替换的 evaluator
serialize_decoder decode(blob, out) → bool;name() → string_view(头文件: serialize_decoder.h) blob → dict;工厂:make_json/mcdbus/null/raw
object_group_proxy GetBinaryObjects(filters, owner) → future<response> D-Bus bmc.kepler.ObjectGroup 代理
(object_group_proxy.h) MC_INTERFACE_PROXY("bmc.kepler.ObjectGroup") 通过 connect(connection&) 绑定
方法 描述 异常
substitute_template(text, vars) ${name} 占位符替换为 vars[name] 的字符串化值 std::bad_alloc
evaluate_value(value, vars) 递归处理:string → 替换;dict/array 递归;其他原样 std::bad_alloc
decode(payload, out) blob → dict 格式错误返回 false;std::bad_alloc
name() 解码器名(诊断用) 不抛出
GetBinaryObjects(filters, owner) 异步拉取,返回 future<get_binary_objects_response> std::bad_alloc;D-Bus 失败走 .on_error()
make_null_evaluator() 工厂:不替换的 evaluator std::bad_alloc
make_json/mcdbus/null/raw_*_decoder() 工厂:JSON/mcdbus::serialize/null/passthrough 解码器 std::bad_alloc

owner_filter / object_factory_hook 类

类(头文件) 签名 描述
owner_filter add_allowed_owner(owner) / add_blocked_owner(owner) 白名单/黑名单;should_accept:黑名单→reject,白名单非空未命中→reject
(owner_filter.h) clear() / allowed_size() / blocked_size() 重置/查询
object_factory_hook set_parent_finder(finder) 设置父对象查找器
(object_factory_hook.h) create(dispatch_creation_args) → shared_ptr<abstract_object> 按 class_name 创建对象
方法 描述 异常
add_allowed_owner / add_blocked_owner 白名单/黑名单 std::bad_alloc
should_accept(owner) 判定:黑名单→reject;白名单非空且未命中→reject;其余→accept 不抛出
clear() / allowed_size() / blocked_size() 重置/查询 不抛出
set_parent_finder(finder) 设置父对象查找器 std::bad_alloc
create(args) 通过 mcengine 统一工厂按 args.class_name 创建 object_impl 并填充属性;普通 runtime/static engine 对象均可使用 std::bad_alloc;类名未识别或结果不是 object_impl 时返回 nullptr

公共自由函数(内置工厂与外部 object_creator 共用,保证两条创建路径上下文一致):

函数 描述 异常
make_runtime_extras(args) → mc::dict 构造内建 runtime extras(ClassName/ObjectName/Position/sender/life_cycle_id 含小写别名)并合并业务 extras std::bad_alloc
apply_dispatch_context(obj, args) → void 把派发上下文回填到已创建对象(object_name/position/内建 extras/runtime_object 内建属性);dispatcher 在 object_creator 返回后自动调用,外部系统自建对象时也可显式复用 不抛出(内部属性写入失败仅记日志)

dispatch_creation_context 可安全复制到异步任务;activate() 返回 dispatch_creation_scope,该作用域把当前线程的 expression 自动依赖写入本次创建结果,析构时恢复外层上下文。 作用域支持嵌套,不应跨线程移动。

类型 方法 参数 返回值 异常 线程安全 说明
dispatch_creation_context 默认/复制/移动构造与赋值 同类型上下文 上下文或引用 默认构造可能抛 std::bad_alloc;其余不抛出 不同副本可跨线程传递和顺序激活 副本共享同一次对象创建的依赖收集状态
dispatch_creation_context activate() 无 dispatch_creation_scope 不抛出 仅影响调用线程 在实际执行 init 的线程激活创建状态
dispatch_creation_context is_cancelled() 无 bool 不抛出 线程安全 查询该创建是否已被批次抢占
dispatch_creation_context try_complete(object) shared_ptr<abstract_object> bool 不抛出 线程安全 在解析异步 creator future 前原子提交;若抢占先发生则自动放弃对象并返回 false
dispatch_creation_scope 析构 无 无 不抛出 必须在创建它的线程析构 恢复进入作用域前的嵌套创建状态

dispatcher 的对象准入边界是 mc::engine::object_impl。dispatch_aware 只为 mc::app::object<T> 提供业务主动声明的 wait_for/defer_registration;表达式解析出的自动依赖由 dispatcher 的创建状态承载,因此普通 runtime_object 也会在 required ref 满足前延迟注册。

辅助类型

类型 字段
binary_object_info class_name, object_name, object_props_bytes (blob), object_extends_bytes (blob)
get_binary_objects_response position, objects (vector), life_cycle_id (uint32_t), object_identifier, sender, source_group_path
dispatch_creation_args class_name, object_name, position, sender, life_cycle_id, properties (dict), extras (dict), host_service, functions (dict), debounce_configs (unordered_map<string, string>), precision_configs (unordered_map<string, int>), object_identifier (object_identifier_t)
object_dispatch_failure class_name, object_name, position, error_message
ref_semantics enum { optional, required }

dispatch_creation_args 新增字段说明

字段 类型 用途
debounce_configs unordered_map<string, string> 属性防抖配置,prop_name → debounce_interval 映射,由 object_factory_hook::create 据此对属性写入做防抖处理
precision_configs unordered_map<string, int> 属性精度配置,prop_name → precision_value 映射,由 object_factory_hook::create 据此对属性值做精度计算
functions dict 本组携带的 CSR Func 定义表(name → mc::expr::func)。非空时由 object_factory_hook::create 在 init 前挂到对象 extras(mc.expr.functions),使资源域对象属性上的 $Func_/$func: 表达式可经作用域链 find_function_table 解析,对齐设备域 attach_functions_extra
object_identifier object_identifier_t 对象标识符,包含 systemId、domain、instance、sub 四元组,用于唯一标识对象的归属信息
creation_context dispatch_creation_context 可跨 executor 复制的创建上下文;默认工厂自动激活并提交,异步 creator 在实际 init 线程显式激活,并在交付前调用 try_complete

object_identifier_t 定义为 std::tuple<uint8_t, mc::string, mc::string, mc::string>,各字段含义:

  • systemId (uint8_t): 系统标识,对应 bmc.kepler.Object.Properties.ObjectIdentifier 的第一个字段
  • domain (string): 域标识
  • instance (string): 实例标识
  • sub (string): 子标识

在自发现流程中,service_object_group_source 通过异步调用 get_property("ObjectIdentifier") 获取该值,经由 dispatch_creation_args 传递到 object_factory_hook::create,最终通过 set_object_identifier() 设置到对象实例。

annotation_setters(自由函数)

Header: #include <mc/app/discovery/annotation_setters.h>   Namespace: mc::app::discovery

@Debounce/@Precision 注解的解析与内部 setter 安装,供 mcapp 自发现路径与 devmon 直挂对象路径共用。

std::pair<std::unordered_map<std::string, std::string>, std::unordered_map<std::string, int>>
extract_annotation_configs(mc::dict& properties);   // 返回 {debounce_configs, precision_configs}

void install_internal_setters(mc::engine::object_impl* impl,
                              const std::unordered_map<std::string, std::string>& debounce_configs,
                              const std::unordered_map<std::string, int>& precision_configs);
函数 参数 返回值 异常 线程安全 说明
extract_annotation_configs properties:待提取的原始属性 dict(原地修改,移除 @Debounce/@Precision 键) pair<debounce_configs, precision_configs>:属性名 → 配置对象名 / 属性名 → 精度值 std::bad_alloc(dict 拷贝);类型不匹配的项被忽略 否,调用方独占该 dict @Debounce 仅取 string 值、@Precision 仅取 integer 值;提取后注解键不再残留
install_internal_setters impl:目标对象;debounce_configs、precision_configs 同返回值 void 不抛出(内部逐属性捕获异常并记日志) 否,应在对象 init() 后、注册前单线程调用 按「防抖 → 精度」链安装;无防抖且 precision ≤ 1 跳过;纯精度属性安装后回写一次初始值;impl == nullptr 直接返回

语义要点:

  • 精度:(value / precision) * precision 向零截断,precision ≤ 1 不处理;double 属性保留浮点运算,整型使用 int64。
  • 防抖:沿用整型语义(get_debounce_val(int));Monitor.Value 由 object_factory_hook 使用 double 专用处理器。
  • 初始值回写:仅纯精度属性(未配置防抖)回写一次;含防抖属性有状态、非幂等,不回写。

✅ 在驱动/对象 init 之前提取,init 之后安装

// 提取会原地移除注解键,避免 init 把 @Debounce/@Precision 当作普通属性
auto [debounce_cfg, precision_cfg] = mc::app::discovery::extract_annotation_configs(props);
device.init(obj, &props, &connector);
if (auto* impl = dynamic_cast<mc::engine::object_impl*>(obj); impl != nullptr) {
    mc::app::discovery::install_internal_setters(impl, debounce_cfg, precision_cfg);
}

❌ 不要在安装后才提取,或把注解键继续透传给属性注入

extract_annotation_configs 负责原地删除注解键;若在安装后才提取,或提取后仍把原始 dict 注入对象,会令 @Precision 之类的注解键被当作普通属性处理。

最佳实践

✅ 用 allowed_owners 过滤派发来源

每个业务服务应明确声明接受哪些 sender,避免错误实例化非预期对象:

class FanService : public mc::app::discovery_service {
public:
    explicit FanService(mc::string name) : mc::app::discovery_service(std::move(name)) {}
protected:
    std::vector<mc::string> allowed_owners() const override {
        return {"bmc.kepler.hwdiscovery"};
    }
    bool on_start() override { return mc::app::discovery_service::on_start(); }
    bool on_stop() override {
        return mc::app::discovery_service::on_stop();
    }
};

✅ 覆写 expr_evaluator 支持属性模板替换

当上游对象属性包含 ${slot} 等占位符时,提供自定义 evaluator:

struct slot_evaluator : mc::app::discovery::expression_evaluator {
    mc::string substitute_template(mc::string_view text,
                                   const mc::dict& vars) const override {
        mc::string result(text);
        for (const auto& [k, v] : vars) {
            mc::string ph = mc::string("${") + k + "}";
            auto pos = result.find(ph);
            while (pos != mc::string::npos) {
                result.replace(pos, ph.size(), v.as<mc::string>());
                pos = result.find(ph, pos + v.as<mc::string>().size());
            }
        }
        return result;
    }
};

std::shared_ptr<mc::app::discovery::expression_evaluator>
expr_evaluator() const override {
    return std::make_shared<slot_evaluator>();
}

✅ 通过 discovery_service::on_stop 完成有序关闭

基类已按“source 拒绝新回调并排空在途回调 → dispatcher 拒绝更新并清理对象”的顺序关闭, 子类不要绕过基类,也无需重复调用 dispatcher 的两阶段关闭接口:

// 错误
bool on_stop() override {
    source().stop(); // 遗漏 dispatcher 的准备与最终清理
    return true;
}

// 正确
bool on_stop() override {
    return mc::app::discovery_service::on_stop();
}

❌ 不要在 configure 之前访问 dispatcher()

discovery_service 延迟构建,configure() 前访问会拿到无效 service 连接:

// 错误
bool on_configure() override {
    auto& d = dispatcher(); // context().connection() 尚未就绪
    return true;
}

// 正确
bool on_start() override {
    return mc::app::discovery_service::on_start();
}

❌ 不要在数据源回调中执行长时间阻塞操作

回调在 D-Bus 线程同步调用,阻塞会导致信号积压和超时:

// 错误
source->set_group_callback([](const auto& group) {
    for (const auto& obj : group.objects) { heavy_decode(obj); }
});

// 正确:投递到工作线程
source->set_group_callback([this](const auto& group) {
    auto strand = context().create_work_strand();
    mc::runtime::post(strand, [this, group]() {
        dispatcher().on_object_group(group);
    });
});