-- 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 class = require 'mc.class'
local class_mgnt = require 'mc.class_mgnt'
local Singleton = require 'mc.singleton'
local signal = require 'mc.signal'
local log = require 'mc.logging'
local custom_msg = require 'messages.custom'
local privilege = require 'mc.privilege'
local utils = require 'basic_cooling.cooling_utils'
local f_enum = require 'class.types.types'
local cooling_enums = require 'basic_cooling.define.cooling_enums'
local props = require 'basic_cooling.define.cooling_properties'
local enums = require 'basic_cooling.define.cooling_enums'
local cooling_policys = require 'basic_cooling.cooling_policys'
local mc_utils = require 'mc.utils'
local sqlite3 = require 'lsqlite3'
local cjson = require 'cjson'
local fans_config = require 'basic_cooling.cooling_config.fans_config'
local data_keeping = require 'basic_cooling.data_keeping.external_data_keeping'

local SMART_COOLING_TABLE_LENGTH<const> = 3
local cooling_requirements = class()
local MAX_REQ_ID<const> = 0xff
local START_SLOT<const> = 255

local POLICY_TYPE_MAP<const> = {
    [1] = 'Inlet',
    [2] = 'Disk'
}
local TEMPERATURE_TYPE_MAP<const> = {
    [1] = 'Cpu',
    [2] = 'Outlet',
    [3] = 'Disk',
    [4] = 'Memory',
    [5] = 'PCH',
    [6] = 'VRD',
    [7] = 'VDDQ',
    [8] = 'NPUHbm',
    [9] = 'NPUAiCore',
    [10] = 'NPUBoard',
    [11] = 'Inlet',
    [12] = 'SoCBoardOutlet',
    [13] = 'SoCBoardInlet',
    [0xffffffff] = 'Unknown',
}

function cooling_requirements:ctor(db, base_service, cooling_data, persist)
    self.db = db
    self.persist = persist
    self.objs = {}
    self.objs_last_valid_state = {} -- 记录上一次目标调速策略的生效状态
    self.requirement_id_map = {} -- CoolingRequirement对象的id表，用于冲突判断
    self.is_requirement_updated = false
    self.type_obj_map = {}
    self.requirement_info_changed = signal.new()
    self.base_service = base_service
    self.cooling_areas = cooling_data.cooling_areas -- coolingarea对象表，key为AreaId,value为对象
    self.cooling_data = cooling_data
    self.policys_instance = cooling_policys.get_instance()
    self.fans_config_instance = fans_config.get_instance()
    self.data_keeping_objs = data_keeping.get_instance().data_keeping_objs
    self.cpu_data = self.data_keeping_objs.cpu_data_keeping
    self.memory_data = self.data_keeping_objs.memory_data_keeping
    self.valid_cpu_requirements = {} -- 存储生效的CPU目标调速策略
    self.valid_memory_requirements = {} -- 存储生效的memory目标调速策略
    self.is_first = true -- 是否为第一次进行valid生效条件判断
    self.default_cpu_valid = false -- 默认CPU调速策略是否生效
    self.default_memory_valid = false -- 默认内存调速策略是否生效
end

function cooling_requirements:init()
    -- 私有属性属性变更监听
    class_mgnt('CoolingRequirement').property_changed:on(function(object, prop, value)
        self:private_property_changed_callback(object, prop, value)
    end)
end

-- 获取Requirement前8位的base_id
local function get_base_id(id)
    if id < MAX_REQ_ID then
        return id
    end
    return (id >> 8) & MAX_REQ_ID
end

-- 获取Requirement后8位的槽位号
local function get_slot(id)
    return id & MAX_REQ_ID
end

local function get_requirement_id(base_id, slot)
    return (base_id << 8) | slot
end

local function get_new_slot(self, base_id)
    local new_requirement_id
    for new_slot = START_SLOT, 1, -1 do
        new_requirement_id = get_requirement_id(base_id, new_slot)
        if not self.requirement_id_map[new_requirement_id] then
            return new_slot
        end
    end
    return 0
end

-- 框架持久化前回调，如果preprocess_db返回true则立刻进行资源上树,否则业务自己上树
function cooling_requirements:on_preprocess_cb(object)
    -- CoolingRequirementId对象主键出现相同值冲突
    if self.requirement_id_map[object[props.REQUIREMENT_ID]] then
        local origin_requirement_id = object[props.REQUIREMENT_ID]
        local base_id = get_base_id(object[props.REQUIREMENT_ID])
        local new_slot = get_new_slot(self, base_id)
        local new_requirement_id = get_requirement_id(base_id, new_slot)
        local ok, err_msg = pcall(object.force_set_prop, object, props.REQUIREMENT_ID, new_requirement_id)
        if not ok then
            log:error('Set the value %s of the conflict property RequirementId of object %s to %s failed, err_msg:%s',
                origin_requirement_id, object[props.OBJECT_NAME], new_requirement_id, err_msg
            )
        else
            self.requirement_id_map[new_requirement_id] = true
            log:warn('Set the value %s of the conflict property RequirementId of object %s to 0x%x successfully',
                origin_requirement_id, object[props.OBJECT_NAME], new_requirement_id
            )
        end
    else
        self.requirement_id_map[object[props.REQUIREMENT_ID]] = true
    end

    object[props.ORIGIN_TARGET_TEMP_CELSIUS] = object[props.TARGET_TEMP_CELSIUS]
    object[props.ORIGIN_FAILED_VALUE] = object[props.FAILED_VALUE]
    object[props.ORIGIN_MAX_ALLOWED_TEMPERATURE_CELSIUS] = object[props.MAX_ALLOWED_TEMPERATURE_CELSIUS]
    if #object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] ~= 0 then
        object[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE] = object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS]
    else
        object[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE] = object[props.SMART_COOLING_TARTGET_TEMPERATURE]
    end
    
    return true
end

function cooling_requirements:obj_add_callback(class_name, object, position)
    object[props.REQ_BASE_ID] = get_base_id(object[props.REQUIREMENT_ID])
    object[props.REQ_SLOT] = get_slot(object[props.REQUIREMENT_ID])
    log:debug('Add CoolingRequirement Id: 0x%x', object[props.REQUIREMENT_ID])
    self.objs_last_valid_state[object[props.REQUIREMENT_ID]] = -1 -- 初始化为-1表示调速策略不生效
    self.objs[object[props.REQUIREMENT_ID]] = object -- 用CSR配置的Requirement的Id作为key进行存储，应当唯一

    self:set_requirement_updated_flag(true)

    if object[props.TEMPERATURE_TYPE] ~= f_enum.TemperatureType.InvalidType:value() then
        self:insert_to_type_obj_map(object)
    end

    if #object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] ~= 0 then
        object[props.SMART_COOLING_TARTGET_TEMPERATURE] = object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS]
    else
        object[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] = object[props.SMART_COOLING_TARTGET_TEMPERATURE]
    end

    object:get_mdb_object('bmc.kepler.Systems.CoolingRequirement').property_before_change:on(
        function(name, value, sender)
            return self:requirement_props_before_change_callback(object, name, value, sender)
    end)
    object:get_mdb_object('bmc.kepler.Systems.CoolingRequirement').property_changed:on(function(name, value, sender)
        self:requirement_props_changed_callback(object, name, value, sender)
    end)

    self.base_service:ImplCoolingRequirementCoolingRequirementSetTargetTemperature(function(obj, ctx, ...)
        return self:SetTargetTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetSmartCoolingTargetTemperature(function(obj, ctx, ...)
        return self:SetSmartCoolingTargetTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetBasicCoolingTargetTemperature(function(obj, ctx, ...)
        return self:SetBasicCoolingTargetTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetMaxAllowedTemperature(function(obj, ctx, ...)
        return self:SetMaxAllowedTemperature(obj, ctx, ...)
    end)
    self.base_service:ImplCoolingRequirementCoolingRequirementSetFailedValue(function(obj, ctx, ...)
        return self:SetFailedValue(obj, ctx, ...)
    end)
end

function cooling_requirements:requirement_props_before_change_callback(object, name, value, sender)
    return true
end

function cooling_requirements:requirement_props_changed_callback(object, name, value, sender)
    if name == props.TARGET_TEMP_CELSIUS then
        self:set_requirement_updated_flag(true)
    end
end

function cooling_requirements:get_requirement_updated_flag()
    return self.is_requirement_updated
end

function cooling_requirements:set_requirement_updated_flag(flag)
    self.is_requirement_updated = flag
end

function cooling_requirements:get_inlet_requirement()
    if not self.inlet_requirement then
        local inlet_o = self:get_first_obj_by_temp_type(f_enum.TemperatureType.Inlet:value())
        self.inlet_requirement = inlet_o
    end

    return self.inlet_requirement
end

function cooling_requirements:get_cpu_requirements()
    if not self.cpu_requirements then
        local cpu_objs = self:get_objs_by_temp_type(f_enum.TemperatureType.Cpu:value())
        self.cpu_requirements = cpu_objs
    end

    return self.cpu_requirements
end

function cooling_requirements:get_npu_requirements()
    if not self.npu_requirements then
        local npu_objs = self:get_objs_by_temp_type(f_enum.TemperatureType.NPUAiCore:value())
        self.npu_requirements = npu_objs
    end

    return self.npu_requirements
end

function cooling_requirements:get_memory_requirements()
    return self:get_objs_by_temp_type(f_enum.TemperatureType.Memory:value())
end

function cooling_requirements:obj_delete_callback(class_name, object, position)
    -- 对象卸载需要将相关的异常调速删除
    self.requirement_info_changed:emit('DeleteRequirementExp', object)

    if object[props.TEMPERATURE_TYPE] ~= f_enum.TemperatureType.InvalidType:value() then
        self:delete_from_type_obj_map(object)
    end

    if not self.objs[object[props.REQUIREMENT_ID]] then
        log:error("Delete CoolingRequirement(0x%x) but object is nil", object[props.REQUIREMENT_ID])
        return
    end
    self.requirement_id_map[object[props.REQUIREMENT_ID]] = nil
    self.objs[object[props.REQUIREMENT_ID]] = nil
    self.objs_last_valid_state[object[props.REQUIREMENT_ID]] = nil

    self:set_requirement_updated_flag(true)
end

local component_name_map = {
    [1] = 'CPU core',
    [2] = 'outlet',
    [3] = 'disk',
    [4] = 'memory',
    [5] = 'pch',
    [6] = 'vrd',
    [7] = 'vddq',
    [8] = 'hbm',
    [9] = 'aicore',
    [10] = 'npuboard',
}

-- 设置CoolingRequirement数据库数据
local function set_requirement_db_prop(self, primary_key_value, prop_name, prop_value)
    local json_prop_value = prop_value
    if self.db.CoolingRequirement:persistences(prop_name) == 'protect_power_off' then
        if type(prop_value) == 'table' then
            json_prop_value = cjson.encode(prop_value)
        end
        self.persist:per_save(sqlite3.UPDATE, 't_cooling_requirement', {{'RequirementId', primary_key_value}},
            {[prop_name] = {value = json_prop_value, persist_type = 'protect_power_off'}})
    end
end

local smart_cooling_target_temperature = {
    [enums.smart_cooling_mode.COOLING_ENERGY_SAVING_MODE] = 1,
    [enums.smart_cooling_mode.COOLING_HIGH_PERFORMANCE_MODE] = 2,
    [enums.smart_cooling_mode.COOLING_LOW_NOISE_MODE] = 3
}

-- 设置SmartCooling数组
function cooling_requirements:SetSmartCoolingTargetTemperature(obj, ctx, smart_cooling_temp_arr)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if #req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] ~= SMART_COOLING_TABLE_LENGTH then
        log:error('CoolingRequirement(id: %s) is not support smart cooling target temperature celsius',
            req_obj[props.REQUIREMENT_ID])
        error(custom_msg.PropertyModificationNotSupported('%SmartCoolingTargetTemperature'))
    end

    if #smart_cooling_temp_arr ~= SMART_COOLING_TABLE_LENGTH then
        log:error('The length of SmartCoolingTargetTemperature is %s, which is invalid', #smart_cooling_temp_arr)
        error(custom_msg.ArraySizeTooShort('%SmartCoolingTargetTemperature', SMART_COOLING_TABLE_LENGTH))
    end
    for idx = 1, SMART_COOLING_TABLE_LENGTH, 1 do
        if smart_cooling_temp_arr[idx] > req_obj[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE][idx] then
            log:error('The value [%s] of the property CoolingRequirement(id: %s) %s is invalid, ' ..
                'each imported value must be smaller than the original value[%s]',
                table.concat(smart_cooling_temp_arr, ','),
                req_obj[props.REQUIREMENT_ID],
                'SmartCoolingTargetTemperatureCelsius',
                table.concat(req_obj[props.ORIGIN_SMART_COOLING_TARTGET_TEMPERATURE], ',')
            )
            error(custom_msg.ValueOutOfRange('%SmartCoolingTargetTemperature'))
        end
    end
    if mc_utils.table_compare(req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS], smart_cooling_temp_arr) then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.SMART_COOLING_TARTGET_TEMPERATURE,
            smart_cooling_temp_arr)
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS,
            smart_cooling_temp_arr)
    else
        req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE] = smart_cooling_temp_arr
        req_obj[props.SMART_COOLING_TARTGET_TEMPERATURE_CELSIUS] = smart_cooling_temp_arr
    end
    local smart_cooling_mode = self.fans_config_instance:get_smart_cooling_mode()
    if not smart_cooling_mode then
        return
    end
    local index = smart_cooling_target_temperature[smart_cooling_mode]
    if index then
        req_obj[props.TARGET_TEMP_CELSIUS] = smart_cooling_temp_arr[index]
    end
    self:set_requirement_updated_flag(true)
end

-- 设置目标温度
function cooling_requirements:SetBasicCoolingTargetTemperature(obj, ctx, target_temp)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if target_temp < 0 or target_temp > req_obj[props.ORIGIN_TARGET_TEMP_CELSIUS] then
        log:error('The value %s of the property CoolingRequirement(id: %s) %s is out of range, expect[0-%s]',
            target_temp, req_obj[props.REQUIREMENT_ID],
            'TargetTemperatureCelsius', 
            req_obj[props.ORIGIN_TARGET_TEMP_CELSIUS]
        )
        error(custom_msg.ValueOutOfRange('%TargetTemperature'))
    end
    if target_temp == req_obj[props.TARGET_TEMP_CELSIUS] then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.TARGET_TEMP_CELSIUS, target_temp)
    else
        req_obj[props.TARGET_TEMP_CELSIUS] = target_temp
    end
    self:set_requirement_updated_flag(true)
end

function cooling_requirements:SetTargetTemperature(obj, ctx, temp_value, area_no_check)
    local temp_type, success = obj.TemperatureType, false
    local objs = self:get_objs_by_temp_type(temp_type)
    if not objs then
        utils.op(ctx, "Set target temperature of %s temperature adjustment policy(type:%s) failed",
            component_name_map[temp_type] and component_name_map[temp_type] or 'N/A', temp_type)
        error(custom_msg.PropertyModificationNotSupported('%TemperatureCelsius'))
    end
    local base_id
    for _, o in pairs(objs) do
        if not area_no_check then
            base_id = get_base_id(o[props.REQUIREMENT_ID])
            -- 若是无法匹配到对应的CoolingArea，那么无法设置自定义目标温度
            if not self.cooling_areas[base_id] then
                log:error('The CoolingArea(%s) is not exist', base_id)
                utils.op(ctx, "Set target temperature of %s temperature adjustment policy(id:0x%x) failed",
                    component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
                    o[props.REQUIREMENT_ID])
                goto continue
            end
        end

        if not o.CustomSupported then
            log:error('Cooling requirement(id:0x%x) dose not support custom', obj[props.REQUIREMENT_ID])
            utils.op(ctx, "Set target temperature of %s temperature adjustment policy(id:0x%x) failed",
                component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
                o[props.REQUIREMENT_ID])
            goto continue
        end

        local tg_temp = o[props.TARGET_TEMP_RANGE_CELSIUS]
        local temp_range = type(tg_temp) == 'string' and {string.byte(tg_temp, 1, #tg_temp)} or tg_temp
        if not temp_range or #temp_range ~= 2 or temp_value < temp_range[1] or
            temp_value > temp_range[2] then
            log:error('Current val(%u) out of range[%u %u]', temp_value, temp_range[1], temp_range[2])
            utils.op(ctx, "Set target temperature of %s temperature adjustment policy(id:0x%x) failed",
                component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
                o[props.REQUIREMENT_ID])
            goto continue
        end
        o.CustomTargetTemperatureCelsius = temp_value
        utils.op(ctx, "Set target temperature to %d of %s temperature adjustment policy(id:0x%x) successfully",
            temp_value, component_name_map[temp_type] and component_name_map[temp_type] or 'N/A',
            o[props.REQUIREMENT_ID])
        log:info('Set the target temp of requirement objs(id:0x%x, type:%u) to %u', o[props.REQUIREMENT_ID],
            temp_type, temp_value)
        success = true
        ::continue::
    end
    if not success then
        utils.op(ctx, "Set target temperature of %s temperature adjustment policy(type:%s) failed",
            component_name_map[temp_type] and component_name_map[temp_type] or 'N/A', temp_type)
        error(custom_msg.PropertyModificationNotSupported('%TemperatureCelsius'))
    end
end

-- 设置全速阈值
function cooling_requirements:SetMaxAllowedTemperature(obj, ctx, max_allowed_temp)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if max_allowed_temp < 0 or max_allowed_temp > req_obj[props.ORIGIN_MAX_ALLOWED_TEMPERATURE_CELSIUS] then
        log:error('The value %s of the property CoolingRequirement(id: %s) %s is out of range, expect[0-%s]',
            max_allowed_temp, obj[props.REQUIREMENT_ID], max_allowed_temp,
            req_obj[props.ORIGIN_MAX_ALLOWED_TEMPERATURE_CELSIUS]
        )
        error(custom_msg.ValueOutOfRange('%MaxAllowedTemperature'))
    end
    if max_allowed_temp == req_obj[props.MAX_ALLOWED_TEMPERATURE_CELSIUS] then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.MAX_ALLOWED_TEMPERATURE_CELSIUS,
            max_allowed_temp)
    else
        req_obj[props.MAX_ALLOWED_TEMPERATURE_CELSIUS] = max_allowed_temp
    end
    self:set_requirement_updated_flag(true)
end

-- 温度失效转速
function cooling_requirements:SetFailedValue(obj, ctx, failed_value)
    local req_obj = self:get_requirement_by_id(obj[props.REQUIREMENT_ID])
    if failed_value > 100 or failed_value < req_obj[props.ORIGIN_FAILED_VALUE] then
        log:error('The value %s of the property CoolingRequirement(id: %s) %s is out of range, expect[%s-100]',
            failed_value, req_obj[props.REQUIREMENT_ID], 'FailedValue', req_obj[props.ORIGIN_FAILED_VALUE]
        )
        error(custom_msg.ValueOutOfRange('%FailedValue'))
    end
    if failed_value == obj[props.FAILED_VALUE] then
        set_requirement_db_prop(self, req_obj[props.REQUIREMENT_ID], props.FAILED_VALUE, failed_value)
    else
        req_obj[props.FAILED_VALUE] = failed_value
    end
    self:set_requirement_updated_flag(true)
end

-- 通过temperaturetype获取到对应的requirement对象，同一类型requirement可能有多个，返回一个table
function cooling_requirements:get_objs_by_temp_type(temp_type)
    return self.type_obj_map[temp_type]
end

-- 获取第一个temperaturetype为特定值的对象
function cooling_requirements:get_first_obj_by_temp_type(temp_type)
    local objs = self.type_obj_map[temp_type]
    if not objs then
        return nil
    end
    for _, v in pairs(objs) do
        return v
    end
end

function cooling_requirements:insert_to_type_obj_map(obj)
    if not self.type_obj_map[obj.TemperatureType] then
        self.type_obj_map[obj.TemperatureType] = {}
    end
    self.type_obj_map[obj.TemperatureType][obj[props.REQUIREMENT_ID]] = obj
end

function cooling_requirements:delete_from_type_obj_map(obj)
    local objs = self.type_obj_map[obj.TemperatureType]
    if not objs then
        log:error("Delete obj from type_obj_map failed, type:%u, id:0x%x", obj.TemperatureType,
            obj[props.REQUIREMENT_ID])
        return
    end

    objs[obj[props.REQUIREMENT_ID]] = nil
end

function cooling_requirements:get_requirement_by_id(requirement_id)
    return self.objs[requirement_id]
end

-- 获取主用温度点
function cooling_requirements:get_requirement_by_backup_idx(backup_requirement)
    for _, obj in pairs(self.objs) do
        if obj.BackupRequirementIdx and obj.BackupRequirementIdx == backup_requirement[props.REQUIREMENT_ID] then
            return obj
        end
    end
end

function cooling_requirements:get_objs()
    return self.objs
end

-- 内存和cpu的温度监控点不做删除
-- 兼容一些机型风液共板的问题
function cooling_requirements:delete_obj_with_id(requirement_id)
    if self.objs[requirement_id] and
       self.objs[requirement_id].TemperatureType ~= f_enum.TemperatureType.Cpu:value() and
       self.objs[requirement_id].TemperatureType ~= f_enum.TemperatureType.Memory:value() then
        self.objs[requirement_id] = nil
    end
end

function cooling_requirements:delete_objs(requirement_ids)
    if type(requirement_ids) == 'table' then
        for _, requirement_id in pairs(requirement_ids) do
            self:delete_obj_with_id(requirement_id)
        end
    elseif type(requirement_ids) == 'number' then
            self:delete_obj_with_id(requirement_ids)
    else
        log:debug('RequirementId is invalid type')
    end
end


function cooling_requirements:is_monitoring_abnormal(requirement_o)
    if not requirement_o.BackupRequirementIdx or requirement_o.BackupRequirementIdx <= 0 then
        -- 无备用温度点
        return requirement_o.MonitoringStatus ~= 0
    end

    if requirement_o.MonitoringStatus ~= 0 then
        -- 有备用温度点
        local backup_requirement_o = self:get_requirement_by_id(requirement_o.BackupRequirementIdx)
        if not backup_requirement_o then
            log:debug('Backup requirement does not find, check whether backup requirement id(%s) exists',
                requirement_o.BackupRequirementIdx)
            return true
        end
        -- 主用温度点状态异常，则判断备用温度点状态
        return backup_requirement_o.MonitoringStatus ~= 0
    end

    return false
end

function cooling_requirements:get_temperature_value(requirement_o)
    -- 无备用温度点
    if not requirement_o.BackupRequirementIdx or requirement_o.BackupRequirementIdx <= 0 then
        return requirement_o.MonitoringValue
    end
    -- 主温度点异常，则使用备用温度点调速
    if requirement_o.MonitoringStatus ~= 0 then
        -- 有备用温度点
        local backup_requirement_o = self:get_requirement_by_id(requirement_o.BackupRequirementIdx)
        return backup_requirement_o and backup_requirement_o.MonitoringValue or 255
    else
        return requirement_o.MonitoringValue
    end
end

function cooling_requirements:is_component_identifier_match(identifiers)
    local cpu_models = self.cpu_data:get_cpu_model()
    local memory_part_numbers = self.memory_data:get_memory_part_number()
    for _, identifier in pairs(identifiers) do
        if cpu_models[identifier] or memory_part_numbers[identifier] then
            return true
        end
    end
    return false
end

-- requirement生效判断，返回不生效返回原因
-- 新增标记is_first，用于区分第一次判断还是第二次判断，第二次判断时无法匹配ComponentIdentifier
function cooling_requirements:get_requirement_invalid_cause(disk_temp_available, tmp_requirement, power_state)
    -- 如果用户配置的自定义生效条件为false则表示不生效
    if not tmp_requirement.Enabled then
        return 'Disabled'
    end

    -- 作为备用温度点，且未配置目标调速值则无效
    if tmp_requirement.IsBackupRequirement and tmp_requirement.TargetTemperatureCelsius == 0 then
        return 'IsBackupRequirement'
    end

    -- 某些温度值获取不到才生效
    if tmp_requirement.ObtainTempFaildToValid and not disk_temp_available then
        return 'ObtainTempSuccess'
    end
    if (power_state == enums.power_status.OFF or power_state == enums.power_status.ONING) and
        not tmp_requirement[props.ACTIVE_IN_STANDBY] then
        return 'PowerNotMeet'
    end

    -- 针对CPU和内存，若存在默认的调速策略在生效，则对应直接返回None
    if tmp_requirement.TemperatureType == f_enum.TemperatureType.Cpu:value() and self.default_cpu_valid and
        tmp_requirement.IsValid == 1 then
        return 'None'
    end
    if tmp_requirement.TemperatureType == f_enum.TemperatureType.Memory:value() and self.default_memory_valid and
        tmp_requirement.IsValid == 1 then
        return 'None'    
    end

    -- 若ComponentIdentifier不为空，则判断型号是否匹配，匹配上则生效
    local identifiers = tmp_requirement.ComponentIdentifier
    if self.is_first and next(identifiers) and not self:is_component_identifier_match(identifiers) then
        return 'identifierNotMatch'
    end

    return 'None'
end

local function is_special_temperature_type(requirement, temp_type)
    if requirement[props.TEMPERATURE_TYPE] == temp_type then
        return true
    end
    return false
end

-- 刷新特殊的调速策略，当前需更新生效的CPU和内存调速策略，用于判断当前是否有CPU和内存调速策略生效
local function refresh_special_requirement(self, requirement)
    if is_special_temperature_type(requirement, f_enum.TemperatureType.Cpu:value()) and requirement.IsValid == 1 then
        table.insert(self.valid_cpu_requirements, requirement)
        -- 若已存在默认的调速策略生效，则default_cpu_valid值无需再置为false
        self.default_cpu_valid = self.default_cpu_valid and true or false
    end
    if is_special_temperature_type(requirement, f_enum.TemperatureType.Memory:value()) and requirement.IsValid == 1 then
        table.insert(self.valid_memory_requirements, requirement)
        -- 若已存在默认的调速策略生效，则default_memory_valid值无需再置为false
        self.default_memory_valid = self.default_memory_valid and true or false
    end
end

-- 针对的调速策略（CPU和内存），获取唯一的目标温度值最低且SensorName唯一的调速策略，目的是保证针对CPU1，CPU2均有独立的调速策略
function cooling_requirements:get_unique_requirement(requirements)
    local valid_requirement = {}
    if not requirements or (not next(requirements)) then
        return valid_requirement
    end
    local temp_requirement = {}
    for _, requirement in pairs(requirements) do
        table.insert(temp_requirement, requirement)
    end
    -- 从小到大排序，保证能获取到唯一的SensorName目标温度值最低的策略
    table.sort(temp_requirement, function(requirement1, requirement2)
        return requirement1[props.TARGET_TEMP_CELSIUS] < requirement2[props.TARGET_TEMP_CELSIUS]
    end)
    for _, requirement in ipairs(temp_requirement) do
        if not valid_requirement[requirement[props.SENSOR_NAME]] then
            valid_requirement[requirement[props.SENSOR_NAME]] = requirement
        end
    end
    return valid_requirement
end

function cooling_requirements:make_requirement_valid(data, disk_temp_available, power_state, fans_config_o, obj)
    local invalid_cause = self:get_requirement_invalid_cause(disk_temp_available, obj, power_state)
    if invalid_cause == 'None' then
        obj.IsValid = 1
    else
        obj.IsValid = 0
    end
    -- 更新曲线调速的状态
    self.policys_instance:update_policy_isvalid(data, obj, fans_config_o)
    if self.objs_last_valid_state[obj.RequirementId] ~= obj.IsValid and obj.IsValid == 0 then
        log:notice("Update requirement(id:0x%x) IsValid to %u, Invalid cause: %s",
            obj[props.REQUIREMENT_ID], obj.IsValid, invalid_cause)
        self.objs_last_valid_state[obj.RequirementId] = obj.IsValid
    elseif self.objs_last_valid_state[obj.RequirementId] ~= obj.IsValid and obj.IsValid == 1 then
        log:notice("Update requirement(id:0x%x) IsValid to %u", obj[props.REQUIREMENT_ID], obj.IsValid)
        self.objs_last_valid_state[obj.RequirementId] = obj.IsValid
    end
end

-- 更新目标调速和曲线调速的生效状态
function cooling_requirements:update_objs_isvalid(data, disk_temp_available, power_state, fans_config_o)
    self.is_first = true
    self.valid_cpu_requirements = {}
    self.valid_memory_requirements = {}
    for _, obj in pairs(self.objs) do
        self:make_requirement_valid(data, disk_temp_available, power_state, fans_config_o, obj)
        refresh_special_requirement(self, obj)
    end
    self.is_first = false
    -- 判断是否存在CPU/MEM调速策略未生效，若未生效，则以其所有配置中的最低目标温度值生效
    if not next(self.valid_cpu_requirements) and not self.default_cpu_valid then
        self.default_cpu_valid = true
        local cpu_requirements = self:get_unique_requirement(self:get_cpu_requirements())
        for _, obj in pairs(cpu_requirements) do
            self:make_requirement_valid(data, disk_temp_available, power_state, fans_config_o, obj)
        end
    end
    if not next(self.valid_memory_requirements) and not self.default_memory_valid then
        self.default_memory_valid = true
        local memory_requirements = self:get_unique_requirement(self:get_memory_requirements())
        for _, obj in pairs(memory_requirements) do
            self:make_requirement_valid(data, disk_temp_available, power_state, fans_config_o, obj)
        end
    end
    -- 执行遍历所有的环温调速曲线，判断是否有环温调速曲线生效，如果没有，则生效默认调速策略
    self.policys_instance:enable_default_cooling_policy()
end

-- 私有属性变更回调
function cooling_requirements:private_property_changed_callback(object, prop, value)
    if prop == 'IsValid' then
        -- 生效状态变更，重新下发目标调速配置到pid
        self:set_requirement_updated_flag(true)
    end
end

function cooling_requirements.is_target_temp_cooling(object)
    if not object.MaxAllowedTemperatureCelsius or not object.TargetTemperatureCelsius then
        return false
    end
    if object.MaxAllowedTemperatureCelsius == 0 or object.TargetTemperatureCelsius == 0 then
        return false
    end

    return true
end

function cooling_requirements:get_sensor_name_by_id(requirement_id, exp_type)
    local obj = self:get_requirement_by_id(requirement_id)
    if not obj then
        log:error('Requirement(%s) does not exist', requirement_id)
        return nil
    end
    if exp_type == cooling_enums.max_fan_pwm_type.REQUIREMENT then
        return obj.SensorName
    end
    if obj.BackupRequirementIdx ~= 0 then
        -- 存在备用温度点索引
        local back_obj = self:get_requirement_by_id(obj.BackupRequirementIdx)
        -- 若未获取到备用温度点对象说明配置错误或者备用温度点对象未分发
        if not back_obj then
            log:error('BackupRequirementIdx(%s) does not exist', requirement_id)
            return nil
        end
        return obj.MonitoringStatus == 0 and obj.SensorName or back_obj.SensorName
    end
    return obj.SensorName
end

-- 通过CoolingAreaId获取对应的风扇Id
function cooling_requirements:get_fan_idx_group_by_areaId(areaId)
    if not self.cooling_areas then
        log:debug('The Cooling area is null')
        return {}
    end
    if not self.cooling_areas[areaId] then
        log:debug('The CoolingArea(%s) is not exist', areaId)
        return {}
    end
    return self.cooling_areas[areaId][props.FAN_IDX_GROUP]
end

function cooling_requirements:get_pump_idx_group()
    if not self.cooling_areas then
        log:debug('The cooling area is null')
        return {}
    end
    for _, obj in pairs(self.cooling_areas) do
        if string.find(obj[props.OBJECT_NAME], props.PUMP_AREA_NAME) and obj[props.LIQUID_DEVICE_GROUP] then
            return obj[props.LIQUID_DEVICE_GROUP]
        end
    end
    return {}
end

-- 初始化获取自定义目标温度点返回体
local function init_custom_target_temperatures(data)
    for type_id = 1, 10, 1 do
        data[type_id].TemperatureType = type_id
        data[type_id].TargetTemperatureCelsius = 0
        data[type_id].MinTargetTemperatureCelsius = 0
        data[type_id].MaxTargetTemperatureCelsius = 0
    end
end

-- 获取自定义目标温度点
function cooling_requirements:get_custom_target_temperatures()
    -- 初始化为10个空数组，防止接口直接nil.属性访问异常
    local rsp = {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}}
    init_custom_target_temperatures(rsp)
    -- 温度点类型 1:Cpu 2:Outlet 3:Disk 4:Memory 5:PCH 6:VRD 7:VDDQ 8:NPUHbm 9:NPUAiCore 10:NPUBoard
    for type_id = 1, 10, 1 do
        if not self.type_obj_map[type_id] then
            goto continue
        end
        -- 同一类型Type的requirement可能有多个
        for _, obj in pairs(self.type_obj_map[type_id]) do
            local base_id = get_base_id(obj[props.REQUIREMENT_ID])
            -- 若是无法匹配到对应的CoolingArea，那么认为是不支持温度点自定义(共板场景)
            if not self.cooling_areas[base_id] then
                goto next
            end

            -- 不支持设置的不返回
            if obj[props.POLICY_CUSTOM_SUPPORTED] then
                rsp[type_id].TemperatureType = type_id
                rsp[type_id].TargetTemperatureCelsius = obj.CustomTargetTemperatureCelsius
                -- 配置了支持设置但是漏配置上下限范围返回0而不是导致接口报错
                rsp[type_id].MinTargetTemperatureCelsius = obj.TargetTemperatureRangeCelsius[1] or 0
                rsp[type_id].MaxTargetTemperatureCelsius = obj.TargetTemperatureRangeCelsius[2] or 0
                break
            end
            ::next::
        end
        ::continue::
    end
    return rsp
end

-- 添加目标调速值
function cooling_requirements:get_custom_target_temp()
    local table = {}
    local custom_temperature_type = {
        [f_enum.TemperatureType.Outlet:value()] = 'Custom_OutletObjTem',
        [f_enum.TemperatureType.Cpu:value()] = 'Custom_CoreRemObjTem'
    }
    for type, obj_name in pairs(custom_temperature_type) do
        local find_obj = self:get_first_obj_by_temp_type(type)
        if not find_obj then
            log:error('Get object(%s) failed', obj_name)
            goto continue
        end
        local custom_supported = find_obj[props.POLICY_CUSTOM_SUPPORTED]
        if not custom_supported then
            log:error('Object(%s) cannot be customized', obj_name)
            goto continue
        end
        local base_id = get_base_id(find_obj[props.REQUIREMENT_ID])
        -- 若是无法匹配到对应的CoolingArea，那么认为是温度点无效(共板场景)
        if not self.cooling_areas[base_id] then
            goto continue
        end
        table[obj_name] = find_obj[props.CUSTOM_TARGET_TEMP_CELSIUS]
        ::continue::
    end
    return table
end

local function getSpeed(temp, temp_low, temp_high, speed_low, speed_high)
    -- 遍历温度范围，找到对应的属性
    for i = 1, #temp_low, 1 do
        -- 根据调速策略配置规则，左闭右开
        if temp >= temp_low[i] and temp < temp_high[i] then
            -- 计算转速
            local Ratio = (speed_high[i] - speed_low[i]) / (temp_high[i] - temp_low[i])
            local speed = Ratio * (temp - temp_low[i]) + speed_low[i]
            return speed
        end
        -- 存在某个温度下计算不出转速直接返回0
        if temp < temp_low[i] then
            return 0
        end
    end
end

function cooling_requirements:GetSpecificPWM(Medium, PolicyType, SmartCoolingMode, Temperature)
    local control_pwm = 0
    local temp_pwm = 0
    local policies = self.policys_instance:get_objs()

    for _, requirement_obj in pairs(self.objs) do
        if TEMPERATURE_TYPE_MAP[requirement_obj[props.TEMPERATURE_TYPE]] ~= POLICY_TYPE_MAP[PolicyType] then
            goto continue
        end
        -- 温度点不生效
        if requirement_obj[props.IS_VALID] == 0 then
            goto continue
        end
        local req_base_id = requirement_obj[props.REQ_BASE_ID]
        local area_id = self.cooling_data.requirement_area_map[req_base_id]
        -- 通过Requirement的baseid无法匹配到Area,即Area对象中无requirementidx为baseid的
        if not area_id then
            log:debug('The CoolingRequirement(0x%x) can not match CoolingArea(RequirementIdx:%s)',
                requirement_obj[props.REQUIREMENT_ID], req_base_id)
            goto continue
        end
        local area_obj = self.cooling_data.cooling_areas[area_id]
        if not area_obj then
            goto continue
        end
        for _, policy_id in pairs(area_obj[props.POLICY_IDX_GROUP]) do
            local policy_obj = policies[policy_id]
            if policy_obj and self.policys_instance:get_policy_isvalid(policy_obj, Medium, SmartCoolingMode) then
                temp_pwm = getSpeed(Temperature, policy_obj.TemperatureRangeLow, policy_obj.TemperatureRangeHigh,
                    policy_obj.SpeedRangeLow, policy_obj.SpeedRangeHigh
                )
                control_pwm = temp_pwm > control_pwm and temp_pwm or control_pwm
            end
        end

        ::continue::
    end
    -- 如果返回为0表示当前输入参数无法计算出转速
    return control_pwm
end

return Singleton(cooling_requirements)