-- 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: 检测电源状态突变

local log = require 'mc.logging'
local class = require 'mc.class'
local m_enums = require 'types.enums'
local skynet = require 'skynet'
local vos = require 'utils.vos'

local pwr_mutation = class()

function pwr_mutation:ctor(fructrl_obj)
    self.restore = fructrl_obj.pwr_restore
    self.fructrl = fructrl_obj.fructrl
    self.hs = fructrl_obj.hotswap
    self.ctrl = fructrl_obj.ctrlstate
    self.system_id = fructrl_obj.system_id
    self.previous_m6_ms_cnt = 0
    self.previous_status = false -- 记录上一次检查状态机时获取的aclost状态，作用域限于pp_check_pwr_mutation函数
end

function pwr_mutation:init()
    -- 常驻任务，不退出，失败后重试，间隔10s
    skynet.fork_loop({count = 0}, function()
        while true do
            skynet.sleep(100) -- 每1s进行一次电源状态突变例测
            self:pp_check_pwr_mutation()
        end
    end)
end

local hs_state_to_event_type = {
    -- cpld异常翻转电源状态，或上电状态下复位bmc
    [m_enums.HotswapStateToNum.M1] = m_enums.HotswapEventTypes.FruInsertionCriteriaMet,
    -- 激活，跳到M3
    [m_enums.HotswapStateToNum.M2] = m_enums.HotswapEventTypes.FruActivated,
    -- 激活完成，跳到M4
    [m_enums.HotswapStateToNum.M3] = m_enums.HotswapEventTypes.FruActivatedCompleted
}

local FORCE_POWEROFF_TIME = 6 -- 强制下电等待时长为6s
local function recover_hotswap_state_to_m4(self, hs_state)
    if hs_state ~= m_enums.HotswapStateToNum.M6 and self.previous_m6_ms_cnt ~= 0 then
        self.previous_m6_ms_cnt = 0
        return
    end
    local wait_time = self.fructrl:get_PowerOffTimeoutEN() and self.fructrl:get_PowerOffTimeout() +
        FORCE_POWEROFF_TIME or FORCE_POWEROFF_TIME
    if self.previous_m6_ms_cnt == 0 then
        self.previous_m6_ms_cnt = vos.vos_tick_get()
    elseif (vos.vos_tick_get() - self.previous_m6_ms_cnt) // 1000 > wait_time then
        log:notice_easy("[System:%s]unnormal status recovery", self.system_id)
        self.hs:send_hotswap_event(m_enums.HotswapEventTypes.FruUnnomalStateRecovery)
        self.previous_m6_ms_cnt = 0
    end
end

local function pg_signal_power_on_proc(self, hs_state)
    if hs_state <= m_enums.HotswapStateToNum.M3 then
        -- 下电状态下突然检测到上电信号
        self.previous_status = self.fructrl.board_power_drop
        self.fructrl.board_power_drop = false
        self.ctrl:send_powerctrl_event(m_enums.CtrlEventTypes.CtrlFinished)
        self.fructrl:set_PwrStateBeforeACLost(true) -- 硬件信号为上电，更新AC前电源状态为ON
        local event_type = hs_state_to_event_type[hs_state]
        if not event_type then
            log:error_easy("[System:%s]map event type is invalid!!", self.system_id)
            return
        end
        log:notice_easy("[System:%s]map event type = %s", self.system_id, event_type)
        self.hs:send_hotswap_event(event_type)
        return
    end

    if hs_state > m_enums.HotswapStateToNum.M3 then
        self.previous_status = false
        if hs_state ~= m_enums.HotswapStateToNum.M4 then
            -- 状态机非M4，驱动状态机转为稳态，发送电源异常复位
            self.fructrl:set_PwrStateBeforeACLost(true) -- 硬件信号为上电，更新AC前电源状态为ON
            recover_hotswap_state_to_m4(self, hs_state)
        end
    end
end

local function pg_signal_power_off_proc(self, hs_state)
    if hs_state <= m_enums.HotswapStateToNum.M3 then
        self.previous_status = self.fructrl.board_power_drop
        if self.fructrl.board_power_drop then -- AC闪断
            log:notice_easy("[System:%s]Level-2 aclost event, execute the power-on restore policy", self.system_id)
            skynet.sleep(600) -- 6s后执行通电开机
            self.restore:pp_do_pwr_restore_process(self.ctrl, self.fructrl)
            self.fructrl.board_power_drop = false
            return
        end
        if hs_state ~= m_enums.HotswapStateToNum.M1 and self.fructrl:get_PowerState() == 'OFF' then
            self.ctrl:send_powerctrl_event(m_enums.CtrlEventTypes.CtrlFinished)
            self.hs:send_hotswap_event(m_enums.HotswapEventTypes.FruPowerFailure)
            log:notice_easy("[System:%s]Detect fru0 payload power dropped, hotswap:M%s pwr_state:%s pg_state:%s",
                self.system_id, hs_state, self.fructrl:get_PowerState(),
                self.fructrl.pg_signal:get_PGSignal())
        elseif hs_state == m_enums.HotswapStateToNum.M1 and self.fructrl:get_PowerState() == 'OFF' and
            self.previous_m6_ms_cnt ~= 0 then
            self.previous_m6_ms_cnt = 0 -- 下电成功，将M6状态计时时间戳清零
        end
        if not self.fructrl.board_power_drop and self.fructrl:get_PwrStateBeforeACLost() then
            self.fructrl:set_PwrStateBeforeACLost(false) -- 未发生ACLOST但是异常掉电，更新AC前电源状态为OFF
        end
        return
    end

    if hs_state > m_enums.HotswapStateToNum.M3 then
        self.ctrl:send_powerctrl_event(m_enums.CtrlEventTypes.CtrlFinished)
        if hs_state == m_enums.HotswapStateToNum.M6 then -- 正常流程，去激活驱动状态机转为M1状态
            self.hs:send_hotswap_event(m_enums.HotswapEventTypes.FruDeactivatedCompleted)
            log:notice_easy('[System:%s]HotswapState:M6, execute FruDeactivatedCompleted event, ' ..
                'set PwrStateBeforeACLost to false', self.system_id)
        else -- M4 走异常掉电处理，先让热插拔状态机跟随硬件状态变化状态机
            self.hs:send_hotswap_event(m_enums.HotswapEventTypes.FruPowerFailure)
        end
        if not self.fructrl.board_power_drop then
            self.fructrl:set_PwrStateBeforeACLost(false) -- 异常掉电但未AC闪断, 更新AC前电源状态为OFF
        end
        log:notice_easy("[System:%s]Detect fru0 payload power dropped, hotswap:M%s m_pwr_state:%s pg_state:%s",
            self.system_id, hs_state, self.fructrl:get_PowerState(),
            self.fructrl.pg_signal:get_PGSignal())
    end
end

function pwr_mutation:pp_check_pwr_mutation()
    -- 获取热插拔状态机的状态
    local hs_state = self.hs:get_hs_state()

    -- 硬件为上电状态
    if self.fructrl.pg_signal:get_PGSignal() == 1 then
        pg_signal_power_on_proc(self, hs_state)
        return
    end

    -- 硬件为下电状态
    if self.fructrl.pg_signal:get_PGSignal() == 0 then
        pg_signal_power_off_proc(self, hs_state)
        return
    end
end


return pwr_mutation
