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);
});
});