-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local common_def = require('common_def')
local method_misc = require('method_misc')
local queue = require('skynet.queue')
local ctrl_commu_loss_monitor = require('ctrl_commu_loss_monitor')
local c_storageconfig = require('storageconfig.storageconfig_object')
local STORAGE_MGNT_STATE = common_def.STORAGE_MGNT_STATE
local log = require('mc.logging')
local debounce = require('mc.debounce.debounce')
local mctp_service = require('mctp.mctp_service')
local mdb = require('mc.mdb')
local skynet = require('skynet')
local storage_bus = require('storage_bus')
local controller_dump = require('controller.controller_dump')
local sml = require('sml')
local cjson = require('cjson')
local phy_err
local signal = require('mc.signal')
local c_object = require('mc.orm.object')
local pd_object = require('pd.pd_object')
local volume_obj_info = require('volume.volume_obj_info')
local volume_collection = require('volume.volume_collection')
local battery_collection = require('battery.battery_collection')
local operation_code = require('controller.ctrl_operation_cmd')
local ctrl_config_backup = require('controller.ctrl_config_backup')
local drive_collection = require('drive.drive_collection')
local array_collection = require('array.array_collection')
local custom_msg = require('messages.custom')
local upgrade_defs = require('upgrade.upgrade_defs')
local CTRL_OPTION = common_def.CTRL_OPTION
local MAX_RETRY_TIMES = 30

-- PCIeCard资源树对象(由pcie_device组件挂载),用于获取RAID卡的四元组
local PCIE_CARD_PATH <const> = '/bmc/kepler/Systems/1/PCIeDevices/PCIeCards'
local PCIE_CARD_INTERFACE <const> = 'bmc.kepler.Systems.PCIeDevices.PCIeCard'
-- 四元组获取失败重试次数与间隔(skynet.sleep单位为10ms,500即5s)
local QUAD_RETRY_TIMES <const> = 3
local QUAD_RETRY_INTERVAL <const> = 500

local TASK_UPDATE <const> = 'update'
local TASK_UPDATE_PD_LIST <const> = 'update_pd_list'
local TASK_UPDATE_LD_LIST <const> = 'update_ld_list'

local VOLUME_TYPE_PHYSICAL_DRIVE_BIT <const> = 0x10000000
local VOLUME_TYPE_LOGICAL_DRIVE_BIT <const> = 0x20000000
local CONTROLLER_ID = 1
local rate_array = { 'Low', 'Medium', 'High' }
local running_state = { 'Off', 'On' }
local work_mode = { 'RAID', 'HBA', 'JBOD', 'Mixed' }

---@class c_controller: c_object
---@field Id integer
---@field TemperatureCelsius integer
---@field ControllerName string
---@field OOBSupport integer
---@field TypeId integer
---@field SystemId integer
---@field SocketId integer
---@field Segment integer
---@field DevBus integer
---@field DevDevice integer
---@field DevFunction integer
---@field CtrlOption1 integer
---@field CtrlOption2 integer
---@field CtrlOption3 integer
---@field Eid integer
---@field Phyaddr integer
---@field FaultCode integer
---@field BootDevices string[]
---@field RefDrives string[]
---@field RefChip table
---@field DeviceName string
---@field SlotId integer
---@field BatteryHealth integer
---@field on_add_pd c_basic_signal
---@field on_del_pd c_basic_signal
---@field on_update c_basic_signal
---@field new fun(...): c_controller
local c_controller = c_object('Controller', { 'position' }) -- Controller 类,主键是 position 字段

local CONTROLLER_TYPE = {
    [0] = 'SAS3108',
    [1] = 'SAS3108',
    [2] = 'SAS3108',
    [3] = 'SAS3008',
    [4] = 'SAS3008',
    [5] = 'SAS3008',
    [6] = 'SAS3508',
    [7] = 'SAS3516',
    [8] = 'SAS3408',
    [9] = 'SAS3416',
    [10] = 'SAS3408',
    [11] = 'SAS3416',
    [12] = 'SAS3004',
    [13] = 'SAS3908',
    [14] = 'SAS3908',
    [15] = 'SAS3916',
    [16] = 'SAS3808',
    [17] = 'SAS3816',
    [18] = 'SAS3808',
    [19] = 'SAS3808',
    [20] = 'SAS3816',
    [64] = 'PM3152',
    [65] = 'PM2100',
    [66] = 'PM8204',
    [67] = 'PM8254',
    [68] = 'PM8222',
    [96] = 'B80121',
    [97] = 'B80121',
    [98] = 'B80121',
    [99] = 'B80121',
    [100] = 'B80121',
    [101] = 'B80121',
    [102] = 'B80121',
    [103] = 'B80121',
    [113] = 'B80121',
    [200] = 'JZ9234',
}

local SLOW_DISK_MODES = common_def.SLOW_DISK_DETECTION_MODE
local SLOW_DISK_TYPES = common_def.SLOW_DISK_TYPE
local MODE_TRANSITIONS = {
    [SLOW_DISK_MODES.DISABLE] = {
        [SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE] = {
            { type = SLOW_DISK_TYPES.MEMBER, sw = 1 },
            { type = SLOW_DISK_TYPES.RAW, sw = 1 },
        },
        [SLOW_DISK_MODES.ENABLE_RAW_DRIVE] = {
            { type = SLOW_DISK_TYPES.RAW, sw = 1 },
        },
        [SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE] = {
            { type = SLOW_DISK_TYPES.MEMBER, sw = 1 },
        },
    },
    [SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE] = {
        [SLOW_DISK_MODES.DISABLE] = {
            { type = SLOW_DISK_TYPES.MEMBER, sw = 0 },
            { type = SLOW_DISK_TYPES.RAW, sw = 0 },
        },
        [SLOW_DISK_MODES.ENABLE_RAW_DRIVE] = {
            { type = SLOW_DISK_TYPES.MEMBER, sw = 0 },
        },
        [SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE] = {
            { type = SLOW_DISK_TYPES.RAW, sw = 0 },
        },
    },
    [SLOW_DISK_MODES.ENABLE_RAW_DRIVE] = {
        [SLOW_DISK_MODES.DISABLE] = {
            { type = SLOW_DISK_TYPES.RAW, sw = 0 },
        },
        [SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE] = {
            { type = SLOW_DISK_TYPES.MEMBER, sw = 1 },
        },
        [SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE] = {
            { type = SLOW_DISK_TYPES.RAW, sw = 0 },
            { type = SLOW_DISK_TYPES.MEMBER, sw = 1 },
        },
    },
    [SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE] = {
        [SLOW_DISK_MODES.DISABLE] = {
            { type = SLOW_DISK_TYPES.MEMBER, sw = 0 },
        },
        [SLOW_DISK_MODES.ENABLE_RAW_AND_MEMBER_DRIVE] = {
            { type = SLOW_DISK_TYPES.RAW, sw = 1 },
        },
        [SLOW_DISK_MODES.ENABLE_RAW_DRIVE] = {
            { type = SLOW_DISK_TYPES.RAW, sw = 1 },
            { type = SLOW_DISK_TYPES.MEMBER, sw = 0 },
        },
    },
}

local SLOW_DESK_TYPE_TO_DISK = {
    [SLOW_DISK_TYPES.RAW] = 'raw',
    [SLOW_DISK_TYPES.MEMBER] = 'RAID member',
}

local SLOW_DESK_TYPE_TO_SW = {
    [0] = 'Disable',
    [1] = 'Enable',
}

local function handle_slow_disk_mode_setting(ctrl_obj, mode, new_mode)
    if ctrl_obj.OOBSupport ~= 1 or ctrl_obj.is_working then
        log:error(
            '[Storage]Controller%s no support set operation. is_suport:%s, is_working:%s',
            ctrl_obj.Id,
            ctrl_obj.OOBSupport,
            ctrl_obj.is_working
        )
        return false
    end
    -- 获取当前模式到新模式的转换操作
    local operations = MODE_TRANSITIONS[mode] and MODE_TRANSITIONS[mode][new_mode] or {}
    if #operations == 0 and mode ~= new_mode then
        log:error('[Storage]No transition defined from %s to %s', mode, new_mode)
        return false
    end
    for _, op in ipairs(operations) do
        ctrl_obj.is_working = true
        local ok, ret = pcall(function()
            return sml.ctrl_operation(
                ctrl_obj.Id,
                operation_code.CTRL_OPERATION_SET_SLOW_DISK_DETECTION,
                CTRL_OPTION.drive_detection:pack({ type = op.type, sw = op.sw })
            )
        end)
        ctrl_obj.is_working = false
        if not ok or ret ~= common_def.RET_OK then
            log:error('[Storage]Failed to set SlowDriveDetectionMode: %s', ret)
            log:operation(
                method_misc:get_op_initiator(),
                'storage',
                '%s %s drives detection failed',
                SLOW_DESK_TYPE_TO_SW[op.sw],
                SLOW_DESK_TYPE_TO_DISK[op.type]
            )
            return false
        else
            log:notice('[Storage]Successfully set SlowDriveDetectionMode for Controller%s.', ctrl_obj.Id)
            log:operation(
                method_misc:get_op_initiator(),
                'storage',
                '%s %s drives detection successfully',
                SLOW_DESK_TYPE_TO_SW[op.sw],
                SLOW_DESK_TYPE_TO_DISK[op.type]
            )
        end
    end
    return true
end

function c_controller:ctor(obj)
    self.bus = storage_bus.get_instance().bus
    self.obj = obj
    obj.Eid = 0
    obj.Phyaddr = 0
    self.NodeId = ''
    self.controller_path = string.match(obj.path, 'Controller_%d+_%x+')

    self.pd_list = {}
    self.on_add_pd = signal.new()
    self.on_add_pd_complete = signal.new()
    self.on_del_pd = signal.new()

    self.volume_list = {}
    self.array_list = {}

    self.on_update = signal.new()

    self.on_update_volume_array = signal.new()

    self.is_working = false

    self.registered_state = false
    self.registering = false
    self.temp_debounce = debounce[debounce.DEBOUNCED_CONT].new(5, 0) -- 5 次防抖
    self.presence_debounce = false
    self.tasks_pause = false
    self.first_register_task = false
    self.timeout_times = 0
    self.update_queue = queue()
    self.firmware_register_status = false
    self.config_backed_up = false
end

function c_controller:dtor()
    c_storageconfig.get_instance().controller_mask = c_storageconfig.get_instance().controller_mask ~ (1 << self.Id)

    if self.firmware_register_status then
        method_misc:retry_unregister_firmware_info(self.NodeId, function()
            self.firmware_register_status = false
        end)
    end
    self:stop()
end

function c_controller:init()
    log:info('controller init obj.Id = %s, object_id = %s, key = %s', self.obj.Id, CONTROLLER_ID, self.DeviceName)
    self.obj.Id = (self.obj.Id == common_def.INVALID_U8) and CONTROLLER_ID - 1 or self.obj.Id
    CONTROLLER_ID = CONTROLLER_ID + 1
    self.super.init(self)
    -- 由于更新BBU的State的周期为60s,更新BatteryPresence的周期为20s
    -- 所以BatteryPresence的连续三次更新的值相同,防抖次数需要乘以3
    self.presence_debounce = debounce[debounce.DEBOUNCED_CONT].new(9, self.BatteryPresence) -- 9 次防抖
    if not method_misc:test_controller_oob_support(self.TypeId) then
        self.Type = CONTROLLER_TYPE[self.TypeId]
    end
    self.NodeId = string.gsub(self.DeviceName, '[^%a%d]', '')
    self.ComponentName = self.DeviceName
    self.ComponentType = 'Controller'
    self:set_default_values()
    if not self.CreatedByDeviceObject then
        self:set_default_volume_manage_values()
    end
    self:set_default_health_status()

    self.on_update_volume_array:on(function()
        self.update_queue(function()
            self:update_volume_list()
            self:update_array_list()
        end)
    end)

    if c_storageconfig.get_instance().power_on == 1 then
        self:register()
    end
end

local function prefix_equal(str, prefix)
    return string.sub(str, 1, string.len(prefix)) == prefix
end

function c_controller.before_add_object(object)
    while true do
        skynet.sleep(10)
        -- 如果主键为空或Slot为255或Slot为0则不加载,等待有效主键分发
        if
            not prefix_equal(object.DeviceName, 'PCIe Card')
            or prefix_equal(object.DeviceName, 'PCIe Card $')
            or prefix_equal(object.DeviceName, 'PCIe Card 0')
            or prefix_equal(object.DeviceName, 'PCIe Card 255')
        then
            skynet.sleep(500)
        else
            break
        end
    end
end

function c_controller:fill_supported_raid_levels()
    local supported_raid_levels = {}
    if not self.CtrlOption3 then
        return supported_raid_levels
    end

    local level_array = { '0', '1', '5', '6', '10', '50', '60', '1(ADM)', '10(ADM)', '1Triple', '10Triple' }
    -- 从第8位开始
    local bit_val = 1 << 8
    for i = 1, #level_array do
        if bit_val & self.CtrlOption3 ~= 0 then
            table.insert(supported_raid_levels, 'RAID' .. level_array[i])
        end

        bit_val = bit_val << 1
    end

    return supported_raid_levels
end

-- 启动关联的更新任务
function c_controller:start_related_tasks()
    log:notice('controller%s start_related_tasks, registered_state = %s', self.Id, self.registered_state)
    if self.registered_state then
        self:start()
    else
        if not self.registering then
            self:register()
        end
    end
end

-- 关闭关联的更新任务
function c_controller:stop_related_tasks()
    log:notice('controller%s stop_related_tasks', self.Id)
    if self.registering then
        self.registering = false
    end
    if self.TypeId == 20 then
        self.registered_state = false
        local ok, ret = pcall(self.unregister_controller_to_sml, self)
        if not ok then
            log:error('controller:%s unregister failed, ret:%s', self.Id, ret)
        end
    end
    self:stop()
    log:notice('controller%s set_default_values', self.Id)
    self:set_default_values()
    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        log:notice('controller%s set_default_consistency_values', self.Id)
        self:set_default_consistency_values()
    end
    if not self.CreatedByDeviceObject then
        log:notice('controller%s set_default_volume_manage_values', self.Id)
        self:set_default_volume_manage_values()
    end
    log:notice('controller%s set_default_health_status', self.Id)
    self:set_default_health_status()
    log:notice('controller%s set_default_battery_info', self.Id)
    self:set_default_battery_info()
    log:notice('controller%s set_default_phy_err', self.Id)
    self:set_default_phy_err()
    self:clear_asset_data_info()
end

-- 根据OS状态改变启停更新任务
function c_controller:change_tasks_state(os_state)
    log:notice('controller%s change task state to %s', self.Id, os_state and 'start' or 'stop')
    if os_state then
        self:start_related_tasks()
    else
        self:stop_related_tasks()
    end
end

local function record_commu_loss(ret, ctrl_index)
    -- CreatedByDeviceObject CommunicationLoss 属性直接从设备树同步
    -- collection 依赖 ORM 环境(持久化 db 等),环境未就绪时 find 会抛错,回退到自发现通路原有逻辑
    local ctrl_obj
    local ok = pcall(function()
        ctrl_obj = c_controller.collection:find({ Id = ctrl_index })
    end)
    if ok and ctrl_obj and ctrl_obj.CreatedByDeviceObject then
        return
    end
    -- 下电状态不算是通信丢失
    if c_storageconfig.get_instance().power_on ~= 1 then
        return
    end
    if ret ~= common_def.SML_ERR_I2C_READ_WRITE_FAILED then
        c_storageconfig.get_instance().configuration_ready_mask = c_storageconfig.get_instance().configuration_ready_mask
            ~ (1 << ctrl_index)
        c_storageconfig.get_instance().on_update_flag:emit(STORAGE_MGNT_STATE.STORAGE_MGNT_CTRL_ABNOMAL)
    end
    -- 如果调用sml库返回的是timeout错误,则不认为是通信丢失
    if type(ret) == 'table' and ret.name == 'kepler.hwproxy.RequestTimeout' then
        return
    end
    if ret ~= common_def.SML_ERR_CTRL_STATUS_INVALID and ret ~= common_def.SML_ERR_NULL_INFTERFACE then
        ctrl_commu_loss_monitor.get_instance():update(true, ctrl_index)
    end
end

-- 判断控制器是否直接管理物理驱动器
function c_controller:is_directly_manage_physical_drive()
    return self.TypeId == common_def.HRDT_B261s
end

function c_controller:register()
    log:notice('controller%s begin register', self.Id)
    self:new_task({ 'register_controller', self.Id })
        :loop(function(task)
            self.registering = true
            log:info(
                'controller%s register task loop, CreatedByDeviceObject=%s, power_on=%s',
                self.Id,
                tostring(self.CreatedByDeviceObject),
                c_storageconfig.get_instance().power_on
            )
            if not self.CreatedByDeviceObject then
                if c_storageconfig.get_instance().power_on == 0 then
                    self.registering = false
                    task:stop()
                    return
                end

                if self.OOBSupport ~= 1 then
                    c_storageconfig.get_instance().on_update_flag:emit(STORAGE_MGNT_STATE.STORAGE_MGNT_OOB_NOT_SUPPORT)
                    self.registering = false
                    task:stop()
                    return
                end

                if method_misc:get_oob_interface_type(self.TypeId) == common_def.INVALID_U8 then
                    log:error('[Storage]Invalid TypeId, controller_id = %d', self.Id)
                    self.registering = false
                    task:stop()
                    return
                end

                if method_misc:get_oob_interface_type(self.TypeId) == common_def.OOB_TYPE_OVER_PCIE then
                    if not mctp_service.get_instance().mctp_state then
                        return
                    end
                    if self.DevBus == 0 and self.DevDevice == 0 and self.DevFunction == 0 then
                        return
                    end
                end

                self:add_controller_to_link_topo()
                log:notice('ctrl%s add_controller_to_link_topo successfully', self.Id)
                self:add_controller_to_sml()
                log:notice('ctrl%s add_controller_to_sml successfully', self.Id)
            end

            self:register_controller_to_sml()
            log:notice('ctrl%s register_controller_to_sml successfully', self.Id)

            self.registering = false
            self.registered_state = true
            self:start()

            log:notice('controller_collection:add_object success, controller_id = %d', self.Id)
            self:trans_pd_vendor_data()
            self:property_change_listen_func()
            self:register_controller_firmware_info()

            task:stop()
        end)
        :set_timeout_ms(5000)
end

function c_controller:property_change_listen_func()
    if not method_misc:test_controller_vendor(self.TypeId, common_def.VENDER_HUAWEI) then
        return
    end
    -- 只有自研卡起监听流程
    self:get_mdb_object('bmc.kepler.Systems.Storage.Controller').property_before_change
        :on(function(prop_name, prop_value, sender)
            -- 非SlowDriveDetectionMode不处理;SlowDriveDetectionMode os下电设置为255时,正常赋值
            if prop_name ~= 'SlowDriveDetectionMode' or prop_value == common_def.INVALID_U8 then
                return true
            end
            -- 周期更新controller信息流程,255->有效值,直接赋值
            if prop_name == 'SlowDriveDetectionMode' and self.SlowDriveDetectionMode == common_def.INVALID_U8 then
                log:notice(
                    'controller %s SlowDriveDetectionMode changed, %s -> %s',
                    self.Id,
                    self.SlowDriveDetectionMode,
                    prop_value
                )
                return true
            end
            if prop_value < SLOW_DISK_MODES.DISABLE or prop_value > SLOW_DISK_MODES.ENABLE_MEMBER_DRIVE then
                log:error('[Storage]Invalid SlowDriveDetectionMode: %s', prop_value)
                return false
            end
            local success = handle_slow_disk_mode_setting(self, self.SlowDriveDetectionMode, prop_value)
            if not success then
                log:error(
                    '[Storage]Failed to set SlowDriveDetectionMode from %s to %s',
                    self.SlowDriveDetectionMode,
                    prop_value
                )
                error(custom_msg.CurrentStatusNotSupportOperation(prop_value, '%SlowDriveDetectionMode'))
            end
            return true
        end)
    -- 监听升级导致的固件版本变化
    self.on_property_changed:on(function(prop_name, prop_value)
        -- FirmwareVersion默认值为:''; OS下电场景为‘N/A’
        if prop_name ~= 'FirmwareVersion' or prop_value == '' or prop_value == common_def.INVALID_STRING then
            return
        end
        if not self.firmware_register_status then
            self:register_controller_firmware_info()
        end
        if self.firmware_register_status then
            method_misc:update_firmware_info(self, prop_value)
        end
    end)
end
function c_controller:update_temp_abnormal(ret)
    pcall(function()
        if
            c_storageconfig.get_instance().obj
            and c_storageconfig.get_instance().obj.StorageConfigReady
                == common_def.STORAGE_MGNT_STATE.STORAGE_MGNT_READY
        then
            self.TemperatureAbnormal = self.temp_debounce:get_debounced_val(ret and 0 or 1)
        end
    end)
end

function c_controller:fetch_controller_init_state()
    local ok, ret = pcall(sml.update_ctrl_init_state, self.Id)
    if not ok then
        record_commu_loss(ret, self.Id)
        return false
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    return ret == 2
end

function c_controller:fetch_controller_info(priority)
    local err_count = 0
    ::continue::
    log:debug('controller%s start s.ctl_info %s times, typeid is %d', self.Id, err_count, self.TypeId)
    local ok, ret = pcall(sml.get_ctrl_info, { Priority = priority or 'Secondary' }, self.Id)
    self:update_temp_abnormal(ok)
    if not ok then
        if (self.TypeId == 19 or self.TypeId == 20) and err_count < 5 then
            err_count = err_count + 1
            goto continue
        end
        record_commu_loss(ret, self.Id)
    else
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        pcall(function()
            self:update_ctrl_info_from_sml(ret)
        end)
    end

    pcall(function()
        self:update_controller_info_via_bma()
    end)
    pcall(function()
        self:update_sas_addr()
    end)
    pcall(function()
        self:update_boot_devices()
    end)
    pcall(function()
        self:update_fault_code()
    end)
    pcall(function()
        self:update_asset_data_info()
    end)

    if not self.CreatedByDeviceObject then
        self.CommunicationLoss = ctrl_commu_loss_monitor.get_instance():get_ctrl_commu_loss(self.Id)
    end
    self.RefDrives = drive_collection.get_instance():get_drives_by_controller_id(self.Id)
    pcall(function()
        self:update_controller_battery_info()
    end)

    if ok then
        c_storageconfig.get_instance().configuration_ready_mask = c_storageconfig.get_instance().configuration_ready_mask
            | (1 << self.Id)
        c_storageconfig.get_instance().on_update_flag:emit(STORAGE_MGNT_STATE.STORAGE_MGNT_READY)
    end
end

function c_controller:start_update_task()
    local id = tostring(self.Id)
    log:notice('controller%s start TASK_UPDATE', self.Id)
    self:new_task({ TASK_UPDATE, id })
        :loop(function(task)
            if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
                return
            end
            if not self:fetch_controller_init_state() then
                return
            end
            if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
                return
            end
            self:fetch_controller_info()
        end)
        :set_timeout_ms(20000)
end

function c_controller:start_update_pd_list_task()
    local id = tostring(self.Id)
    log:notice('controller%s start TASK_UPDATE_PD_LIST', self.Id)
    self:new_task({ TASK_UPDATE_PD_LIST, id })
        :loop(function(task)
            if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
                return
            end
            self:update_pd_list()
        end)
        :set_timeout_ms(10000)
end

function c_controller:start_update_ld_list_task()
    local id = tostring(self.Id)
    if self.TypeId == 19 or self.TypeId == 20 then
        return
    end
    log:notice('controller%s start TASK_UPDATE_LD_LIST', self.Id)
    self:new_task({ TASK_UPDATE_LD_LIST, id })
        :loop(function(task)
            if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
                return
            end
            self.update_queue(function()
                self:update_volume_list()
                self:update_array_list()
            end)
            self:update_phy_info()
        end)
        :set_timeout_ms(60000)
end

function c_controller:start()
    log:notice('controller%s start update tasks', self.Id)
    self.tasks_pause = false
    if not self.first_register_task then
        log:notice('[Storage] first register task!!')
        self:start_update_task()
        self:start_update_pd_list_task()
        self:start_update_ld_list_task()
        self.first_register_task = true
    end

    self:update_supported_boot_devices_count()
    drive_collection.get_instance().on_controller_commu_changed:emit(true, self.Id)
    battery_collection.get_instance().on_controller_commu_changed:emit(true, self.Id)
    if not phy_err then
        phy_err = require('diagnose.phy_biterr')
    end
    phy_err.get_instance().on_controller_commu_changed:emit(true)
end

function c_controller:stop()
    log:notice('controller%s stop tasks', self.Id)
    self.tasks_pause = true
    drive_collection.get_instance().on_controller_commu_changed:emit(false, self.Id)
    battery_collection.get_instance().on_controller_commu_changed:emit(false, self.Id)
    -- 删除controller需要删除 array 列表并发送 on_del_array 信号
    self:remove_array_list()
    -- 删除controller需要删除 volume 列表并发送 on_del_volume 信号
    self:remove_volume_list()
end

-- 将值设为初始值
function c_controller:set_default_values()
    -- 对于不支持带外管理的RAID卡,CSR中配置的静态信息不清除
    if method_misc:test_controller_oob_support(self.TypeId) then
        self.ControllerName = common_def.INVALID_STRING
        self.Type = common_def.INVALID_STRING
    end
    self.FirmwareVersion = common_def.INVALID_STRING
    self.DeviceInterface = common_def.INVALID_STRING
    self.SASAddr = common_def.INVALID_STRING
    if not method_misc:test_controller_vendor(self.TypeId, common_def.VENDER_HUAWEI) then
        self.SerialNumber = common_def.INVALID_STRING
    end
    self.MinStripSizeBytes, self.MaxStripSizeBytes = common_def.INVALID_U32, common_def.INVALID_U32
    self.MemorySizeMiB = common_def.INVALID_U16
    self.NVDataVersion = common_def.INVALID_STRING
    self.HardwareRevision = common_def.INVALID_STRING
    self.PCIeLinkWidth = common_def.INVALID_STRING
    self.TemperatureCelsius = common_def.INVALID_U8
    self.TemperatureAbnormal = 0
    self.SupportedMode = { common_def.INVALID_STRING }
    self.SupportedRAIDTypes = self:fill_supported_raid_levels()
    self.DDREccCount = common_def.INVALID_U16
    self.UncorrectableParityErrorCount = common_def.INVALID_U32
    self.CachePinnedState = common_def.INVALID_U8
    self.MaintainPDFailHistrory = common_def.INVALID_U8
    self.WorkMode = common_def.INVALID_STRING
    self.JBODState = common_def.INVALID_U8
    self.CopyBackState = common_def.INVALID_U8
    self.SmarterCopyBackState = common_def.INVALID_U8
    self.BootDevices = { common_def.INVALID_STRING, common_def.INVALID_STRING }
    self.SupportedBootDeviceCount = common_def.BOOT_DEVICE_COUNT_DEFAULT
    self.RefDrives = {}
    self.NoBatteryWriteCacheEnabled = common_def.INVALID_BOOLEAN
    self.ReadCachePercent = common_def.INVALID_U8
    self.DriverName = common_def.INVALID_STRING
    self.DriverVersion = common_def.INVALID_STRING
    self.SlowDriveDetectionMode = common_def.INVALID_U8
end

function c_controller:set_default_ctrl1_values()
    local string_pack = string.pack('I4', self.CtrlOption1)
    local opt1 = CTRL_OPTION.option1_format:unpack(string_pack)

    -- 读策略相关
    local prop_value = {}
    if opt1.no_read_ahead_opt == 1 then
        table.insert(prop_value, common_def.READ_POLICY_LIST[0])
    end
    if opt1.read_ahead_opt == 1 then
        table.insert(prop_value, common_def.READ_POLICY_LIST[1])
    end
    self.SupportedReadPolicyList = prop_value -- 需要用修改后的数据显式地设置属性值,修改才会生效
    self.DefaultReadPolicy = common_def.READ_POLICY_LIST[opt1.read_policy_default]
    self.ReadPolicyWritable = opt1.read_policy_support ~= 0

    -- 写策略相关
    prop_value = {}
    if opt1.write_through_opt == 1 then
        table.insert(prop_value, common_def.WRITE_PLOICY_LIST[0])
    end
    if opt1.write_back_opt == 1 then
        table.insert(prop_value, common_def.WRITE_PLOICY_LIST[1])
    end
    if opt1.write_back_if_bad_bbu_opt == 1 then
        table.insert(prop_value, common_def.WRITE_PLOICY_LIST[2])
    end
    self.SupportedWritePolicyList = prop_value -- 需要用修改后的数据显式地设置属性值,修改才会生效
    self.DefaultWritePolicy = common_def.WRITE_PLOICY_LIST[opt1.write_policy_default]
    self.WritePolicyWritable = opt1.write_policy_support ~= 0

    -- IO策略相关
    prop_value = {}
    if opt1.cached_io_opt == 1 then
        table.insert(prop_value, common_def.IO_PLOICY_LIST[0])
    end
    if opt1.direct_io_opt == 1 then
        table.insert(prop_value, common_def.IO_PLOICY_LIST[1])
    end
    self.SupportedIOPolicyList = prop_value -- 需要用修改后的数据显式地设置属性值,修改才会生效
    self.DefaultIOPolicy = common_def.IO_PLOICY_LIST[opt1.io_policy_default]
    self.IOPolicyWritable = opt1.io_policy_support ~= 0

    -- 访问策略相关
    prop_value = {}
    if opt1.access_rw_opt == 1 then
        table.insert(prop_value, common_def.ACCESS_POLICY_LIST[0])
    end
    if opt1.access_read_only_opt == 1 then
        table.insert(prop_value, common_def.ACCESS_POLICY_LIST[1])
    end
    if opt1.access_blocked_opt == 1 then
        table.insert(prop_value, common_def.ACCESS_POLICY_LIST[2])
    end
    self.SupportedAccessPolicyList = prop_value -- 需要用修改后的数据显式地设置属性值,修改才会生效
    self.DefaultAccessPolicy = common_def.ACCESS_POLICY_LIST[opt1.access_policy_default]
    self.AccessPolicyWritable = opt1.access_policy_support ~= 0
end

function c_controller:set_default_ctrl2_values()
    local string_pack = string.pack('I4', self.CtrlOption2)
    local opt2 = CTRL_OPTION.option2_format:unpack(string_pack)

    local prop_value = {}
    if opt2.pd_cache_unchanged_opt == 1 then
        table.insert(prop_value, common_def.DRIVE_CACHE_POLICY_LIST[0])
    end
    if opt2.pd_cache_enable_opt == 1 then
        table.insert(prop_value, common_def.DRIVE_CACHE_POLICY_LIST[1])
    end
    if opt2.pd_cache_disable_opt == 1 then
        table.insert(prop_value, common_def.DRIVE_CACHE_POLICY_LIST[2])
    end
    self.SupportedDriveCachePolicyList = prop_value -- 需要用修改后的数据显式地设置属性值,修改才会生效
    self.DefaultDriveCachePolicy = common_def.DRIVE_CACHE_POLICY_LIST[opt2.pd_cache_policy_default]
    self.DriveCachePolicyWritable = opt2.pd_cache_policy_support ~= 0
    self.EpdSupported = opt2.ctrl_support_epd ~= 0 and true or false
    self.JbodSupported = opt2.ctrl_support_jbod_state ~= 0 and true or false
end

function c_controller:set_default_ctrl3_values()
    local string_pack = string.pack('I4', self.CtrlOption3)
    local opt3 = CTRL_OPTION.option3_format:unpack(string_pack)

    self.ConfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[opt3.configured_drive_wcp] or ''
    self.UnconfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[opt3.unconfigured_drive_wcp] or ''
    self.HBADriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[opt3.hba_drive_wcp] or ''
end

-- 解析VPD上的值来初始化
function c_controller:set_default_volume_manage_values()
    self.CreateVolumeSupported = false
    self:set_default_ctrl1_values()
    self:set_default_ctrl2_values()
    self:set_default_ctrl3_values()
end

function c_controller:update_supported_boot_devices_count()
    if not method_misc:test_controller_vendor(self.TypeId, common_def.VENDER_HUAWEI) then
        self.SupportedBootDeviceCount = common_def.BOOT_DEVICE_COUNT_DEFAULT
        return
    end
    -- HUAWEI Raid卡根据fw_type再初始化设置一次SupportedBootDeviceCount
    local ok, ret = pcall(sml.get_ctrl_fw_type, self.Id)
    if ok then
        local cfg = self.BootDeviceCountMappings
        if not cfg or #cfg ~= 2 then
            self.SupportedBootDeviceCount = common_def.BOOT_DEVICE_COUNT_DEFAULT
            return
        end

        local fw_list = cfg.ControllerFwType or cfg[1]
        local count_list = cfg.BootDeviceCount or cfg[2]
        if not fw_list or not count_list or #fw_list ~= #count_list or #fw_list == 0 then
            self.SupportedBootDeviceCount = common_def.BOOT_DEVICE_COUNT_DEFAULT
            return
        end
        for i, v in ipairs(fw_list) do
            if v == ret then
                self.SupportedBootDeviceCount = count_list[i]
                log:notice('update supported boot devices count to %d', self.SupportedBootDeviceCount)
                if self.SupportedBootDeviceCount ~= common_def.BOOT_DEVICE_COUNT_DEFAULT then
                    self.BootDevices = {}
                    for j = 1, self.SupportedBootDeviceCount do
                        table.insert(self.BootDevices, common_def.INVALID_STRING)
                    end
                end
                return
            end
        end
    end

    self.SupportedBootDeviceCount = common_def.BOOT_DEVICE_COUNT_DEFAULT
    return
end

function c_controller:set_default_consistency_values()
    self.ConsistencyCheckState = common_def.INVALID_BOOLEAN
    self.Rate = common_def.INVALID_STRING
    self.PeriodOfHours = common_def.INVALID_U16
    self.AutoRepairEnabled = common_def.INVALID_BOOLEAN
    self.RunningStatus = common_def.INVALID_STRING
    self.TotalVolumeCounts = common_def.INVALID_U16
    self.CompletedVolumeCounts = common_def.INVALID_U16
end

function c_controller:set_default_health_status()
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    self.FaultCode = common_def.INVALID_U16
    self.CommunicationLoss = 0
end

-- OS下电场景,清除BBU故障告警
function c_controller:set_default_battery_info()
    self.BatteryFault = 0
end

function c_controller:set_default_phy_err()
    local phy_children = self:get_phy_children()
    for _, v in pairs(phy_children) do
        v.PhyId = common_def.INVALID_U8
        v.InvalidDwordCount = 0
        v.LossDwordSyncCount = 0
        v.PhyResetProblemCount = 0
        v.RunningDisparityErrorCount = 0
    end
end

local function generate_controller_health_maintain_log(controller, ret)
    local last_fault_code = controller.FaultCode == common_def.INVALID_U16 and 0 or controller.FaultCode
    local cur_fault_code = ret.health_status_code
    local tmp = last_fault_code ~ cur_fault_code
    if tmp == 0 and ret.err_change == 0 then
        return
    end

    if tmp & 1 ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_CE,
            'RAID controller (%s) detected memory correctable errors - %s.',
            controller.DeviceName,
            cur_fault_code & 1 ~= 0 and 'Asserted' or 'Deasserted'
        )
    end
    if tmp & 1 << common_def.BIT_OFFSET_MEMORY_CORRECTABLE_ERROR ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_UCE,
            'RAID controller (%s) detected memory uncorrectable errors - %s.',
            controller.DeviceName,
            cur_fault_code & 1 << common_def.BIT_OFFSET_MEMORY_CORRECTABLE_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end
    if tmp & 1 << common_def.BIT_OFFSET_MEMORY_UNCORRECTABLE_ERROR ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_ECC,
            'RAID controller (%s) detected memory ECC errors reaching limit - %s.',
            controller.DeviceName,
            cur_fault_code & 1 << common_def.BIT_OFFSET_MEMORY_UNCORRECTABLE_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end
    if tmp & 1 << common_def.BIT_OFFSET_ECC_ERROR ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_NVRAM,
            'RAID controller (%s) detected NVRAM errors - %s.',
            controller.DeviceName,
            cur_fault_code & 1 << common_def.BIT_OFFSET_ECC_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_HW_ERROR ~= 0 or ret.err_change & 2 ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_BOARD_HARDWARE,
            'RAID controller (%s) detected BOARD_HW errors, err_code: 0x%s - %s.',
            controller.DeviceName,
            string.format('%x', ret.hw_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_HW_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_CLK_ERROR ~= 0 or ret.err_change & 4 ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_BOARD_CLOCK,
            'RAID controller (%s) detected BOARD_CLK errors, err_code: 0x%s - %s.',
            controller.DeviceName,
            string.format('%x', ret.clk_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_CLK_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_POWER_ERROR ~= 0 or ret.err_change & 8 ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_BOARD_POWER,
            'RAID controller (%s) detected BOARD_POWER errors, err_code: 0x%s - %s.',
            controller.DeviceName,
            string.format('%x', ret.power_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_POWER_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end

    if tmp & 1 << common_def.BIT_OFFSET_RAID_CAPACITY_ERROR ~= 0 or ret.err_change & 16 ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_BOARD_CAPACITY,
            'RAID controller (%s) detected BOARD_CAPACITY errors, err_code: 0x%s - %s.',
            controller.DeviceName,
            string.format('%x', ret.capacity_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_CAPACITY_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end
    if tmp & 1 << common_def.BIT_OFFSET_RAID_FLASH_ERROR ~= 0 or ret.err_change & 32 ~= 0 then
        log:maintenance(
            log.MLOG_ERROR,
            common_def.FC_STORAGE_RAID_BOARD_FLASH,
            'RAID controller (%s) detected FLASH errors, err_code: 0x%s - %s.',
            controller.DeviceName,
            string.format('%x', ret.flash_err),
            cur_fault_code & 1 << common_def.BIT_OFFSET_RAID_FLASH_ERROR ~= 0 and 'Asserted' or 'Deasserted'
        )
    end
end

-- 在通信链路中添加控制器对象
function c_controller:add_controller_to_link_topo()
    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_I2C then
        local ok, _ = sml.set_i2c_chip(self.Id, self.RefChip)
        if not ok then
            error('[Storage]Failed to set i2c chip, controller_id = %d', self.Id)
        end
        log:notice('Controller_%d, RefChip.Path:%s', self.Id, self.RefChip.path)
    elseif method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        local ok, ret = pcall(mctp_service.get_instance().register_mctp_object, mctp_service.get_instance(), self)
        if not ok then
            error(string.format('[Storage]Failed to register mctp object, ret = %s', ret))
        end
        log:notice('Register Controller_%d to mctp successfully', self.Id)
    end
end

-- 在smlib中添加控制器对象
function c_controller:add_controller_to_sml()
    local data = {}
    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        data = {
            Eid = self.Eid,
            Phyaddr = self.Phyaddr,
        }
    end

    local ret = sml.register_controller(self.Id, self.TypeId, sml.SML_ADD_CTRL, data)
    if ret ~= 0 then
        if ret ~= common_def.SML_ERR_CTRL_DUPLICATE_REGISTERED then
            ctrl_commu_loss_monitor.get_instance():update(true, self.Id)
        end
        error(string.format('[Storage] Failed to add controller %d, ret: %s', self.Id, ret))
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
end

-- 将控制器管理对象注册给smlib
function c_controller:register_controller_to_sml()
    local data = {}
    local type_id_param = self:get_mapping_type_id()
    local ret
    log:notice(
        'ctrl%s register_controller_to_sml, CreatedByDeviceObject=%s, DevicePath=%s',
        self.Id,
        tostring(self.CreatedByDeviceObject),
        self.DevicePath
    )
    if self.CreatedByDeviceObject then
        ret = sml.register_dev_controller(self.Id, type_id_param, sml.SML_REGISTER_CTRL, data, self.DevicePath)
    else
        ret = sml.register_controller(self.Id, type_id_param, sml.SML_REGISTER_CTRL, data)
    end
    log:info('ctrl%s register_controller_to_sml ret=%s', self.Id, tostring(ret))
    if ret ~= 0 then
        if ret ~= common_def.SML_ERR_CTRL_DUPLICATE_REGISTERED then
            ctrl_commu_loss_monitor.get_instance():update(true, self.Id)
        end
        error(string.format('[Storage] Failed to register controller %d, ret: %s', self.Id, ret))
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
end

-- 将控制器管理对象取消注册
function c_controller:unregister_controller_to_sml()
    local data = {}
    local type_id_param = self:get_mapping_type_id()
    local ret = sml.unregister_controller(self.Id, type_id_param, sml.SML_UNREGISTER_CTRL, data)
    if ret ~= 0 then
        error(string.format('[Storage] Failed to unregister controller %d, ret: %s', self.Id, ret))
    end
end

local function get_state(v)
    return (v == 0 or v == 1) and v or 2
end

local function set_suitable_state(v)
    return (v == 0 or v == 1) and v or common_def.INVALID_U8
end

local function str_or_na(v)
    if not v or #v == 0 then
        return common_def.INVALID_STRING
    end
    return v
end

-- 0-关闭,1-开启裸盘、成员盘慢盘检测,2-开启裸盘慢盘检测,3-开启成员盘慢盘检测
local function update_slow_drive_dection_mode(type_id, support_properties)
    if not method_misc:test_controller_vendor(type_id, common_def.VENDER_HUAWEI) then
        return common_def.INVALID_U8
    end
    local res
    if support_properties.o_raw_drive_sw ~= support_properties.o_member_drive_sw then
        res = support_properties.o_raw_drive_sw == 0 and common_def.SLOW_DISK_DETECTION_MODE.ENABLE_MEMBER_DRIVE
            or common_def.SLOW_DISK_DETECTION_MODE.ENABLE_RAW_DRIVE
    else
        res = support_properties.o_raw_drive_sw == 0 and common_def.SLOW_DISK_DETECTION_MODE.DISABLE
            or common_def.SLOW_DISK_DETECTION_MODE.ENABLE_RAW_AND_MEMBER_DRIVE
    end
    return res
end

function c_controller:update_sas_addr()
    local ok, ret = pcall(sml.get_ctrl_sas_addr, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        self.SASAddr = ret
    else
        record_commu_loss(ret, self.Id)
    end
end

function c_controller:update_boot_devices()
    local ok, ret = pcall(sml.get_ctrl_boot_devices, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        local device_names = {}
        for _, volume_id in ipairs(ret) do
            local device_name = self:generate_boot_device_desc(volume_id)
            table.insert(device_names, device_name)
        end

        local valid_device_names = {}
        for i = 1, self.SupportedBootDeviceCount do
            if device_names[i] then
                table.insert(valid_device_names, device_names[i])
            else
                table.insert(valid_device_names, 'None')
            end
        end
        self.BootDevices = valid_device_names
    else
        record_commu_loss(ret, self.Id)
    end
end

function c_controller:update_fault_code()
    local ok, ret = pcall(sml.get_ctrl_faultcode, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        generate_controller_health_maintain_log(self, ret)
        self.FaultCode = ret.health_status_code
    else
        record_commu_loss(ret, self.Id)
    end
end

function c_controller:update_ctrl_info_from_sml(info)
    self.ControllerName = info.ctrl_name
    self.Type = CONTROLLER_TYPE[self.TypeId]
    self.FirmwareVersion = info.fw_version
    self.DeviceInterface = info:get_ctrl_device_interface()

    if not method_misc:test_controller_vendor(self.TypeId, common_def.VENDER_HUAWEI) then
        self.SerialNumber = info.ctrl_sn
    end

    self.MinStripSizeBytes, self.MaxStripSizeBytes = info:get_ctrl_strip_szie_options()
    self.MemorySizeMiB = method_misc:update_if_valid(0, info.memory_size, common_def.INVALID_U16)
    self.NVDataVersion = str_or_na(info.nvdata_version)
    self.HardwareRevision = str_or_na(info.hardware_revision)
    self.PCIeLinkWidth = (info.pcie_link_width and info.pcie_link_width ~= common_def.INVALID_U8)
            and string.format('x%d', info.pcie_link_width)
        or common_def.INVALID_STRING
    self.SupportedMode = info.work_mode
    self.HotSpareActivationMode = common_def.ACTIVATION_MODE[info.spare_activation_mode] or ''
    self.SupportedRAIDTypes = info:get_raid_types()
    self.NoBatteryWriteCacheEnabled = get_state(info.nobattery_write_cache)
    self.ReadCachePercent = info.read_cache_percent

    local two_bytes_width = info.ctrl_temp & common_def.U16_MAX
    local trans_temperature = two_bytes_width >= common_def.SIGNED_BIT and -(common_def.U16_MAX - two_bytes_width + 1)
        or two_bytes_width
    self.TemperatureCelsius = (trans_temperature >= 255) and 255 or trans_temperature
    self.DDREccCount = info.memory_ecc_count
    self.UncorrectableParityErrorCount = info.memory_uce_count
    self.CachePinnedState = set_suitable_state(info.cache_pinned)
    self.MaintainPDFailHistrory = set_suitable_state(info.maint_pd_fail_history)
    self.WorkMode = (info.mode and info.mode < #work_mode) and work_mode[info.mode + 1] or common_def.INVALID_STRING

    local operations = info:get_support_operations()
    local ld_operations = operations.ld_operations
    self.WritePolicyWritable = ld_operations.o_support_write_policy == 1
    self.IOPolicyWritable = ld_operations.o_support_io_policy == 1
    self.AccessPolicyWritable = ld_operations.o_support_access_policy == 1
    self.ReadPolicyWritable = ld_operations.o_support_read_policy == 1
    self.DriveCachePolicyWritable = ld_operations.o_support_disk_cache_policy == 1

    local ctrl_operations = operations.ctrl_operations
    self.HBADriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[ctrl_operations.o_hba_drive_wcp] or ''
    self.ConfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[ctrl_operations.o_configured_drive_wcp] or ''
    self.UnconfiguredDriveWriteCachePolicy = common_def.RAID_CTRL_WCP_NAME[ctrl_operations.o_unconfigured_drive_wcp]
        or ''
    self.EpdSupported = ctrl_operations.o_support_epd ~= 0 and true or false
    self.JbodSupported = ctrl_operations.o_support_jbod_state ~= 0 and true or false

    local support_properties = info:get_support_properties(self.TypeId)
    self.JBODState = set_suitable_state(support_properties.o_jbod_enabled)
    self.CopyBackState = set_suitable_state(support_properties.o_copyback_enabled)
    self.SmarterCopyBackState = set_suitable_state(support_properties.o_smarter_copyback_enabled)
    self.CryptoEraseSupported = get_state(support_properties.o_support_crypto_erase) ~= 0
    self.CreateVolumeSupported = c_storageconfig.get_instance():get_set_configuration_ready_flag(self.Id)
    self.SlowDriveDetectionMode = update_slow_drive_dection_mode(self.TypeId, support_properties)

    if method_misc:get_oob_interface_type(self.TypeId) == sml.OOB_TYPE_OVER_PCIE then
        self:update_consistency_check(support_properties)
    end

    -- 配置刷新:未备份时触发备份/恢复检测;已备份时检测带内/外部配置变更并同步备份
    if not self.config_backed_up then
        skynet.fork(function()
            ctrl_config_backup.get_instance():on_first_config_loaded(self)
        end)
    else
        skynet.fork(function()
            ctrl_config_backup.get_instance():sync_inband_config(self)
        end)
    end
end

function c_controller:generate_boot_device_desc(volume_id)
    if not volume_id or volume_id == 0 then
        return 'None'
    end
    if volume_id & VOLUME_TYPE_PHYSICAL_DRIVE_BIT ~= 0 then
        local drive_obj =
            drive_collection:get_drive_by_physic_id(self.Id, volume_id & 0xFF, (volume_id & 0xFFFF00) >> 8)
        if drive_obj then
            return drive_obj.Name
        end
    end
    if volume_id & VOLUME_TYPE_LOGICAL_DRIVE_BIT ~= 0 then
        local ret = 'Volume' .. (volume_id & 0xFFFF)
        return ret
    end
end

function c_controller:update_consistency_check(consis_check_table)
    self.ConsistencyCheckState = get_state(consis_check_table.o_consis_check_enabled)
    self.PeriodOfHours = consis_check_table.o_consis_check_period
    self.Rate = rate_array[consis_check_table.o_consis_check_rate]
    self.AutoRepairEnabled = get_state(consis_check_table.o_consis_check_repair)
    self.RunningStatus = running_state[consis_check_table.o_consis_check_status + 1]
    self.TotalVolumeCounts = consis_check_table.o_consis_check_totalvd
    self.CompletedVolumeCounts = consis_check_table.o_consis_check_completedvd
end

function c_controller:update_controller_battery_info()
    local ref_battery = battery_collection.get_instance():get_capcacitance_by_controller_id(self.Id)
    if not ref_battery then
        return
    end
    self.BatteryPresence =
        self.presence_debounce:get_debounced_val(ref_battery.State and ref_battery.State or common_def.INVALID_U8)
    if ref_battery.Fault ~= common_def.INVALID_U8 then
        self.BatteryFault = (ref_battery.Fault == 0 and 0 or 1)
    end
    ref_battery.Health = self.BatteryHealth
end

-- 比较控制器 pd_list,计算新增和删除的 pd 列表
function c_controller:compare_pd_list(pd_list)
    local new_pd_list = {}
    local del_pd_list = {}
    local pd_count = 0
    for _, pd in ipairs(pd_list) do
        if not self.pd_list[pd.key] then
            new_pd_list[#new_pd_list + 1] = pd
        end
        new_pd_list[pd.key] = true
    end

    for key in pairs(self.pd_list) do
        pd_count = pd_count + 1
        if not new_pd_list[key] then
            del_pd_list[#del_pd_list + 1] = key
        end
    end

    return new_pd_list, del_pd_list
end

-- 获取控制器 pd_list
function c_controller:get_ctrl_pd_list()
    local pd_list = {}
    local controller_id = self.Id
    if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
        return
    end
    local ok, ret = pcall(sml.get_ctrl_pd_list, controller_id)
    if not ok then
        log:error('get_ctrl_pd_list failed and return %s', ret)
        if
            ret == common_def.SML_ERR_MCTP_TIME_OUT
            and method_misc:test_controller_vendor(self.TypeId, common_def.VENDER_HUAWEI)
        then
            self.timeout_times = self.timeout_times + 1
            if self.timeout_times == MAX_RETRY_TIMES then
                self:register()
                self.timeout_times = 0
            end
        end
        record_commu_loss(ret, self.Id)
        return nil
    end
    self.timeout_times = 0
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    for _, pd in ipairs(ret) do
        pd_list[#pd_list + 1] = pd_object.new(controller_id, pd, self.TypeId)
    end
    table.sort(pd_list, function(a, b)
        return a.slot_num < b.slot_num
    end)
    log:debug(pd_list)
    return pd_list
end

function c_controller:check_pd_missing(pd_list)
    local drive_list = drive_collection:get_all_drives()
    for _, drive_obj in pairs(drive_list) do
        -- BMC启动,硬盘定位identify后RefControllerTypeId会被赋值有效值,模拟硬盘missing该值不会恢复默认值
        if
            drive_obj.Presence == 1
            and drive_obj.RefControllerId == self.Id
            and drive_obj.RefControllerTypeId ~= common_def.INVALID_U8
        then
            drive_obj:check_missing(pd_list)
        end
    end
end

-- 更新控制器 pd_list
function c_controller:update_pd_list()
    local ok, ret = pcall(sml.get_ctrl_init_state, self.Id)
    if not ok or ret ~= 2 then
        return
    end

    local pd_list = self:get_ctrl_pd_list()
    if pd_list == nil then
        return
    end
    self:check_pd_missing(pd_list)
    local new_pd_list, del_pd_list = self:compare_pd_list(pd_list)
    for _, key in ipairs(del_pd_list) do
        local deleted_pd = self.pd_list[key]
        self.pd_list[key] = nil

        -- 当PD被删除时,更新对应drive的presence为0
        if self:is_directly_manage_physical_drive() then
            local drive = drive_collection
                .get_instance()
                :get_drive_by_physic_id(self.Id, deleted_pd.slot_num, deleted_pd.enclosure_id)
            if drive then
                drive:set_presence(0)
            end
        end

        deleted_pd:invalid()
        self.on_del_pd:emit(deleted_pd)
    end

    for _, pd in ipairs(new_pd_list) do
        self.pd_list[pd.key] = pd

        -- 当PD被添加时,更新对应drive的presence为1
        if self:is_directly_manage_physical_drive() then
            local start_drive_id = common_def.HRDT_B261_START_DRIVE_ID
            local drive_id = start_drive_id + pd.slot_num - 1
            local drive_name = string.format('Disk%d', drive_id)
            local drive = drive_collection.get_instance():get_drive(drive_name)
            if drive then
                drive:set_presence(1)
            end
        end

        self.on_add_pd:emit(pd)
    end
    self.on_add_pd_complete:emit(true)
end

function c_controller:get_sub_objs(path)
    local service = 'bmc.kepler.host_agent'
    local bus_resource = self.bus
    for _ = 1, 3 do
        local err, objects =
            bus_resource:pcall(service, path, 'org.freedesktop.DBus.ObjectManager', 'GetManagedObjects', '')
        if not err and objects then
            return objects
        end
        skynet.sleep(100)
    end
end

-- 通过BMA更新raid
function c_controller:update_controller_info_via_bma()
    local ok_sms_status, sms_status_obj =
        pcall(mdb.get_object, self.bus, '/bmc/kepler/Systems/1/Sms', 'bmc.kepler.Systems.Sms.SmsStatus')
    local ok_sms, sms_obj = pcall(mdb.get_object, self.bus, '/bmc/kepler/Systems/1/Sms', 'bmc.kepler.Systems.Sms')

    if not ok_sms_status or not ok_sms then
        return
    end

    if sms_status_obj.State == 0 and sms_obj.Registered == true then
        self:update_info_via_bma()
    else
        self.DriverName = common_def.INVALID_STRING
        self.DriverVersion = common_def.INVALID_STRING
    end
end

local CONTROLLER_PATH = '/bmc/kepler/Systems/1/Sms/1/ComputerSystem/Systems/1/Storage/1/StorageControllers'

local function convert_bdf(bdf)
    if not bdf then
        error('no bdf')
    end
    return string.match(bdf, '(.+):(.+):(.+).(.+)')
end

local BDF_PATH = 'bmc.kepler.sms.redfish.Oem.Huawei.BDFNumber'
function c_controller:bdf_match(bma_resource)
    if not bma_resource[BDF_PATH] or not bma_resource[BDF_PATH].BDF then
        log:error('bma_resource bdf path:%s  or BDF property not exist', BDF_PATH)
        return false
    end
    local ok, _, dev_bus, dev_device, dev_func = pcall(convert_bdf, bma_resource[BDF_PATH].BDF:value():value())
    if not ok or not dev_bus or not dev_device or not dev_func then
        log:error(
            '[controller_object] Failed to convert BDF, ctrl_id=%s, BDF=%s:%s:%s',
            self.Id,
            dev_bus,
            dev_device,
            dev_func
        )
        return false
    end
    if
        self.DevBus == tonumber(dev_bus, 16)
        and self.DevDevice == tonumber(dev_device, 16)
        and self.DevFunction == tonumber(dev_func, 16)
    then
        return true
    end
    return false
end

local function update_drive_info_from_bma(driveinfo)
    local driveinfo_str = common_def.INVALID_STRING
    if driveinfo ~= 'null' then
        driveinfo_str = driveinfo
    end
    return driveinfo_str
end

function c_controller:update_info_via_bma()
    local info_interface = 'bmc.kepler.sms.redfish.Oem.Huawei.DriverInfo'
    local objects = self:get_sub_objs(CONTROLLER_PATH)
    if not objects or not next(objects) then
        return
    end
    for _, object in pairs(objects) do
        if self:bdf_match(object) then
            self.DriverName = update_drive_info_from_bma(object[info_interface].DriverName:value():value())
            self.DriverVersion = update_drive_info_from_bma(object[info_interface].DriverVersion:value():value())
            return
        end
    end
    log:info('Controller %s update DriverName and DriverVersion failed!', self.Id)
end

-- 比较控制器 volume_list,计算新增和删除的 volume 列表
function c_controller:compare_volume_list(volume_list)
    local new_volume_list = {}
    local del_volume_list = {}
    for _, volume in ipairs(volume_list) do
        if not self.volume_list[volume.key] then
            new_volume_list[#new_volume_list + 1] = volume
        end
        new_volume_list[volume.key] = true
    end

    for key in pairs(self.volume_list) do
        if not new_volume_list[key] then
            del_volume_list[#del_volume_list + 1] = key
        end
    end

    if log:getLevel() >= log.INFO then
        log:info(
            'Controller %s add new volume list:%s, del volume list:%s',
            self.Id,
            cjson.encode(new_volume_list),
            cjson.encode(del_volume_list)
        )
    end
    return new_volume_list, del_volume_list
end

-- 获取控制器 volume_list
function c_controller:get_ctrl_volume_list()
    local volume_list = {}
    local controller_id = self.Id
    if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
        return
    end
    local ok, ret = pcall(sml.get_ctrl_ld_list, controller_id)
    if not ok then
        log:debug('get_ctrl_ld_list failed and return %s', ret)
        record_commu_loss(ret, self.Id)
        return nil
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    for _, volume_id in ipairs(ret) do
        volume_list[#volume_list + 1] =
            volume_obj_info.new(self.position, controller_id, volume_id, self.CreatedByDeviceObject)
    end
    return volume_list
end

-- 更新控制器 volume_list
function c_controller:update_volume_list()
    local ok, ret = pcall(sml.get_ctrl_init_state, self.Id)
    if not ok or ret ~= 2 then
        return
    end
    local volume_list = self:get_ctrl_volume_list()
    if volume_list == nil then
        return
    end
    local new_volume_list, del_volume_list = self:compare_volume_list(volume_list)

    for _, key in ipairs(del_volume_list) do
        local deleted_volume = self.volume_list[key]
        self.volume_list[key] = nil
        volume_collection.get_instance().on_del_volume:emit(deleted_volume)
    end

    for _, volume in ipairs(new_volume_list) do
        self.volume_list[volume.key] = volume
        volume_collection.get_instance().on_add_volume:emit(volume)
    end
end

function c_controller:remove_volume_list()
    local old_volume_list = self.volume_list
    self.volume_list = {}

    for _, volume_obj in pairs(old_volume_list) do
        volume_collection.get_instance().on_del_volume:emit(volume_obj)
    end
end

-- 比较控制器 array_list,计算新增和删除的 array 列表
function c_controller:compare_array_list(sml_array_list)
    local add_array_list = {}
    local del_array_list = {}
    local new_array_list = {}
    for _, array_id in ipairs(sml_array_list) do
        if not self.array_list[array_id] then
            add_array_list[#add_array_list + 1] = array_id
        end
        new_array_list[array_id] = true
    end
    for array_id, _ in pairs(self.array_list) do
        if not new_array_list[array_id] then
            del_array_list[#del_array_list + 1] = array_id
        end
    end
    return add_array_list, del_array_list
end

-- 更新控制器的硬盘阵列信息
function c_controller:update_array_list()
    local controller_id = self.Id
    local ok, ret = pcall(sml.get_ctrl_init_state, self.Id)
    if not ok or ret ~= 2 then
        return
    end
    if self.tasks_pause or c_storageconfig.get_instance().power_on == 0 then
        return
    end
    -- 从RAID卡获取硬盘阵列列表
    ok, ret = pcall(sml.get_ctrl_array_list, controller_id)
    if not ok then
        record_commu_loss(ret, self.Id)
        return
    end
    ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
    -- 从硬盘阵列服务获取硬盘阵列列表
    self.array_list = array_collection.get_instance():get_ctrl_array_list_by_ctrl_id(self.Id)

    local add_array_list, del_array_list = self:compare_array_list(ret)

    for _, array_id in ipairs(del_array_list) do
        self.array_list[array_id] = nil
        array_collection.get_instance().on_del_array:emit(self.Id, array_id)
    end

    for _, array_id in ipairs(add_array_list) do
        self.array_list[array_id] = true
        array_collection.get_instance().on_add_array:emit(self.Id, self.controller_path, array_id)
    end
end

function c_controller:remove_array_list()
    local old_array_list = self.array_list
    self.array_list = {}

    for array_id, _ in pairs(old_array_list) do
        array_collection.get_instance().on_del_array:emit(self.Id, array_id)
    end
end

function c_controller:get_phy_children()
    local phy_children = {}
    if self.CreatedByDeviceObject and self.DevicePath then
        -- 按 DevicePath 前缀过滤出本控制器的 PHY 对象
        local controller_mgmt = require('controller.controller_mgmt')
        local mgmt = controller_mgmt.get_instance()
        if mgmt and mgmt.sasphys then
            local prefix = self.DevicePath .. '/'
            for device_path, obj in pairs(mgmt.sasphys) do
                if type(obj) == 'table' and string.sub(device_path, 1, #prefix) == prefix then
                    table.insert(phy_children, obj)
                end
            end
        end

        if next(phy_children) then
            table.sort(phy_children, function(a, b)
                return a.PhyId < b.PhyId
            end)
        end
        return phy_children
    end

    local children = self:get_children()
    for _, v in pairs(children) do
        if v['PhyId'] then
            table.insert(phy_children, v)
        end
    end

    if next(phy_children) then
        table.sort(phy_children, function(a, b)
            return a.path < b.path
        end)
    end

    return phy_children
end

function c_controller:update_phy_info()
    local ok, phy_addr = pcall(function()
        return sml.get_ctrl_sas_phy_err(self.Id, 0)
    end)

    if not ok or not phy_addr then
        return
    end

    local phy_children = self:get_phy_children()
    -- #计算从下标1开始,phy_addr数组从下标0开始,因此+1
    if #phy_addr + 1 ~= #phy_children then
        return
    end

    local phy_id
    for k, v in pairs(phy_children) do
        phy_id = k - 1
        v.PhyId = phy_id
        v.InvalidDwordCount = phy_addr[phy_id].invalid_dword_count
        v.LossDwordSyncCount = phy_addr[phy_id].loss_dword_sync_count
        v.PhyResetProblemCount = phy_addr[phy_id].phy_reset_problem_count
        v.RunningDisparityErrorCount = phy_addr[phy_id].running_disparity_error_count
    end
end

function c_controller:dump_info(fp_w)
    controller_dump:dump_info(fp_w, self)
end

-- 清除资产信息
function c_controller:clear_asset_data_info()
    self.AssetType = ''
    self.AssetName = ''
    self.InventorySerialNumber = ''
    self.InventoryFirmwareVersion = ''
    self.Manufacturer = ''
    self.AssetTag = ''
    self.PartNumber = ''
    self.ManufactureDate = ''
    self.Slot = ''
    self.UUID = ''
    self.InventoryModel = ''
end

-- 收集资产清单
function c_controller:update_asset_data_info()
    self.AssetType = 'PCIe RAID Card'
    self.AssetName = self.ControllerName
    self.InventorySerialNumber = self.SerialNumber
    self.InventoryFirmwareVersion = self.FirmwareVersion
    self.Manufacturer = self.ChipManufacturer
    self.InventoryModel = self.Model
    self.AssetTag = 'N/A'
    self.PartNumber = self.BOMNumber
    self.ManufactureDate = 'N/A'
    self.Slot = tostring(self.Id)
    self.UUID = 'N/A'
end

-- 传递硬盘厂商数据
function c_controller:trans_pd_vendor_data()
    local data_str = cjson.encode(common_def.DRIVE_VENDOR_TABLE)
    local ok, ret = pcall(sml.trans_drive_data, self.Id, data_str)
    if not ok then
        log:error('trans pd_vendor_data failed ,ret is %s', ret)
    end
end

-- 将新配置的raid卡的TypeId映射到已有的TypeId上
function c_controller:get_mapping_type_id()
    return common_def.TYPEID_MAPPING[self.TypeId] and common_def.TYPEID_MAPPING[self.TypeId] or self.TypeId
end

-- 从PCIeCard资源树对象获取RAID卡的SoftwareId:'RAID-VID.DID.SVID.SDID'(十六进制4位)
function c_controller:get_pcie_card_quad()
    local software_id = 'RAID-NA'
    local retries = 0
    while retries < QUAD_RETRY_TIMES do
        retries = retries + 1
        local ok, card_list = pcall(mdb.get_sub_objects, self.bus, PCIE_CARD_PATH, PCIE_CARD_INTERFACE)
        if ok and card_list then
            for _, card in pairs(card_list) do
                if card.DeviceName == self.DeviceName then
                    software_id = string.format(
                        'RAID-%04x.%04x.%04x.%04x',
                        card.VendorID,
                        card.DeviceID,
                        card.SubVendorID,
                        card.SubDeviceID
                    )
                    log:notice(
                        '[Controller] get PCIeCard quad success, attempt %s/%s, DeviceName=%s, SoftwareId=%s',
                        retries,
                        QUAD_RETRY_TIMES,
                        tostring(self.DeviceName),
                        software_id
                    )
                    return software_id
                end
            end
        end
        if retries < QUAD_RETRY_TIMES then
            skynet.sleep(QUAD_RETRY_INTERVAL)
        else
            log:error(
                '[Controller] get PCIeCard quad failed after %s times, DeviceName=%s, SlotId=%s',
                QUAD_RETRY_TIMES,
                tostring(self.DeviceName),
                tostring(self.SlotId)
            )
        end
    end
    return software_id
end

function c_controller:register_controller_firmware_info()
    if not method_misc:check_is_suport_upgrade(self.TypeId) then
        return
    end

    local data = {
        Id = self.NodeId,
        Name = self.DeviceName,
        Version = self.FirmwareVersion, -- 确定出事状态时是否符合预期
        Manufacturer = self.ChipManufacturer,
        State = 'Enabled',
        Severity = 'Informational',
        EstimatedDuration = '30',
        Location = tostring(self.Id),
        SoftwareId = self:get_pcie_card_quad(),
        ActiveCondition = 'None',
        StageSupported = 'false',
        ComponentID = tostring(upgrade_defs.RAID_COMPONENT_ID),
        SubComponentID = tostring(upgrade_defs.RAID_COMPONENT_IDEX),
        FirmwareType = upgrade_defs.FIRMWARE_TYPE_RAID,
        TargetUIDs = '',
        RelatedItem = upgrade_defs.REDFISH_RAID_URI_PREFIX .. self.Id,
    }
    method_misc:retry_register_firmware_info(data, true, 4, 10, function()
        self.firmware_register_status = true
    end)
end

c_controller.record_commu_loss = record_commu_loss

return c_controller