-- 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 = {
    ['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,
    [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,
    [DEFALUTACPISTATE] = DEFALUTACPISTATE
}

local DevicePowerState = {
    [0x00] = 'D0',
    ['D0'] = 0x00,
    [0x01] = 'D1',
    ['D1'] = 0x01,
    [0x02] = 'D2',
    ['D2'] = 0x02,
    [0x03] = 'D3',
    ['D3'] = 0x03,
    [0x2a] = 'unknown',
    ['unknown'] = 0x2a,
    [0x7f] = NOCHANGE,
    [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

local function record_accycle_failed(ctx)
    ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set FRU0 to ACCycle failed')
    error(m_error_custom.IPMICommandCannotExecute())
end

local chassis_ctrl_handers = {
    [0x00] = function (self) -- ForceOff
        local ctrl_msg = 'power down'
        local ok, err = pcall(function() self.powerapi:system_reset(ctx_new,
            tostring(m_enums.PowerCtrlType.ForceOff),
            tostring(m_enums.RestartCause.ChassisControlCommand))
        end)
        return ctrl_msg, ok, err
    end,
    [0x01] = function (self) -- On
        local ctrl_msg = 'power on'
        local lock_flag = self.fructrl:get_PwrOnLocked()
        if lock_flag == true and self.fructrl.power_on_lock and
            self.fructrl.power_on_lock:is_power_locked_by_upgrade() then
            return ctrl_msg, false, "poweron is locked", 0xd1
        end

        self.fructrl:set_poweron_from_ipmi_flag(true) -- 记录ipmi发送上电
        local ok, err = pcall(function() self.powerapi:system_reset(ctx_new,
            m_enums.PowerCtrlType.On,
            m_enums.RestartCause.ChassisControlCommand)
        end)
        return ctrl_msg, ok, err
    end,
    [0x02] = function (self)  -- ForcePowerCycle
        local ctrl_msg = 'force power off the service system, and then power it on'
        local ok, err = pcall(function() self.powerapi:system_reset(ctx_new,
            tostring(m_enums.PowerCtrlType.ForcePowerCycle),
            tostring(m_enums.RestartCause.ChassisControlCommand))
        end)
        return ctrl_msg, ok, err
    end,
    [0x03] = function (self) -- ForceRestart
        local ctrl_msg = 'reset'
        local ok, err = pcall(function() self.powerapi:system_reset(ctx_new,
            tostring(m_enums.PowerCtrlType.ForceRestart),
            tostring(m_enums.RestartCause.ChassisControlCommand))
        end)
        return ctrl_msg, ok, err
    end,
    [0x04] = function (self) -- Diagnostic
        local ctrl_msg = 'diagnostic interrupt'
        return ctrl_msg, false, "diagnostic interrupt is not supported", 0xd5
    end,
    [0x05] = function (self) -- GracefulShutdown
        local ctrl_msg = 'soft-shutdown'
        local ok, err = pcall(function() self.powerapi:system_reset(ctx_new,
            tostring(m_enums.PowerCtrlType.GracefulShutdown),
            tostring(m_enums.RestartCause.ChassisControlCommand))
        end)
        return ctrl_msg, ok, err
    end,
    [0x06] = function (self) -- PowerCycle
        local ctrl_msg = 'soft shutdown the service system, and then power it on'
        local ok, err = pcall(function() self.powerapi:system_reset(ctx_new,
            tostring(m_enums.PowerCtrlType.PowerCycle),
            tostring(m_enums.RestartCause.ChassisControlCommand))
        end)
        return ctrl_msg, ok, err
    end,
}

function ipmi_payload:pp_ipmi_cmd_chassis_ctrl(req, ctx)
    local ctrl_cmd = req.Cmd
    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
    ctx_new['fru_id'] = 0

    log:notice("Ipmi call powerctrl cmd (%d).", ctrl_cmd)

    if ctrl_cmd == 0x07 then -- ACCycle
        if not self.fructrl:get_ACCycleEnabled() then
            record_accycle_failed(ctx)
        end

        local ok, _ = pcall(function() self.powerapi:system_reset(ctx_new, tostring(m_enums.PowerCtrlType.ACCycle),
            tostring(m_enums.RestartCause.ChassisControlCommand)) end)
        if not ok then
            record_accycle_failed(ctx)
        end

        self.fructrl:set_restart_channel(ctx.chan_num)
        return {CompletionCode = 0x00}
    end

    local handler = chassis_ctrl_handers[ctrl_cmd]
    if not handler then
        ipmi.ipmi_operation_log(ctx, 'fructrl','Set chassis control failed, unknown control action')
        error(m_error_custom.IPMIInvalidFieldRequest())
    end

    local ctrl_msg, ok, err, cc = handler(self)
    ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set chassis control to (%s) %s', ctrl_msg,
        ok and 'successfully' or 'failed')
    self.fructrl:set_restart_channel(ctx.chan_num)
    if not ok then
        log:error('Ipmi call PowerCtrl rpc has error (%s)', err)
        if not cc then
            error(m_error_custom.IPMICommandCannotExecute())
        end
        return {CompletionCode = cc}
    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
    if req.Status > 1 then
        log:error("Parameter is out of range.")
        ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set power state before AC lost failed')
        error(m_error_custom.IPMIOutOfRange())
    end
    local ac_status = req.Status == 1
    self.fructrl:set_PwrStateBeforeACLost(ac_status)
    ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set power state before AC lost to (%s) successfully',
        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

-- 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 fructrl
    -- 保证通电开机策略是对所有host进行的
    if not self.multihost:is_multihost_type() then
        fructrl = self.fructrl_obj_mgnt.host_obj[1].fructrl
    else
        fructrl = self.fructrl_obj_mgnt.chassis_fructrl_ins
    end
    local restore_policy = req.RestorePolicy
    local rsp = ipmi_msg.SetRestorePolicy.rsp.new()
    if restore_policy == POWER_RESTORE_POLICY_ALWAYS_OFF then
        fructrl:set_PowerOnStrategy(tostring(m_enums.PowerRestorePolicy.AlwaysPowerOff))
    elseif restore_policy == POWER_RESTORE_POLICY_PREVIOUS then
        fructrl:set_PowerOnStrategy(tostring(m_enums.PowerRestorePolicy.LastState))
    elseif restore_policy == POWER_RESTORE_POLICY_ALWAYS_ON then
        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 = 7
        return rsp
    else
        ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set power restore policy failed')
        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 data = req.Seconds
    self.fructrl:set_PwrCycleDelaySeconds(data)
    ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set power cycle interval to (%u) seconds successfully', 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

    if level_option > 1 then
        ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set power level failed')
        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', 'Set power level failed')
        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', 'Set power level to (RAW:%02xh-%02xh-%02xh) successfully',
        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)
    ctx_new['fru_id'] = 0

    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

    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', 'Set system lockdown failed')
        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 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', 'Set FRU0 activation to (%s) successfully', msg)

    return rsp
end

function ipmi_payload:pp_ipmi_cmd_set_fru_activation_policy(req, ctx)
    local rsp = ipmi_msg.SetFruActivationPolicy.rsp.new()
    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', 'Set FRU0 activation policy to (mask:0x%02x|bits:0x%02x) successfully',
        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 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', 'Set ACPI System Power State failed')
        ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set ACPI Device Power State failed')
        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', 'Set ACPI System Power State to (%s) successfully',
            SystemState)
    end

    if DeviceState_Active and DeviceState ~= NOCHANGE then
        fructrl:set_ACPIDevicePowerState(DeviceState)
        ipmi.ipmi_operation_log(ctx, 'fructrl', 'Set ACPI Device Power State to (%s) successfully',
            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
