代码自动生成
更新时间:2024/12/19
在Gitcode上查看源码

前言:在openUBMC中,我们定义了MDS数据模型,为了简化数据模型代码的开发流程,openUBMC在bingo中嵌套了接口代码自动生成脚本,以辅助开发人员对数据模型定义的快速开发。

在上手接口代码自动生成之前,请务必了解MDS数据模型的意义及其在openUBMC中发挥的作用。

接口代码自动生成

本章节会简要地介绍自动生成代码的流程以及生成的接口代码与MDS之间的对应关系。

在本章节中你将会掌握如下技能:

  1. 理解每一份自动生成代码的来源和其使用方法。
  2. 学会新增MDS对象、接口、属性、方法。
  3. 学会新增一个IPMI命令。
  4. 学会新增其他组件APP资源协作接口的订阅。

自动生成代码时机

在开展自动生成代码之前,需要明确什么时机需要进行接口代码自动生成。当组件需要新增对外能力或需要新增对其他组件的引用时,就需要执行自动生成代码。在满足如下具体条件时,通过接口代码自动生成的方法能够简化人工开发代码量:

  1. 组件APP的对象新增,接口新增,属性(私有、公有)新增,方法新增等。
  2. 组件APP新增IPMI方法等。
  3. types.json中的对象新增等。
  4. 组件APP需要新增对其他组件APP的接口订阅(可用代码方式替代)。

自动生成代码依赖项之间的关系

在执行自动生成代码之前,需要明确站在开发者的角度自动生成代码过程中的参与者。

  1. bingo(自动生成代码的执行者)
  2. 组件MDS的定义(定义属性来源、对象接口关系、方法的请求与接收格式等的定义)
  3. mdb_interface(接口属性的类型,接口通用方法请求和接收格式定义)

MDS的定义

组件APP的开发过程中,需要对MDS进行定义,其中涉及如下json文件(均放置于/mds/目录下):

  • model.json
  • service.json
  • ipmi.json
  • types.json

具体MDS的配置请参考《MDS》

环境说明

依赖关系说明中,明确了自动生成代码需要依赖mdb_interface的接口定义,组件侧可以通过在service.json中的dependencies配置mdb_interface的来源。如下所示:

json
{
    "name": app_name,
    ....
    "dependencies":{
        "test":[
            {
                "conan":"mdb_interface/[>=x.x.x]"
            },
            ...
        ],
    }
}

在上述的service.json中我们定义了mdb_interface代码来源于conan镜像仓,并且会抓取>=x.x.x版本的最新版。对于开发人员,可以在本地构建mdb_interface后,利用本地出包替换service.json中的mdb_interface,例如:

json
{
    "name": app_name,
    ....
    "dependencies":{
        "test":[
            {
                "conan":"mdb_interface/x.x.x@xxx.dev/dev"
            },
            ...
        ],
    }
}

执行自动生成

如果你已经完成bingo的安装、MDS的定义和mdb_interface的依赖引入。那就可以通过如下一行命令执行自动生成代码:

shell
 bingo gen

执行自动生成之前请确保/.conan/data中的mdb_interface为最新版, 若不是最新版,可执行rm -rf ~/.conan/data/mdb_interface清除缓存的mdb_interface,并再次执行bingo gen命令。

自动生成代码对应关系

在执行完自动生成代码后,所有的生成代码都会存放于/gen目录下,所有的代码都可以在Lua中使用require快速调用,自动生成代码目录结构如下所示:

shell
.
├── class
   ├── model.lua
   └── types
       ├── TypesName_1.lua
       ├── ...
       ├── TypesName_n.lua
       └── types.lua
└── app_name
    ├── client.lua
    ├── datas.lua
    ├── db.lua
    ├── lcoal_db.lua
    ├── ipmi
   ├── cmds
   ├── Getxxx.lua
   ├── ...
   └── Setxxx.lua
   ├── ipmi.lua
   └── ipmi_message.lua
    ├── json_types
   ├── JsonTypes_1.lua
   ├── ...
   └── JsonTypes_k.lua
    ├── local_db.lua
    ├── orm_classes.lua
    └── service.lua

自动生成代码文件与MDS的关系,和简要说明:

目录名
文件名
依据文件
说明
/gen/classmodel.luamodel.json和mdb_interface接口定义将model.json中的配置转换为lua代码,用于校验每个类属性的类型、方法请求体和响应体格式,属性默认值的默认值设置,服务端资源协作接口方法注册函数。详情见MDS对象
/gen/class/typesTypesName_x.luamodel.jsonmodel.json中每一个类的定义都会生成对应的TypesName_x.lua文件,其中申明类私有属性的默认值、索引方式、解包、验证方法的定义。会被model.lua引用,辅助完成类的校验。
/gen/typestypes.luatypes.json包含枚举、结构体、字典等自定义类型的定义,可用于model.json中某些属性的定义,例如在types.lua中定义的自定义结构体,在对应类中使用该结构体作为成员变量的类型。
/gen/app_nameclient.luaservice.json和mdb_interface接口定义service.json中required部分定义了组件APP业务处理中所需的非本组件APP的资源数接口,在client.lua中自动生成对应的资源协作接口方法调用、对象获取和信号订阅等函数。详情见跨组件通信
/gen/app_namedatas.luadatas.yaml本地持久化数据库,组件APP初始化时默认加载,可通过lua require的方式引用数据库。
/gen/app_namedb.luamodel.json远程持久化数据库表格创建(包含数据字段、主键申明、默认值定义),远程数据库操作方法封装(含数据库增删改查方法)
/gen/app_namelocal_db.luamodel.json本地持久化数据库表格创建(包含数据字段、主键申明、默认值定义),远程数据库操作方法封装(含数据库增删改查方法),通过在service.json中配置"{tableLocation": "Local}"方式与远程持久化区分。
/gen/app_name/ipmiipmi.luaipmi.json组件APP的IPMI方法的定义,封装IPMI的netfn、cmd和subcmd等等,并封装请求体和响应体。
/gen/app_name/ipmiipmi_message.luaipmi.jsonIPMI消息体(请求体和响应体)的集合,被ipmi.lua调用。
/gen/app_name/ipmi/cmdsIpmiMethod_x.luaipmi.jsonIPMI方法的消息体的定义及校验方法的生成
/gen/app_name/json_types接口末端名.luamodel.json和service.json搭配mdb_interface生成每一个接口的定义,包含注册属性的定义方法和参数校验方法。 被model.lua引用,辅助完成接口属性的校验
/gen/app_nameorm_classes.luamodel.json具备数据库管理的对象,在model.json中通过配置tableName和属性持久化方式开启。
/gen/app_nameservice.luamodel.json和service.json搭配mdb_interface生成服务入口,包含APP初始化,服务端资源协作接口方法注册,对象创建。详情见微组件框架

自动生成代码详解及使用方法

在本小节中,我们会从APP的初始化的角度,逐步剖开自动生成代码的作用。如下图所示,自动生成代码与业务APP之间的关系:

微组件框架

在接口代码自动生成中,openUBMC提供了组件的微组件框架,并将service.lua作为业务组件的服务框架,在service.lua中定义了业务组件的基本初始化方法、MDS对象创建方法、对外方法的定义等。openUBMC采用了面向对象方式辅助开发者快速完成开发,见《Lua开发框架》

APP初始化方法

在service.lua中定义了业务组件的基本构造方法等,如下所示:

lua
function app_name_service:ctor(){...} --构造函数定义,当app_name_app初始化时执行
function app_name_service:pre_init(){...}  --预初始化定义,可在app_name_app中使用app.super.pre_init()方法调用
function app_name_service:init(){...} --初始化定义,可在app_name_app中使用app.super.init()方法调用

开发者能在业务开发代码/src/lualib/app_name_app.lua中通过继承service.lua的方式,快速进行业务的开发。

lua
local c_service = require 'app_name.service'
local class = reuquire 'mc.class'

local app = class(c_service)
-- 组件app的初始化
function app:ctor(){...}
function app:init(){...}

MDS对象创建方法

开发人员能够通过在model.json中的定义对象名、对象内含接口,对象的私有属性,快速完成对MDS对象的定义,并采用自生成的方法完成对象的创建。如下所示:

json
{
    "ResourceObject":{
        "path" : "/bmc/kepler/Systems/:SystemId/ResourceObject/:ID" ,
        "privilege" : ["ConfigureSelf"],
        "interface" : {
            "bmc.kepler.Systems.ResourceObjectInterface1" : {
                "properties" : {
                    "ID" :{
                        ...
                    },
                    ... 
                }
            },
            ...
        },
        "method" : {
            ...
        },
        "properties" : {
            "ObjectProperty_1" : {
                ... 
            },
            ...
        }
    }
}

path: MDS对象路径申明

interface: MDS对象的资源协作接口,其中的properties为资源协作接口的属性。

method: MDS对象对外提供给的方法定义

properties:MDS对象的私有属性定义

完成model.json的对象配置并执行自动生成代码后,service.lua中会定义MDS对象的接口(含接口名、接口类型),MDS对象的构造方法等等。并提供在业务代码中快速创建MDS对象的方法:

lua
function app_name_service:CreateResourceObject(SystemID, ID, prop_setting_cb)
    local path = '/bmc/kepler/Systems/' .. SystemId .. '/ResourceObject' -- 对象对外路径
    return object_manage.create_object('ResourceObject', path, path, prop_setting_cb)  -- 完成对象对外能力开放动作
end

在业务代码中通过调用CreateResourceObject方法能够实现快速的MDS对象创建。如下所示:

lua
function c_resource_object.create_mdb_object(value)
    local app = c_object_manage.get_instance().app
    return app:CreateResourceObject(1, value.ID, fucntion(obj)
        ... -- 业务处理
    end)
end

对外方法实现

微组件框架定义了rpc方法,增强了组件的对外能力,rpc方法的生成依赖model.jsonmdb_interface的定义,rpc方法演示我们使用network_adapter中的GetPortSpeed进行展示。

json
{
    "NetworkAdapters":{
        ...
        "method":{
            "GetPortSpeed": {
                "privilege": [
                    "ConfigureSelf"
                ],
                "req": {
                    "Type": {
                        "baseType": "U8"
                    },
                    "Slot": {
                        "baseType": "U8"
                    },
                    "PortID": {
                        "baseType": "U8"
                    }
                },
                "rsp": {
                    "Status": {
                        "baseType": "U8"
                    },
                    "Speed": {
                        "baseType": "U32"
                    }
                }
            },
        }
    }
}

method中可以定义多个rpc方法。每一个方法结构如GetPortSpeed所示,定义其请求体和响应体的结构。

GetPortSpeed中定义了请求体的三个整型入参Type(网卡类型)、Slot(网卡卡槽号)、PortID(网口ID),响应体的两个返回值Status(响应状态)、Speed(网口速率)。当我们在model.json中的接口下定义了方法(需要与mdb_interface中的定义对应),就会在自动生成时,在service.lua中生成对应方法的实现接口。

lua
function M.ImplNetworkAdaptersNetworkAdaptersGetPortSpeed(cb)
    class('NetworkAdapters')['bmc.kepler.Systems.NetworkAdapters'].GetPortSpeed = function(obj, ctx, ...)
        local req = network_adapters_intf_types.GetPortSpeedReq.new(...):validate(nil, nil, true)
        local rsp = network_adapters_intf_types.GetPortSpeedRsp.new(cb(obj, ctx, req:unpack())):validate()
        return rsp:unpack(true)
    end
end

其中cbcallback回调函数的缩写,在network_adapter_app.lua文件中使用注册方式实现具体的业务:

lua
function app:register_app_name_rpc()
    self:ImplNetworkAdaptersNetworkAdaptersGetPortSpeed(function(obj, ctx, ...)
        return self.device_manager:method_get_netport_speed(...)
    end)
end

function c_device_manager:method_get_netport_speed(type, slot, port_id)
    ... -- 业务处理代码
end

通过重写方法的cb函数,当调用该rpc方法时,就能够完成对应的业务处理。对于具体的传入参数objctx等等,在model.lua中会有参数校验。其中obj是调用该rpc方法时访问的MDS对象,ctx为传入参数时的上下文信息,以key-value的方式存储。

在完成APP业务处理定义后,可以通过busctl命令完成rpc方法的调用。首先可以通过introspect方法查看NetworkAdapters的方法定义:

shell
busctl --user introspect bmc.kepler.network_adapter /bmc/kepler/Systems/1/NetworkAdapters

NAME                                TYPE      SIGNATURE   RESULT/VALUE                            FLAGS
bmc.kepler.Systems.NetworkAdapters  interface -           -                                       -
.GetPortSpeed                       method    a{ss}yyy    yu                                      -

可以看到GetPortSpeed方法的签名为a{ss}yyy,签名具体含义可以请参照《组件的开发和代码编写》。rpc方法的入参ctxa{ss}中的输入,不定长参数...对应yyy的输入。因此可通过如下命令调用GetPortSpeed方法查看类型为1,槽位为2,网口号为0的网口速率:

shell
busctl --user call bmc.kepler.network_adapter /bmc/kepler/Systems/1/NetworkAdapters bmc.kepler.Systems.NetworkAdapters GetPortSpeed a{ss}yyy 0 1 2 0

输出为

shell
yu 0 4294967295

其中yu为返回签名,0代表成功返回响应码,4294967295为查询到的网口速率。

最后总结一下rpc方法调用流程,如下所示:

如果需要新增方法,需要在model.json和mdb_interface的接口下添加方法的配置。

MDS对象

在自动生成代码和MDS文件关系中,我们介绍了model.lua的作用,主要分为:MDS对象的接口、属性的类型定义,方法请求体和响应体的类型定义,MDS方法的接口定义。

这些属性的类型定义等如果由开发者来完成将会非常的繁琐且易错,openUBMC的自动生成代码能够保证在配置正确的情况下进行自动参数校验方法等重复繁琐代码的生成。

其中MDS对象的接口和属性类型的主要用于校验,如下所示:

lua
local ResourceObject = {
    ['table_name'] = 't_resource_object',
    ['alias_map'] = {
        ...
    }, 
    ['prop_configs'] = {  -- 私有属性定义
        ['Property1'] = {
            ['usage'] = {'CSR'},
            ['baseType'] = 'U8',  -- 类型定义
            ['validator'] = require'class.types.ResourceObject'.Property1 --校验方法(在/class/types里)
        },
        ... -- 其他的私有属性定义
    },
    ['default_props'] = {
        ['Property1'] = require'class.types.ResourceObject'.Property1.default[1], -- 默认值
        ...
    },
    ['mdb_prop_configs'] = {
        ['bmc.kepler.Systems.ResourceObjectInterface1'] ={ -- MDS对象内含的接口
            ['ID'] = {
                ['baseType'] = 'String',
                ['readOnly'] = true,
                ['description'] = '网卡ID',
                ['primaryKey'] = true,
                ['validator'] = require'app_name.json_types.ResourceObjectInterface1'.ID  --校验方法(在/app_name/json_types里)
            },
            ... -- 其他接口属性的定义
        },
        ... -- 其他接口的定义
    },
    ['mdb_method_configs'] = {  -- 资源协作接口方法的方法
        ['bmc.kepler.Systems.ResourceObjectInterface1'] = { -- 接口下的方法定义
            ['StarMethod'] = {
                ['req'] = {{['baseType'] = 'String', ['param'] = 'ID'}},
                ['rsp'] = {},
                ['privilege'] = {'BasicSetting'}
            },
            ... -- 其他方法的请求响应体定义
        }
    },
    ['mdb_classes'] = mdb.get_class_obj('/bmc/kepler.Systems/:SystemID/ResourceObject/:ID'), -- MDS对象获取方式 
    ['new_mdb_object'] = mdb.new_objects_builder({
        ['bmc.kepler.System.ResourceObjectInterface1'] = {
            ['property_defaults'] = {
                ['ID'] = ResourceObjectInterface1_intf_types.ID.default[1], -- 接口属性初始化
                ...
            },
            ['privileges'] = {
                ['path'] = privilege.ConfigureSelf,
                ['method'] = {['StarMethod'] = privilege.BasicSetting} 
            },
            ['interface_types'] = ResourceObjectInterface1_intf_types -- 接口类型定义,来源于/json_types
        },
        ... -- 其他接口属性的初始化
    })
}

在上述代码示例中,能看到model.lua中定义了每个类的每个接口的每个属性的类型以及其校验方法,并且还定义了method的请求与返回的参数类型。接下来我们将介绍model.lua中方法接口的定义,在service.lua的介绍中,我们了解到service.lua对APP暴露的接口定义,本质上是调用了model.lua的方法。在model.lua中,我们对方法的请求体和响应体进行了初始化,并完成了校验工作。如下所示:

lua
function M.ImplNetworkAdapterNetworkAdapterStartRemovingDevice(cb)
    class('ResourceObject')['bmc.kepler.Sytems.ResourceObjectInterface1'].StarMethod = function(obj, ctx, ...)
    local req = ResourceObjectInterface1_intf.types.StarMethodReq.new(...):validate(nil, nil, true)
    local rsp = ResourceObjectInterface1_intf.types.StarMethodRsp.new(cb(obj, ctx, req.unpack())):validate()
    return rsp:unpack(true)
end

至此,我们已经介绍完了model.lua的功能,总而言之,model.lua是MDS的定义转换的lua脚本,方便了开发人员能够快速地获取到MDS对象,并且简化了对象对外暴露流程,让开发人员更加关注业务的开发。

MDS对象的使用

openUBMC在对象创建时,为对象属性访问提供了直观的映射,可以直接通过成员变量的方式访问:

lua
function app:do_something()
    self.resource_obj = app:CreateResourceObject(1, value.ID, fucntion(obj)
        ... -- 业务处理
    end)
    -- 获取接口下的属性,例如ID
    local id = self.resource_obj.ID
    -- 获取私有属性
    local private_prop = self.resource_obj.Property1
end

如果一个MDS对象整合了多个interface,如果interface下存在同名的属性,例如interfaceA下存在属性ID,interfaceB下也存在属性ID,如果不做修改,在自动生成代码时会检查出有同名属性并报错。为此,可以采用别名方式对其中一个属性进行定义,在model.json中按照如下的定义:

json
"bmc.kepler.Systems.ResourceObjectInterface1": {
    "properties":{
        "ID": {}
    }
    
},
"bmc.kepler.Systems.ResourceObjectInterface2": {
    "properties":{
        "ID": {
            "alias": "ResourceID"
        }
    }
}

在完成自动代码生成后,能够通过self.resource_obj.ResourceID访问该别名ID。

跨组件通信

client.lua中定义了对其他组件APPMDS对象的订阅和访问,实现了组件和组件之间的通信。我们可以在业务代码中通过调用client.xxx的方法快速实现对MDS对象的访问。

跨组件rpc方法调用

在service.json中,可以在required中定义对其他资源协作接口的访问,例如要访问bmc.kepler.System.OtherResourceOInterface接口,可以过如下的定义:

json
-- service.json
-- 其他组件APP接口定义
{
    ...
    "required" : [
        {
            "path" : "*",
            "interface" : "bmc.kepler.System.OtherResourceOInterface",
            "properties" : {}
        }
        ...
    ]
}

path值在required中可以缺省,这是因为client调用过程中会自动寻找到具备该interface的所有MDS对象,例如client中生成的如下方法:

lua
-- client.lua
-- 自动生成对应接口下的方法
-- 获取具备该接口的所有MDS对象
function app_name_client:GetOtherResourceOInterfaceObjects()
    return get_non_virtual_interface_objects(self.get_bus(), 'bmc.kepler.Systems.OtherResourceOInterface', true)
end

-- 获取具备该接口的MDS对象的遍历方法
function app_name_client:ForeachOtherResourceOInterfaceObjects(cb)
    return foreach_non_virtual_interface_objects(self:get_bus(), 'bmc.kepler.Systems.OtherResourceOInterface', cb, true)
end

-- 监听接口对象的对象接口新增
function app_name_client:OnOtherResourceOInterfacesAdded(cb)
    self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_interfaces_added(self:get_bus(), '/bmc', cb, 'bmc.kepler.Systems.OtherResourceOInterface')
end

-- 监听接口对象的接口移除
function app_name_client:OnOtherResourceOInterfacesRemoved(cb)
    self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_interfaces_removed(self:get_bus(), '/bmc', cb, 'bmc.kepler.Systems.OtherResourceOInterface')
end

自动生成资源协作接口访问方法后,就能够通过引用client中的方法访问其他组件的对应接口的MDS对象,如下所示:

lua
-- /src/lualib/your_service.lua
local client = require 'app_name.client'
...
function your_service_process()
    local objects = client:GetOtherResourceOInterfaceObjects()
    ...
end

通过Foreach方法的调用,能够使用cb执行对象的删选,如下面代码,通过Foreach筛选该具备该接口的所有对象中满足bond_id的对象。

lua
-- Foreach 使用方法
local function find_bond_object(bond_id)
    local object
    client:ForeachOtherResourceOInterfaceObjects(function(obj)
        log:info('obj.Id = %s, bond_id = %s', obj.Id, bond_id)
        if obj.Id == bond_id then
            object = obj
        end
    end)
    return object
end

同时能通过对对象interface的添加和移除进行监听:

lua
client:OnOtherResourceOInterfacesAdded(function (sender, path, props)
        self.status = props.Status:value()
    end)

sender表示发送方身份,通常可_`缺省设置

path表示interface变化的路径

props:对应接口及接口属性值

OnOtherResourceOInterfacesRemoved方法的使用与OnOtherResourceOInterfacesAdded类似,此处不再赘述。

接口属性订阅

对于MDS对象的订阅,我们会用信号的方式对其进行监听,例如:

json
-- service.json
-- 属性订阅定义
{
    "required" : [
        {
            "path" : "/bmc/kepler/Systems/${SystmeId}",
            "interface" : "bmc.kepler.Systems.OtherResourceObject2",
            "properties" : {
                "State" : ["subscribe"]  -- 属性订阅配置
            }
        }
    ]
}
lua
-- client.lua
function app_name_client:OnOtherResourceObject2PropertiesChanged(cb, path_params)
    local path_namespace = path_params and ('/bmc/kepler/Systems/' .. path_params['SystemId'] ) or '/bmc/kepler/Systems')
    self.signal_slots[#self.signal_slots + 1] = subscribe_signal.on_properties_changed(self:get_bus(), path_namespace, cb, 'bmc.kepler.Systems.OtherResourceObject2', {'State'}) -- 监听信号槽的定义
end
lua
-- /src/lualib/your_service.lua
local client = require 'app_name.client'
...
function your_service_process()
    client:OnOtherResourceObject2PropertiesChanged(function(values, msg_path, interface)
        local state = values.State:value()
        -- 监听信号后的实际业务处理
        if state == 1 then
            ...
        else if state == 0 then 
            ...
        end
    end)
end

values表示监听过程中变化的属性值。

msg_path为消息发送的路径,通常可以缺省,用_代替。

interface为interface路径,通常也可以缺省,用_代替。

IPMI

openUBMC提供了IPMI方法的自动生成模板,并且根据配置的各种命令字完成对本IPMI的搜索。ipmi.lua中定义了IPMI命令的netfn(网络功能号)、cmd(命令之)等等参数,以及IPMI的搜索路径(由req中参数组成),编解码的格式和请求体与响应体的格式。如下所示:

json
-- ipmi.json
{
    "package" : "AppNameIpmiCmds",
    "cmds": {
        "GetMethod" : {
            "netfn" : "0x30",
            "cmd" : "0x93",
            ...
            "req" : [
                {"data" : "ManuId", "baseType": "U32", "len": "3B", "customizedRule": "Manufacturer"},
                {"data": "SubCommand", "baseType": "U8", "len": "1B", "value": "0x00"},
                {"data": "FruId", "baseType": "U8", "len": "1B"},
            ],
            "rsp" : [
                {"data" : "CompletionCode", "baseType" : "U8", "len": "1B"},
                {"data": "result", "baseType": "U8", "len": "1B"}
            ]
        },
        ...
    }
}

在此IPMI配置中,配置了req有三个参数,第一个参数manuId为生产厂家id,由3个字节数组成,由于没有配置默认值,使用该IPMI时可以使用任意值进行发送;第二个参数为SubCommand为IPMI的子命令字,由1个字节数组成,且设置了固定值0x00,也代表了只有该参数输入为0x00时才能够路由到该IPMI命令;第三个参数为FruId为业务所需的传入参数,由1个字节组成,可以以任意值发送。

因此发送的IPMI命令可以通过ipmitool + 对应的命令字进行发送,例如:

shell
ipmitool [options ...] raw 0x30 0x93 0xdb 0x07 0x00 0x00 0x01

其中0x30对应netfn;0x93对应cmd;0xdb 0x07 0x00代表ManuId的三个字节,原始ManuId0x0007db,发送命令时低字节在前,且ManuId也可以为别的值。0x00对应SubCommand,为固定值;0x01FruId,可以为任意值。

在执行自动代码生成后,能够得到如下的定义:

lua
-- ipmi.lua
NetworkAdapterIpmiCmds.GetMethod = {
    name = 'GetMethod',
    prio = types.Priority.Default,
    netfn = 0x30,
    cmd = 0x93,
    ...
    filters = [=[*,*,*,00]=], -- IPMI 搜索路径,*代表任意值
    decode = [=[<<ManuId:3/unit:8, 0x00:1/unit:8, FruId:1/unit:8]=] , -- 解码格式定义
    encode = [=[<<CompletionCode:1/unit:8, result:1/unit:8]=], -- 编码格式定义
    req = msg.GetMethodReq, -- 请求体类型定义
    rsp = msg.GetMethodRsp, -- 响应体类型定义
    ...
}

上述定义中的filters对应了IPMI的搜索路径,对于前3个字节可以由任意值进行匹配,第4个字节必须为0x00,之后的字节由于无限定,因此可以用任意值匹配。

在业务处理代码中,我们需要对该IPMI命令进行处理,并按照encode格式返回响应。注册方式如下所示:

lua
-- app_name_app.lua
local ipmi_struct = require 'app_name.ipmi.ipmi'
local ipmi = require 'ipmi'

...

function app:init_internal()
    self:register_ipmi()
end

function app:register_ipmi()
    self.register_ipmi_cmd(ipmi_struct.GetMethod, funciton(...)
        return self.device_manager:get_method_from_ipmi(...) -- 业务处理流程
    end)
end

-- device_manager.lua 
local msg = require 'app_name..ipmi.ipmi_message'
function c_device_manager:get_method_from_ipmi(req, ctx)
    ... -- 业务处理
    return msg.GetMethodRsp(cc.Success, result)
end

具体IPMI命令调用流程如下所示:

总结

至此我们已经完成了自动生成代码的介绍,自动生成代码是对MDS数据模型的快速实现,同时自动代码生成也是对象对外提供信息的重要步骤。