-- 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.

-- Description: IPMI PEF(Platform Event Filter) management.
local ipmi_req = require 'sensor.ipmi.ipmi'
local ipmi_rsp = require 'sensor.ipmi.ipmi_message'
local ipmi_enums = require 'ipmi.enums'
local cc = require 'pef.pef_const'
local common = require 'pef.pef_common'
local err = require 'sensor_error'
local utils = require 'sensor_utils'
local log = require 'mc.logging'
local oper = require 'sensor_operation'
local ct = ipmi_enums.ChannelType

local pef_management = {}
pef_management.__index = pef_management

function pef_management.new(db)
    return setmetatable({
        db = db,
        pef_filter = {},
        alert_filter = {},
        alert_string = {}
    }, pef_management)
end

local register_pef_handler = {
    [cc.CLZ_PEF_FILTER] = function (self, o, id)
        self.pef_filter[#self.pef_filter+1] = o
        utils.push_regist_dump('PEF filter [%s] is registered.', id)
    end,
    [cc.CLZ_PEF_ALERT_FILTER] = function (self, o, id)
        self.alert_filter[#self.alert_filter+1] = o
        utils.push_regist_dump('PEF alert filter [%s] is registered.', id)
    end,
    [cc.CLZ_PEF_ALERT_STRING] = function (self, o, id)
        self.alert_string[#self.alert_string+1] = o
        utils.push_regist_dump('PEF alert string [%s] is registered.', id)
    end,
    ['IpmiPefConfig'] = function(self, o, id)
        self.config = o
        utils.push_regist_dump('PEF config is registered.')
    end,
    ['IpmiPefControl'] = function(self, o, id)
        local data = self.db:query_pef_control()
        if not data then
            -- 如果当前持久化无记录，则先根据分发对象生成记录
            self.db:insert_pef_control(o)
        end
        self.control = o
        utils.push_regist_dump('PEF control config is registered.')
    end
}

function pef_management:register(clz, obj)
    local handler = register_pef_handler[clz]
    if not handler then
        log:notice('PEF class [%s] has no handler.', clz)
        return
    end
    handler(self, obj, obj:get_object_name())
end

function pef_management:initialize()
    -- 当前初始化不需要额外动作，直接返回
    return
end

function pef_management:register_ipmi(cb)
    cb(ipmi_req.GetPEFCapabilities, function(...) return self:ipmi_get_pef_capabilities(...) end)
    cb(ipmi_req.GetPEFParameters, function(...) return self:ipmi_get_pef_parameters(...) end)
    cb(ipmi_req.SetPEFParameters, function(...) return self:ipmi_set_pef_parameters(...) end)
    cb(ipmi_req.GetPEFLastEventId, function(...) return self:ipmi_get_pef_last_event(...) end)
    cb(ipmi_req.SetPEFPostponeTimer, function(...) return self:ipmi_set_pef_postpone(...) end)
    cb(ipmi_req.SetPEFLastEventId, function(...) return self:ipmi_set_pef_last_event(...) end)
end

function pef_management:ipmi_set_pef_last_event(req, ctx)
    if self.config.Enabled == 0 then
        log:error('set pef last event failed [pef not enabled]')
        oper.log(ctx, oper.PEF_LAST_EVENT, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_INVALID_CMD))
    end
    if req.BMC ~= 0 then
        self.control.LastEventBMC = req.RecordId
    else
        self.control.LastEventSMS = req.RecordId
    end

    local rsp = ipmi_rsp.SetPEFLastEventIdRsp.new()
    rsp.CompletionCode = 0x00
    oper.log(ctx, oper.PEF_LAST_EVENT, oper.SUCCESS)
    return rsp
end

function pef_management:ipmi_set_pef_postpone(req, ctx)
    if self.config.Enabled == 0 then
        log:error('set pef postpone failed [pef not enabled]')
        oper.log(ctx, oper.PEF_POSTPONE, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_INVALID_CMD))
    end
    if req.Timeout ~= 0xFF then
        self.control.PostponeTimeout = req.Timeout
    end

    local rsp = ipmi_rsp.SetPEFPostponeTimerRsp.new()
    rsp.CompletionCode = 0x00
    rsp.Timeout = self.control.PostponeTimeout
    oper.log(ctx, oper.PEF_POSTPONE, oper.SUCCESS)
    return rsp
end

function pef_management:ipmi_get_pef_capabilities()
    local rsp = ipmi_rsp.GetPEFCapabilitiesRsp.new()
    rsp.CompletionCode = 0x00
    rsp.PEFVersion = 0x51
    rsp.Alert = 0
    rsp.PowerDown = 1
    rsp.Reset = 1
    rsp.PowerCycle = 1
    rsp.OEMAction = 0
    rsp.DiagInterrupt = 0
    rsp.Reserved = 0
    rsp.OEMFilterSupport = 0
    rsp.FilterNumber = #self.pef_filter
    return rsp
end

function pef_management:get_pef_progress_status()
    local datas = string.char(self.control.InProgress)
    return err.SUCCESS, datas
end

function pef_management:get_pef_control()
    local data = 0
    data = (data | (self.config.AlertStartupDelayDisabled << 3))
    data = (data | (self.config.StartupDelayDisabled << 2))
    data = (data | (self.config.ActionEnabled << 1))
    data = (data | self.config.Enabled)
    local datas = string.char(data)
    return err.SUCCESS, datas
end

function pef_management:get_pef_action_control(req, ctx)
    local flag = req.SetSelector & 0x80
    local data = 0
    data = (data | (self.config.DiagInterruptEnabled << 5))
    data = (data | (self.config.OEMEnabled << 4))
    data = (data | (self.config.PowerCycleEnabled << 3))
    data = (data | (self.config.ResetEnabled << 2))
    data = (data | (self.config.PowerOffEnabled << 1))
    if ctx.ChanType == ct.CT_SMM:value() or ctx.ChanType == ct.CT_MMC:value() then
        if flag == 1 and self.config.Enabled == 0 then
            log:error('can not response the pef control on mm board.')
            return err.ERR_CANNOT_SUPPORT
        end
        if flag == 0 then
            log:warn('set 0 as default trap enabled flag.')
        else
            data = (data | self.config.AlertEnabled)
        end
    else
        data = (data | self.config.Enabled)
    end
    local datas = string.char(data)
    return err.SUCCESS, datas
end

function pef_management:get_startup_delay()
    local datas = string.char(self.config.StartupDelay)
    return err.SUCCESS, datas
end

function pef_management:get_alert_startup_delay()
    local datas = string.char(self.config.AlertStartupDelay)
    return err.SUCCESS, datas
end

function pef_management:get_filter_num()
    local datas = string.char(#self.pef_filter)
    return err.SUCCESS, datas
end

function pef_management:get_filter_table(req)
    if req.SetSelector == 0 or req.SetSelector > #self.pef_filter then
        return err.ERR_OUT_OF_RANGE
    end
    local f = self.pef_filter[req.SetSelector]
    if not f then
        log:error('pef filter [%d] is invalid.', req.SetSelector)
        return err.ERR_CANNOT_RESPONSE
    end

    local filter = {}
    filter[#filter+1] = string.char(req.SetSelector)
    filter[#filter+1] = utils.toc(f['Configuration'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Action'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['PolicyNumber'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Severity'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['GeneratorId1'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['GeneratorId2'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['SensorType'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['SensorNumber'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['EventTrigger'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['OffsetMask'], utils.BITS_16)
    filter[#filter+1] = utils.toc(f['Event1AndMask'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event1Compare1'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event1Compare2'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event2AndMask'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event2Compare1'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event2Compare2'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event3AndMask'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event3Compare1'], utils.BITS_8)
    filter[#filter+1] = utils.toc(f['Event3Compare2'], utils.BITS_8)
    local datas = table.concat(filter, '')
    return err.SUCCESS, datas
end

function pef_management:get_filter_data(req)
    local ret, data = self:get_filter_table(req)
    if ret ~= err.SUCCESS then
        return ret
    end

    -- data 只取 table 的前两个字符
    local datas = data:sub(1, 2)
    return err.SUCCESS, datas
end

function pef_management:get_policy_num()
    local datas = string.char(#self.alert_filter)
    return err.SUCCESS, datas
end

function pef_management:get_policy_table(req)
    if req.SetSelector == 0 or req.SetSelector > #self.alert_filter then
        return err.ERR_OUT_OF_RANGE
    end
    local o = self.alert_filter[req.SetSelector]
    if not o then
        log:error('[get policy table] alert filter [%d] is invalid', req.SetSelector)
        return err.ERR_CANNOT_RESPONSE
    end

    local filter = {}
    filter[#filter + 1] = string.char(req.SetSelector)
    filter[#filter + 1] = utils.toc(o['PolicyNum'], utils.BITS_8)
    filter[#filter + 1] = utils.toc(o['Destination'], utils.BITS_8)
    filter[#filter + 1] = utils.toc(o['AlertString'], utils.BITS_8)
    local datas = table.concat(filter, '')
    return err.SUCCESS, datas
end

function pef_management:get_system_guid()
    -- 取当前系统GUID的前17位
    local datas = self.config.SystemGUID:sub(1, 17)
    return err.SUCCESS, datas
end

function pef_management:get_alert_string_num()
    local datas = string.char(#self.alert_string - 1)
    return err.SUCCESS, datas
end

function pef_management:get_alert_string_keys(req)
    local index = 1 + req.SetSelector & 0x7F
    if index > #self.alert_string then
        return err.ERR_OUT_OF_RANGE
    end
    local o = self.alert_string[index]
    if not o then
        log:error('[get alert string keys] alert string [%d] is invalid', index)
        return err.ERR_CANNOT_RESPONSE
    end

    local filter = {}
    filter[#filter+1] = string.char(req.SetSelector)
    filter[#filter+1] = utils.toc(o['FilterNumber'], utils.BITS_8)
    filter[#filter+1] = utils.toc(o['StringSet'], utils.BITS_8)
    filter[#filter+1] = utils.toc(o['AlertString'], utils.BITS_8)
    -- 只取前3个字节
    local datas = table.concat(filter, ''):sub(1, 3)
    return err.SUCCESS, datas
end

function pef_management:get_alert_string(req)
    if req.BlockSelector == 0 or req.BlockSelector > cc.ALERT_STRING_BLOCK_CNT then
        return err.ERR_OUT_OF_RANGE
    end
    local index = 1 + req.SetSelector & 0x7F
    if index > #self.alert_string then
        return err.ERR_OUT_OF_RANGE
    end
    local o = self.alert_string[index]
    if not o then
        log:error('[get alert string] alert string [%d] is invalid', index)
        return err.ERR_CANNOT_RESPONSE
    end

    local filter = {}
    filter[#filter+1] = string.char(req.SetSelector)
    filter[#filter+1] = string.char(req.BlockSelector)
    filter[#filter+1] = string.char(o['FilterNumber'])
    filter[#filter+1] = string.char(o['StringSet'])
    filter[#filter+1] = o['AlertString']

    -- 只取前18个字节
    local datas = table.concat(filter, ''):sub(1, 18)
    return err.SUCCESS, datas
end

function pef_management:get_pef_getting_handler(name)
    local handler = {}
    function handler.get_pef_progress_status(req, ctx)
        return self:get_pef_progress_status(req, ctx)
    end
    function handler.get_pef_control(req, ctx)
        return self:get_pef_control(req, ctx)
    end
    function handler.get_pef_action_control(req, ctx)
        return self:get_pef_action_control(req, ctx)
    end
    function handler.get_startup_delay(req, ctx)
        return self:get_startup_delay(req, ctx)
    end
    function handler.get_alert_startup_delay(req, ctx)
        return self:get_alert_startup_delay(req, ctx)
    end
    function handler.get_filter_num(req, ctx)
        return self:get_filter_num(req, ctx)
    end
    function handler.get_filter_table(req, ctx)
        return self:get_filter_table(req, ctx)
    end
    function handler.get_filter_data(req, ctx)
        return self:get_filter_data(req, ctx)
    end
    function handler.get_policy_num(req, ctx)
        return self:get_policy_num(req, ctx)
    end
    function handler.get_policy_table(req, ctx)
        return self:get_policy_table(req, ctx)
    end
    function handler.get_system_guid(req, ctx)
        return self:get_system_guid(req, ctx)
    end
    function handler.get_alert_string_num(req, ctx)
        return self:get_alert_string_num(req, ctx)
    end
    function handler.get_alert_string_keys(req, ctx)
        return self:get_alert_string_keys(req, ctx)
    end
    function handler.get_alert_string(req, ctx)
        return self:get_alert_string(req, ctx)
    end
    return handler[name]
end

function pef_management:ipmi_get_pef_parameters(req, ctx)
    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.Revision = cc.PEF_CONFIG_REVISION
    if req.Revision == 1 then
        return rsp
    end

    local handler_name = {
        "get_pef_progress_status",
        "get_pef_control",
        "get_pef_action_control",
        "get_startup_delay",
        "get_alert_startup_delay",
        "get_filter_num",
        "get_filter_table",
        "get_filter_data",
        "get_policy_num",
        "get_policy_table",
        "get_system_guid",
        "get_alert_string_num",
        "get_alert_string_keys",
        "get_alert_string"
    }

    local handler = self:get_pef_getting_handler(handler_name[1 + req.Selector])
    local ret, datas
    if handler then
        ret, datas = handler(req, ctx)
    else
        ret = err.ERR_CANNOT_SUPPORT
    end
    if ret ~= err.SUCCESS then
        error(err.ipmi_error_map(ret))
    end
    if not datas then
        log:error('pef parameter selector is invalid')
        error(err.ipmi_error_map(err.ERR_CANNOT_RESPONSE))
    end
    rsp.Datas = datas
    return rsp
end

function pef_management:set_pef_progress_status(s, datas, ctx)
    local flag = datas[2] & 0x3F
    if flag == 0 then
        self.control.InProgress = 0
    elseif flag == 1 then
        if self.control.InProgress == 1 then
            return err.ERR_PEF_IN_PROGRESS
        end
        self.control.InProgress = 1
    else
        return err.ERR_OUT_OF_RANGE
    end
    oper.log(ctx, oper.PEF_PROGRESS, oper.SUCCESS, s, datas[2])
    return err.SUCCESS
end

function pef_management:set_pef_control(s, datas, ctx)
    self.config.Enabled = datas[2] & 0x01
    self.config.ActionEnabled = (datas[2] & 0x02) >> 1
    self.config.StartupDelayDisabled = 0
    self.config.AlertStartupDelayDisabled = 0
    oper.log(ctx, oper.PEF_CONTROL, oper.SUCCESS, s, datas[2])
    return err.SUCCESS
end

function pef_management:set_pef_action_control(s, datas, ctx)
    if ctx.ChanType == ct.CT_SMM:value() or ctx.ChanType == ct.CT_MMC:value() then
        if self.config.Enabled == 0 then
            if datas[2] & 0x80 ~= 0 then
                return err.ERR_CANNOT_SUPPORT
            end
            local trap_enable = datas[2] & 0x01
            if trap_enable ~= 0 then
                self.config.PowerCycleEnabled = (datas[2] & 0x08) >> 3
                self.config.ResetEnabled = (datas[2] & 0x04) >> 2
                self.config.PowerOffEnabled = (datas[2] & 0x02) >> 1
                self.config.AlertEnabled = (datas[2] & 0x01)
                self.config.DiagInterruptEnabled = 0
                self.config.OEMEnabled = 0
            end
            log:info('PEF cannot be supported to set trap enabled status.')
        else
            if datas[2] & 0x80 == 0 then
                log:info('PEF cannot be supported to set trap enabled status.')
            else
                self.config.PowerCycleEnabled = (datas[2] & 0x08) >> 3
                self.config.ResetEnabled = (datas[2] & 0x04) >> 2
                self.config.PowerOffEnabled = (datas[2] & 0x02) >> 1
                self.config.AlertEnabled = (datas[2] & 0x01)
                self.config.DiagInterruptEnabled = 0
                self.config.OEMEnabled = 0
            end
        end
    else
        if datas[2] & 0x30 ~= 0 then
            return err.ERR_INVALID_FIELD
        end
        self.config.PowerCycleEnabled = (datas[2] & 0x08) >> 3
        self.config.ResetEnabled = (datas[2] & 0x04) >> 2
        self.config.PowerOffEnabled = (datas[2] & 0x02) >> 1
        self.config.AlertEnabled = (datas[2] & 0x01)
        self.config.DiagInterruptEnabled = 0
        self.config.OEMEnabled = 0
    end
    oper.log(ctx, oper.PEF_ACTION, oper.SUCCESS, s, datas[2])
    return err.SUCCESS
end

function pef_management:set_startup_delay(s, datas, ctx)
    self.config.StartupDelay = datas[2]
    oper.log(ctx, oper.PEF_STARTUP, oper.SUCCESS, s, datas[2])
    return err.SUCCESS
end

function pef_management:set_alert_startup_delay(s, datas, ctx)
    self.config.AlertStartupDelay = datas[2]
    oper.log(ctx, oper.PEF_AL_STARTUP, oper.SUCCESS, s, datas[2])
    return err.SUCCESS
end

function pef_management:set_filter_table(s, datas, ctx)
    local selector = datas[2]
    if selector == 0 or selector > #self.pef_filter then
        return err.ERR_OUT_OF_RANGE
    end
    if not self.pef_filter[selector] then
        log:error('[set filter table] pef filter [%d] is invalid', selector)
        return err.ERR_CANNOT_RESPONSE
    end

    local configuration = self.pef_filter[selector]['Configuration']
    local conf = common.configuration:unpack(string.pack('I1', configuration))
    if not conf then
        log:error('pef filter table configuration is invalid.')
        return err.ERR_CANNOT_RESPONSE
    end
    if conf.type == 1 or conf.type == 3 then
        return err.ERR_INVALID_FIELD
    elseif conf.type == 2 then
        conf.enabled = (datas[3] & 0x80) == 0x80 and 1 or 0
        self.pef_filter[selector]['Configuration'] = string.byte(common.configuration:pack(conf))
    else
        self.pef_filter[selector]['Configuration'] = datas[3]
        self.pef_filter[selector]['Action'] = datas[4]
        self.pef_filter[selector]['PolicyNumber'] = datas[5]
        self.pef_filter[selector]['Severity'] = datas[6]
        self.pef_filter[selector]['GeneratorId1'] = datas[7]
        self.pef_filter[selector]['GeneratorId2'] = datas[8]
        self.pef_filter[selector]['SensorType'] = datas[9]
        self.pef_filter[selector]['SensorNumber'] = datas[10]
        self.pef_filter[selector]['EventTrigger'] = datas[11]
        self.pef_filter[selector]['OffsetMask'] = (datas[12] << 8) | datas[13]
        self.pef_filter[selector]['Event1AndMask'] = datas[14]
        self.pef_filter[selector]['Event1Compare1'] = datas[15]
        self.pef_filter[selector]['Event1Compare2'] = datas[16]
        self.pef_filter[selector]['Event2AndMask'] = datas[17]
        self.pef_filter[selector]['Event2Compare1'] = datas[18]
        self.pef_filter[selector]['Event2Compare2'] = datas[19]
        self.pef_filter[selector]['Event3AndMask'] = datas[20]
        self.pef_filter[selector]['Event3Compare1'] = datas[21]
        self.pef_filter[selector]['Event3Compare2'] = datas[22]
    end

    local c = {}
    c[#c + 1] = string.format('%02X', s)
    for i = 3, #datas do c[#c + 1] = string.format('%02X', datas[i]) end
    oper.log(ctx, oper.PEF_AL_TABLE, oper.SUCCESS, table.concat(c, '-'))
    return err.SUCCESS
end

function pef_management:set_filter_data(s, datas, ctx)
    local selector = datas[2]
    if selector == 0 or selector > #self.pef_filter then
        return err.ERR_OUT_OF_RANGE
    end
    if not self.pef_filter[selector] then
        log:error('[set filter data] pef filter [%d] is invalid', selector)
        return err.ERR_CANNOT_RESPONSE
    end

    local configuration = self.pef_filter[selector]['Configuration']
    local conf = common.configuration:unpack(string.pack('I1', configuration))
    if not conf then
        log:error('pef filter table configuration is invalid.')
        return err.ERR_CANNOT_RESPONSE
    end
    if conf.type == 1 or conf.type == 3 then
        return err.ERR_INVALID_FIELD
    elseif conf.type == 2 then
        conf.enabled = (datas[3] & 0x80) == 0x80 and 1 or 0
    else
        conf = common.configuration:unpack(string.pack('I1', datas[3]))
    end
    self.pef_filter[selector]['Configuration'] = string.byte(common.configuration:pack(conf))
    oper.log(ctx, oper.PEF_AL_DATA, oper.SUCCESS, s, datas[2], datas[3])
    return err.SUCCESS
end

function pef_management:set_policy_table(s, datas, ctx)
    local selector = datas[2]
    if selector == 0 or selector > #self.alert_filter then
        return err.ERR_OUT_OF_RANGE
    end
    if not self.alert_filter[selector] then
        log:error('[set policy table] alert table [%d] is invalid', selector)
        return err.ERR_CANNOT_RESPONSE
    end
    self.alert_filter[selector]['PolicyNum'] = datas[3]
    self.alert_filter[selector]['Destination'] = datas[4]
    self.alert_filter[selector]['AlertString'] = datas[5]
    local c = string.format('%02X-%02X-%02X-%02X-%02X', s, datas[2], datas[3], datas[4], datas[5])
    oper.log(ctx, oper.PEF_PLCY_TABLE, oper.SUCCESS, c)
    return err.SUCCESS
end

function pef_management:set_system_guid(s, datas, ctx)
    if #datas - 2 ~= 17 then  --GUID长度为17位
        log:error('length of datas is invalid')
        return err.ERR_INVALID_LENGTH
    end
    self.config.SystemGUIDEnabled = datas[2] & 0x01
    self.config.SystemGUID = table.concat(datas, '', 3)
    local c = {}
    c[#c+1] = string.format('%02X', s)
    for i = 3, #datas do c[#c+1] = string.format('%02X', datas[i]) end
    oper.log(ctx, oper.PEF_GUID, oper.SUCCESS, table.concat(c, '-'))
    return err.SUCCESS
end

function pef_management:set_alert_string_keys(s, datas, ctx)
    local selector = datas[2] & 0x7F
    if selector > #self.alert_string or datas[3] == 0 or datas[4] == 0 then
        return err.ERR_OUT_OF_RANGE
    end
    if not self.alert_string[selector] then
        log:error('[set alert string keys] alert string [%d] is invalid', selector)
        return err.ERR_CANNOT_RESPONSE
    end

    self.alert_string[selector]['FilterNumber'] = datas[3] & 0x7F
    self.alert_string[selector]['StringSet'] = datas[4] & 0x7F
    local c = string.format('%02X-%02X-%02X-%02X', s, datas[2], datas[3], datas[4])
    oper.log(ctx, oper.PEF_AL_KEYS, oper.SUCCESS, c)
    return err.SUCCESS
end

function pef_management:set_alert_string(s, datas, ctx)
    local selector = datas[2] & 0x7F
    local block_selector = datas[3]
    local str = table.concat(datas, '', 4)
    if selector > #self.alert_string or block_selector == 0 then
        return err.ERR_OUT_OF_RANGE
    elseif block_selector >= cc.ALERT_STRING_BLOCK_CNT then
        return err.ERR_OUT_OF_RANGE
    elseif #str > cc.ALERT_STRING_BLOCK_SIZE then
        return err.ERR_OUT_OF_RANGE
    else
        if not self.alert_string[selector] then
            log:error('[set alert string keys] alert string [%d] is invalid', selector)
            return err.ERR_CANNOT_RESPONSE
        end

        self.alert_string[selector]['AlertString'] = str
        local c = {}
        c[#c+1] = string.format('%02X', s)
        for i = 2, #datas do c[#c+1] = string.format('%02X', datas[i]) end
        oper.log(ctx, oper.PEF_AL_STRING, oper.SUCCESS, table.concat(c, '-'))
        return err.SUCCESS
    end
end

function pef_management:get_pef_setting_handler(name)
    local handler = {}
    function handler.set_pef_progress_status(s, d, c)
        return self:set_pef_progress_status(s, d, c)
    end
    function handler.set_pef_control(s, d, c)
        return self:set_pef_control(s, d, c)
    end
    function handler.set_pef_action_control(s, d, c)
        return self:set_pef_action_control(s, d, c)
    end
    function handler.set_startup_delay(s, d, c)
        return self:set_startup_delay(s, d, c)
    end
    function handler.set_alert_startup_delay(s, d, c)
        return self:set_alert_startup_delay(s, d, c)
    end
    function handler.set_event_filter_num(s, d, c)
        return err.ERR_PEF_READONLY
    end
    function handler.set_filter_table(s, d, c)
        return self:set_filter_table(s, d, c)
    end
    function handler.set_filter_data(s, d, c)
        return self:set_filter_data(s, d, c)
    end
    function handler.set_alert_policy_num(s, d, c)
        return err.ERR_PEF_READONLY
    end
    function handler.set_policy_table(s, d, c)
        return self:set_policy_table(s, d, c)
    end
    function handler.set_system_guid(s, d, c)
        return self:set_system_guid(s, d, c)
    end
    function handler.set_alert_string_num(s, d, c)
        return err.ERR_PEF_READONLY
    end
    function handler.set_alert_string_keys(s, d, c)
        return self:set_alert_string_keys(s, d, c)
    end
    function handler.set_alert_string(s, d, c)
        return self:set_alert_string(s, d, c)
    end
    return handler[name]
end

function pef_management:ipmi_set_pef_parameters(req, ctx)
    if self.config.Enabled == 0 then
        log:error('set pef parameters failed [pef not enabled]')
        oper.log(ctx, oper.PEF_CONF, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_INVALID_CMD))
    end
    -- 解析IPMI传过来的流数据
    local datas = {}
    for i = 1, #req.Datas do
        datas[#datas + 1] = req.Datas:byte(i)
    end

    local handler_name = {
        "set_pef_progress_status",
        "set_pef_control",
        "set_pef_action_control",
        "set_startup_delay",
        "set_alert_startup_delay",
        "set_event_filter_num",
        "set_filter_table",
        "set_filter_data",
        "set_alert_policy_num",
        "set_policy_table",
        "set_system_guid",
        "set_alert_string_num",
        "set_alert_string_keys",
        "set_alert_string"
    }

    local selector = datas[1] & 0x7F
    local handler = self:get_pef_setting_handler(handler_name[1 + selector])
    local ret = handler and handler(selector, datas, ctx) or err.ERR_PEF_CANNOT_SUPPORT
    if ret ~= err.SUCCESS then
        oper.log(ctx, oper.PEF_CONF, oper.FAILED)
        error(err.ipmi_error_map(ret))
    end

    local rsp = {}
    rsp.CompletionCode = 0x00
    return rsp
end

function pef_management:ipmi_get_pef_last_event()
    if self.config.Enabled == 0 then
        log:error('get pef last event failed [pef not enabled]')
        error(err.ipmi_error_map(err.ERR_INVALID_CMD))
    end
    local sel = self.db:query_sel_info()
    local cfg = self.db:query_pef_control()
    local rsp = ipmi_rsp.GetPEFLastEventIdRsp.new()
    rsp.CompletionCode = 0x00
    rsp.Timestamp = sel.AddTimestamp
    rsp.SELId = cfg.NextEvent == 0 and 0xFFFF or cfg.NextEvent
    rsp.SMSId = cfg.LastEventSMS
    rsp.BMCId = cfg.LastEventBMC
    return rsp
end

function pef_management:on_import(ctx, data)
    local import = data.PefEnabled
    if not import or not import.Import or type(import.Value) ~= 'boolean' then
        oper.log(ctx, oper.PEF_ENABLE, oper.FAILED)
        log:error('pef config [PefEnabled] import not supported.')
        return common.RET_ERROR
    end

    local enable = import.Value and 1 or 0
    if enable ~= self.config.Enabled then
        self.config.Enabled = enable
        oper.log(ctx, oper.PEF_ENABLE, oper.SUCCESS, import.Value)
    end
    log:info('pef config [PefEnabled] import to %s successfully.', import.Value)
    return common.RET_OK
end

function pef_management:on_export(ctx)
    log:info('pef config [PefEnabled] export successfully.')
    return {PefEnabled = self.config.Enabled == 1}
end

return pef_management