基于资源协作接口的订阅机制指导
更新时间: 2025/12/10
在Gitcode上查看源码

基于资源协作接口的订阅机制指导

资源协作接口:mdb_interface

功能介绍

资源协作接口(mdb_interface)订阅机制学习时,可以先学习资源协作接口调测机制介绍

整体设计实现

mdb_interface中的模型部分主要是由MDS完成,接口部分主要是DBUS。类比其他的进程间通信(IPC)机制,用于模块间的通信,资源定义总体上通过 json 格式进行描述,分为 path 和 intf 两部分:

当A模块需要获取到B模块的信息时,需要两个步骤: 1、A模块得到B模块对应rpc的入口(interface)。 2、调用B模块对应的属性(properties)或者方法(methods)。

当A模块需要主动感知B模块的属性变化时,需要订阅B模块的属性变化。 1、A模块得到B模块对应rpc的入口(interface)。 2、订阅(Subscribe)B模块里的属性(properties)变化。

正常情况下,如果在A模块的service.json文件里面required了B模块的接口(interface),那B模块里面的接口下对应的属性(properties)和方法(methods)都能够获通过A模块本身自动生成的代码client.lua中的方法获取到,本篇主要介绍如何使用client方式订阅。

单个接口下的属性变化订阅

以下是已有代码中订阅方式举例:

在mdb组件的json\intf\mdb\bmc\kepler\Systems\FruCtrl.json里面定义了PowerState属性

json
"PowerState": {
    "baseType": "String",
    "readOnly": true,
    "description": "反映实时电源状态"
},

这里没有定义emitsChangedSignal属性,使用的是默认值,默认为true

emitsChangedSignal表示是否发送属性变化信号,默认为"true" i)如果取值为false,表示属性变化时不发送信号; ii)如果取值为const,表示属性永不变化,不需要发送信号; iii)如果取值为true,表示属性变化时发送信号,信号携带属性值; iv)如果取值为invalidates,表示属性变化时发送信号,但信号不携带属性值;

在bios组件里面的service.json文件里订阅了属性PowerState和属性SysResetDetected,那么bios模块就可以通过client方式获取到A模块的属性PowerState和属性SysResetDetected。

json
{
    "path": "/bmc/kepler/Systems/${SystemId}/FruCtrl/${Id}",
    "interface": "bmc.kepler.Systems.FruCtrl",
    "properties": {
        "PowerState": ["subscribe"],
        "SysResetDetected": ["subscribe"]
    }
},

bios组件执行bingo gen后自动生成了client.lua文件,自动生成如下代码,调用client对象中的OnFruCtrlPropertiesChanged方法,获取属性值。

text
function bios_client:OnFruCtrlPropertiesChanged(cb, path_params)
    local path_namespace = path_params and
                               ('/bmc/kepler/Systems/' .. path_params['SystemId'] .. '/FruCtrl/' .. path_params['Id']) or
                               '/bmc/kepler/Systems/:SystemId/FruCtrl/:Id'
    self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_properties_changed(self:get_bus(), path_namespace,
        cb, 'bmc.kepler.Systems.FruCtrl', {'PowerState', 'SysResetDetected'})
end

如下代码对应的是调用PowerState和SysResetDetected订阅和相关的操作函数。

text
client:OnFruCtrlPropertiesChanged(function(values, path)
    if values[POWER_STATE_PROPERTY] or values[SYS_RESET_FLAG] then
        ......
        end
        UpgradeSignal.power_state_changed_callback()
    end
end)

回调中的第一个参数是values,values[POWER_STATE_PROPERTY]表示属性值PowerState,values[SYS_RESET_FLAG]表示属性值SysResetDetected; 第二个参数是path,表示属性所在的路径,这里取值要注意,path里面有SystemId,Id,这两个参数,需要根据path去取值; 第三个参数是interface,表示属性所在的接口;

signals方式的订阅

mdb_interface组件在json\intf\mdb\bmc\kepler\Systems\FruCtrl.json里面定义了signals:

json
"signals": {
    "BeforePowerOnSignal": {
        "Timeout": {
            "baseType": "U8",
            "default": 3,
            "description": "上电前广播,通知其他App"
        }
    }
}

bios组件在模块的service.json文件里面订阅相关接口,

json
{
    "path": "/bmc/kepler/Systems/${SystemId}/FruCtrl/${Id}",
    "interface": "bmc.kepler.Systems.FruCtrl",
    "properties": {
        "PowerState": ["subscribe"],
        "SysResetDetected": ["subscribe"]
    }
},

执行bingo gen后自动生成了client.lua文件,有如下自动生成的代码:

text
function bios_client:SubscribeFruCtrlFruCtrlBeforePowerOnSignal(cb)
    local sig = match_rule.signal('BeforePowerOnSignal', 'bmc.kepler.Systems.FruCtrl'):with_path(
        '/bmc/kepler/Systems/:SystemId/FruCtrl/:Id')
    self.signal_slots[#self.signal_slots + 1] = self:get_bus():match(sig, function(msg)
        cb(msg:read())
    end)
end

在bios组件的signal.lua里面有如下调用BeforePowerOnSignal的代码,UpgradeSignal.verify_flash(timeout)就是回调函数。

text
    client:SubscribeFruCtrlFruCtrlBeforePowerOnSignal(UpgradeSignal.verify_flash)
    log:notice('[bios]subscribe before power on signal success')

此处传出来的第一个参数应该是ctx,第二个参数才是Timeout,注意使用的时候取值要注意;

接口创建和删除时的订阅

mdb_interface组件的json\intf\mdb\bmc\kepler\下面对应的接口,都可以被订阅。

比如在network的代码目录中的mds\service.json,有如下定义:

json
{
    "path": "*",
    "interface": "bmc.kepler.Systems.SmBios",
    "properties": {
        "SmBiosStatus":  ["subscribe"]
    }
}

在network组件执行bingo gen之后,在gen\network_adapter\client.lua中会有如下自动生成的代码

text
function network_adapter_client:OnSmBiosInterfacesAdded(cb)
    self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_interfaces_added(self:get_bus(), '/bmc', cb,
        'bmc.kepler.Systems.SmBios')
end

在src\lualib\device\class\network_adapter.lua使用过程中,有如下的调用,此处回调函数的三个参数分别是msg, path, obj;

text
client:OnSmBiosInterfacesAdded(function (_, _, props)
    resource_obj.smbios_status = props.SmBiosStatus:value()
    log:debug('updtate smbios_status=%s', resource_obj.smbios_status)
end)

通过lua代码直接订阅,不通过client方式

在storage组件里面,c_bus_monitor_service:fru_monitor()这个函数中有如下实现:

text
local fru_bus_sig = org_freedesktop_dbus.MatchRule.signal('PropertiesChanged'):with_interface(
    org_freedesktop_dbus.SD_BUS_PROPERTIES):with_path_namespace(fru_path)

-- 注册响应函数
local fru_slot = self.bus:match(fru_bus_sig, function(msg)
    local _, values = msg:read('sa{sv}as')
    ......
    end
end)

这种方式实际上和client的方式相同,也可以实现订阅的功能。

注意事项

1、确保mdb中的属性值可以被emit,以下这种属性在变化时是不会发送属性变化信号的,这个不配置默认是ture,可以发送

json
"options": {
    "emitsChangedSignal": "false"
},

2、可以使用busctl命令观察是否产生了PropertiesChanged和on_interfaces_added事件的通知

text
telnet连接到bmc环境上,执行
source /etc/profile
busctl --user monitor bmc.kepler.xxx

可以看到类似的输出:

text
Type=signal  Endian=l  Flags=1  Version=1 Cookie=164  Timestamp="xxxxx UTC"
  Sender=:1.41  Destination=:1.1  Path=/bmc/kepler/Systems/1/Bios  Interface=org.freedesktop.DBus.Properties  Member=PropertiesChanged
  UniqueName=:1.41
  MESSAGE "sa{sv}as" {
          STRING "bmc.kepler.Systems.Bios";
          ARRAY "{sv}" {
                  DICT_ENTRY "sv" {
                          STRING "Version";
                          VARIANT "s" {
                                  STRING "KL4.41.001.xxxxxx.R";
                          };
                  };
          };
          ARRAY "s" {
          };
  };

3、signals方式的订阅目前没法监控到,可以考虑回调函数中加打印进行调试

比如在app.log中有如下打印bios receive before power on UpgradeSignal,但是在busctl --user monitor中找不到这种signal,实际是可以调到的。

参考文档