-- Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
--
-- this file licensed under the 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 skynet = require 'skynet'
local class = require 'mc.class'
local log = require 'mc.logging'
local props = require 'basic_cooling.define.cooling_properties'
local cooling_pid_intf = require 'basic_cooling.cooling_pid_intf'
local libpid = require 'libpid'
local utils = require 'basic_cooling.cooling_utils'
local cooling_enums = require 'basic_cooling.define.cooling_enums'
local exp_policy = require 'basic_cooling.exception_policy'
local pump_exp_policy = require 'basic_cooling.cooling_abnormal_device.abnormal_pump'
local fans_config = require 'basic_cooling.cooling_config.fans_config'
local pumps_config = require 'basic_cooling.cooling_config.pumps_config'
local cooling_policys = require 'basic_cooling.cooling_policys'
local cooling_requirements = require 'basic_cooling.cooling_requirememts'
local external_data_keeping = require 'basic_cooling.data_keeping.external_data_keeping'
local cooling_fans = require 'basic_cooling.cooling_device.cooling_fans'
local cooling_pumps = require 'basic_cooling.cooling_device.cooling_pumps'
local singleton = require 'mc.singleton'
local context = require 'mc.context'

local client = require 'thermal_mgmt.client'

local cooling_mgmt = class()
local PUMP_ID_OFFSET <const> = 128
local LEN_PER_FAN_INFO <const> = 2
local PID_READ_HEAD_LEN <const> = 6
local PID_SPD_ONE_FAN_LEN <const> = 5

function cooling_mgmt:ctor(service, db, bus, data)
    self.service = service
    self.db = db
    self.bus = bus
    self.data = data
    self.cooling_inited = false
    self.is_fan_manual_mode = false
    self.is_pump_manual_mode = false
    self.fructl_data_keeping = external_data_keeping.get_instance().data_keeping_objs.fructl_data_keeping
    self.fans_data = external_data_keeping.get_instance().data_keeping_objs.fans_data
    self.pumps_data = external_data_keeping.get_instance().data_keeping_objs.pump_data_keeping
    self.fan_group = external_data_keeping.get_instance().objs.fan_group_data_keeping
    self.pump_group = external_data_keeping.get_instance().objs.pump_group_data_keeping
    self.fans_instance = cooling_fans.get_instance()
    self.pumps_instance = cooling_pumps.get_instance()
    self.requirements_instance = cooling_requirements.get_instance()
    self.policys_instance = cooling_policys.get_instance()
    self.fans_config_instance = fans_config.get_instance()
    self.pumps_config_instance = pumps_config.get_instance()
    self.pump_exp_policy = pump_exp_policy.get_instance()
    self.exp_policy = exp_policy.get_instance()
    self.cooling_device_table = {}
    self.control_mode = {}
    self.pwm_list_with_rpc = {}
end

function cooling_mgmt:update_control_mode()
    -- 风扇调速
    self.control_mode[props.COOLING_AIR] = self.fans_config_instance:get_ctrl_mode()
    -- 泵调速
    self.control_mode[props.COOLING_LIQUID] = self.pumps_config_instance:get_ctrl_mode()
end

function cooling_mgmt:update_control_pwm(device_control_table)
    -- 获取目标调速信息
    self:get_pid_pwm(device_control_table)
    -- 基于rpc方法更新pwm
    self:update_pwm_with_rpc(device_control_table)
    -- 基于异常调速值更新pwm
    self:update_pwm_with_exp_speed(device_control_table)
    -- 混动模式基于手动转速更新pwm
    self:update_pwm_with_level(device_control_table)
end

function cooling_mgmt:cooling_device_control(fan_bck_t, pump_bck_t)
    local device_control_table = {}
    -- 更新调速模式
    self:update_control_mode()
    -- 更新调速pwm
    self:update_control_pwm(device_control_table)
    -- 手动模式转速时设置
    self:set_manual_pwm_to_cooling_device(fan_bck_t, pump_bck_t)
    -- 自动模式或混动模式转速设置
    self:set_best_pwm_to_cooling_device(device_control_table)
end

local function update_manual_timeout(config_obj, cur_sys_time, bck_t, device_type)
    if not config_obj then -- 不存在配置对象时直接退出
        return
    end
    local last_timeout = bck_t.last_timeout
    local expir_time = bck_t.expir_time
    local timeout = config_obj[props.TIME_OUT]
    if last_timeout == nil then
        expir_time = timeout + cur_sys_time
    elseif last_timeout ~= timeout then -- timeout有更新，则重新计算expir_time
        if last_timeout == 0 then
            expir_time = timeout + cur_sys_time
        else
            expir_time = expir_time + timeout - last_timeout
        end
    end
    timeout = (expir_time > cur_sys_time) and (expir_time - cur_sys_time) or 0
    log:debug('[cooling] update %s timeout: %d', device_type, timeout)
    config_obj[props.TIME_OUT] = timeout
    if timeout == 0 then
        if config_obj[props.MIXED_MODE_SUPPORTED] then
            config_obj[props.CTL_MODE] = cooling_enums.modes.Mixed
        else
            config_obj[props.CTL_MODE] = cooling_enums.modes.Auto
        end
    end
    last_timeout = timeout

    bck_t.last_timeout = last_timeout
    bck_t.expir_time = expir_time
end

local function update_custom_mode_target_temp(config, reqs)
    if config[props.SMART_COOLING_MODE] ~= cooling_enums.smart_cooling_mode.COOLING_CUSTOM_MODE then
        return
    end
    for _, req_o in pairs(reqs) do
        -- 如果不支持自定义调速，不更新其目标温度值  
        if not req_o[props.POLICY_CUSTOM_SUPPORTED] then
            goto continue
        end
        log:debug("Requirement(0x%x) current target temperature(%u), custom target temperature(%u)",
            req_o[props.REQUIREMENT_ID], req_o[props.TARGET_TEMP_CELSIUS], req_o[props.CUSTOM_TARGET_TEMP_CELSIUS])
        local exp_target_temperature = req_o[props.TARGET_TEMP_CELSIUS]
        local custom_target_temperature = req_o[props.CUSTOM_TARGET_TEMP_CELSIUS]
        if exp_target_temperature ~= custom_target_temperature and custom_target_temperature ~= 0xff then
            req_o[props.TARGET_TEMP_CELSIUS] = req_o[props.CUSTOM_TARGET_TEMP_CELSIUS]
            log:notice("Requirement(0x%x) target temperature is updated to custom value(%u)",
                req_o[props.REQUIREMENT_ID], req_o[props.TARGET_TEMP_CELSIUS])
        end
        ::continue::
    end
end

-- SmartCooling mode变化后根据调整目标温度
local function update_requirement_target_temp(pre_smt_cl_mode, reqs, conf)
    if conf[props.SMART_COOLING_MODE] == cooling_enums.smart_cooling_mode.COOLING_CUSTOM_MODE then
        return
    end

    if pre_smt_cl_mode and pre_smt_cl_mode == conf[props.SMART_COOLING_MODE] then
        log:debug('SmartCooling mode has not changed, pre(%s), cur(%s)', pre_smt_cl_mode and pre_smt_cl_mode or nil,
            conf[props.SMART_COOLING_MODE])
        return conf[props.SMART_COOLING_MODE]
    end

    log:notice('SmartCooling mode has changed, pre(%s), cur(%s)', pre_smt_cl_mode and pre_smt_cl_mode or nil,
        conf[props.SMART_COOLING_MODE])

    local mode_idx_mapping = {
        [cooling_enums.smart_cooling_mode.COOLING_ENERGY_SAVING_MODE] = 1,    -- 对应温度值索引为1
        [cooling_enums.smart_cooling_mode.COOLING_HIGH_PERFORMANCE_MODE] = 2, -- 对应温度值索引为2
        [cooling_enums.smart_cooling_mode.COOLING_LOW_NOISE_MODE] = 3        -- 对应温度值索引为3
    }

    if conf[props.SMART_COOLING_MODE] ~= cooling_enums.smart_cooling_mode.COOLING_CUSTOM_MODE and
        not mode_idx_mapping[conf[props.SMART_COOLING_MODE]] then
        return conf[props.SMART_COOLING_MODE]
    end

    local exp_target_temperature
    for _, req_o in pairs(reqs) do
        if utils.size_of(req_o.SmartCoolingTargetTemperatureCelsius) <
            mode_idx_mapping[conf[props.SMART_COOLING_MODE]] then
            goto continue
        end
        exp_target_temperature =
            req_o.SmartCoolingTargetTemperatureCelsius[mode_idx_mapping[conf[props.SMART_COOLING_MODE]]]

        if req_o[props.TARGET_TEMP_CELSIUS] == exp_target_temperature then
            goto continue
        end

        req_o[props.TARGET_TEMP_CELSIUS] = exp_target_temperature

        log:notice('The target temperature of requirement(id:0x%x) has changed to %u due to smartcooling mode change',
            req_o[props.REQUIREMENT_ID], exp_target_temperature)

        ::continue::
    end

    return conf[props.SMART_COOLING_MODE]
end

function cooling_mgmt:cooling_policy_scan(bck_t)
    local power_state = self.fructl_data_keeping.power_state
    local is_power_state_changed = bck_t.power_status_bck ~= power_state
    bck_t.power_status_bck = power_state

    log:debug("Current power state:%s, power is changed:%s", power_state and power_state or 'nil',
        is_power_state_changed and 'true' or 'false')
    update_custom_mode_target_temp(self.fans_config_instance:get_obj(), self.requirements_instance:get_objs())

    -- 更新温度点目标值
    bck_t.pre_smt_cl_mode =
        update_requirement_target_temp(bck_t.pre_smt_cl_mode, self.requirements_instance:get_objs(),
        self.fans_config_instance:get_obj())

    -- 更新温度点生效状态更新
    self.requirements_instance:update_objs_isvalid(self.data, self.fans_config_instance:get_disk_row_temp_available(),
        power_state, self.fans_config_instance:get_obj())

    -- 调速policy生效状态更新后下发给pid
    if self.policys_instance:get_policy_updated_flag() then
        cooling_pid_intf.cooling_set_envtempconfig_to_pid(self.service, self.data,
            self.cooling_device_table, self.requirements_instance:get_objs(), self.policys_instance:get_objs())
        self.policys_instance:set_policy_updated_flag(false)
    end
    if self.requirements_instance:get_requirement_updated_flag() or is_power_state_changed then
        self.requirements_instance:set_requirement_updated_flag(false)
        local ok, res = pcall(cooling_pid_intf.cooling_set_tempconfig_to_pid, self.service, self.data, 
            self.cooling_device_table, self.requirements_instance:get_objs())
        if not ok then
            log:error('Requirement is added, set tempconfig to pid failed, %s', res)
        end
    end
end

local function is_manual_mode(config_obj, cur_sys_time, bck_t)
    if not config_obj then
        return false
    end
    if config_obj[props.CTL_MODE] ~= cooling_enums.modes.Manual then
        bck_t.last_timeout = nil
        bck_t.expir_time = cur_sys_time
        return false
    end
    return true
end

local function update_device_manual_timeout(self, cur_sys_time, fan_bck_t, pump_bck_t)
    local ok, rsp
    if self.is_fan_manual_mode then
        ok, rsp = pcall(update_manual_timeout, self.fans_config_instance:get_obj(), cur_sys_time, fan_bck_t, 'fan')
        if not ok then
            utils.get_instance():frequency_limit_log(cooling_enums.log_level.ERROR, 60,
                'Update manual fan timeout failed, error: %s', tostring(rsp))
        end
    end
    if self.is_pump_manual_mode then
        ok, rsp = pcall(
            update_manual_timeout, self.pumps_config_instance:get_obj(), cur_sys_time, pump_bck_t, 'pump')
        if not ok then
            utils.get_instance():frequency_limit_log(cooling_enums.log_level.ERROR, 60,
                'Update manual pump timeout failed, error: %s', tostring(rsp))
        end
    end
end

-- 删除无效的CoolingRequirement对象
-- 具体为：CoolingRequirement对应的CoolingArea中的PolicyIdxGroup，FanIdxGroup，LiquidCoolingDeviceGroup均为空数组时为无效对象
function cooling_mgmt:delete_invalid_requirements()
    local invalid_requirements = {}
    local req_base_id, area_id, area_obj
    for _, requirement_obj in pairs(self.requirements_instance:get_objs()) do
        -- 如果为备用温度点直接跳过
        if requirement_obj.IsBackupRequirement then
            goto continue
        end

        req_base_id = requirement_obj[props.REQ_BASE_ID]
        area_id = self.data.requirement_area_map[req_base_id]
        if not area_id then
            table.insert(invalid_requirements, requirement_obj[props.REQUIREMENT_ID])
            log:debug("Requirement(0x%x) not find CoolingArea(%u)", requirement_obj[props.REQUIREMENT_ID], req_base_id)
            goto continue
        end
        area_obj = self.data.cooling_areas[area_id]
        if not area_obj then
            table.insert(invalid_requirements, requirement_obj[props.REQUIREMENT_ID])
            log:debug("Requirement(0x%x)-CoolingArea(%u) does not exist",
                requirement_obj[props.REQUIREMENT_ID], req_base_id)
            goto continue
        end
        if not next(area_obj[props.POLICY_IDX_GROUP]) and not next(area_obj[props.FAN_IDX_GROUP]) and
            not next(area_obj[props.LIQUID_DEVICE_GROUP]) then
            table.insert(invalid_requirements, requirement_obj[props.REQUIREMENT_ID])
            log:debug("PolicyIdxGroup, FanIdxGroup, LiquidCoolingDeviceGroup are empty in \
                Requirement(0x%x)-CoolingArea(%u)", requirement_obj[props.REQUIREMENT_ID], req_base_id)
        end
        ::continue::
    end
    self.requirements_instance:delete_objs(invalid_requirements)
end

function cooling_mgmt:start_cooling_job()
    -- 下发温度点读数给PID, 获取转速并配置给风扇
    local T1, T2, T3 = 0, 0, 0
    local ok, rsp, cur_sys_time
    local fan_bck_t = self.fans_config_instance:get_manual_mode_status_table()
    local pump_bck_t = self.pumps_config_instance:get_manual_mode_status_table()
    while true do
        cur_sys_time = os.time()
        self.is_fan_manual_mode = is_manual_mode(self.fans_config_instance:get_obj(), cur_sys_time, fan_bck_t)
        self.is_pump_manual_mode = is_manual_mode(self.pumps_config_instance:get_obj(), cur_sys_time, pump_bck_t)
        if not self.is_fan_manual_mode and not self.is_pump_manual_mode then
            skynet.sleep(50) -- 若风扇和泵均为自动模式，则等待0.5s，并直接跳过刷新超时时间
            goto continue
        end
        skynet.sleep(20) -- 继承v2的sleep时间
        update_device_manual_timeout(self, cur_sys_time, fan_bck_t, pump_bck_t)
        ::continue::
        -- or后的判断为了确保修改os时间后调速正常执行
        if cur_sys_time - T1 > 1 or cur_sys_time < T1 then
            ok, rsp = pcall(self.cooling_policy_scan, self, fan_bck_t)
            if not ok then
                utils.get_instance():frequency_limit_log(cooling_enums.log_level.ERROR, 60,
                    '[Cooling] Update policy isvalid failed, error: %s', tostring(rsp))
            end
            T1 = cur_sys_time
        end

        if cur_sys_time - T2 >= 1 or cur_sys_time < T2 then
            ok, rsp = pcall(exp_policy.exception_scan, exp_policy.get_instance(), self.data)
            if not ok then
                utils.get_instance():frequency_limit_log(cooling_enums.log_level.ERROR, 60,
                    '[Cooling] exception scan failed, error: %s', tostring(rsp))
            end
            T2 = cur_sys_time
        end

        -- 从PID获取计算后的转速并下发
        if cur_sys_time - T3 >= 2 or cur_sys_time < T3 then
            ok, rsp = pcall(self.cooling_device_control, self, fan_bck_t, pump_bck_t)
            if not ok then
                utils.get_instance():frequency_limit_log(cooling_enums.log_level.ERROR, 60,
                    '[Cooling] Fan control failed, error: %s', tostring(rsp))
            end
            T3 = cur_sys_time
        end
        self:delete_invalid_requirements()
    end
end

function cooling_mgmt:add_cooling_device(device_objs, syn_id, device_type)
    local device_table = {}
    for _, obj in pairs(device_objs) do
        local device_obj = {}
        -- 传入PID前，为防止泵id与风扇id冲突，泵id偏移128
        if device_type == props.COOLING_LIQUID then
            device_obj.Id = obj[syn_id] + PUMP_ID_OFFSET
        elseif device_type == props.COOLING_AIR then
            device_obj.Id = obj[syn_id]
        else
            log:notice("The device type:%s is not supported", device_type)
        end
        device_obj.Slot = obj.Slot
        device_table[obj[syn_id]] = device_obj
    end
    self.cooling_device_table[device_type] = device_table
end

function cooling_mgmt:cooling_device_init()
    if self.fans_config_instance:get_obj() then
        local fans_objs = self.fans_instance:get_objs()
        local fans_syn_id = self.fans_instance:get_syn_id()
        self:add_cooling_device(fans_objs, fans_syn_id, props.COOLING_AIR)
    end
    if self.pumps_config_instance:get_obj() then
        local pumps_objs = self.pumps_instance:get_objs()
        local pumps_syn_id = self.pumps_instance:get_syn_id()
        self:add_cooling_device(pumps_objs, pumps_syn_id, props.COOLING_LIQUID)
    end
end

function cooling_mgmt:smart_cooling_mode_init()
    local fan_obj = self.fans_config_instance:get_obj()
    if fan_obj then
        -- 同步OriginalSmartCoolingMOde和SmartCoolingMode
        log:debug('SMART_COOLING_MODE: %s, ORIGINAL_SMART_COOLING_MODE: %s', fan_obj[props.SMART_COOLING_MODE],
            fan_obj[props.ORIGINAL_SMART_COOLING_MODE])
        -- 防止现网第一次升级到包含OriginalSmartCoolingMode属性的bmc版本误覆盖
        if fan_obj[props.ORIGINAL_SMART_COOLING_MODE] == '' then
            fan_obj[props.ORIGINAL_SMART_COOLING_MODE] = fan_obj[props.SMART_COOLING_MODE]
        end
        if fan_obj[props.SMART_COOLING_MODE] ~= fan_obj[props.ORIGINAL_SMART_COOLING_MODE] then
            log:notice('smart_cooling_mode from %s change to %s', fan_obj[props.SMART_COOLING_MODE],
                fan_obj[props.ORIGINAL_SMART_COOLING_MODE])
            fan_obj[props.SMART_COOLING_MODE] = fan_obj[props.ORIGINAL_SMART_COOLING_MODE]
        end
    else
        log:error('Can not get fan_obj, smart_cooling_mode_init failed')
    end
end

function cooling_mgmt:init()
    self:register()
    self:cooling_global_init()
    self:active_cooling_init()
    self:smart_cooling_mode_init()
    skynet.fork_loop({ count = 0 }, function()
        self:start_cooling_job()
    end)
end

function cooling_mgmt:set_fan_ctrl_mode(mode, manual_time)
    if mode ~= cooling_enums.modes.Auto and mode ~= cooling_enums.modes.Manual and
       mode ~= cooling_enums.modes.Mixed then
        log:error('Cooling mode is not supported')
        return false
    end

    if mode == cooling_enums.modes.Manual then
        self.fans_config_instance:set_manual_timeout(manual_time)
        local manual_level = self.fans_config_instance:get_manual_level()
        self.fans_instance:set_all_cooling_device_manual_level(manual_level)
    end

    self.fans_config_instance:set_ctrl_mode(mode)

    return true
end

function cooling_mgmt:set_rpc_pwm_list(device_type, device_ids, device_speeds)
    if device_type ~= 'Fan' and device_type ~= 'Pump' then
        log:error('Set external pwm failed, because device type is not supported', device_type)
        error('Set external pwm failed, because device type is not supported', device_type)
    end
    local ids = utils.get_array_from_rpc_u8_array(device_ids)
    local speeds = utils.get_array_from_rpc_u8_array(device_speeds)
    -- id和speed的数量必须相同
    if #ids ~= #speeds then
        log:error('Set external pwm failed, because the sizes of device ids and device speeds are different')
        error("Set external pwm failed, because the sizes of device ids and device speeds are different")
    end
    local medium = (device_type == 'Fan') and props.COOLING_AIR or props.COOLING_LIQUID
    self.pwm_list_with_rpc[medium] = {}
    if #device_ids == 0 then
        return
    end
    -- 根据风扇/泵的转速区间进行判断
    local speed_range = (device_type == 'Fan') and self.fans_config_instance:get_level_range() or
    self.pumps_config_instance:get_level_range()
    for i = 1, #device_ids do
        if self.pwm_list_with_rpc[medium][ids[i]] then
            self.pwm_list_with_rpc[medium] = {}
            log:error('Set external pwm failed, because device ids have the same element')
            error("Set external pwm failed, because device ids have the same element")
        end
        if speeds[i] < speed_range[1] or speeds[i] > speed_range[2] then
            self.pwm_list_with_rpc[medium] = {}
            log:error('Set external pwm failed, because speed is out of range')
            error("Set external pwm failed, because speed is out of range")
        end
        self.pwm_list_with_rpc[medium][ids[i]] = speeds[i]
    end
end

-- 设置风扇的转速和手动模式的转速
function cooling_mgmt:set_fan_level_hw(fan_id, fan_level)
    local fans_info = self.fans_data:get_fans_info_with_fan_id()
    local ret = true
    local thermal_obj = self.fans_data:get_thermal_obj()
    if fan_id == 0xff and thermal_obj then -- 批量设置所有风扇转速
        local fan_table = self.fans_instance:get_all_cooling_device_manual_level()
        if not next(fan_table) then
            return false
        end
        if not self:cooling_set_fan_pwm_batch(fan_table, thermal_obj) then
            return false
        end
        return true
    elseif fan_id == 0xff and not thermal_obj then
        for _, fan_obj in pairs(fans_info) do
            local ok, rsp = pcall(fan_obj.SetFanPWM_PACKED, fan_obj, context.new(), fan_level)
            if not ok then
                log:error('Set fan(%d) pwm to %d failed, err_msg: %s', fan_obj[props.FAN_ID], fan_level, rsp)
                ret = false
                goto continue
            end
            if rsp.Result ~= 0 then
                log:error('Set fan(%d) pwm to %d failed, result: %s', fan_obj[props.FAN_ID], fan_level, rsp.Result)
                ret = false
                goto continue
            end
            ::continue::
        end
        return ret
    end
    -- 设置单个风扇的转速
    for _, fan_obj in pairs(fans_info) do
        if fan_obj[props.FAN_ID] == fan_id then
            local rsp = fan_obj:SetFanPWM_PACKED(context.new(), fan_level)
            if rsp.Result ~= 0 then
                log:error('Set fan(%d) pwm to %d failed', fan_id, fan_level)
                return false
            end
            return true
        end
    end
    log:error("Set fan pwm failed, fanid(%s) is not found", fan_id)
    return false
end

-- 初始化手动模式下的风扇转速
function cooling_mgmt:cooling_global_init()
    -- 风扇手动转速初始化
    if self.fans_config_instance:get_obj() then
        local level
        -- 支持混合模式场景，模式初始化为混合模式
        if self.fans_config_instance:get_obj() and
            self.fans_config_instance:get_mixed_supported() then
            self.fans_config_instance:set_ctrl_mode(cooling_enums.modes.Mixed)
        else
            level = self.fans_config_instance:get_level_range()[1]
            self.fans_config_instance:set_ctrl_mode(cooling_enums.modes.Auto)
            self.fans_config_instance:set_manual_level(level) -- 初始化手动转速
        end
    end

    -- 泵的手动转速初始化
    if self.pumps_config_instance:get_obj() then
        local level
        -- 支持混合模式场景，模式初始化为混合模式,fans_config_instance为总体配置，共同使用混合模式配置
        if self.fans_config_instance:get_obj() and 
            self.fans_config_instance:get_mixed_supported() then
            self.pumps_config_instance:set_ctrl_mode(cooling_enums.modes.Mixed)
        else
            level = self.pumps_config_instance:get_level_range()[1]
            self.pumps_config_instance:set_ctrl_mode(cooling_enums.modes.Auto)
            self.pumps_config_instance:set_manual_level(level) -- 初始化手动转速
        end
    end
end

function cooling_mgmt:custom_pid_init()
    local ulTimeCnt = 0 -- 计时从0开始
    local ok = true
    while true do
        skynet.sleep(100)
        if ulTimeCnt % 1 == 0 then
            ok = pcall(self.service.CustomPidUpdateSensorValue, self.service)
            if not ok then
                log:error('[Cooling] Pid update sensor value failed')
            end
        end
        if ulTimeCnt % 2 == 0 then
            ok = pcall(self.service.CustomPidCountValue, self.service)
            if not ok then
                log:error('[Cooling] Pid count value failed')
            end
        end
        ulTimeCnt = ulTimeCnt + 1 -- 每1s后ulTimeCnt加1
    end
end

-- 主动散热初始化
function cooling_mgmt:active_cooling_init()
    -- 调速相关的全局结构体初始化入口,要先初始化后才能下发,放到线程外面防止线程未起来未初始化
    self.service:CustomTarTempCtrInit()
    -- 重写TargetTempFanControl.c的TargetTempControlThread计算调速线程函数，用skynet进行管理
    skynet.fork_loop({ count = 0 }, function()
        self:custom_pid_init()
    end)
    -- 添加散热对象
    self:cooling_device_init()
    -- 因为现在PID和cooling模块之间的转速传递不是绝对转速，因此最大转速是没有用到的。
    cooling_pid_intf.cooling_set_device_config_to_pid(self.service, self.cooling_device_table, 0, 20)
end

-- 对pid计算的pwm值处理后得到实际要下发的风扇pwm值
function cooling_mgmt:update_fan_pwm_with_exp_speed(fan_table)
    local exp_speed = self.exp_policy:get_exp_policy_table()
    if not next(fan_table) then
        log:debug("No fan in fan table.")
        return
    end

    for _, policy in pairs(exp_speed) do
        for id, exp_pwm  in pairs(policy) do
            if fan_table[id] then
                fan_table[id] = fan_table[id] < exp_pwm and exp_pwm or fan_table[id]
            end
        end
    end
end

function cooling_mgmt:update_pump_pwm_with_exp_speed(pump_table)
    local exp_policys = self.pump_exp_policy:get_exp_policy_table()
    if not next(pump_table) then
        log:debug("No pump in pump table.")
        return
    end
    for _, policy in pairs(exp_policys) do
        for pump_id, exp_pwm in pairs(policy) do
            if pump_table[pump_id] then
                pump_table[pump_id] = pump_table[pump_id] < exp_pwm and exp_pwm or pump_table[pump_id]
            end
        end
    end
end

function cooling_mgmt:update_pwm_with_rpc(device_control_table)
    for medium, items in pairs(self.pwm_list_with_rpc) do
        for device_id, pwm in pairs(items) do
            if device_control_table[medium] and device_control_table[medium][device_id] and
                pwm > device_control_table[medium][device_id] then
                    device_control_table[medium][device_id] = pwm
            end
        end
    end
end

function cooling_mgmt:update_pwm_with_exp_speed(device_control_table)
    -- 更新风扇异常转速
    self:update_fan_pwm_with_exp_speed(device_control_table[props.COOLING_AIR])
    -- 更新泵异常转速
    self:update_pump_pwm_with_exp_speed(device_control_table[props.COOLING_LIQUID])
end

local function make_word(high_byte, low_byte)
    return (high_byte << 8) | low_byte
end

function cooling_mgmt:update_pwm_with_level(device_control_table)
    self:update_fan_pwm_with_level(device_control_table)
    self:update_pump_pwm_with_level(device_control_table)
end

function cooling_mgmt:update_fan_pwm_with_level(device_control_table)
    local manual_device_table = {}
    if not device_control_table[props.COOLING_AIR] then
        log:debug("Get fan table failed.")
        return
    end

    if self.control_mode[props.COOLING_AIR] and self.control_mode[props.COOLING_AIR] == cooling_enums.modes.Mixed then
        manual_device_table[props.COOLING_AIR] = self.fan_group:get_fan_group_control_table()
        for id, pwm in pairs(manual_device_table[props.COOLING_AIR]) do
            if device_control_table[props.COOLING_AIR][id] and device_control_table[props.COOLING_AIR][id] < pwm then
                device_control_table[props.COOLING_AIR][id] = pwm
            end
        end
    end
    log:debug("policy(%s) after the pwm is updated with manual speed.",
        utils.parse_table_info(device_control_table[props.COOLING_AIR]))
end

function cooling_mgmt:update_pump_pwm_with_level(device_control_table)
    local manual_device_table = {}
    if not device_control_table[props.COOLING_LIQUID] then
        log:debug("Get pump table failed.")
        return
    end

    if self.control_mode[props.COOLING_LIQUID] and 
        self.control_mode[props.COOLING_LIQUID] == cooling_enums.modes.Mixed then
        manual_device_table[props.COOLING_LIQUID] = self.pump_group:get_pump_group_control_table()
        for id, pwm in pairs(manual_device_table[props.COOLING_LIQUID]) do
            if device_control_table[props.COOLING_LIQUID][id] and 
                device_control_table[props.COOLING_LIQUID][id] < pwm then
                device_control_table[props.COOLING_LIQUID][id] = pwm
            end
        end
    end
    log:debug("policy(%s) after the pwm is updated with manual speed.", 
                utils.parse_table_info(device_control_table[props.COOLING_LIQUID]))
end

local function get_device_id_and_slot(pid_id)
    local slot = pid_id >> 8
    local id = pid_id & 0xFF
    return slot, id
end

local function get_pid_fan_info(self)
    local ok, fan_buffer = pcall(libpid.PidGetFanSpeedFactor)
    if not ok then
        log:error('Pid get fan speed factor failed')
        return nil, nil
    end
    local max_fan_pwm_buffer_len = make_word(fan_buffer[1], fan_buffer[0])
    if max_fan_pwm_buffer_len <= LEN_PER_FAN_INFO then
        log:error("PID buffer error, len = %s", max_fan_pwm_buffer_len)
        return nil, nil
    end
    local fan_objs = self.fans_instance:get_objs()
    if not next(fan_objs) then
        log:error("Get fan objs failed")
        return nil, nil
    end
    if #fan_objs ~= (max_fan_pwm_buffer_len - LEN_PER_FAN_INFO) / PID_SPD_ONE_FAN_LEN then
        log:error("PID buffer error, fan num is wrong") -- PID中返回的风扇数量与当前风扇数量不一致
        return nil, nil
    end
    return fan_buffer, #fan_objs
end

function cooling_mgmt:get_faninfo_overall(ctx)
    local fan_info = {
        ['ret'] = false, -- 表示风扇最大转速信息获取是否成功
        ['fan_speed_status'] = 0, -- 风扇转速状态，0表示正常，1表示异常
        ['sensor_name'] = '',
        ['max_fan_pwm'] = 0,
    }
    -- 获取异常调速数据
    local exception_info = exp_policy.get_instance():get_exception_info()
    if not exception_info.exp_type or not exception_info.max_fan_pwm or not exception_info.requirementId then
        log:error("Get exception info failed")
        return {fan_info.ret, fan_info.fan_speed_status, fan_info.max_fan_pwm, fan_info.sensor_name}
    end
    local fan_buffer, fan_num = get_pid_fan_info(self)
    if not fan_buffer or not fan_num then
        return {fan_info.ret, fan_info.fan_speed_status, fan_info.max_fan_pwm, fan_info.sensor_name}
    end
    local requirement_id
    for i = 0, fan_num - 1 do
        local idx0 = LEN_PER_FAN_INFO + i * PID_SPD_ONE_FAN_LEN
        if fan_buffer[idx0 + 4] / 2.55 > exception_info.max_fan_pwm then
            -- CoolingRequirementId的配置会根据槽位进行偏移，以偏移后的id作为最高优先级
            -- 即若有偏移的requirement对象，则使用该对象，否则使用无偏移的对象
            -- requirement的偏移格式为expr((baseId << 8) | $Slot)
            requirement_id = (fan_buffer[idx0 + 2] << 8) | fan_buffer[idx0 + 3]
            exception_info.requirementId = not self.requirements_instance:get_requirement_by_id(requirement_id) and
                fan_buffer[idx0 + 2] or requirement_id
            exception_info.max_fan_pwm = fan_buffer[idx0 + 4] / 2.55
            exception_info.exp_type = cooling_enums.max_fan_pwm_type.PID
        end
    end
    if exception_info.exp_type == cooling_enums.max_fan_pwm_type.FAN then
        fan_info.sensor_name = exception_info.requirementId
    else
        local sensor_name = self.requirements_instance:
            get_sensor_name_by_id(exception_info.requirementId, exception_info.exp_type)
        if not sensor_name then
            log:error('Get requirement sensor name failed')
            return {fan_info.ret, fan_info.fan_speed_status, fan_info.max_fan_pwm, fan_info.sensor_name}
        end
        fan_info.sensor_name = sensor_name
    end
    fan_info.ret = true
    fan_info.fan_speed_status = exception_info.exp_type == cooling_enums.max_fan_pwm_type.PID and 0 or 1
    fan_info.max_fan_pwm = math.ceil(exception_info.max_fan_pwm)
    return {fan_info.ret, fan_info.fan_speed_status, fan_info.max_fan_pwm, fan_info.sensor_name}
end

function cooling_mgmt:get_pid_pwm(pid_device_table)
    local pwm_buffer = self.service:CustomReadInfo()
    log:info("[Cooling] PID readinfo: %s", table.concat(pwm_buffer, ' ', 0))
    local buf_len = make_word(pwm_buffer[1], pwm_buffer[0])
    -- 下电且无standby调速温度点的buf_len计算为6
    if buf_len <= PID_READ_HEAD_LEN or pwm_buffer[3] ~= 0x06 then
        log:debug("PID buffer error, len=%u, CMD(buffer[3])=%u", buf_len, pwm_buffer[3])
        return
    end

    local fan_num, idx0, pid_id, fan_pwm, target_pwm, id, slot
    local max_fanid = 0 -- 维护一个最大风扇id给多风扇板场景
    local pump_table = {}
    local fan_table = {}

    fan_num = (buf_len - PID_READ_HEAD_LEN) / PID_SPD_ONE_FAN_LEN
    for i = 0, fan_num - 1 do
        idx0 = PID_READ_HEAD_LEN + i * PID_SPD_ONE_FAN_LEN
        pid_id = make_word(pwm_buffer[idx0 + 1], pwm_buffer[idx0])
        fan_pwm = pwm_buffer[idx0 + 4]
        target_pwm = fan_pwm / 2.55  -- 转换为[0,100]的转速百分比

        -- 为0xffff为无效散热器件id
        if pid_id == 0xffff then
           goto continue
        end
        slot, id = get_device_id_and_slot(pid_id)
        if id > PUMP_ID_OFFSET then
            pump_table[slot] = target_pwm
            log:debug("[Cooling] target_pwm:%s", pump_table[slot])
        else
            target_pwm = target_pwm < 10 and 10 or target_pwm -- 若计算出来的转速小于10%，则下发10%转速
            fan_table[slot] = target_pwm
            max_fanid = id > max_fanid and id or max_fanid
        end
        ::continue::
    end
    pid_device_table[props.COOLING_LIQUID] = pump_table
    -- 未拿到风扇对象的将
    for i = 1, max_fanid, 1 do
        if not fan_table[i] then
            fan_table[i] = 0
        end
    end
    pid_device_table[props.COOLING_AIR] = fan_table
end

-- 风扇和泵的自动调速
function cooling_mgmt:set_best_pwm_to_cooling_device(pid_device_table)
    local fan_ctrl_mode = self.control_mode[props.COOLING_AIR]
    if fan_ctrl_mode and (fan_ctrl_mode == cooling_enums.modes.Auto or fan_ctrl_mode == cooling_enums.modes.Mixed) then
        skynet.fork_once(function()
            -- 设置风扇转速
            self:cooling_set_best_pwm_to_fan(pid_device_table[props.COOLING_AIR])
        end)
    end

    local pump_ctrl_mode = self.control_mode[props.COOLING_LIQUID]
    if pump_ctrl_mode and 
        (pump_ctrl_mode == cooling_enums.modes.Auto or pump_ctrl_mode == cooling_enums.modes.Mixed) then
        skynet.fork_once(function()
            -- 设置泵转速
            self:cooling_set_best_pwm_to_pump(pid_device_table[props.COOLING_LIQUID])
        end)
    end
end

-- 风扇和泵的手动调速
function cooling_mgmt:set_manual_pwm_to_cooling_device(fan_bck_t, pump_bck_t)
    -- 风扇手动调速
    if not self.control_mode[props.COOLING_AIR] then -- 未找到风扇的控制模式
        log:debug("Get fan ctrl mode property failed")
        return
    end
    if self.control_mode[props.COOLING_AIR] ~= cooling_enums.modes.Manual then
        -- 切换自动模式后需要将之前的标志重新初始化
        fan_bck_t.pre_manual_level = nil
        fan_bck_t.manual_level_set_flag = false
    else
        local cur_manual_level = self.fans_config_instance:get_manual_level()
        -- 已下发过一次手动转速不再重复下发
        if fan_bck_t.pre_manual_level == cur_manual_level and not fan_bck_t.manual_level_set_flag then
            log:debug('Cur manual level: %s, pre manual level: %s', cur_manual_level, fan_bck_t.pre_manual_level)
            goto continue
        end
        local ok, err_msg = pcall(self.cooling_set_manual_level_to_fan, self, cur_manual_level)
        if not ok then
            log:error("Set all fan level to (%s) failed, err_msg: %s", cur_manual_level, err_msg)
        end
        fan_bck_t.pre_manual_level = cur_manual_level
        fan_bck_t.manual_level_set_flag = false
    end

    ::continue::

    -- 泵的手动调速
    if not self.control_mode[props.COOLING_LIQUID] then -- 未找到泵的控制模式
        log:debug("Get pump ctrl mode property failed")
        return
    end
    if self.control_mode[props.COOLING_LIQUID] ~= cooling_enums.modes.Manual then
        pump_bck_t.pre_manual_level_tab = nil
    else
        local cur_manual_level_tab = self.pumps_instance:get_all_cooling_device_manual_level()
        if type(pump_bck_t.pre_manual_level_tab) == 'table' and type(cur_manual_level_tab) == 'table' and
            table.concat(pump_bck_t.pre_manual_level_tab) == table.concat(cur_manual_level_tab) then
            return
        end
        local ok, err_msg = pcall(self.cooling_set_best_pwm_to_pump, self, cur_manual_level_tab)
        if not ok then
            log:error("Set all pump level to (%s) failed, %s", table.concat(cur_manual_level_tab, ' ', 1), err_msg)
        end
        pump_bck_t.pre_manual_level_tab = cur_manual_level_tab
    end
end


function cooling_mgmt:set_one_fan_pwm(fan_table)
    for fan_id, target_pwm in pairs(fan_table) do
        self:cooling_set_fan_speed(fan_id, target_pwm)
    end
end

function cooling_mgmt:cooling_set_fan_pwm_batch(fan_table, thermal_obj)
    fan_table[0] = 0 -- 0表示自动调速，1表示手动调速
    local ok, rsp = pcall(thermal_obj.SetPWM_PACKED, thermal_obj, context.new(), fan_table)
    if not ok then
        log:info("Set all fan pwm failed, err_msg: %s", rsp)
        return false
    else
        -- 0设置成功 1设置失败
        log:info("Set all fan pwm, pwm(%s), result(%s)", table.concat(fan_table, ' ', 1), rsp.Result)
        return true
    end
end

-- 风扇手动转速比下发,手动模式转速仅变化后下发一次
function cooling_mgmt:cooling_set_manual_level_to_fan(value)
    local thermal_obj = self.fans_data:get_thermal_obj()
    local ok, rsp
    -- 此处下发的转速表一定是全一样的值
    local cur_manual_level_tab = self.fans_instance:get_same_manual_level_table(value)
    if thermal_obj then -- 支持风扇转速批量设置
        -- 如果风扇转速批量设置成功直接退出，否则调用每个风扇对象单独下发接口再次设置一次
        -- 兼容1U机型批量下发转速必须保证每个转速值一致
        cur_manual_level_tab[0] = 1 -- 0表示自动调速，1表示手动调速
        ok, rsp = pcall(thermal_obj.SetPWM_PACKED, thermal_obj, context.new(), cur_manual_level_tab)
        if not ok then
            log:error("Set all fan pwm failed, pwm(%s), err_msg: %s", value, rsp)
        elseif rsp.Result ~= 0 then -- 0设置成功 1设置失败
            log:error("Set all fan pwm failed, pwm(%s), result(%s)", value, rsp.Result)
        else
            return true
        end
    end
    -- 调用单个风扇设置转速接口下发
    local set_single_level_result = true
    for fan_id, fan_level in pairs(cur_manual_level_tab) do
        local fans_info = self.fans_data:get_fans_info_with_fan_id()
        local fan_obj = fans_info[fan_id]
        if not fan_obj and fan_level ~= 0 then -- 多风扇板机型fan_level填充为0则不下发
            log:error("The fan(%s) object is not found", fan_id)
            set_single_level_result = false
            goto continue
        end
        ok, rsp = pcall(fan_obj.SetFanPWM_PACKED, fan_obj, context.new(), fan_level)
        if not ok then
            log:error("Set fan(%s) pwm(%s) failed, err_msg: %s", fan_id, fan_level, rsp)
            set_single_level_result = false
        elseif rsp.Result ~= 0 then -- 0设置成功 1设置失败
            log:error("Set fan(%s) pwm(%s) failed, Result: %s", fan_id, fan_level, rsp.Result)
            set_single_level_result = false
        end
        ::continue::
    end
    return set_single_level_result
end

-- 从PID获取风扇转速，然后上传到资源树
function cooling_mgmt:cooling_set_best_pwm_to_fan(fan_table)
    if not next(fan_table) or not self.fans_config_instance:get_obj() then
        log:debug("Set fan pwm failed, fan_table:%s, fan_config:%s",
            next(fan_table), self.fans_config_instance:get_obj())
        return
    end
    local thermal_obj = self.fans_data:get_thermal_obj()
    if thermal_obj then -- 支持风扇转速批量设置
        self:cooling_set_fan_pwm_batch(fan_table, thermal_obj)
    else
        self:set_one_fan_pwm(fan_table)
    end
end

function cooling_mgmt:cooling_set_best_pwm_to_pump(pump_table)
    local thermal_pumps_obj = self.pumps_data:get_obj()
    if not next(pump_table) or thermal_pumps_obj == nil or not self.pumps_config_instance:get_obj() then
        log:debug("Set all pump pwm failed, pump_table:%s, thermal_obj:%s, config_obj:%s",
            next(pump_table), thermal_pumps_obj, self.pumps_config_instance:get_obj())
        return
    end

    self.pumps_data:set_pumps_level(context.new(), pump_table)
end

local fan_position_double_check = false
-- 传入pwm百分比
function cooling_mgmt:cooling_set_fan_speed(fan_id, pwm)
    ::continue::
    local fans_info = self.fans_data:get_fans_info_with_fan_id()
    local fan_obj = fans_info[fan_id]
    if not fan_obj then
        log:debug("cannot find fan in resource tree list, fan=%d", fan_id)
        return
    end

    -- 风扇不在位，后转子一定不在位
    if fan_obj[props.FAN_REAR_PRESENCE] ~= 1 then
        -- 能效内存中风扇不在位，重新获取一遍资源树信息，进行double check
        if not fan_position_double_check then
            self.fans_data:update_one_fan_info(fan_obj.path) -- 更新一次单个风扇信息
            fan_position_double_check = true
            log:debug('double check is finished')
            goto continue
        end
        log:debug('Fan(id:%u) is not in position, cant set pwm, rear presence(%u)', fan_obj[props.FAN_ID],
            fan_obj[props.FAN_REAR_PRESENCE])
        return
    end

    local rsp = fan_obj:SetFanPWM_PACKED(context.new(), pwm)
    log:debug("set pwm to Resource tree, fan_id=%d, fan_slot=%d, pwm=%.2f%%, HardwarePWM=%.2f ret:%d",
        fan_obj[props.FAN_ID], fan_obj[props.FAN_SLOT], pwm, 2.55 * pwm, rsp.Result) -- 乘2.55转换为PWM值
    -- 保证每一次风扇异常都能实时刷新一次资源树
    fan_position_double_check = false
end

-- 准备复位
function cooling_mgmt:on_reboot_prepare()
    -- 记录平滑重启前的调速模式，复位失败需还原回之前的模式
    self.fans_config_instance:mark_cooling_mode()

    local MANUFACTURE_PATH = '/bmc/kepler/Managers/1/Manufacture'
    -- 检查是否是装备包
    local is_manufacture = pcall(require, 'thermal_mgmt_manufacture')
    local manufacturing_mode
    local manufacture_obj
    client:ForeachStatusObjects(function (obj)
        if obj.path == MANUFACTURE_PATH then
            manufacture_obj = obj
        end
    end)
    if not manufacture_obj then
        log:error("Get manufacture obj(%s) failed", MANUFACTURE_PATH)
        manufacturing_mode = 'get obj failed'
    else
        manufacturing_mode = manufacture_obj.Mode == 0 and 'ft' or 'st'
    end

    -- 如果是装备模式时，则手动设置风扇转速50%，然后设复位保持转速为50%(保持只高不低), 非装备模式，复位时100%满转
    -- 是装备包且Mode为ft则为装备模式(0 ft, 1 st)
    local fan_level = (is_manufacture and manufacturing_mode == 'ft') and 50 or 100
    self.fans_config_instance:set_manual_mode_and_level(fan_level)
    log:notice("Is manufacture pack(%s), manufacture mode(%s)", is_manufacture, manufacturing_mode)
    local ok, err_msg =  pcall(self.set_fan_level_hw, self, 0xff, fan_level)
    if ok then
        log:notice("Set all fan level to (%d) successfully", fan_level)
    else
        log:error("Set all fan level to (%d) failed, %s", 0xff, fan_level, err_msg)
    end
    -- 平滑重启泵下发100%转速
    if self.pumps_config_instance:pump_ctrl_supported() then
        self.pumps_config_instance:set_ctrl_mode(cooling_enums.modes.Manual)
        self.pumps_config_instance:set_manual_timeout(100000000)
        ok = self.pumps_config_instance:set_manual_level(100)
        if ok then
            log:notice("Set all pump level to 100 successfully")
        else
            log:notice("Set all pump level to 100 failed")
        end
    end
    return 0
end

-- 执行复位
function cooling_mgmt:on_reboot_action()
    log:notice('[cooling_app] has no extra action for reboot')

    return 0
end

-- 取消复位
function cooling_mgmt:on_reboot_cancel()
    -- 复位失败，恢复之前的调速模式
    self.fans_config_instance:recover_mark_cooling_mode()
    log:notice('[cooling_app] recover cooling mod successfully')
end

function cooling_mgmt:register()
    self.service:ImplAirCoolingConfigFanSnapshotGetActivatedPolicyFactors(function(obj, ctx, ...)
        return self:get_faninfo_overall(ctx, ...)
    end)
end

return singleton(cooling_mgmt)
