-- 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 canbus_base = require 'device_tree.adapters.power_mgmt.protocol.monitor.canbus'
local log = require 'mc.logging'

local E_OK = nil -- 函数执行成功返回nil
local E_FAILED = '' -- 空错误信息
local QUERY_INFO_OK<const> = 0
local QUERY_INFO_FAIL<const> = 1
local function get_pbit(val)
    return 1 << val
end

return function (obj)
    local psu_slot = canbus_base(obj)

    function psu_slot:health_check(query_info)
        self:update_health_event(query_info)
        self.scan_status = self.scan_status | get_pbit(0)
        self:dal_refresh_property('scan_status', self.scan_status)
    end

    function psu_slot:refresh_batch_query_info()
        -- 0x40 批量查询信息
        local batch_query_info = self.protocol['refresh_batch_query_info']
        local batch_query_info_ok, batch_query_info_resp = pcall(batch_query_info, self.protocol)
        if batch_query_info_ok then
            log:info('[power_mgmt]ps%d refresh_batch_query_info ok', self.ps_id)
            for prop, value in pairs(batch_query_info_resp) do
                self:dal_refresh_property(prop, value)
            end
            self:health_check(batch_query_info_resp)
            self.protocol:canbus_ms_channel_switch(QUERY_INFO_OK)
        else
            self.protocol:canbus_ms_channel_switch(QUERY_INFO_FAIL)
            log:info('[power_mgmt]ps%s refresh_batch_query_info failed, response = %s', self.ps_id,
                        batch_query_info_resp)
        end
    end

    function psu_slot:update_health_event(query_info)
        if not query_info then
            return
        end
        local alarm_status = query_info.AlarmStatus
        local pre_Alarm_Status = query_info.PreAlarmStatus
        local event = 0
        local output_voltage_fault = 0
        local input_voltage_fault = 0
        -- 输出欠压
        if (alarm_status & get_pbit(10)) > 0 then
            --canbus协议AlarmStatus属性bit10为1，代表输出欠压。但是根据电源告警，若输出欠压，OutputVoltageFault bit4需要置为1
            output_voltage_fault = output_voltage_fault | get_pbit(4)
        end
        -- 输出欠压预告警
        if (pre_Alarm_Status & get_pbit(5)) > 0 then
            --canbus协议pre_Alarm_Status属性bit5为1，代表输出欠压预告警。但是根据电源告警，若输出欠压预告警，OutputVoltageFault bit5需要置为1
            output_voltage_fault = output_voltage_fault | get_pbit(5)
        end
        -- 输出过压预告警
        if (pre_Alarm_Status & get_pbit(4)) > 0 then
            --canbus协议AlarmStatus属性bit4为1，代表输出过压预告警。但是根据电源告警，若输出过压预告警，OutputVoltageFault bit6需要置为1
            output_voltage_fault = output_voltage_fault | get_pbit(6)
        end
        -- 输出过压
        if (alarm_status & get_pbit(8)) > 0 then
            --canbus协议AlarmStatus属性bit8为1，代表输出过压。但是根据电源告警，若输出过压，OutputVoltageFault bit7需要置为1
            output_voltage_fault = output_voltage_fault | get_pbit(7)
        end

        -- 输入欠压
        if (alarm_status & get_pbit(1)) > 0 then
            -- canbus协议AlarmStatus属性bit1为1，代表输入欠压。但是根据电源告警，若输入欠压，InputVoltageFault bit4需要置为1
            input_voltage_fault = input_voltage_fault | get_pbit(4)
            event = event | get_pbit(4)
        end
        -- 输入欠压预告警
        if (pre_Alarm_Status & get_pbit(1)) > 0 then
            --canbus协议pre_Alarm_Status属性bit1为1，代表输入欠压预告警。但是根据电源告警，欠压预告警，InputVoltageFault bit5需要置为1
            input_voltage_fault = input_voltage_fault | get_pbit(5)
        end
        -- 输入过压预告警
        if (pre_Alarm_Status & get_pbit(0)) > 0 then
            --canbus协议AlarmStatus属性bit0为1，代表输入过压预告警。但是根据电源告警，过压预告警，InputVoltageFault bit6需要置为1
            input_voltage_fault = input_voltage_fault | get_pbit(6)
        end
        -- 输入过压
        if (alarm_status & get_pbit(0)) > 0 then
            -- 根据canbus协议AlarmStatus属性bit0为1，代表输入过压。但是根据电源告警，若输入过压，InputVoltageFault bit7需要置为1
            input_voltage_fault = input_voltage_fault | get_pbit(7)
            event = event | get_pbit(4)
        end

        -- 过温告警
        local over_temper_fault = 0
        if (alarm_status & get_pbit(16)) > 0 then
            -- 根据canbus协议AlarmStatus属性bit16为1，代表过温。但是根据电源告警，若过温，OverTemperature bit7需要置为1
            over_temper_fault = over_temper_fault | get_pbit(7)
            event = event | get_pbit(3)
        end
        -- 过温预告警
        if (pre_Alarm_Status & get_pbit(16)) > 0 then
            -- 根据canbus协议pre_Alarm_Status属性bit16为1，代表过温。但是根据电源告警，若过温，OverTemperature bit6需要置为1
            over_temper_fault = over_temper_fault | get_pbit(6)
        end

        -- 输出过流
        local output_over_current = 0
        if (alarm_status & get_pbit(9)) > 0 then
            -- 根据canbus协议AlarmStatus属性bit16为1，代表输出过流。但是根据电源告警，若输出过流，OutputCurrentFault bit7需要置为1
            output_over_current = output_over_current | get_pbit(7)
        end
        if (pre_Alarm_Status & get_pbit(8)) > 0 then
            -- 根据canbus协议alarm_status属性bit8为1，代表输出过流。但是根据电源告警，若输出过流，OutputCurrentFault bit5需要置为1
            output_over_current = output_over_current | get_pbit(5)
        end

        local canbus_fan_fault = 0
        if (alarm_status & get_pbit(17)) > 0 then
            -- 根据canbus协议AlarmStatus属性bit17为1，代表风扇错误。但是根据电源告警，若风扇错误，FanFault bit7需要置为1
            canbus_fan_fault = get_pbit(7)
        end
        if self.protocol:get_power_state() == 'Off' then
            alarm_status = alarm_status & 0xffff7fff7dffffff --turbo关机后屏蔽bit47 bit31 bit25告警
        end
        self:dal_refresh_property('health_event', {
            event = event,
            output_voltage_fault = output_voltage_fault,
            output_current_fault = output_over_current,
            temper_fault = over_temper_fault,
            input_voltage_fault = input_voltage_fault,
            fan_fault = canbus_fan_fault,
            turbo_status_high = (alarm_status & 0xffffffff00000000) >> 32,
            turbo_status_low = alarm_status & 0xffffffff
        })
        self:dal_refresh_property('health', event)
        return E_OK
    end

    function psu_slot:check_psu_lock_status()
        -- 电源锁死时，电源上报输出过压或者过流告警
        if self.prop_value and self.prop_value.health_event and (
            (self.prop_value.health_event.output_voltage_fault & get_pbit(7) ~= 0) or
            (self.prop_value.health_event.output_current_fault & get_pbit(7) ~= 0)) then
            return true
        end
        return false
    end

    function psu_slot:update_communication_status()
        local ok, _
        if self.refresh_info_nums < 10 then
            return
        end
        self.refresh_info_nums = 0
        ok, _ = pcall(function()
            return self.protocol:can_get_canbus_basic_info()
        end)
        if not ok then
            if self.read_fail_count < 4 then
                self.read_fail_count = self.read_fail_count + 1
            else
                self:dal_refresh_property('communication_status', 1)
            end
        else
            self.read_fail_count = 0
            self:dal_refresh_property('communication_status', 0)
        end
    end

    function psu_slot:psm_task_power_mode()
        local ret, rsp, _
        local read_fail_count = 0
        while true do
            rsp, _ = self:get_dynamic_data('sleep_mode')
            ret = ret or rsp
            if ret ~= E_OK then
                if read_fail_count < 3 then
                    read_fail_count = read_fail_count + 1
                else
                    self.scan_status = self.scan_status & ~get_pbit(3)
                end
            else
                read_fail_count = 0
                self.scan_status = self.scan_status | get_pbit(3)
            end
            self:dal_refresh_property('scan_status', self.scan_status)
            ret = nil
            self:sleep_ms(2000)
        end
    end

    function psu_slot:psm_task_send_heart_beat()
        local ok
        while true do
            if not self.protocol or not self.protocol.send_heart_beat then
                goto continue
            end
            for _ = 1, 3 do
                ok = pcall(self.protocol.send_heart_beat, self.protocol)
                if ok then
                    goto continue
                end
                self:sleep_ms(100)
            end
            ::continue::
            self:sleep_ms(10000)
        end
    end

    function psu_slot:psm_task_refresh_capacitor_info()
        while true do
            self:get_dynamic_data('hardware_version', 1)
            self:get_dynamic_data('power_state', 1)
            self:get_dynamic_data('capacity_microfarads', 1)
            self:get_dynamic_data('vout_debounce_milliseconds', 1)
            self:get_dynamic_data('depth_of_discharge_volts', 1)
            self:get_dynamic_data('relay_switched_count', 1)
            self:get_dynamic_data('output_power_limit_watts', 1)
            self:sleep_ms(60000) -- 1分钟刷新一次电容相关信息
        end
    end

    function psu_slot:psm_task_circuit_status()
        while true do
            self:get_dynamic_data('main_circuit_vin_status', 1)
            self:get_dynamic_data('backup_circuit_vin_status', 1)
            self:sleep_time(1000, 1000)
       end
    end

    local PS_TRY_CNT = 30 -- 重试次数
    function psu_slot:power_monitor_entry()
        self.refresh_info_nums = 0
        self.read_fail_count = 0
        self:add_monitor_tasks(function()
            while true do
                self:refresh_batch_query_info()
                self.scan_status = self.scan_status | get_pbit(2) | get_pbit(1)
                self:dal_refresh_property('scan_status', self.scan_status)
                self.refresh_info_nums = self.refresh_info_nums + 1
                self:update_communication_status()
                self:sleep_time(1000, 1000)
            end
        end)
        self:add_monitor_tasks(
            function()
                self:time_service_task()
            end
        )
        self:add_monitor_tasks(
            function()
                self:psm_task_circuit_status()
            end
        )
        self:add_monitor_tasks(
            function()
                while true do
                    self.protocol:canbus_recover_to_main_channel()
                    self:sleep_time(30000, 30000)
                end
            end
        )
        self:add_monitor_tasks(
            function ()
                self:psm_task_power_mode()
            end
        )
        self:add_monitor_tasks(
            function ()
                self:psm_task_send_heart_beat()
            end
        )
        self:add_monitor_tasks(
            function ()
                self:psm_task_refresh_capacitor_info()
            end
        )
        self.scan_status = self.scan_status | get_pbit(3)
        self:dal_refresh_property('scan_status', self.scan_status)
        self:get_dynamic_data('rate', PS_TRY_CNT)
    end
    return psu_slot
end