-- 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 fan_exp_policy = require 'basic_cooling.cooling_abnormal_device.abnormal_fan'
local fans_config = require 'basic_cooling.cooling_config.fans_config'
local air_config = require 'basic_cooling.cooling_config.air_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 cooling_group = require 'basic_cooling.cooling_device.cooling_group'
local fan_service = require 'fan_service'
local pump_service = require 'pump_service'
local singleton = require 'mc.singleton'
local context = require 'mc.context'
local mc_utils = require 'mc.utils'
local custom_msg = require 'messages.custom'
local vos = require 'utils.vos'

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
local TIME_1S <const> = 1000
local TIME_2S <const> = 2000

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.cooling_group = cooling_group.get_instance()
    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.air_config_instance = air_config.get_instance()
    self.pumps_config_instance = pumps_config.get_instance()
    self.fan_exp_policy = fan_exp_policy.get_instance()
    self.pump_exp_policy = pump_exp_policy.get_instance()
    self.fan_service_instance = fan_service.get_instance()
    self.pump_service_instance = pump_service.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

local active_algorithm = {
    [props.COOLING_AIR] = function(self, value)
        self.air_config_instance:set_active_algorithm(value)
    end,
    [props.COOLING_LIQUID] = function(self, value)
        self.pumps_config_instance:set_active_algorithm(value)
    end
}

function cooling_mgmt:set_active_algorithm(active_algorithm_table)
    for medium, value in pairs(active_algorithm_table) do
        active_algorithm[medium](self, value)
    end
end

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

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

function cooling_mgmt:update_manual_timeout(mixed_mode_supported, 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 = bck_t.real_timeout == nil and (config_obj[props.TIME_OUT] * TIME_1S) or bck_t.real_timeout
    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: %s', device_type, timeout)
    bck_t.real_timeout = timeout
    config_obj[props.TIME_OUT] = math.floor(timeout / TIME_1S) -- timeout使用vos_tick_get获取整除1000为秒
    -- 风扇模式持久化时不对手动模式超时做处理
    if timeout == 0 and (device_type == 'pump' or 
        self.air_config_instance:get_ctrl_mode_persist_type() == cooling_enums.persist_type.Memory) then
        if 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:get_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')
    local fans_config_obj = self.fans_config_instance:get_obj()
    local requirements_obj = self.requirements_instance:get_objs()
    update_custom_mode_target_temp(fans_config_obj, requirements_obj)

    -- 更新温度点目标值
    bck_t.pre_smt_cl_mode =
        update_requirement_target_temp(bck_t.pre_smt_cl_mode, requirements_obj, fans_config_obj)

    -- 更新温度点生效状态更新
    self.requirements_instance:update_objs_isvalid(self.data, self.fans_config_instance:get_disk_row_temp_available(),
        power_state, fans_config_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, requirements_obj, 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, requirements_obj)
        if not ok then
            log:error('Requirement is added, set tempconfig to pid failed, %s', res)
        end
        ok, res = pcall(cooling_pid_intf.cooling_sensor_name_to_pid, self.service, requirements_obj)
        if not ok then
            log:error('Requirement is added, set sensor name 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.real_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 mixed_mode_supported = self.fans_config_instance:get_obj()[props.MIXED_MODE_SUPPORTED]
    local ok, rsp
    if self.is_fan_manual_mode then
        ok, rsp = pcall(
            self.update_manual_timeout, self,
            mixed_mode_supported, 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(
            self.update_manual_timeout, self,
            mixed_mode_supported, 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()
    local exp_policy_instance = exp_policy.get_instance()
    while true do
        cur_sys_time = vos.vos_tick_get()
        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 > TIME_1S 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 >= TIME_1S or cur_sys_time < T2 then
            ok, rsp = pcall(exp_policy_instance.exception_scan, exp_policy_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 >= TIME_2S 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

-- 初始状态若风扇持久化为Manual则刷新风扇模式同时每个风扇下发其持久化的手动模式转速百分比
function cooling_mgmt:update_fans_manual_mode_status()
    local ctrl_mode_persist_type = self.air_config_instance:get_ctrl_mode_persist_type()
    local ctrl_mode = self.air_config_instance:get_ctrl_mode_persist()
    local manual_level = self.air_config_instance:get_manual_speed_percent_persist()

    if ctrl_mode_persist_type == cooling_enums.persist_type.Memory or ctrl_mode ~= cooling_enums.modes.Manual then
        return
    end
    log:notice("fan ctrl mode persist type is %s, manual level is %s", ctrl_mode_persist_type, manual_level)
    self.fans_config_instance:set_ctrl_mode(cooling_enums.modes.Manual)
    local ok, err_msg = pcall(self.set_fan_level_hw, self, 0xff, nil)
    if ok then
        log:notice("Set all fan level successfully")
    else
        log:error("Set all fan level failed, %s", err_msg)
    end
end

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

function cooling_mgmt:set_fan_ctrl_mode(mode, manual_time)
    self:set_fan_ctrl_mode_persist_type(cooling_enums.persist_type.Memory)

    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)
        -- 此处置为nil原因为设置新的超时时间后续需重新倒计时
        self.fans_config_instance.manual_mode_status_table.real_timeout = nil
        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_fan_ctrl_mode_persist(mode, manual_time, ctrl_mode_persist_type)
    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)
        -- 此处置为nil原因为设置新的超时时间后续需重新倒计时
        self.fans_config_instance.manual_mode_status_table.real_timeout = nil
        local manual_level = self.air_config_instance:get_manual_speed_percent_persist()
        -- 未设置过手动转速时，以当前风扇转速为其手动转速
        if manual_level == 0 then
            self.fans_instance:set_all_cooling_device_manual_level_init(ctrl_mode_persist_type)
        else
            self.fans_instance:set_all_cooling_device_manual_level_persist(manual_level, ctrl_mode_persist_type)
        end
    end

    self.fans_config_instance:set_ctrl_mode(mode)
    self.air_config_instance:set_ctrl_mode_persist(mode)

    return true
end

-- 设置风扇模式的持久化类型
function cooling_mgmt:set_fan_ctrl_mode_persist_type(ctrl_mode_persist_type)
    if ctrl_mode_persist_type ~= cooling_enums.persist_type.Memory and 
        ctrl_mode_persist_type ~= cooling_enums.persist_type.ResetPer and
        ctrl_mode_persist_type ~= cooling_enums.persist_type.PoweroffPer then
        log:error('Cooling mode persist type is not supported')
        return false
    end
    self.air_config_instance:set_ctrl_mode_persist_type(ctrl_mode_persist_type)
    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)
    if fan_id == 0xff then
        local fan_table = self.fans_instance:get_all_cooling_device_manual_level()
        if not next(fan_table) then
            return false
        end
        local ok, rsp = pcall(function()
            return self.fan_service_instance:set_fans_pwm(fan_table)
        end)
        if not ok then
            log:error("Set all fan pwm failed, pwm(%s), err_msg: %s", table.concat(fan_table), rsp)
            return false
        end
        return rsp == 0 and true or false
    end
    local fan_obj = self.cooling_group:get_fan_obj(fan_id)
    if not fan_obj then
        log:error('Get fan(id=%s) obj failed', fan_id)
        return false
    end
    local ok, rsp = pcall(function()
        return self.fan_service_instance:set_fan_pwm(fan_obj.SystemId, fan_id, fan_level)
    end)
    if not ok then
        log:error('Set fan(%d) pwm to %d failed, err_msg: %s', fan_id, fan_level, rsp)
        return false
    end
    return rsp == 0 and true or 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
            self.fans_config_instance:set_ctrl_mode(cooling_enums.modes.Auto)
        end
        if self.air_config_instance:get_ctrl_mode_persist_type() ~= cooling_enums.persist_type.Memory then
            level = self.air_config_instance:get_manual_speed_percent_persist()
        end
        if not level then
            level = self.fans_config_instance:get_level_range()[1]
        end
        log:notice("Init manual speed percent is %s", level)
        self.fans_config_instance:set_manual_level_start(level) -- 初始化手动转速
    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
            self.pumps_config_instance:set_ctrl_mode(cooling_enums.modes.Auto)
        end
        level = self.pumps_config_instance:get_level_range()[1]
        self.pumps_config_instance:set_manual_level(level) -- 初始化手动转速
    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, active_algorithm_table)
    local exp_speed = self.fan_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]
                active_algorithm_table[props.COOLING_AIR] = cooling_enums.auto_active_algorithm.PID
            end
        end
    end
end

function cooling_mgmt:update_pump_pwm_with_exp_speed(pump_table, active_algorithm_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]
                active_algorithm_table[props.COOLING_LIQUID] = cooling_enums.auto_active_algorithm.PID
            end
        end
    end
end

function cooling_mgmt:update_pwm_with_rpc(device_control_table, active_algorithm_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
                    active_algorithm_table[medium] = cooling_enums.auto_active_algorithm.MPC
            end
        end
    end
end

function cooling_mgmt:update_pwm_with_exp_speed(device_control_table, active_algorithm_table)
    -- 更新风扇异常转速
    self:update_fan_pwm_with_exp_speed(device_control_table[props.COOLING_AIR], active_algorithm_table)
    -- 更新泵异常转速
    self:update_pump_pwm_with_exp_speed(device_control_table[props.COOLING_LIQUID], active_algorithm_table)
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, active_algorithm_table)
    self:update_fan_pwm_with_level(device_control_table, active_algorithm_table)
    self:update_pump_pwm_with_level(device_control_table, active_algorithm_table)
end

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

    if not self.control_mode[props.COOLING_AIR] or
        self.control_mode[props.COOLING_AIR] ~= cooling_enums.modes.Mixed then
        return
    end
    manual_device_table[props.COOLING_AIR] = self.cooling_group:get_fan_group_control_table()
    local min_pwm
    for id, pwm in pairs(manual_device_table[props.COOLING_AIR]) do
        if self.fans_config_instance:get_min_speed_enabled() == true then
            min_pwm = self.fans_config_instance:get_min_speed_percent()
        else
            min_pwm = pwm
        end
        if device_control_table[props.COOLING_AIR][id] and device_control_table[props.COOLING_AIR][id] < min_pwm then
            device_control_table[props.COOLING_AIR][id] = min_pwm
            active_algorithm_table[props.COOLING_AIR] = cooling_enums.auto_active_algorithm.DEFAULT
        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, active_algorithm_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.cooling_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
                active_algorithm_table[props.COOLING_LIQUID] = cooling_enums.auto_active_algorithm.DEFAULT
            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_nums = self.fans_instance:get_obj_count()
    if fan_nums ~= (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_nums
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, active_algorithm_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
            active_algorithm_table[props.COOLING_LIQUID] = cooling_enums.auto_active_algorithm.PID
            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
            active_algorithm_table[props.COOLING_AIR] = cooling_enums.auto_active_algorithm.PID
        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, active_algorithm_table)
    -- 风扇手动调速
    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
        active_algorithm_table[props.COOLING_AIR] = cooling_enums.auto_active_algorithm.DEFAULT
        local cur_manual_level = self.fans_instance:get_all_cooling_device_manual_level()
        -- 检查风扇转速范围，限制风扇转速在范围内
        self:cooling_check_fan_level_range(cur_manual_level)
        -- 已下发过一次手动转速不再重复下发
        local level_consistent = mc_utils.table_compare(fan_bck_t.pre_manual_level, cur_manual_level)
        -- 这里判断实际转速与预期手动转速是否一致，避免出现AC后模式为手动，预期与实际转速不一致
        local hardware_pwm_equal_level = self.fans_instance:is_hardware_pwm_equal_level()
        if level_consistent and not fan_bck_t.manual_level_set_flag and hardware_pwm_equal_level then
            log:debug('level_consistent: %s, hardware_pwm_equal_level: %s', level_consistent, hardware_pwm_equal_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 failed, err_msg: %s", err_msg)
        else
            log:notice("Set all fan level successfully")
        end
        fan_bck_t.pre_manual_level = mc_utils.table_copy(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
        active_algorithm_table[props.COOLING_LIQUID] = cooling_enums.auto_active_algorithm.DEFAULT
        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:cooling_set_manual_level_to_fan(value)
    local cur_manual_level_tab = mc_utils.table_copy(value)
    cur_manual_level_tab[0] = 1 -- 0表示自动调速，1表示手动调速
    local ok, result = pcall(function()
        return self.fan_service_instance:set_fans_pwm(cur_manual_level_tab)
    end)
    if not ok then
        log:error("Set all fan pwm failed, err_msg: %s", result)
        return false
    end
    return result == 0 and true or false
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
    -- 自动调速检查风扇转速范围，限制风扇转速在范围内
    self:cooling_check_fan_level_range(fan_table)
    log:info("Set all fan pwm, pwm(%s)", table.concat(fan_table, ' '))
    -- 直接调用fan_service的接口
    fan_table[0] = 0 --0表示自动调速，1表示手动调速
    local ok, result = pcall(function()
        return self.fan_service_instance:set_fans_pwm(fan_table)
    end)
    if not ok or result ~= 0 then
        log:info("Set all fan pwm failed, err_msg: %s", result)
    else
        log:info("Set all fan pwm successfully")
    end
end

function cooling_mgmt:cooling_check_fan_level_range(fan_level_table)
    local min = self.fans_config_instance:get_coolingconfig_minimal_level() or 0
    local max = self.fans_config_instance:get_coolingconfig_max_level() or 100
    for k, _ in pairs(fan_level_table) do
        fan_level_table[k] = math.max(fan_level_table[k], min)
        fan_level_table[k] = math.min(fan_level_table[k], max)
    end
end

function cooling_mgmt:cooling_set_best_pwm_to_pump(pump_table)
    if not next(pump_table) then
        log:debug("Set all pump pwm failed, pump_table:%s", next(pump_table))
        return
    end
    log:info("Set all pump pwm, pwm(%s)", table.concat(pump_table, ' '))
    local ok, result = pcall(function()
        return self.pump_service_instance:set_pumps_pwm(pump_table)
    end)
    if ok and result == 0 then
        log:info('Set all pump pwm successfully')
    else
        log:info('Set all pump pwm failed, err_msg: %s', result)
    end
    return result == 0 and true or 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 and err_msg then
        log:notice("Set all fan level to (%d) successfully", fan_level)
    else
        log:error("Set all fan level to (%d) failed, %s", 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:get_cooling_device_policy(device_type, mode)
    if device_type ~= 'Fan' then
        log:error('Get cooling device policy failed, device type is out of range')
        error(custom_msg.PropertyValueOutOfRange(device_type, '%Type'))
    end
    local res = nil
    if mode == 0 then
        res = self.air_config_instance:get_active_algorithm()
        if not res then
            log:error('Get cooling device policy failed')
            error(custom_msg.OperationFailed())
        end
        if res == cooling_enums.auto_active_algorithm.PID then
            res = self:get_faninfo_overall()[4]
            return res
        end
        return res
    elseif mode == 1 then
        res = self.air_config_instance:get_active_algorithm()
        if not res then
            log:error('Get cooling device policy failed')
            error(custom_msg.OperationFailed())
        end
        return res
    else
        log:error('Get cooling device policy failed, mode is out of range')
        error(custom_msg.PropertyValueOutOfRange(mode, '%Mode'))
    end
end

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

return singleton(cooling_mgmt)
