-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local log = require 'mc.logging'
local bs = require 'mc.bitstring'
local json = require 'cjson'
local skynet = require 'skynet'
local enums = require 'ipmi.enums'
local lu = require 'luaunit'
local channel_type = enums.ChannelType

local ipmi = {}

local GET_UPDATE_ELABEl_DATA_RSP = '<<CompletionCode, UpdateProgress/string>>'
local GET_PICMG_PROPERTIES_RSP = '<<CompletionCode, PicmgId, PicmgExtensionVersion, FruCount, FruDeviceId>>'
local GET_ADDR_INFO_RSP = '<<CompletionCode, Identifier, HardwareAddr, IpmbAddr, Reserved, Id, SiteNumber, SiteType>>'
local GET_FRUID_INFO = '<<CompletionCode, ManufactureId:3/unit:8, EndofList, Data/string>>'
local GET_COMPONENT_UID_RSP = '<<CompletionCode, ComponentInfo/string>>'
local GET_FRU_CONTROL_RSP = '<<CompletionCode, PicmgIdentifier, FruControlMask>>'
local GET_FRU_INVENTORY_RSP = '<<CompletionCode, LSByte, MSByte, DeviceOfAccess>>'
local GET_FRUID_FROM_UID_RSP = '<<CompletionCode, ManufactureId:3/unit:8, Count, Fruid/string>>'
local GET_FRUDATA_RSP = '<<CompletionCode, EndFlag, RespDatas/string>>'
local GET_READ_FRU_DATA_RSP = '<<CompletionCode, Count, Data/string>>'
local GET_DEVICE_PRESENCE_RSP = '<<CompletionCode, ManufactureId:3/unit:8, EndofList, Data/string>>'
local GET_COMPONENT_POSITION_INFO_RSP = '<<CompletionCode, FruId, UniqueId:4/unit:8, SilkText/string>>'
local GET_GENERAL_DEVICE_MANUFACTURE =
'<<CompletionCode, ManufactureId:3/unit:8, EndofList, DeviceManufactureName/string>>'

local function ipmi_test_tool(bus, ipmi_req)
    local ipmi_package = bs.new('<<_,_:2,DestNetFn:6,_:3/unit:8,Cmd,Payload/string>>')
    local req = ipmi_package:pack(ipmi_req)
    local ctx = json.encode({ ChanType = channel_type.CT_ME:value(), Instance = 0, src_addr = 0x20 }) -- 设置Instance = 0
    local ok, rsp = pcall(bus.call, bus, 'bmc.kepler.ipmi_core', '/bmc/kepler/IpmiCore',
        'bmc.kepler.IpmiCore', 'Route', 'a{ss}ayay', require 'mc.context'.new(), req, ctx)
    return ok, rsp
end

local function test_picmg(bus)
    -- 获取PICMG属性
    local get_picmg_properties = {
        DestNetFn = 0x2C,
        Cmd = 0x00,
        Payload = '\x00\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_picmg_properties)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_PICMG_PROPERTIES_RSP):unpack(rsp)
    assert(data.PicmgId == 0x00 and data.PicmgExtensionVersion == 0x22)

    get_picmg_properties = {
        DestNetFn = 0x2C,
        Cmd = 0x00,
        Payload = '\x01\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_picmg_properties)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xCC, 'actual: ' .. rsp:byte(1))

    -- 获取fru的槽位、地址等相关信息
    local get_addr_info = {
        DestNetFn = 0x2C,
        Cmd = 0x01,
        Payload = '\x00\x00\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_addr_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_ADDR_INFO_RSP):unpack(rsp)
    assert(data.Identifier == 0x00 and data.SiteType == 0x28)

    get_addr_info = {
        DestNetFn = 0x2C,
        Cmd = 0x01,
        Payload = '\x00\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_addr_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_ADDR_INFO_RSP):unpack(rsp)
    assert(data.Identifier == 0x00 and data.SiteType == 0x28)

    get_addr_info = {
        DestNetFn = 0x2C,
        Cmd = 0x01,
        Payload = '\x00\x01\x00' -- 默认最后有一位校验位
    }
    _, rsp = ipmi_test_tool(bus, get_addr_info)
    assert(rsp:byte(1) == 0x00)
end

local function test_get_fruid_info(bus)
    -- 通过三元组获取fruid
    local get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x18\x01\x01\x00\x00\x05\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_FRUID_INFO):unpack(rsp)
    assert(data.EndofList == 0x00 and data.Data == '\x03')

    -- 获取指定类型的fruid
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x18\x01\xFF\x00\x00\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_FRUID_INFO):unpack(rsp)
    assert(data.EndofList == 0x00 and (data.Data == '\x03\x04' or data.Data == '\x04\x03'))

    -- 获取指定类型的fruid,偏移为1
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x18\x01\xFF\x00\x01\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_FRUID_INFO):unpack(rsp)
    assert(data.EndofList == 0x00 and (data.Data == '\x04' or data.Data == '\x03'))

    -- 获取指定类型的fruid,偏移为2,类型为24的Component对象共2个
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x18\x01\xFF\x00\x02\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9, 'actual: ' .. rsp:byte(1))

    -- 通过三元组获取fruid
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x05\x01\x01\x00\x00\xFF\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_FRUID_INFO):unpack(rsp)
    assert(data.EndofList == 0x00 and data.Data == '\x42')
end

local function test_get_component_info(bus)
    local rsp_code = bs.new(GET_COMPONENT_UID_RSP)
    local get_component_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x29\x04\x02\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_component_info)
    local data = rsp_code:unpack(rsp)
    assert(ret, 'call ipmi msg fail')
    assert(data.CompletionCode, 0x00)
    assert(data.ComponentInfo, 1)

    get_component_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x29\x03\x02\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_info)
    assert(ret, 'call ipmi msg fail')
    data = rsp_code:unpack(rsp)
    assert(data.CompletionCode, 0x00)
    assert(data.ComponentInfo, '2')

    local get_component_uid = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x29\x02\x08\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_uid)
    assert(ret, 'call ipmi msg fail')
    data = rsp_code:unpack(rsp)
    lu.assertEquals(data.ComponentInfo, '123')

    get_component_uid = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x29\xff\x08\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_uid)
    assert(ret, 'call ipmi msg fail')
    data = rsp_code:unpack(rsp)
    assert(data.CompletionCode, 0xC9)
end

local function test_dft_custom(bus)
    -- dft定制化命令
    local ipmi_dft_custom = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x21\x04\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, ipmi_dft_custom)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x00, 'actual: ' .. rsp:byte(1))

    -- 获取定制化标志位(装备调用)
    local ipmi_get_dft_custom = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x59\x04\x00\x00\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, ipmi_get_dft_custom)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x00, 'actual: ' .. rsp:byte(1))
end

local function test_get_fruid_info_next(bus)
    -- 获取指定类型的fruid,类型0x0A不在管理范围内,
    local get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x0A\x01\xFF\x00\x02\x05\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)

    -- 获取指定类型的fruid,读取长度为1,类型为24的Component对象共2个
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\x18\x01\xFF\x00\x00\x01\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_FRUID_INFO):unpack(rsp)
    assert(data.EndofList == 0x01 and (data.Data == '\x04' or data.Data == '\x03'))

    -- 获取所有Fru对象的fruid
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\xFF\x01\x01\x00\x00\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_FRUID_INFO):unpack(rsp)
    local fru_id = string.byte(data.Data)
    assert(data.EndofList == 0)
    assert(fru_id == 67 or fru_id == 65 or fru_id == 0 or fru_id == 1, 'assert: ' .. fru_id)

    -- 获取所有Fru对象的fruid
    get_fruid_info = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x3B\x00\xFF\xFF\xFF\xFF\x00\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_FRUID_INFO):unpack(rsp)
    fru_id = string.byte(data.Data)
    assert(data.EndofList == 0)
    assert(fru_id == 67 or fru_id == 65 or fru_id == 0 or fru_id == 1, 'assert: ' .. fru_id)
end

-- 写电子标签
local function ipmi_write_frudata(bus, cmds)
    local write_elabe_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x04' .. cmds .. '\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, write_elabe_data)
    assert(ret, 'cmd(' .. string.format("%q", cmds) .. ') call ipmi msg fail')
    assert(rsp:byte(1) == 0x00, 'actual: ' .. rsp:byte(1))
end

-- 读电子标签
local function ipmi_read_frudata(bus, cmds)
    local read_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x05' .. cmds .. '\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, read_elabel_data)
    assert(ret, 'cmd(' .. string.format("%q", cmds) .. ') call ipmi msg fail')
    return bs.new(GET_FRUDATA_RSP):unpack(rsp)
end

-- 更新电子标签
local function ipmi_update_frudata(bus, cmds)
    local update_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x06' .. cmds .. '\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, update_elabel_data)
    assert(ret, 'cmd(' .. string.format("%q", cmds) .. ') call ipmi msg fail')
    return bs.new(GET_UPDATE_ELABEl_DATA_RSP):unpack(rsp)
end

-- 清除电子标签
local function ipmi_clear_frudata(bus, cmds)
    local clear_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x03' .. cmds .. '\xAA\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, clear_elabel_data)
    assert(ret, 'cmd(' .. string.format("%q", cmds) .. ') call ipmi msg fail')
    assert(rsp:byte(1) == 0x00, 'actual: ' .. rsp:byte(1))
end

local function test_frudata(bus)
    -- 写电子标签
    ipmi_write_frudata(bus, '\x01\x01\x02\x00\x03\x32\x32\x32')

    local data = ipmi_update_frudata(bus, '\x01\xaa')
    assert(data.UpdateProgress == '\x00')

    data = ipmi_update_frudata(bus, '\x01\x00')
    assert(data.UpdateProgress == '\x01\x00')

    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x40\x02\x03\x00\x03')
    assert(data.RespDatas == 'bb', 'assert ' .. data.RespDatas)
end

local function test_frudata_default_value(bus)
    -- 读电子标签,校验版本信息
    local data = ipmi_read_frudata(bus, '\x00\x02\x05\x00\xff')
    assert(data.RespDatas == '5.00.00.01', 'assert ' .. data.RespDatas)

    -- 读电子标签,校验生产商
    data = ipmi_read_frudata(bus, '\x00\x02\x01\x00\xff')
    assert(data.RespDatas == 'Huawei', 'assert ' .. data.RespDatas)
end

local function test_frudata_file_id_rw(bus)
    -- 写电子标签
    ipmi_write_frudata(bus, '\x01\x02\x05\x00\x03\x32\x32\x32')
    ipmi_write_frudata(bus, '\x01\x03\x06\x00\x03\x33\x33\x33')

    -- 读电子标签,校验版本信息
    local data = ipmi_read_frudata(bus, '\x01\x02\x05\x00\xff')
    assert(data.RespDatas == '222', 'assert ' .. data.RespDatas)

    data = ipmi_read_frudata(bus, '\x01\x03\x06\x00\xff')
    assert(data.RespDatas == '333', 'assert ' .. data.RespDatas)

    -- 写电子标签
    ipmi_write_frudata(bus, '\x01\x02\x05\x00\x00')
    ipmi_write_frudata(bus, '\x01\x03\x06\x00\x00')

    -- 读电子标签,校验版本信息
    data = ipmi_read_frudata(bus, '\x01\x02\x05\x00\xff')
    assert(data.RespDatas == '', 'assert ' .. data.RespDatas)

    data = ipmi_read_frudata(bus, '\x01\x03\x06\x00\xff')
    assert(data.RespDatas == '', 'assert ' .. data.RespDatas)

    -- 清除电子标签，再次校验版本信息
    ipmi_clear_frudata(bus, '\x01')
    data = ipmi_read_frudata(bus, '\x01\x02\x05\x00\xff')
    assert(data.RespDatas == '5.00.00.01', 'assert ' .. data.RespDatas)

    data = ipmi_read_frudata(bus, '\x01\x03\x06\x00\xff')
    assert(data.RespDatas == '5.00.00.01', 'assert ' .. data.RespDatas)
end

local function test_frudata_chassis_type(bus)
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x01\x00\x00\x01\x00')

    -- 读电子标签,验证可以读出0x00
    local data = ipmi_read_frudata(bus, '\x00\x01\x00\x00\xff')
    assert(data.RespDatas == '\x00', 'assert ' .. data.RespDatas)

    -- 清除电子标签
    ipmi_clear_frudata(bus, '\x00')

    -- 读电子标签,没有0x00
    data = ipmi_read_frudata(bus, '\x00\x01\x00\x00\xff')
    assert(data.RespDatas == '', 'assert ' .. data.RespDatas)

    -- 读不存在的fruid的chassis type
    local read_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x05\x13\x01\x00\x00\xff\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, read_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xD3, 'actual: ' .. rsp:byte(1))

    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x01\x00\x00\x01\x24')
 
    -- 读电子标签,验证可以读出0x24
    data = ipmi_read_frudata(bus, '\x00\x01\x00\x00\xff')
    assert(data.RespDatas == '\x24', 'assert ' .. data.RespDatas)

    --写超出范围的chassis type值
    local write_elabe_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x04\x00\x01\x00\x00\x01\x25\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, write_elabe_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9, 'actual: ' .. rsp:byte(1))
end

local function test_frudata_mfgdate(bus)
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x02\x00\x00\x03\xde\x00\xf0')

    -- 读电子标签
    local data = ipmi_read_frudata(bus, '\x00\x02\x00\x00\xff')
    assert(data.RespDatas == '\xde\x00\xf0', 'assert ' .. data.RespDatas)

    -- 读电子标签,长度为0
    data = ipmi_read_frudata(bus, '\x00\x02\x00\x00\x00')
    assert(data.RespDatas == '\xde\x00\xf0', 'assert ' .. data.RespDatas)
end

local function test_frudata_write_ex_with_err(bus)
    -- 写电子标签,扩展域有两个':'
    local _, ret1 = pcall(function(...)
        ipmi_write_frudata(bus, '\x00\x03\x07\x00\x05\x31\x3a\x3a\x32\x33')
    end)

    assert(string.find(ret1, 'actual: 201'))

    -- 写电子标签,扩展域有两个'='
    local _, ret2 = pcall(function(...)
        ipmi_write_frudata(bus, '\x00\x03\x07\x00\x05\x31\x3d\x3d\x32\x33')
    end)

    assert(string.find(ret2, 'actual: 201'))

    -- 写电子标签,扩展域同时有':'和'='
    local _, ret3 = pcall(function(...)
        ipmi_write_frudata(bus, '\x00\x03\x07\x00\x05\x31\x3a\x3d\x32\x33')
    end)

    assert(string.find(ret3, 'actual: 201'))
end

local function test_frudata_delete_ex(bus)
    -- 写电子标签扩展域：A=123
    ipmi_write_frudata(bus, '\x00\x03\x07\x00\x05\x41\x3d\x31\x32\x33')

    -- 读电子标签
    local data = ipmi_read_frudata(bus, '\x00\x03\x07\x00\xff')
    assert(data.RespDatas == 'A=123', 'assert ' .. data.RespDatas)

    -- 更新电子标签扩展域：A=456
    ipmi_write_frudata(bus, '\x00\x03\x07\x00\x05\x41\x3d\x34\x35\x36')

    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x03\x07\x00\xff')
    assert(data.RespDatas == 'A=456', 'assert ' .. data.RespDatas)

    -- 删除电子标签扩展域
    ipmi_write_frudata(bus, '\x00\x03\x07\x00\x02\x41\x3d')

    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x03\x07\x00\xff')
    assert(data.RespDatas == '', 'assert ' .. data.RespDatas)
end

local function test_bmc_card(bus)
    -- 写电子标签
    ipmi_write_frudata(bus, '\x50\x01\x02\x00\x03\x32\x32\x32')

    -- 更新电子标签
    local data = ipmi_update_frudata(bus, '\x50\xaa')
    assert(data.UpdateProgress == '\x00')

    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x50\x01\x02\x00\x03')
    assert(data.RespDatas == '222', 'actual: ' .. data.RespDatas)

    -- 清除电子标签后正确读取更新状态
    ipmi_clear_frudata(bus, '\x50')
    data = ipmi_update_frudata(bus, '\x50\x00')
    assert(data.UpdateProgress == '\x01\x00')

    -- 写电子标签
    ipmi_write_frudata(bus, '\x50\x02\x06\x00\x03\x33\x3D\x33')

    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x50\x02\x06\x00\x03')
    assert(data.RespDatas == '3=3', 'actual: ' .. data.RespDatas)
end

local function test_clean_frudata(bus)
    -- 清除电子标签
    local clear_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x03\x01\xAA\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, clear_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x00, 'actual: ' .. rsp:byte(1))

    clear_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x03\x40\xAA\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, clear_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x80)

    -- 标志位不满足范围
    clear_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x03\x40\x11\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, clear_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC1)

    -- 更新标志位不满足范围
    clear_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x06\x00\x11\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, clear_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC1, 'actual: ' .. rsp:byte(1))

    -- fruid不满足范围
    clear_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x06\x11\xAA\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, clear_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xD3)
end

local function test_non_standard_frudata(bus)
    -- 读电子标签
    local read_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x05\x40\x01\x08\x00\x03\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, read_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9, 'actual: ' .. rsp:byte(1))

    -- 读日期
    read_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x05\x40\x02\x00\x00\x03\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, read_elabel_data)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_FRUDATA_RSP):unpack(rsp)
    assert(data.RespDatas == '\x20\x2d\xdf', 'actual: ' .. data.RespDatas)

    -- 写电子标签，不支持
    -- 读日期
    local write_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x04\x40\x02\x00\x00\x01\x32\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, write_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x80)
end

local function test_record_id_and_fru_control(bus)
    -- IPMI命令进行FRU控制
    local fru_control_capabilities = {
        DestNetFn = 0x2C,
        Cmd = 0x1E,
        Payload = '\x00\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, fru_control_capabilities)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_FRU_CONTROL_RSP):unpack(rsp)
    -- 校验PicmgIdentifier等于fruid 1，FruControlMask = 4
    assert(data.PicmgIdentifier == 1 and data.FruControlMask == 4)

    fru_control_capabilities = {
        DestNetFn = 0x2C,
        Cmd = 0x1E,
        Payload = '\x00\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, fru_control_capabilities)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)
end

local function test_fru_inventory(bus)
    -- 获取FRU库存区域信息
    local get_fru_inventory = {
        DestNetFn = 0x0A,
        Cmd = 0x10,
        Payload = '\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_fru_inventory)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_FRU_INVENTORY_RSP):unpack(rsp)
    assert(data.MSByte == 0x08 and data.LSByte == 0x00)

    get_fru_inventory = {
        DestNetFn = 0x0A,
        Cmd = 0x10,
        Payload = '\x05\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fru_inventory)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)

    -- 标准写电子标签ipmi命令
    local write_frudata = {
        DestNetFn = 0x0A,
        Cmd = 0x12,
        Payload = '\x00\x00\x00\x00\x00\x00\x00\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, write_frudata)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x00)

    -- 验证标准写电子标签ipmi命令偏移非0
    write_frudata = {
        DestNetFn = 0x0A,
        Cmd = 0x12,
        Payload = '\x00\x02\x00\x00\x00\x00\x00\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, write_frudata)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0x00)
end

local function test_get_fruid_by_uid(bus)
    -- 通过uid获取指定Fru的Fruid信息
    local get_fruid_by_uid = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x6d\x00\x03\x31\x32\x33\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_fruid_by_uid)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_FRUID_FROM_UID_RSP):unpack(rsp)
    assert(data.Count == 0x01 and data.Fruid == '\x02')

    -- 通过uid获取指定Fru的Fruid信息
    get_fruid_by_uid = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x6d\x00\x03\x61\x62\x63\x64\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_by_uid)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_FRUID_FROM_UID_RSP):unpack(rsp)
    assert(data.Count == 0x01 and data.Fruid == '\x03')

    -- count大于uid数据长度
    get_fruid_by_uid = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x6d\x00\x05\x31\x32\x33\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_fruid_by_uid)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC7)
end

local function test_read_fru_data(bus)
    -- 睡眠8秒等待电子标签初始化完成
    skynet.sleep(800)
    -- 读电子标签
    local read_fru_data = {
        DestNetFn = 0x0A,
        Cmd = 0x11,
        Payload = '\x00\x00\x00\xFF\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, read_fru_data)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_READ_FRU_DATA_RSP):unpack(rsp)

    -- 校验版本信息是否为0x01
    local chassis_offset = data.Data:byte(3) * 8 + 1
    local board_offset = data.Data:byte(4) * 8 + 1
    local product_offset = data.Data:byte(5) * 8 + 1

    assert(data.Data:byte(chassis_offset) == 0x01, 'actual: ' .. data.Data:byte(chassis_offset))
    assert(data.Data:byte(board_offset) == 0x01, 'actual: ' .. data.Data:byte(chassis_offset))
    assert(data.Data:byte(product_offset) == 0x01, 'actual: ' .. data.Data:byte(chassis_offset))
end

local function test_get_device_presence(bus)
    -- 获取FRU库存区域信息
    local get_device_presence = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x02\x00\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_presence)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.Data == '\x01')
end

local function test_get_device_health(bus)
    -- 获取FRU库存health信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x03\x00\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.Data == '\x09')
end

local function test_get_device_boardid(bus)
    -- 获取FRU库存boardid信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x06\x00\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.Data == '\xDB\x00')
end

local function test_get_device_location(bus)
    -- 获取FRU库存location信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x08\x00\x05\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.EndofList == 1)
    assert(data.Data == '\x48\x44\x44\x20\x50')
end

local function test_get_device_function(bus)
    -- 获取FRU库存function信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x09\x00\x07\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.EndofList == 1)
    assert(data.Data == '\x73\x74\x6f\x72\x61\x67\x65')
end

local function test_get_device_name(bus)
    -- 获取FRU库存name信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x0A\x01\x02\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.EndofList == 1)
    assert(data.Data == 'MC')
end

local function test_get_device_groupid(bus)
    -- 获取FRU库存groupid信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x0B\x00\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.Data == '\x01')
end

local function test_get_device_fruid(bus)
    -- 获取FRU库存fruid信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x10\x00\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.Data == '\x03')
end

local function test_get_device_uniqueid(bus)
    -- 获取FRU库存uniqueid信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x01\x23\x01\x02\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_DEVICE_PRESENCE_RSP):unpack(rsp)
    assert(data.Data == 'bc')
end

local function test_get_device_manufacture_name(bus)
    -- 获取FRU库存uniqueid信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x02\x63\x00\xFF\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_GENERAL_DEVICE_MANUFACTURE):unpack(rsp)
    assert(data.EndofList == 0)
    assert(data.DeviceManufactureName == 'Huawei', 'assert' .. data.DeviceManufactureName)


    -- 获取FRU库存uniqueid信息
    local get_device_health = {
        DestNetFn = 0x30,
        Cmd = 0x93,
        Payload = '\xDB\x07\x00\x27\x18\x02\x63\x01\x03\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_device_health)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_GENERAL_DEVICE_MANUFACTURE):unpack(rsp)
    assert(data.EndofList == 1)
    assert(data.DeviceManufactureName == 'uaw', 'assert' .. data.DeviceManufactureName)
end

local function test_compute_power(bus)
    local compute_power = {
        DestNetFn = 0x2C,
        Cmd = 0x10,
        Payload = '\x00\x00\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, compute_power)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC1)
end

local function test_component_position_info(bus)
    -- 根据fruid和查询丝印和groupid
    local get_component_position_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x3F\x01\x01\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_component_position_info)
    assert(ret, 'call ipmi msg fail')
    local data = bs.new(GET_COMPONENT_POSITION_INFO_RSP):unpack(rsp)
    assert(data.FruId == 0x01 and data.SilkText == 'J23')
    local groupid = string.char(data.UniqueId & 0xff)

    -- 根据groupid和查询丝印和fruid
    get_component_position_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x3F\x02' .. groupid .. '\x00\x00\x00\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_position_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_COMPONENT_POSITION_INFO_RSP):unpack(rsp)
    assert(data.FruId == 0x01 and data.SilkText == 'J23')

    -- 根据丝印和查询fruid和groupid
    get_component_position_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x3F\x03\x4A\x32\x33\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_position_info)
    assert(ret, 'call ipmi msg fail')
    data = bs.new(GET_COMPONENT_POSITION_INFO_RSP):unpack(rsp)
    assert(data.FruId == 0x01 and data.SilkText == 'J23')
end

local function test_component_position_outof_range(bus)
    -- 查询类型不在1-3范围内
    local get_component_position_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x3F\x04\x04A\x32\x33\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, get_component_position_info)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)

    -- 查询fruid不存在
    get_component_position_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x3F\x01\x55\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_position_info)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)

    -- 查询fruid长度不是1
    get_component_position_info = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x3F\x01\x01\0x01\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, get_component_position_info)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC7)
end

local function test_product_data(bus)
    -- 部件编码
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x03\x02\x00\x01\x31')

    skynet.sleep(100)
    -- 读电子标签
    local data = ipmi_read_frudata(bus, '\x00\x03\x02\x00\x01')
    assert(data.RespDatas == '1', 'actual: ' .. data.RespDatas)

    -- 序列号
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x03\x04\x00\x01\x32')

    skynet.sleep(100)
    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x03\x04\x00\x01')
    assert(data.RespDatas == '2', 'actual: ' .. data.RespDatas)

    -- ProductVersion
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x03\x03\x00\x01\x32')

    skynet.sleep(100)
    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x03\x03\x00\x01')
    assert(data.RespDatas == '2', 'actual: ' .. data.RespDatas)
end

local function test_chasis_data(bus)
    -- ChassisSerialNumber
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x01\x02\x00\x01\x31')

    skynet.sleep(100)
    -- 读电子标签
    local data = ipmi_read_frudata(bus, '\x00\x01\x02\x00\x01')
    assert(data.RespDatas == '1', 'actual: ' .. data.RespDatas)

    -- ChassisCustomInfo
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x01\x03\x00\x03\x32\x3D\x32')

    skynet.sleep(100)
    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x01\x03\x00\x03')
    assert(data.RespDatas == '2=2', 'actual: ' .. data.RespDatas)

    -- 写电子标签,长度不一致
    local write_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x04\x00\x02\x00\x00\x02\x32\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, write_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)

    -- 写电子标签,特殊字符
    write_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x04\x00\x02\x00\x00\x02\x10\x00' -- 默认最后有一位校验位
    }
    ret, rsp = ipmi_test_tool(bus, write_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)
end

local function test_system_data(bus)
    -- System_Manufacture
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x06\x00\x00\x01\x41')

    skynet.sleep(100)
    -- 读电子标签
    local data = ipmi_read_frudata(bus, '\x00\x06\x00\x00\x01')
    assert(data.RespDatas == 'A', 'actual: ' .. data.RespDatas)

    --System_ProductName
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x06\x01\x00\x01\x42')

    skynet.sleep(100)
    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x06\x01\x00\x01')
    assert(data.RespDatas == 'B', 'actual: ' .. data.RespDatas)

    --System_SerialNumber
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x06\x03\x00\x01\x35')

    skynet.sleep(100)
    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x06\x03\x00\x01')
    assert(data.RespDatas == '5', 'actual: ' .. data.RespDatas)

    --System_Vision
    -- 写电子标签
    ipmi_write_frudata(bus, '\x00\x06\x02\x00\x01\x25')

    skynet.sleep(100)
    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x00\x06\x02\x00\x01')
    assert(data.RespDatas:byte() == 0x25, 'actual: ' .. data.RespDatas)
end

local function test_frudata_write_mcu(bus)
    -- BoardProductName
    -- 写MCU电子标签,仅验证功能
    ipmi_write_frudata(bus, '\x43\x02\x02\x00\x01\x31')

    -- 读电子标签
    local data = ipmi_read_frudata(bus, '\x43\x02\x02\x00\x01')
    assert(data.RespDatas == '1', 'actual: ' .. data.RespDatas)

    -- 清除MCU电子标签,仅验证功能
    ipmi_clear_frudata(bus, '\x43')

    -- 读电子标签
    data = ipmi_read_frudata(bus, '\x43\x02\x02\x00\x01')
    assert(data.RespDatas == '', 'actual: ' .. data.RespDatas)

    -- 写电子标签，不支持的属性
    -- 写BoardPartNumber
    local write_elabel_data = {
        DestNetFn = 0x30,
        Cmd = 0x90,
        Payload = '\x04\x43\x02\x04\x00\x01\x32\x00' -- 默认最后有一位校验位
    }
    local ret, rsp = ipmi_test_tool(bus, write_elabel_data)
    assert(ret, 'call ipmi msg fail')
    assert(rsp:byte(1) == 0xC9)
end

function ipmi:test_ipmi(bus)
    log:info('============== test ipmi start ==============')
    test_frudata(bus)
    test_frudata_default_value(bus)
    test_frudata_file_id_rw(bus)
    test_frudata_mfgdate(bus)
    test_frudata_chassis_type(bus)
    test_frudata_write_ex_with_err(bus)
    test_frudata_delete_ex(bus)
    test_clean_frudata(bus)
    test_non_standard_frudata(bus)
    test_bmc_card(bus)
    test_picmg(bus)
    test_get_fruid_info(bus)
    test_get_fruid_info_next(bus)
    test_get_component_info(bus)
    test_dft_custom(bus)
    test_record_id_and_fru_control(bus)
    test_fru_inventory(bus)
    test_get_fruid_by_uid(bus)
    test_compute_power(bus)
    test_read_fru_data(bus)
    test_get_device_presence(bus)
    test_component_position_info(bus)
    test_component_position_outof_range(bus)
    test_get_device_health(bus)
    test_get_device_boardid(bus)
    test_get_device_location(bus)
    test_get_device_function(bus)
    test_get_device_name(bus)
    test_get_device_groupid(bus)
    test_get_device_fruid(bus)
    test_get_device_uniqueid(bus)
    test_get_device_manufacture_name(bus)
    test_product_data(bus)
    test_chasis_data(bus)
    test_system_data(bus)
    test_frudata_write_mcu(bus)
    log:info('============== test ipmi end ==============')
end

return ipmi
