MDS模型插件机制
更新时间:2025/03/03
在Gitcode上查看源码

简要介绍

在openUBMC中,插件机制是指在不侵入修改组件代码的情况下,其能够覆写组件的资源协作接口公共方法或业务组件的私有方法,从而实现差异化场景的扩展。

插件机制主要用于以下两种场景:

  1. 通过插件机制覆写开放组件的资源协作接口公共方法或私有方法,实现开放组件的定制功能。
  2. 通过插件机制覆写开源组件的资源协作接口公共方法或私有方法,避免因产品功能实现差异化而需要创建多个分支,造成管理不便的问题,从而为实现不同产品差异化定制提供其他途径。

配置流程

在openUBMC中配置一个可扩展的插件,需要完成以下操作:

1、在对应组件中model.json中配置featureTag关键字,用于描述插件方法归属的特性,例如“Feature1”。

2、 执行自动生成代码。

3、 在plugin/Feature1/init.lua文件中实现定制化功能

MDS语法

在openUBMC中配置一个可扩展的插件时,需要配置featureTag关键字。下面分别以覆写开发组件的资源协作接口公共方法和私有方法为例,介绍如何在model.json中新增featureTag属性。

样例说明

  • 资源协作接口的公共方法
json
{
    "ClassA": {
        "path": "/bmc/kepler/xxx/${Id}"
        "interfaces": {
            "bmc.kepler.Interface1": {
                "methods": {
                    "Method1": {
                        "featureTag": "Feature1"
                        ...
                    }
                }
            }
        }
    }
}
  • 私有方法
json
{
    "private": {
        "methods": {
            "Method1": {
                "featureTag": "Feature1"
                ...
            }
        }
    }
}
  1. featureTag用于描述插件方法的归属的特性,需要确保一个插件方法最多有一个特性标签,防止多个组件(特性)插件生效时,插件方法和功能实现冲突。

    错误样例

    json
    {
        "private": {
            "methods": {
                "Method1": {
                    "featureTag": ["Feature1" "Feature2"]
                    ...
                }
            }
        }
    }
  2. 对于一个插件方法存在归属多个特性标签的情况,则该插件方法的粒度可能过大,可以考虑将其拆分多个方法

    将1)中的错误样例变更为

    json
    {
        "private": {
            "methods": {
                "Method1": {
                    "featureTag":"Feature1"
                    ...
                }
                "Method2": {
                    "featrueTag": "Feature2"
                    ...
                }
            }
        }
    }
  3. 同一组件的不同插件方法可以使用相同的featureTag类型

    样例说明

    json
    {
        "ClassA": {
            "path": "/bmc/kepler/xxx/${Id}"
            "interfaces": {
                "bmc.kepler.Interface1": {
                    "methods": {
                        "Method1": {
                            "featureTag": "Feature1"
                            ...
                        }
                    }
                }
            }
        }
        "private": {
            "methods": {
                "Method1": {
                    "featureTag": "Feature1"
                    ...
                }
            }
        }
    }
  4. 资源协作接口的公共插件方法和私有插件方法的区别:

    a. 插件只要基于公共插件方法的约定实现对应的方法,可以被其他实现接口规范的组件使用,具体样例见资源协作接口的公共插件方法

    b. 私有插件方法是组件的接口约束,是组件必须实现的接口规范,并由业务处理过程进行请求和调用;是组件基于业务流程和逻辑分解和提取的业务约束,对组件外部和其他组件没有约束力,具体样例见私有插件方法

更加详细的MDS对象内容可以参考《MDS》

示例

在这里,我们以添加一个私有插件方法为例,公共插件方法与私有插件方法的使用基本一致。

私有插件方法

MDS语法

在../mds/model.json中添加私有插件方法

json
{
    "private": {
        "methods": {
            "PrivateProcess": {
                "featureTag": "Feature1"
                "description": "私有插件方法"
                "req": {
                    "request1": {
                        "baseType": "String"
                    }
                    "request2": {
                        "baseType": "String"
                    }
                }
                "rsp": {
                    "Status": {
                        "baseType": "U8"
                    }
                }
            }
        }
    }
}

自动生成代码

  1. 在model.json中添加私有插件方法后,直接执行bingo gen,自动生成对应的代码
    shell
    bingo gen

此时,会自动生成两部分代码:组件插件服务接口和组件插件模板接口

  1. 组件插件服务接口

    在../gen/sensor/service.lua中会生成私有方法的实现方法和调用方法

    lua
    -- 调用方法
    function sensor_service:ImplPrivateProcess(cb)
        model.ImplPrivateProcess(cb)
    end
    
    -- 实现方法
    function sensor_service:PrivateProcess(...)
        model.PrivateProcess(...)
    end
  2. 组件插件模板接口 插件代码生成在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)

欲想更深入了解自动生成代码,可以参考《代码自动生成》

插件功能的实现

在这里,以实现打印日志信息为样例,讲解组件默认方法和插件方法的功能实现的区别

  1. 组件默认方法的实现

    组件默认方法实现意味着直接在业务侧功能代码中调用./gen/sensor/service.lua的调用方法

    在这里,以../src/lualib/sample.lua中实现打印日志信息为例。注意:业务功能代码存放的路径需要自行定义。

    lua
    local RET<const> = 0
    
    base_service:ImplPrivateProcess(function(requst1, request2)
        log:notice('default: request1 is %s and request2 is %s', requst1, request2)
        return RET
    end)
  2. 插件方法的实现 插件方法的实现需要在../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组件为例

lua
-- 路径:../src/service/main.lua
APP_NAME = 'sensor'
...