-- 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 m_enums = require 'types.enums'
local ipmi_msg = require 'fructrl.ipmi.ipmi'
local ipmi = require 'ipmi'
local ipmi_func = require 'ipmi_functions'
local context = require 'mc.context'
local ctx_new = context.get_context_or_default()
local m_error_custom = require 'messages.custom'
local c_fructrl_obj_mgnt = require 'fructrl_obj_mgnt'
local c_multihost = require 'multi_host'

local DEFALUTACPISTATE = ''
local NOCHANGE = 'nochange'
local SystemPowerState = {
    [0x00] = 'S0/G0',
    [0x01] = 'S1',
    [0x02] = 'S2',
    [0x03] = 'S3',
    [0x04] = 'S4',
    [0x05] = 'S5/G2',
    [0x06] = 'S4/S5',
    [0x07] = 'G3',
    [0x08] = 'sleeping',
    [0x09] = 'G1 sleeping',
    [0x0a] = 'override',
    [0x20] = 'Legacy On',
    [0x21] = 'Legacy Off',
    [0x2a] = 'unknown',
    [0x7f] = NOCHANGE,
    ['S0/G0'] = 0x00,
    ['S1'] = 0x01,
    ['S2'] = 0x02,
    ['S3'] = 0x03,
    ['S4'] = 0x04,
    ['S5/G2'] = 0x05,
    ['S4/S5'] = 0x06,
    ['G3'] = 0x07,
    ['sleeping'] = 0x08,
    ['G1 sleeping'] = 0x09,
    ['override'] = 0x0a,
    ['Legacy On'] = 0x20,
    ['Legacy Off'] = 0x21,
    ['unknown'] = 0x2a,
    [DEFALUTACPISTATE] = DEFALUTACPISTATE
}

local DevicePowerState = {
    [0x00] = 'D0',
    [0x01] = 'D1',
    [0x02] = 'D2',
    [0x03] = 'D3',
    [0x2a] = 'unknown',
    [0x7f] = NOCHANGE,
    ['D0'] = 0x00,
    ['D1'] = 0x01,
    ['D2'] = 0x02,
    ['D3'] = 0x03,
    ['unknown'] = 0x2a,
    [DEFALUTACPISTATE] = DEFALUTACPISTATE
}

local ipmi_payload = {}

function ipmi_payload:register_ipmi(func)
    self.deactivate_lockbit = 0
    self.activate_lockbit = 0

    self.fructrl_obj_mgnt = c_fructrl_obj_mgnt:get_instance()
    self.multihost = c_multihost:get_instance()
    self.button_evt = self.fructrl_obj_mgnt.button_evt

    func(ipmi_msg.SetPowerCmd, function(...) return self:pp_ipmi_cmd_chassis_ctrl(...) end)
    func(ipmi_msg.SetACLostStatus, function(...) return self:ipmi_set_aclost_status(...) end)
    func(ipmi_msg.GetACLostStatus, function(...) return self:ipmi_get_aclost_status(...) end)
    func(ipmi_msg.SetButtonShieldState, function(...) return self.button_evt:ipmi_cmd_set_pwrbutton_shield_state(...) end)
    func(ipmi_msg.GetButtonShieldState, function(...) return self.button_evt:ipmi_cmd_get_pwrbutton_shield_state(...) end)
    func(ipmi_msg.SetRestorePolicy, function(...) return self:pp_ipmi_cmd_set_pwr_restore_policy(...) end)
    func(ipmi_msg.SetCycleInterval, function(...) return self:pp_ipmi_cmd_set_pwr_cycle_interval(...) end)
    func(ipmi_msg.OEMGetRestartCause, function(...) return self:pp_ipmi_oem_get_sys_restart_cause(...) end)
    func(ipmi_msg.CMDGetRestartCause, function(...) return self:pp_ipmi_cmd_get_sys_restart_cause(...) end)
    func(ipmi_msg.SetPowerLevel, function(...) return self:pp_set_power_level(...) end)
    func(ipmi_msg.GetPowerLevel, function(...) return self:pp_get_power_level(...) end)
    func(ipmi_msg.CmdFruCtrl, function(...) return self:pp_ipmi_cmd_fru_ctrl(...) end)
    func(ipmi_msg.SetSysLockDownState, function(...) return self:pp_set_syslockdown_state(...) end)
    func(ipmi_msg.GetSysLockDownState, function(...) return self:pp_get_syslockdown_state(...) end)
    func(ipmi_msg.SetFruActivation, function(...) return self:pp_ipmi_cmd_set_fru_activation(...) end)
    func(ipmi_msg.SetFruActivationPolicy, function(...) return self:pp_ipmi_cmd_set_fru_activation_policy(...) end)
    func(ipmi_msg.GetFruActivationPolicy, function(...) return self:pp_ipmi_cmd_get_fru_activation_policy(...) end)
    func(ipmi_msg.SetACPIPowerState, function(...) return self:pp_ipmi_cmd_set_acpi_powerstate(...) end)
    func(ipmi_msg.GetACPIPowerState, function(...) return self:pp_ipmi_cmd_get_acpi_powerstate(...) end)
end

function ipmi_payload:pp_ipmi_cmd_chassis_ctrl(req, ctx)
    local ctrl_cmd = req.Cmd
    local err, ctrl_msg, ok
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    self.powerapi = self.fructrl_obj_mgnt.host_obj[sys_id].powerapi
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''

    log:notice("Ipmi call powerctrl cmd (%d).", ctrl_cmd)
    if ctrl_cmd == 0x00 then
        -- ForceOff
        ctrl_msg = 'power down'
        ok, err = pcall(function() self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.ForceOff),
            tostring(m_enums.RestartCause.ChassisControlCommand)) end)
    elseif ctrl_cmd == 0x01 then
        -- On
        self.fructrl:set_poweron_from_ipmi_flag(true) -- 记录ipmi发送上电
        ctrl_msg = 'power on'
        ok, err = pcall(function() self.powerapi:system_reset(ctx_new, m_enums.PowerCtrlType.On,
            m_enums.RestartCause.ChassisControlCommand) end)
    elseif ctrl_cmd == 0x02 then
        -- ForcePowerCycle
        ctrl_msg = 'force power off the service system, and then power it on'
        ok, err = pcall(function() self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.ForcePowerCycle),
            tostring(m_enums.RestartCause.ChassisControlCommand)) end)
    elseif ctrl_cmd == 0x03 then
        -- ForceRestart
        ctrl_msg = 'reset'
        ok, err = pcall(function() self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.ForceRestart),
            tostring(m_enums.RestartCause.ChassisControlCommand)) end)
    elseif ctrl_cmd == 0x04 then
        ctrl_msg = 'diagnostic interrupt'
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet chassis control to (%s) failed', print_sys, ctrl_msg)
        return { CompletionCode = 0xd5 }
    elseif ctrl_cmd == 0x05 then
        -- GracefulShutdown
        ctrl_msg = 'soft-shutdown'
        ok, err = pcall(function() self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.GracefulShutdown),
            tostring(m_enums.RestartCause.ChassisControlCommand)) end)
    elseif ctrl_cmd == 0x06 then
        -- PowerCycle
        ctrl_msg = 'soft shutdown the service system, and then power it on'
        ok, err = pcall(function() self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.PowerCycle),
            tostring(m_enums.RestartCause.ChassisControlCommand)) end)
    else
        ipmi.ipmi_operation_log(ctx, 'fructrl','%sSet chassis control failed, unknown control action', print_sys)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    -- 更新复位通道
    self.fructrl:set_restart_channel(ctx.chan_num)
    ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet chassis control to (%s) %s', print_sys, ctrl_msg,
        ok and 'successfully' or 'failed')
    if not ok then
        log:error('Ipmi call PowerCtrl rpc has error (%s)', err)
        error(m_error_custom.IPMICommandCannotExecute())
    end
    return {CompletionCode = 0x00}
end

function ipmi_payload:ipmi_set_aclost_status(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''
    if req.Status > 1 then
        log:error("Parameter is out of range.")
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power state before AC lost failed', print_sys)
        error(m_error_custom.IPMIOutOfRange())
    end
    local ac_status = req.Status == 1
    self.fructrl:set_PwrStateBeforeACLost(ac_status)
    ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power state before AC lost to (%s) successfully',
        print_sys, ac_status and 'power on' or 'power off')

    local rsp = ipmi_msg.SetACLostStatus.rsp.new()
    rsp.CompletionCode = 0x00
    return rsp
end

function ipmi_payload:ipmi_get_aclost_status(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local sta = 0
    local ac_status = self.fructrl:get_PwrStateBeforeACLost()
    if ac_status then
        sta = 1
    end

    local rsp = ipmi_msg.GetACLostStatus.rsp.new()
    rsp.CompletionCode = 0x00
    rsp.Status = sta
    rsp.ManufactureId = req.ManufactureId
    return rsp
end

local PowerOnStrategy_To_Code = {
    ["AlwaysPowerOff"] = 1,
    ["LastState"] = 2,
    ["AlwaysPowerOn"] = 4,
}

-- ipmitool通用命令调此方法 chassis policy
function ipmi_payload:pp_ipmi_cmd_set_pwr_restore_policy(req, ctx)
    local POWER_RESTORE_POLICY_ALWAYS_OFF = 0 -- 0:保持下电
    local POWER_RESTORE_POLICY_PREVIOUS = 1   -- 1：保持上一次状态
    local POWER_RESTORE_POLICY_ALWAYS_ON = 2  -- 2：保持上电
    local POWER_RESTORE_POLICY_NO_CHANGED = 3 -- 3：查询通电开机策略
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''

    local restore_policy = req.RestorePolicy
    local rsp = ipmi_msg.SetRestorePolicy.rsp.new()
    if restore_policy == POWER_RESTORE_POLICY_ALWAYS_OFF then
        self.fructrl:set_PowerOnStrategy(tostring(m_enums.PowerRestorePolicy.AlwaysPowerOff))
    elseif restore_policy == POWER_RESTORE_POLICY_PREVIOUS then
        self.fructrl:set_PowerOnStrategy(tostring(m_enums.PowerRestorePolicy.LastState))
    elseif restore_policy == POWER_RESTORE_POLICY_ALWAYS_ON then
        self.fructrl:set_PowerOnStrategy(tostring(m_enums.PowerRestorePolicy.AlwaysPowerOn))
    elseif restore_policy == POWER_RESTORE_POLICY_NO_CHANGED then
        log:debug("power restore policy is not changed")
        rsp.CompletionCode = 0x00
        rsp.RestorePolicy = PowerOnStrategy_To_Code[self.fructrl:get_PowerOnStrategy()]
        return rsp
    else
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power restore policy failed', print_sys)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    rsp.CompletionCode = 0x00
    rsp.RestorePolicy = 1 << restore_policy
    return rsp
end

function ipmi_payload:pp_ipmi_cmd_set_pwr_cycle_interval(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''
    local data = req.Seconds
    self.fructrl:set_PwrCycleDelaySeconds(data)
    ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power cycle interval to (%u) seconds successfully', print_sys, data)

    local rsp = ipmi_msg.SetCycleInterval.rsp.new()
    rsp.CompletionCode = 0x00
    return rsp
end

function ipmi_payload:pp_ipmi_oem_get_sys_restart_cause(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = c_fructrl_obj_mgnt:get_instance().host_obj[sys_id].fructrl
    local restart_cause = self.fructrl:get_CurrentRestartCause()
    if not restart_cause or restart_cause == '' then
        log:error('The restart cause is null.')
        error(m_error_custom.IPMICommandCannotExecute())
    end

    local rsp = ipmi_msg.OEMGetRestartCause.rsp.new()
    rsp.CompletionCode = 0x00
    rsp.RestartCause = m_enums.RestartCauseId[restart_cause] - 1
    rsp.SrcChan = self.fructrl:get_restart_channel()
    rsp.ManufactureId = req.ManufactureId
    return rsp
end

function ipmi_payload:pp_ipmi_cmd_get_sys_restart_cause(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = c_fructrl_obj_mgnt:get_instance().host_obj[sys_id].fructrl
    local restart_cause = self.fructrl:get_CurrentRestartCause()
    if not restart_cause or restart_cause == '' then
        log:error('The restart cause is null.')
        error(m_error_custom.IPMICommandCannotExecute())
    end

    local rsp = ipmi_msg.CMDGetRestartCause.rsp.new()
    rsp.CompletionCode = 0x00
    rsp.RestartCause = m_enums.RestartCauseId[restart_cause] - 1
    rsp.SrcChan = self.fructrl:get_restart_channel()
    return rsp
end

function ipmi_payload:pp_set_power_level(req, ctx)
    local POWER_LEVEL_POWER_OFF = 0
    local POWER_LEVEL_DONT_CHANGE = 0xff
    local power_level = req.PowerLevel
    local level_option = req.LevelOption
    local err = 0
    local sys_id = ctx.HostId or 1
    self.powerapi = self.fructrl_obj_mgnt.host_obj[sys_id].powerapi
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''

    if level_option > 1 then
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power level failed', print_sys)
        log:error("level_option:%d(Max:%d) invalid!", level_option, 1)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    -- 根据POWER_LEVEL执行不同动作
    if power_level == POWER_LEVEL_POWER_OFF then
        -- 控制系统强制下电
        err = self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.ForceOff),
            tostring(m_enums.RestartCause.ChassisControlCommand))
    elseif power_level ~= POWER_LEVEL_DONT_CHANGE then
        -- 应用当前功率，控制FRU上电
        err = self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.On),
            tostring(m_enums.RestartCause.ChassisControlCommand))
    end
    -- 正常返回0
    if err ~= 0 then
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power level failed', print_sys)
        log:error("pp_action_according_to_powerlevel fail(PowerLevel:%u), (err:%d)!", power_level, err)
        return {CompletionCode = 0xff}
    end

    ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet power level to (RAW:%02xh-%02xh-%02xh) successfully',
        print_sys, 0x00, power_level, level_option) -- FruId为0x00，只有底板处理消息

    local rsp = ipmi_msg.SetPowerLevel.rsp.new()
    rsp.CompletionCode = 0x00
    return rsp
end

function ipmi_payload:pp_get_power_level(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local rsp = ipmi_msg.GetPowerLevel.rsp.new()
    local power_type = req.PowerType

    if power_type == 0 then
        -- POWER_STEADY_STATE_POWERDRAW_LEVELS
        rsp.PowerState = m_enums.PGSignalState[self.fructrl:get_PowerState()] == m_enums.PowerStatus.ON and 1 or 0
    elseif power_type == 1 then
        -- POWER_DESIRED_STEADY_STATE_DRAW_LEVELS
        rsp.PowerState = 1
    else
        log:error("power_type:%u invalid.", power_type)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    rsp.CompletionCode = 0x00
    rsp.DelayToStable = 0x1e
    rsp.PowerMultiplier = 0x1e
    rsp.PowerDraw = "\xb8\xb8"
    return rsp
end

local function ipmi_chassis_fructrl(ctx, fructrl_obj_mgmt, option)
    if not fructrl_obj_mgmt.chassis_fructrl_ins then
        log:error("no chassis fructrl obj to power ctrl")
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    local ret
    local ctrl_desc = ''
    -- force powercycle
    if option == 0x02 then
        ret = fructrl_obj_mgmt.chassis_fructrl_ins:power_ctrl(ctx_new,
            m_enums.PowerCtrlType.ForcePowerCycle, 'ChassisControlCommand')
        ctrl_desc = 'forced power off, and then power it on'
    -- graceful powercycle
    elseif option == 0x06 then
        ret = fructrl_obj_mgmt.chassis_fructrl_ins:power_ctrl(ctx_new,
            m_enums.PowerCtrlType.PowerCycle, 'ChassisControlCommand')
        ctrl_desc = 'normal power off, and then power it on '
    else
        log:error("unsupported option %s", option)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    if ret ~= m_enums.RetValue.OK then
        ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set chassis to ' .. ctrl_desc .. ' failed')
        return {CompletionCode = 0xd5}
    end

    ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set chassis to ' .. ctrl_desc .. ' successfully')
    local rsp = ipmi_msg.CmdFruCtrl.rsp.new()
    rsp.CompletionCode = 0x00
    return rsp
end

local function Forbid_GlobalDomainOperation(fruid)
    if fruid == 4 then
        log:error("[fructrl] fruId (%u) is undefined.", fruid)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end
end

function ipmi_payload:pp_ipmi_cmd_fru_ctrl(req, ctx)
    local fru_dev_id = req.FruId
    local option = req.Option
    log:debug("ipmi cmd fructrl fruid:%s option:%s", req.FruId, option)
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    self.powerapi = self.fructrl_obj_mgnt.host_obj[sys_id].powerapi
    -- FruId为0xff，代表对chassis域做上下电，在multihost场景使用
    if fru_dev_id == 0xff then
        return ipmi_chassis_fructrl(ctx, self.fructrl_obj_mgnt, option)
    end

    if fru_dev_id ~= 0 and fru_dev_id ~= 4 then
        log:error("[fructrl] FruId (%u) is undefined.", fru_dev_id)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    if option == 0 then
        -- Cold Reset / ForceRestart
        return ipmi_func:ipmi_cold_reset(fru_dev_id, ctx, self.fructrl, self.powerapi)
    elseif option == 1 then
        -- Warm Reset
        Forbid_GlobalDomainOperation(fru_dev_id)
        return ipmi_func:ipmi_warm_reset(fru_dev_id, ctx, self.fructrl)
    elseif option == 2 then
        -- Force Reboot / Forced PowerCycle
        Forbid_GlobalDomainOperation(fru_dev_id)
        return ipmi_func:ipmi_force_reboot(fru_dev_id, ctx, self.fructrl, self.powerapi)
    elseif option == 3 then
        -- Issue Diagnostic Interrupt
        Forbid_GlobalDomainOperation(fru_dev_id)
        return ipmi_func:ipmi_issue_diagnostic_interrupt(fru_dev_id, ctx)
    elseif option == 6 then
        -- Graceful Reboot / PowerCycle
        return ipmi_func:ipmi_graceful_reboot(fru_dev_id, ctx, self.fructrl, self.powerapi)
    elseif option == 5 then
        -- Graceful ShutDown
        return ipmi_func:ipmi_graceful_shutdown(fru_dev_id, ctx, self.powerapi)
    else
        log:error("[fructrl] fru control (%u) is undefined.", option)
        error(m_error_custom.IPMIInvalidFieldRequest())
    end
end

-- 对应V2函数UserIpmiSetSysLockDownState
function ipmi_payload:pp_set_syslockdown_state(req, ctx)
    local sys_id = ctx.HostId or 1
    self.syslock = self.fructrl_obj_mgnt.host_obj[sys_id].syslock
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''

    if self.syslock:get_system_lockdown_support() == 'Unsupported' then
        log:error("Set system lockdown state failed: unsupport ipmi cmd.")
        error(m_error_custom.IPMICommandCannotExecute())
    end

    if req.State ~= 0 and req.State ~= 1 then
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet system lockdown failed', print_sys)
        error(m_error_custom.IPMIInvalidCommand())
    end

    local syslockdown_state = 'Unlocked'
    if req.State == 1 then
        syslockdown_state = 'Locked'
    end

    self.syslock:set_system_lockdown_status(syslockdown_state)

    local rsp = ipmi_msg.SetSysLockDownState.rsp.new()
    rsp.CompletionCode = 0x00
    rsp.ManufactureId = req.ManufactureId
    return rsp
end

-- 对应V2函数UserIpmiGetSysLockDownState
function ipmi_payload:pp_get_syslockdown_state(req, ctx)
    local sys_id = ctx.HostId or 1
    self.syslock = self.fructrl_obj_mgnt.host_obj[sys_id].syslock
    local rsp = ipmi_msg.GetSysLockDownState.rsp.new()
    rsp.CompletionCode = 0x00
    rsp.ManufactureId = req.ManufactureId

    if self.syslock:get_system_lockdown_support() == 'Unsupported' then
        rsp.State = 2 -- 2表示不支持系统锁定
    else
        rsp.State = self.syslock:get_system_lockdown_status() == 'Locked' and 1 or 0
    end

    return rsp
end

function ipmi_payload:pp_ipmi_cmd_set_fru_activation(req, ctx)
    local sys_id = ctx.HostId or 1
    self.fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''
    local rsp = ipmi_msg.SetFruActivation.rsp.new()
    local msg
    rsp.CompletionCode = 0x00
    if req.FruId > 0 then
        log:error("Parameter is out of range.")
        error(m_error_custom.IPMIOutOfRange())
    end
    if req.Cmd ~= 0 and req.Cmd ~= 1 then
        error(m_error_custom.IPMIInvalidCommand())
    end

    if req.Cmd == 1 then
        msg = 'active'
    else
        msg = 'deactive'
        self.fructrl:poweroff()
    end
    ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet FRU0 activation to (%s) successfully', print_sys, msg)

    return rsp
end

function ipmi_payload:pp_ipmi_cmd_set_fru_activation_policy(req, ctx)
    local rsp = ipmi_msg.SetFruActivationPolicy.rsp.new()
    local sys_id = ctx.HostId or 1
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''
    rsp.CompletionCode = 0x00
    if req.FruId > 0 then
        log:error("Parameter is out of range.")
        error(m_error_custom.IPMIOutOfRange())
    end

    ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet FRU0 activation policy to (mask:0x%02x|bits:0x%02x) successfully',
        print_sys, req.Mask, req.Bit)

    return rsp
end

function ipmi_payload:pp_ipmi_cmd_get_fru_activation_policy(req, ctx)
    local rsp = ipmi_msg.GetFruActivationPolicy.rsp.new()
    rsp.CompletionCode = 0x00
    if req.FruId > 0 then
        log:error("Parameter is out of range.")
        error(m_error_custom.IPMIOutOfRange())
    end
    rsp.Policy = (self.deactivate_lockbit << 1) | (self.activate_lockbit)

    return rsp
end

function ipmi_payload:pp_ipmi_cmd_set_acpi_powerstate(req, ctx)
    local sys_id = ctx.HostId or 1
    local fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local print_sys = self.multihost:is_multihost_type() and '[System:' .. sys_id .. ']' or ''
    local rsp = ipmi_msg.SetACPIPowerState.rsp.new()
    local SystemState = SystemPowerState[req.ACPISystemPowerState & 127]
    local DeviceState = DevicePowerState[req.ACPIDevicePowerState & 127]
    -- 低位是否有效
    if not SystemState or not DeviceState then
        log:error('[System:%s]Set ACPI SystemPowerState or DevicePowerState faild', sys_id)
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet ACPI System Power State failed',
            print_sys)
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet ACPI Device Power State failed',
            print_sys)
        error(m_error_custom.IPMIOutOfRange())
    end

    local SystemState_Active = (req.ACPISystemPowerState & 128) >> 7 == 1
    local DeviceState_Active = (req.ACPIDevicePowerState & 128) >> 7 == 1

    if SystemState_Active and SystemState ~= NOCHANGE then
        fructrl:set_ACPISystemPowerState(SystemState)
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet ACPI System Power State to (%s) successfully',
            print_sys, SystemState)
    end

    if DeviceState_Active and DeviceState ~= NOCHANGE then
        fructrl:set_ACPIDevicePowerState(DeviceState)
        ipmi.ipmi_operation_log(ctx, 'fructrl', '%sSet ACPI Device Power State to (%s) successfully',
            print_sys, DeviceState)
    end

    rsp.CompletionCode = 0x00
    return rsp
end

function ipmi_payload:pp_ipmi_cmd_get_acpi_powerstate(req, ctx)
    local sys_id = ctx.HostId or 1
    local fructrl = self.fructrl_obj_mgnt.host_obj[sys_id].fructrl
    local rsp = ipmi_msg.GetACPIPowerState.rsp.new()
    rsp.ACPISystemPowerState = SystemPowerState[fructrl:get_ACPISystemPowerState()]
    rsp.ACPIDevicePowerState = DevicePowerState[fructrl:get_ACPIDevicePowerState()]
    if not rsp.ACPISystemPowerState or not rsp.ACPIDevicePowerState then
        log:error('Get ACPI SystemPowerState or ACPI DevicePowerState faild')
        error(m_error_custom.IPMICommandCannotExecute())
    end
    -- 如果默认没有配置,则返回真实上下电的信息
    if rsp.ACPISystemPowerState == DEFALUTACPISTATE then
        rsp.ACPISystemPowerState = m_enums.PGSignalState[fructrl:get_PowerState()] == m_enums.PowerStatus.ON and
            0 or 6
    end
    if rsp.ACPIDevicePowerState == DEFALUTACPISTATE then
        rsp.ACPIDevicePowerState = m_enums.PGSignalState[fructrl:get_PowerState()] == m_enums.PowerStatus.ON and
            0 or 3
    end
    rsp.CompletionCode = 0x00
    return rsp
end

return ipmi_payload
