简要介绍
在openUBMC中,插件机制是指在不侵入修改组件代码的情况下,其能够覆写组件的资源协作接口公共方法或业务组件的私有方法,从而实现差异化场景的扩展。
插件机制主要用于以下两种场景:
- 通过插件机制覆写开放组件的资源协作接口公共方法或私有方法,实现开放组件的定制功能。
- 通过插件机制覆写开源组件的资源协作接口公共方法或私有方法,避免因产品功能实现差异化而需要创建多个分支,造成管理不便的问题,从而为实现不同产品差异化定制提供其他途径。
配置流程
在openUBMC中配置一个可扩展的插件,需要完成以下操作:
1、在对应组件中model.json中配置featureTag关键字,用于描述插件方法归属的特性,例如“Feature1”。
2、 执行自动生成代码。
3、 在plugin/Feature1/init.lua文件中实现定制化功能。
MDS语法
在openUBMC中配置一个可扩展的插件时,需要配置featureTag关键字。下面分别以覆写开发组件的资源协作接口公共方法和私有方法为例,介绍如何在model.json中新增featureTag属性。
样例说明
- 资源协作接口的公共方法
{
"ClassA": {
"path": "/bmc/kepler/xxx/${Id}",
"interfaces": {
"bmc.kepler.Interface1": {
"methods": {
"Method1": {
"featureTag": "Feature1",
...
}
}
}
}
}
}
- 私有方法
{
"private": {
"methods": {
"Method1": {
"featureTag": "Feature1",
...
}
}
}
}
featureTag用于描述插件方法的归属的特性,需要确保一个插件方法最多有一个特性标签,防止多个组件(特性)插件生效时,插件方法和功能实现冲突。
错误样例
json{ "private": { "methods": { "Method1": { "featureTag": ["Feature1", "Feature2"], ... } } } }
对于一个插件方法存在归属多个特性标签的情况,则该插件方法的粒度可能过大,可以考虑将其拆分多个方法
将1)中的错误样例变更为
json{ "private": { "methods": { "Method1": { "featureTag":"Feature1", ... }, "Method2": { "featrueTag": "Feature2", ... } } } }
同一组件的不同插件方法可以使用相同的featureTag类型
样例说明
json{ "ClassA": { "path": "/bmc/kepler/xxx/${Id}", "interfaces": { "bmc.kepler.Interface1": { "methods": { "Method1": { "featureTag": "Feature1", ... } } } } }, "private": { "methods": { "Method1": { "featureTag": "Feature1", ... } } } }
资源协作接口的公共插件方法和私有插件方法的区别:
a. 插件只要基于公共插件方法的约定实现对应的方法,可以被其他实现接口规范的组件使用,具体样例见资源协作接口的公共插件方法
b. 私有插件方法是组件的接口约束,是组件必须实现的接口规范,并由业务处理过程进行请求和调用;是组件基于业务流程和逻辑分解和提取的业务约束,对组件外部和其他组件没有约束力,具体样例见私有插件方法
更加详细的MDS对象内容可以参考《MDS》
示例
在这里,我们以添加一个私有插件方法为例,公共插件方法与私有插件方法的使用基本一致。
私有插件方法
MDS语法
在../mds/model.json中添加私有插件方法
{
"private": {
"methods": {
"PrivateProcess": {
"featureTag": "Feature1",
"description": "私有插件方法",
"req": {
"request1": {
"baseType": "String"
},
"request2": {
"baseType": "String"
}
},
"rsp": {
"Status": {
"baseType": "U8"
}
}
}
}
}
}
自动生成代码
- 在model.json中添加私有插件方法后,直接执行bingo gen,自动生成对应的代码shell
bingo gen
此时,会自动生成两部分代码:组件插件服务接口和组件插件模板接口
组件插件服务接口
在../gen/sensor/service.lua中会生成私有方法的实现方法和调用方法
lua-- 调用方法 function sensor_service:ImplPrivateProcess(cb) model.ImplPrivateProcess(cb) end -- 实现方法 function sensor_service:PrivateProcess(...) model.PrivateProcess(...) end
组件插件模板接口 插件代码生成在temp/plugin中,需要将其移到app的plugin目录:../plugin/Feature1/init.lua
在init.lua中预留三种方法的功能定制:
lua-- PreProcess:插件预处理方法(可选),支持对其入参做参数校验,参数转换等处理后,将处理后的参数传入插件处理方法 local function PrivateProcessPreProcess(...) --todo return ... end -- Process:插件处理方法(可选),实现插件的定制功能,插件处理方法未实现时,使用默认的方法实现 local function PrivateProcessProcess(...) --todo return ... end -- Postprocess:插件后置处理方法(可选),对插件处理方法或默认实现的出餐进行处理,将处理后的参数返回 local function PrivateProcessPostprocess(...) --todo return ... end -- 入参分别为插件预处理、插件处理和插件后置处理方法的回调,实现相关回调后请将方法名填入,nil表示不进行相应处理 Feature1:ImplPrivateProcess(nil, nil, nil)
欲想更深入了解自动生成代码,可以参考《代码自动生成》
插件功能的实现
在这里,以实现打印日志信息为样例,讲解组件默认方法和插件方法的功能实现的区别
组件默认方法的实现
组件默认方法实现意味着直接在业务侧功能代码中调用./gen/sensor/service.lua的调用方法
在这里,以../src/lualib/sample.lua中实现打印日志信息为例。注意:业务功能代码存放的路径需要自行定义。
lualocal RET<const> = 0 base_service:ImplPrivateProcess(function(requst1, request2) log:notice('default: request1 is %s and request2 is %s', requst1, request2) return RET end)
插件方法的实现 插件方法的实现需要在../plugin/Feature1/init.lua的Proccess方法中添加打印日志信息功能,进而实现插件的定制处理
lua-- 不覆写插件预处理方法的功能,无须处理 local function PrivateProcessPreProcess(...) --todo return ... end -- 覆写插件处理方法 local RET<const> = 0 local function PrivateProcessProcess(...) return function(requst1, request2) log:notice('plugin: request1 is %s and request2 is %s', requst1, request2) return RET end -- 不覆写后置插件处理方法,无须处理 local function PrivateProcessPostprocess(...) --todo return ... end -- 由于实现了插件处理方法的覆写,需要将其传入调用方法;而插件预处理和后置处理方法没有实现插件功能覆写,直接传入nil Feature1:ImplPrivateProcess(nil, PrivateProcessProcess, nil)
开发者测试
在对应组件实现插件机制后,若需要运行其开发者测试,需要在../src/service/main.lua添加APP组件名
在这里,以sensor组件为例
-- 路径:../src/service/main.lua
APP_NAME = 'sensor'
...