-- 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 drive_collection = require 'drive.drive_collection'
local array_collection = require 'array.array_collection'
local custom_msg = require 'messages.custom'
local CTRL_OPTION = common_def.CTRL_OPTION
local MAX_RETRY_TIMES = 30

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] = "PM8222",
    [96] = 'B80121',
    [97] = 'B80121',
    [98] = 'B80121',
    [99] = 'B80121',
    [100] = 'B80121',
    [101] = 'B80121',
    [102] = 'B80121',
    [103] = 'B80121',
    [113] = 'B80121'
}

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.CryptoEraseSupported = false
    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 = debounce[debounce.DEBOUNCED_CONT].new(5, common_def.INVALID_U8) -- 5 次防抖
    self.tasks_pause = false
    self.first_register_task = false
    self.timeout_times = 0
    self.update_queue = queue()
    self.firmware_register_status = 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:notice('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)
    ---@type c_signal_slot
    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:set_default_values()
    self:set_default_volume_manage_values()
    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
    log:notice('controller%s set_default_volume_manage_values', self.Id)
    self:set_default_volume_manage_values()
    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)
    -- 下电状态不算是通信丢失
    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:register()
    log:notice('controller%s begin register', self.Id)
    self:new_task({ 'register_controller', self.Id }):loop(function(task)
        self.registering = true
        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)
        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)
    
    self.CommunicationLoss = ctrl_commu_loss_monitor.get_instance():get_ctrl_commu_loss(self.Id)
    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

    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.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 and false or true

    -- 写策略相关
    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 and false or true

    -- 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 and false or true

    -- 访问策略相关
    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 and false or true
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 and false or true
    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: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 = sml.register_controller(self.Id, type_id_param, sml.SML_REGISTER_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 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 = common_def.INVALID_U8
    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, ret1, ret2 = pcall(sml.get_ctrl_boot_devices, self.Id)
    if ok then
        ctrl_commu_loss_monitor.get_instance():update(false, self.Id)
        local primary_device = self:generate_boot_device_desc(ret1)
        local secondary_device = self:generate_boot_device_desc(ret2)
        self.BootDevices = {primary_device, secondary_device}
    else
        record_commu_loss(ret1, 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 and false or true
    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
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_drives_policy(info)
    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
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
        if drive_obj.Presence == 1 and drive_obj.RefControllerId == self.Id and drive_obj.identify_pd 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]
        local drive = drive_collection.get_instance():get_drive_by_physic_id(self.Id, deleted_pd.slot_num,
            deleted_pd.enclosure_id)
        self.pd_list[key] = nil

        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
        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('Invalid 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('Failed to convert BDF')
        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)
    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 children = self:get_children()
    local phy_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

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.DeviceName,
    }
    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
