-- 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 skynet = require 'skynet'
local mctp_lib = require 'mctp_lib'
local file_sec = require 'utils.file'
local utils_core = require 'utils.core'
local ctx = require 'mc.context'
local bs = require 'mc.bitstring'
local mdb = require 'mc.mdb'
local log = require 'mc.logging'
local c_object = require 'mc.orm.object'
local c_object_manage = require 'mc.orm.object_manage'
local c_tasks = require 'mc.orm.tasks'
local signal = require 'mc.signal'
local utils = require 'mc.utils'
local pmu_cmd = require 'imu'
local ncsi_core = require 'ncsi.ncsi_core'
local libmgmt_protocol = require 'libmgmt_protocol'
local client = require 'network_adapter.client'
local fructl = require 'infrastructure.fructl'
local log_collector = require 'device.class.log_collector'
local npu_port_link_log_collection = require 'device.class.npu_port_link_log_collection'
local c_network_port = require 'device.class.network_port'
local c_optical_module = require 'device.class.optical_module'
local log_netcard_info = require 'device.class.log_netcard_info'
local org_freedesktop_dbus = require 'sd_bus.org_freedesktop_dbus'
local MatchRule = org_freedesktop_dbus.MatchRule

local MODULE_NAME_NETWORK_ADAPTER<const> = 'network_adapter'
local LOG_DUMP_BUSY<const> = 1 -- 1表示正在收集
local LOG_DUMP_IDLE<const> = 0 -- 0 表示空闲

local LOG_RETRY_TIMES<const> = 3
local NCSI_FAILED_THRESHOLD<const> = 3 -- NCSI连续失败阈值，达到后启动SMBUS备选
local OCP_CARD_TYPE<const> = 10
local PACKAGE_ID_MAX<const> = 7

local PMU_PATH<const> = '/bmc/kepler/Systems/1/Pmu'
local PMU_INTERFACE<const> = 'bmc.kepler.Systems.Pmu'

local MODEL_PATTERN<const> = 'Hi182%d+$'
local MODEL_182X<const> = 'Hi182X'

local CDR_UPPERNONCRITICAL<const> = 100

local HOTPLUG_STATE<const> = {
    Inoperable = 0, -- 不可操作
    Removable = 1, -- 可拔出
    Uninstalling = 3, -- 卸载中
    Uninstalled = 4 -- 卸载完成
}

-- 网卡需要收集的日志类型
-- 必须要使用匿名函数，只有这样时间戳才会变化
local LOG_TYPES<const> = {
    [0] = function ()
        return 'type0_' .. log_collector.get_time() .. '.bin'
    end,    -- runlog
    [1] = function ()
        return 'type1_' .. log_collector.get_time() .. '.bin'
    end,    -- counter
    [2] = function ()
        return 'type2_' .. log_collector.get_time() .. '.bin'
    end,    -- register
    [3] = function ()
        return 'type3_' .. log_collector.get_time() .. '.bin'
    end     -- black box
}

local BIOS_STARTUP_POST_STAGE_FINISH<const> = 0xFE -- BIOS启动完成
local READY_TO_REMOVE_VALUE<const> = 1 -- 通知网卡电源关闭
local NETWORK_POWER_OFF<const> = 0 -- 网卡电源关闭
local GETLOG_SUBCMD_WITH_NEW_CARD_FW<const> = 0x15
local GETLOG_SUBCMD_WITH_OLD_CARD_FW<const> = 0x12

local MPU_IDLE<const> = 0 -- 0 表示MPU空闲

local DPU_STATE<const> = {
    'Reset/BooDROM',
    'BL2',
    'BL31',
    'UEFI',
    'OS starting',
    'OS is running',
    'Low-Power standby',
    'Firmware update in progress',
    'OS Crash Dump in progress',
    'OS Crash Dump is complete',
    'FW Fault Crash Dump in progress',
    'FW Fault Crash Dump is complete'
}
local DPU_STATE_NUM<const> = 12

local c_network_adapter = c_object('NetworkAdapter')

local function param_validator(DeviceLocator)
    if DeviceLocator == '' then
        return false
    end
    if  DeviceLocator:find('%$') then
        return false
    end
    return true
end

local function wait_param_validator(object)
    local retry_times = 0
    while true do
        skynet.sleep(10)
        if (object.DeviceLocator and not object.DeviceLocator:find('%$') and
            object.SlotNumber and object.SlotNumber ~= 0) or retry_times > 1200 then
                -- 网卡的主键依赖 pcie_device 组件填充 DeviceLocator 属性（引用）
                -- pcie_device组件设置Slot后，框架同步更新DeviceLocator需要一定时间，实测5s能满足业务
                skynet.sleep(500)
            break
        end
        retry_times = retry_times + 1
    end
    return
end

function c_network_adapter:update_quater_info()
    if STOP_INIT then
        -- ut不进行这里，否则会死循环
        return
    end
    skynet.sleep(12000) -- 启动阶段等待2分钟后更新网卡四元组
    log:notice('init quater info')
    local ret
    local pcie_info = {
        system_id = 1,
        is_local = false,
        cpu_id = self.SocketId,
        did = 0,
        vid = 0,
        sdid = 0,
        svid = 0
    }
    while true do
        if self.DevBus == 0 or fructl.get_power_status() ~= 'ON' then
            goto continue
        end
        pcie_info.bus_num = self.DevBus
        pcie_info.device_num = self.DevDevice
        pcie_info.function_num = self.DevFunction
        ret = self:get_vid_did_svid_sdid(pcie_info)
        if ret then
            self.VendorID = string.format('0x%04x', pcie_info.vid)
            self.DeviceID = string.format('0x%04x', pcie_info.did)
            self.SubsystemVendorID = string.format('0x%04x', pcie_info.svid)
            self.SubsystemDeviceID = string.format('0x%04x', pcie_info.sdid)
        end
        ::continue::
        -- 10s轮询一次
        self:sleep_ms(10000)
    end
end

-- 框架收到自发现分发的对象后，在回调 on_add_object 函数前会装载持久化数据回对象，但是
-- 网卡的主键依赖 pcie_device 组件填充 DeviceLocator 属性（引用），必须在这里填充好
-- NetworkAdapter 对象的主键值，确保框架从持久化能找回正确的持久化数据
function c_network_adapter.before_add_object(object)
    wait_param_validator(object)

    if object.Type == 3 then
        object.ID = string.gsub(object.DeviceLocator, '%s+', '')
        object.NodeId = string.gsub(object.DeviceLocator, '%s+', '')
    else
        object.ID = string.gsub(object.Position, '%s+', '') ..
                        string.gsub(object.DeviceLocator, '%s+', '')
        object.NodeId = string.gsub(object.Position, '%s+', '') ..
                            string.gsub(object.DeviceLocator, '%s+', '')
    end
    return false
end

-- on_add_obj_complete时会调用，最好确保start不会阻塞on_add_obj_complete，
-- 复杂操作用新的协程来完成
function c_network_adapter:start()
    if self.CreatedByDeviceObject then
        return
    end
    log:notice("c_network_adapter start, name:%s, type=%s", self.NodeId, self.Type)
    self:update_asset_uuid_info()
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    for _, port in pairs(ports) do
        port.mac_change:on(function ()
            self:update_asset_uuid_info()
        end)
    end
    self.ports_count = #ports
    self:update_net_dev_func_capabilities(ports)
    -- NIC卡需要从pmu同步光模块
    if string.find(self.Name, 'NIC') then
        self:next_tick(self.sync_nic_op_temp, self, ports)
    end
    if self.HotPluggable then
        self:next_tick(self.set_attention_on_complete, self)
    end
    local model_name = string.match(self.Model, MODEL_PATTERN) and MODEL_182X or self.Model
    local ok, hardware_config = pcall(require, string.format('hardware_config.%s', model_name))
    if not ok or not hardware_config then
        log:info('No specific hardware config file for this network adapter(model: %s)', self.Model)
    else
        self:next_tick(self.init_protocol, self, hardware_config, ports)
    end

    if self.SpecialPcieCard then
        self:next_tick(self.update_1822_port_bdf, self, ports)
    else
        self:next_tick(self.update_port_bdf, self, ports)
    end

    -- 获取四元组信息
    self:next_tick(self.update_quater_info, self)

    -- 获取六口卡的后2口的mac地址
    self:next_tick(self.update_port_mac, self, ports)

    if self.Model == 'NPU' then
        self:next_tick(function ()
            self:get_max_npu_cdr_temp_from_imu()
            pcall(function()
                self.log_collection:link_log_collect(ports)
                self.log_collection:link_log_hccs_collect(ports)
            end)
        end)
        -- npu虚拟网卡开启监听信号函数
        self:register_npu_listen_callback()
    end
end

local function get_npu_op_temperature_status(ops)
    local op_temperature_status = 0
    local abnormal_count = 0
    local present_count = 0
    for _, op in ipairs(ops) do
        if not op.npu_om_not_present then
            present_count = present_count + 1
            if op.npu_optical_module_info_abnormal then
                abnormal_count = abnormal_count + 1
            end
        end
    end
    if abnormal_count == 0 then
        return op_temperature_status
    end
    if abnormal_count == present_count then
        op_temperature_status = 12 -- TemperatureStatus的第三、第四位表示CDR光模块温度获取情况。12表示全部CDR光模块没有获取到。
    elseif abnormal_count < present_count then
        op_temperature_status = 4 -- TemperatureStatus的第三、第四位表示CDR光模块温度获取情况。4表示部分CDR光模块没有获取到。
    end
    return op_temperature_status
end

local function update_port_mac_action(mac_info, port)
    local MAC_ADDR_DEFAULT<const> = '00:00:00:00:00:00'
    local INVALID_DATA_STRING<const> = 'N/A'

    if port.WorkloadType ~= 1 then
        return
    end

    for _, mac in ipairs(mac_info) do
        if port.PortID == mac[1] then
            port.MACAddress = mac[3]
        end
    end

    if port.PermanentMACAddress == MAC_ADDR_DEFAULT or
        port.PermanentMACAddress == INVALID_DATA_STRING then 
        port.PermanentMACAddress = port.MACAddress
    end
end

function c_network_adapter:update_port_mac(ports)
    if STOP_INIT then
        -- ut不进行这里，否则会死循环
        return
    end

    while true do
        for _, port in pairs(ports) do
            update_port_mac_action(self.PfMacInfo, port)
        end
        self:sleep_ms(20000) -- 等待20秒钟
    end
end

local OPTICALMODULE_TEMP_NA_VALUE<const> = 255

function c_network_adapter:update_npu_max_sfp_temp(ops)
    local max_temp, opt_temp
    max_temp = 0
    for _, op in ipairs(ops) do
        if not op.npu_om_not_present then
            -- 光模块温度不为非法值，则参与最值比较
            opt_temp = (op.ReadingCelsius < OPTICALMODULE_TEMP_NA_VALUE) and op.ReadingCelsius or 0
            max_temp = opt_temp > max_temp and opt_temp or max_temp
        end
    end
    self.SFPMaxTemperatureCelsius = max_temp
    local new_temperature_status = self.TemperatureStatus & 3 -- 保留cdr温度的状态
    self.TemperatureStatus = new_temperature_status | get_npu_op_temperature_status(ops)
end

local function get_npu_port_temperature_status(ports)
    local port_temperature_status = 0
    local ports_count = #ports
    local abnormal_count = 0
    for _, port in ipairs(ports) do
        if port.npu_port_cdr_info_abnormal then
            abnormal_count = abnormal_count + 1
        end
    end
    if abnormal_count == ports_count then
        port_temperature_status = 3 -- TemperatureStatus的第一、二位表示cdr温度获取情况，3表示全部cdr没有获取到。
    elseif abnormal_count > 0 and abnormal_count < ports_count then
        port_temperature_status = 1 -- TemperatureStatus的第一、二位表示cdr获取温度情况，1表示部分cdr没有获取到。
    end
    return port_temperature_status
end

function c_network_adapter:record_cdr_max_temp_log(max_temp_npu_id, max_temp)
    if max_temp < CDR_UPPERNONCRITICAL then
        if self.cdr_overtemp_table.npu_id ~= 255 then
            log:notice("[network_adapter]the max cdr temperature %s is npu %s remove",
                self.cdr_overtemp_table.max_temp, self.cdr_overtemp_table.npu_id)

            self.cdr_overtemp_table.max_temp = 0
            self.cdr_overtemp_table.npu_id = 255
        end
        return
    end

    if self.cdr_overtemp_table.npu_id == 255 then
        self.cdr_overtemp_table.max_temp = max_temp
        self.cdr_overtemp_table.npu_id = max_temp_npu_id
        log:notice("[network_adapter]the max cdr temperature %s is npu %s", max_temp, max_temp_npu_id)
        return
    end

    if self.cdr_overtemp_table.npu_id ~= max_temp_npu_id then
        log:notice("[network_adapter]the max cdr temperature %s is npu %s remove",
            self.cdr_overtemp_table.max_temp, self.cdr_overtemp_table.npu_id)
        
        self.cdr_overtemp_table.max_temp = max_temp
        self.cdr_overtemp_table.npu_id = max_temp_npu_id

        log:notice("[network_adapter]the max cdr temperature %s is npu %s", max_temp, max_temp_npu_id)
        return
    end

    self.cdr_overtemp_table.max_temp = max_temp
end

function c_network_adapter:update_max_npu_cdr_temp_from_imu(ports)
    local port_temp, cdr_manuf, max_temp_npu_id
    local max_temp = 0
    for _, port in ipairs(ports) do
        port_temp, cdr_manuf = port:get_npu_cdr_temp_from_imu()
        if port_temp > max_temp then
            max_temp = port_temp
            max_temp_npu_id = port.NpuID
        end
    end
    self.TemperatureCelsius = max_temp
    local new_temperature_status = self.TemperatureStatus & 12 -- 保留cdr光模块温度的状态
    self.TemperatureStatus = new_temperature_status | get_npu_port_temperature_status(ports)
    if cdr_manuf ~= "" and self.ChipManufacturer == "" then
        self.ChipManufacturer = cdr_manuf
    end
    self:record_cdr_max_temp_log(max_temp_npu_id, max_temp)
end

function c_network_adapter:get_max_npu_cdr_temp_from_imu()
    self:sleep_ms(15000) -- 等待NPU网口对象全部上树后开始启动更新cdr温度线程
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    log:notice('start to update max npu cdr temp')
    local ops = c_optical_module.collection:fetch({NetworkAdapterId = self.NodeId})
    -- 每15s轮询从所有npu获取cdr温度并比对最大值，将最值赋值上树
    skynet.fork_loop({count = 0}, function()
        while true do
            self:update_max_npu_cdr_temp_from_imu(ports)
            self:update_npu_max_sfp_temp(ops)
            self:sleep_ms(15000)
        end
    end)
end

local function update_link_status_to_default()
    c_network_port.collection:fold(
        function (_ , obj)
            local parent = obj:get_parent()
            if parent and parent.Model == 'NPU' then
                obj.LinkStatusNumeric = 255
            end
        end
    )
end

function c_network_adapter:register_npu_listen_callback()
    -- 监听到系统复位或者下电时，将网卡状态恢复为初始值（255）
    client:OnFruCtrlPropertiesChanged(
        function (values, _, _)
            if values.SysResetDetected then
                local sys_reset_detected = values.SysResetDetected:value()
                if sys_reset_detected == 1 then
                    log:notice('system reset,npu port link status update to default(255)')
                    update_link_status_to_default()
                end
            end
            if values.PowerState then
                local power_state = values.PowerState:value()
                if power_state == 'OFF' then
                    log:notice('system power off,npu port link status update to default(255)')
                    update_link_status_to_default()
                end
            end
        end
    )
end

function c_network_adapter:sync_nic_op_temp(ports)
    if not ports or #ports == 0 or not ports[1]:get_optical_module() then
        return
    end

    client:OnPmuPropertiesChanged(function (values, _, _)
        if values.SFPMaxTemperature then
            for _, port in ipairs(ports) do
                port:get_optical_module().TemperatureCelsius = values.SFPMaxTemperature:value()
            end
        end
    end)

    local bus = c_object_manage.get_instance().bus
    local ok, obj = pcall(mdb.get_object, bus, PMU_PATH, PMU_INTERFACE)
    if not ok or not obj then
        return
    end
    for _, port in ipairs(ports) do
        port:get_optical_module().TemperatureCelsius = obj.SFPMaxTemperature
    end
end

function c_network_adapter:get_pcie_vid_did_info(pcie_info)
    local payload = {
        system_id = pcie_info.system_id,
        is_local = pcie_info.is_local,
        cpu_id = pcie_info.cpu_id,
        bus_num = pcie_info.bus_num,
        device_num = pcie_info.device_num,
        function_num = pcie_info.function_num,
        address = pmu_cmd.pci_info_address_list.VID_DID_INFO_ADDR,
        read_length = 4
    }
    local bus = c_object_manage.get_instance().bus
    local info = pmu_cmd.get_info_from_pmu(bus, payload)
    if info == nil or #info < 4 then
        return
    end
    local device_id = ((info[4] & 0xff) << 8) + (info[3] & 0xff)
    local vendor_id = ((info[2] & 0xff) << 8) + (info[1] & 0xff)
    if vendor_id == 0xffff and device_id == 0xffff then
        return
    end
    return vendor_id, device_id
end

function c_network_adapter:get_pcie_sub_vid_did_info(pcie_info)
    local payload = {
        system_id = pcie_info.system_id,
        is_local = pcie_info.is_local,
        cpu_id = pcie_info.cpu_id,
        bus_num = pcie_info.bus_num,
        device_num = pcie_info.device_num,
        function_num = pcie_info.function_num,
        address = pmu_cmd.pci_info_address_list.SUBVID_SUBDID_INFO_ADDR,
        read_length = 4
    }
    local bus = c_object_manage.get_instance().bus
    local info = pmu_cmd.get_info_from_pmu(bus, payload)
    if info == nil or #info < 4 then
        return
    end
    local sub_device_id = ((info[4] & 0xff) << 8) + (info[3] & 0xff)
    local sub_vendor_id = ((info[2] & 0xff) << 8) + (info[1] & 0xff)
    if sub_vendor_id == 0xffff and sub_device_id == 0xffff then
        return
    end
    return sub_vendor_id, sub_device_id
end

function c_network_adapter:init_smbus_signal_monitor()
    -- 接收到bmc重启信号，BMC重启这种只触发一次，且如果在超时前已经有其它模式触发收集则不再收集
    self:connect_signal(log_collector.log_dump_reset_bmc_sig, function()
        if not self.smbus_has_collected and self.smbus_collect_status == LOG_DUMP_IDLE then
            log:info('receive bmc reset signal')
            self.smbus_collect_status = LOG_DUMP_BUSY
            self:collect_log_by_smbus_task()
            self.smbus_collect_status = LOG_DUMP_IDLE
        end
    end)
    -- 接收到os重启信号，只要没有任务正在收集则可以启动收集
    self:connect_signal(log_collector.log_dump_reset_pmu_sig, function()
        if self.smbus_collect_status == LOG_DUMP_IDLE then
            log:info('receive os reset signal')
            self.smbus_collect_status = LOG_DUMP_BUSY
            self:collect_log_by_smbus_task()
            self.smbus_collect_status = LOG_DUMP_IDLE
        end
    end)
end

function c_network_adapter:stop_pldm_schedulers()
    for _, s in ipairs(self.pldm_schedulers) do
        s:deconstruct()
    end
    self.pldm_schedulers = {}
end

function c_network_adapter:init_pldm_over_mctp_signal_monitor(pldm_config_func, ports)
    -- 接收到os重启信号，清理endpoint后再次创建
    self:connect_signal(log_collector.log_dump_reset_smbios_sig, function()
        self:stop_pldm_schedulers()
        self:clean_transport_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_PLDM)
        local endpoint = self:create_mctp_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_PLDM)
        self.pldm_config_obj = libmgmt_protocol.device_spec_parser(pldm_config_func(endpoint))
        self:update_pldm_properties(ports)
    end)
end

function c_network_adapter:init_ncsi_over_mctp_signal_monitor(ncsi_config_func, ports)
    -- 接收到bmc重启信号，BMC重启这种只触发一次，且如果在超时前已经有其它模式触发收集则不再收集
    self:connect_signal(log_collector.log_dump_reset_bmc_sig, function()
        if not self.ncsi_has_collected and self.ncsi_collect_status == LOG_DUMP_IDLE then
            log:info('receive bmc reset signal')
            self.ncsi_collect_status = LOG_DUMP_BUSY
            self:collect_log_by_ncsi_task()
            self.ncsi_collect_status = LOG_DUMP_IDLE
        end
    end)
    -- 接收到os重启信号，只要没有任务正在收集则可以启动收集
    self:connect_signal(log_collector.log_dump_reset_smbios_sig, function()

        -- 清理之前的scheduler
        self:stop_ncsi_schedulers()
        -- 清理之前的transport endpointls
        self:clean_transport_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_NCSI)
        -- 再次创建endpoint
        local endpoint = self:create_mctp_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_NCSI)
        self.ncsi_config_obj = libmgmt_protocol.device_spec_parser(ncsi_config_func(endpoint))
        self:update_ncsi_properties(ports)

        if self.ncsi_collect_status == LOG_DUMP_IDLE then
            log:info('receive os reset signal')
            self.ncsi_collect_status = LOG_DUMP_BUSY
            self:collect_log_by_ncsi_task()
            self.ncsi_collect_status = LOG_DUMP_IDLE
        end
    end)
end

function c_network_adapter:init_protocol(hardware_config, ports)
    if hardware_config.smbus and self.RefChip and self.RefChip.WriteRead then
        self:next_tick(self.init_smbus, self, hardware_config.smbus, ports)
        self:next_tick(self.init_smbus_signal_monitor, self)
    end
    if hardware_config.mctp_pldm and self.SupportedMctp then
        self:next_tick(self.init_pldm, self, hardware_config.mctp_pldm, ports)
        self:next_tick(self.init_pldm_over_mctp_signal_monitor, self, hardware_config.mctp_pldm, ports)
    end
    if hardware_config.mctp and self.SupportedMctp then
        self:next_tick(self.init_ncsi, self, hardware_config.mctp, ports)
        self:next_tick(self.init_ncsi_over_mctp_signal_monitor, self, hardware_config.mctp, ports)
    end
    if hardware_config.lldp and self.SupportedLLDP then
        self:next_tick(self.setup_vdpci_lldp_listener, self, ports)
    end
    if hardware_config.std_smbus and self.RefChip and self.RefChip.WriteRead then
        self:next_tick(self.init_std_smbus, self, hardware_config.std_smbus, ports)
    end
end

function c_network_adapter:init_std_smbus(std_smbus_config_func, ports)
    local std_smbus_config_obj = std_smbus_config_func.new(self.RefChip)
    -- 获取std_smbus能力码
    local ok, resp = pcall(std_smbus_config_obj.GetCapability, std_smbus_config_obj)
    if not ok then
        log:error('[MCU] get capability failed, %s', resp)
    end
    -- 设置电子标签
    ok = pcall(function()
        std_smbus_config_obj:GetNetElabel(function(elabel)
            local prop = {}
            local value = {}
            for elabel_prop, val in pairs(elabel) do
                table.insert(prop, elabel_prop)
                table.insert(value, val)
            end
            self.RefFrudata:Update(ctx.new(), prop, value)
        end)
    end)
    if not ok then
        log:error('[dpu] set frudata failed, msg: %s', resp)
    end
    local data
    -- 更新PfMacInfo
    self:next_tick(function()
        while true do
            data = std_smbus_config_obj:GetMacAddress()
            self.PfMacInfo = data
            self:sleep_ms(5000) -- 等待5秒钟
        end
    end)
end

function c_network_adapter:init_smbus(smbus_config_func, ports)
    self.smbus_config_obj = libmgmt_protocol.device_spec_parser(smbus_config_func(self.RefChip))
    self:update_smbus_properties(ports)
end

function c_network_adapter:get_endpoint_and_transport_from_mctp(phy_addr, msg_type, mctp_table)
    local ok
    local bus = c_object_manage.get_instance().bus
    ok, mctp_table.endpoint, mctp_table.transport = pcall(mctp_lib.get_endpoint_and_transport, bus,
        MODULE_NAME_NETWORK_ADAPTER, phy_addr, msg_type)
    if not ok or mctp_table.endpoint == nil or mctp_table.transport == nil then
        log:raise('unable to create mctp transport and endpoint. msg: %s', mctp_table.endpoint)
    end
end

function c_network_adapter:create_mctp_endpoint_for_sdi6X(msg_type)
    local count = 0
    local max_retry<const> = 120
    log:notice('start create mctp endpoint with Bus=%s, msg_types=%s', self.DevBus, msg_type)
    while count < max_retry do
        if self.DevBus ~= 0 or self.DevDevice ~= 0 or self.DevFunction ~= 0 then
            local phy_addr_function0 = mctp_lib.bdf_to_phy_addr(self.DevBus, self.DevDevice, 0)
            local phy_addr_function1 = mctp_lib.bdf_to_phy_addr(self.DevBus, self.DevDevice, 1)
            log:notice('creating mctp transport and endpoint with phy_addr0=%s, phy_addr0=%s, msg_types=%s',
                phy_addr_function0, phy_addr_function1, msg_type)
            local mctp_table = {
                endpoint = nil,
                transport = nil
            }

            -- 同一时间仅会有一个endpoint创建成功
            self:next_tick(self.get_endpoint_and_transport_from_mctp, self, phy_addr_function0, msg_type, mctp_table)
            self:next_tick(self.get_endpoint_and_transport_from_mctp, self, phy_addr_function1, msg_type, mctp_table)

            while (mctp_table.endpoint == nil) or (mctp_table.transport == nil) do
                self:sleep_ms(1000)
            end
            self.transports[#self.transports + 1] = mctp_table.transport
            self.mctp_endpoints[#self.mctp_endpoints + 1] = mctp_table.endpoint
            log:notice(
                'register mctp transport and endpoint(bus: %s device:%s) successful', self.DevBus, self.DevDevice)
            log:notice(
                'register mctp self.mctp_endpoints %s, self.transports %s', #self.mctp_endpoints, #self.transports)
            return mctp_table.endpoint
        end
        count = count + 1
        self:sleep_ms(1000)
    end
    log:raise('unable to create mctp transport and endpoint with bdf 0')
end

function c_network_adapter:create_mctp_endpoint_for_others(msg_type)
    local count = 0
    local max_retry<const> = 120
    log:notice('start create mctp endpoint with Bus=%s, msg_types=%s', self.DevBus, msg_type)
    while count < max_retry do
        if self.DevBus ~= 0 or self.DevDevice ~= 0 or self.DevFunction ~= 0 then
            local phy_addr = mctp_lib.bdf_to_phy_addr(self.DevBus, self.DevDevice, self.DevFunction)
            log:notice('creating mctp transport and endpoint with bdf=%s:%s.%s phy_addr=%d, msg_types=%d',
                self.DevBus, self.DevDevice, self.DevFunction, phy_addr, msg_type)
            local bus = c_object_manage.get_instance().bus
            local ok, endpoint, transport = pcall(mctp_lib.get_endpoint_and_transport, bus,
                MODULE_NAME_NETWORK_ADAPTER, phy_addr, msg_type)
            if not ok or not endpoint or not transport then
                log:raise('unable to create mctp transport and endpoint. msg: %s', endpoint)
            end
            self.transports[#self.transports + 1] = transport
            self.mctp_endpoints[#self.mctp_endpoints + 1] = endpoint
            log:notice('register mctp transport and endpoint(phy: %s) successful', phy_addr)
            log:notice(
                'register mctp self.mctp_endpoints %s, self.transports %s', #self.mctp_endpoints, #self.transports)
            return endpoint
        end
        count = count + 1
        self:sleep_ms(1000)
    end
    log:raise('unable to create mctp transport and endpoint with bdf 0')
end

function c_network_adapter:create_mctp_endpoint_for_others_by_bus(msg_type, dev_bus)
    local count = 0
    local max_retry<const> = 120
    log:debug('start create mctp endpoint with Bus=%s, msg_types=%s', dev_bus, msg_type)
    while count < max_retry do
        if dev_bus ~= 0 or self.DevDevice ~= 0 or self.DevFunction ~= 0 then
            local phy_addr = mctp_lib.bdf_to_phy_addr(dev_bus, self.DevDevice, self.DevFunction)
            log:debug('creating mctp transport and endpoint with bdf=%s:%s.%s phy_addr=%d, msg_types=%d',
                dev_bus, self.DevDevice, self.DevFunction, phy_addr, msg_type)
            local bus = c_object_manage.get_instance().bus
            local ok, endpoint, transport = pcall(mctp_lib.get_endpoint_and_transport, bus,
                MODULE_NAME_NETWORK_ADAPTER, phy_addr, msg_type)
            if not ok or not endpoint or not transport then
                log:raise('unable to create mctp transport and endpoint. msg: %s', endpoint)
            end
            return transport, endpoint, phy_addr
        end
        count = count + 1
        self:sleep_ms(1000)
    end
    log:raise('unable to create mctp transport and endpoint with bdf %s-%s-%s', dev_bus, self.DevDevice,
        self.DevFunction)
end

-- 根据root bus查下级的bus范围。
-- 查询下一级总线号/下级最大总线号
function c_network_adapter:get_sec_sub_bus_number()
    local payload = {
        is_local = false,
        cpu_id = self.SocketId,
        bus_num = self.Bus,
        device_num = self.Device,
        function_num = self.Function,
        address = pmu_cmd.pci_info_address_list.BUS_NUMBER_INFO_ADDR,
        read_length = 4
    }
    local bus = c_object_manage.get_instance().bus
    local info = pmu_cmd.get_info_from_pmu(bus, payload)
    if info == nil or #info < 4 then
        log:raise('get bus number failed')
    end
    if type(info[2]) ~= 'number' or type(info[3]) ~= 'number' then
        log:raise('get bus number type invalid')
    end
    return info[2], info[3]
end

-- 对bus范围轮询，不正确的会阻塞，正确的会返回。
function c_network_adapter:loop_bus_create_mctp_endpoint(msg_type)
    local ok, secondary_bus, suboridinate_bus = pcall(self.get_sec_sub_bus_number, self)
    if not ok then
        log:raise('get bus number failed %s', secondary_bus)
    end
    -- 轮询找到endpoint设备
    log:debug('start loop bus to create mctp transport and endpoint(secondary_bus:%s, suboridinate_bus:%s)',
        secondary_bus, suboridinate_bus)
    local transport, endpoint, phy_addr
    for dev_bus = secondary_bus, suboridinate_bus do
        skynet.fork(function ()
            ok, transport, endpoint, phy_addr = pcall(self.create_mctp_endpoint_for_others_by_bus, self, msg_type,
                dev_bus)
            if not ok then
                log:error('create_mctp_endpoint_for_others_by_bus failed %s', transport)
            end
        end)
    end

    local count = 0
    local max_retry<const> = 120
    while count < max_retry do
        count = count + 1
        skynet.sleep(100) -- 等待1秒
        if transport and endpoint then
            self.transports[#self.transports + 1] = transport
            self.mctp_endpoints[#self.mctp_endpoints + 1] = endpoint
            log:notice('register mctp transport and endpoint(phy: %s) successful. mctp_endpoints %s, transports %s',
                phy_addr, #self.mctp_endpoints, #self.transports)
            return endpoint
        end
    end
    log:raise('unable to create mctp transport and endpoint')
end

function c_network_adapter:create_mctp_endpoint(msg_type)
    if self.Name == 'QT100' or self.Name == 'QT100n' then
        return self:create_mctp_endpoint_for_sdi6X(msg_type)
    elseif self.Model == 'BF3' then
        return self:loop_bus_create_mctp_endpoint(msg_type)
    else
        return self:create_mctp_endpoint_for_others(msg_type)
    end
end

function c_network_adapter:update_port_wwnn_by_pldm(ports)
    local default_wwnn_schedule = self.pldm_config_obj:DefaultWWNN()
    default_wwnn_schedule.on_data_change:on(function(default_wwnn)
        if not default_wwnn then
            log:notice("default_wwnn is nil")
            return
        end
        for _, port in pairs(ports) do
            log:notice('%s default_wwnn change to %s', port.NodeId, default_wwnn)
            port.PermanentWWNN = default_wwnn
        end
    end)
    table.insert(self.pldm_schedulers, default_wwnn_schedule)
    default_wwnn_schedule:start()
    log:notice('%s update default_wwnn by pldm start', self.NodeId)

    local work_wwnn_schedule = self.pldm_config_obj:WorkWWNN()
    work_wwnn_schedule.on_data_change:on(function(work_wwnn)
        if not work_wwnn then
            log:notice("work_wwnn is nil")
            return
        end
        for _, port in pairs(ports) do
            log:notice('%s work_wwnn change to %s', port.NodeId, work_wwnn)
            port.WWNN = work_wwnn
        end
    end)
    table.insert(self.pldm_schedulers, work_wwnn_schedule)
    work_wwnn_schedule:start()
    log:notice('%s update work_wwnn by pldm start', self.NodeId)
end

function c_network_adapter:update_port_wwpn_by_pldm(port_id, ports)
    local default_wwpn_fun = string.format('Port%sDefaultWWPN', port_id)
    local default_wwpn_schedule = self.pldm_config_obj[default_wwpn_fun](self.pldm_config_obj)
    default_wwpn_schedule.on_data_change:on(function(default_wwpn)
        if not default_wwpn then
            log:notice("default_wwpn is nil")
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s default_wwpn change to %s', port.NodeId, default_wwpn)
                port.PermanentWWPN = default_wwpn
            end
        end
    end)
    table.insert(self.pldm_schedulers, default_wwpn_schedule)
    default_wwpn_schedule:start()
    log:notice('%s update default_wwpn by pldm start, port_id:%s', self.NodeId, port_id)

    local work_wwpn_fun = string.format('Port%sWorkWWPN', port_id)
    local work_wwpn_schedule = self.pldm_config_obj[work_wwpn_fun](self.pldm_config_obj)
    work_wwpn_schedule.on_data_change:on(function(work_wwpn)
        if not work_wwpn then
            log:notice("work_wwpn is nil")
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s work_wwpn change to %s', port.NodeId, work_wwpn)
                port.WWPN = work_wwpn
            end
        end
    end)
    table.insert(self.pldm_schedulers, work_wwpn_schedule)
    work_wwpn_schedule:start()
    log:notice('%s update work_wwpn by pldm start, port_id:%s', self.NodeId, port_id)
end

function c_network_adapter:update_port_link_status_by_pldm(port_id, ports)
    local link_status_fun = string.format('Port%sLinkStatus', port_id)
    local s = self.pldm_config_obj[link_status_fun](self.pldm_config_obj)
    s.on_data_change:on(function(data)
        if not data then
            log:notice("link_status data is nil")
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s link_status change to %s, speed_mbps change to %s, speed_gbps change to %s',
                    port.NodeId, data.LinkStatus, data.SpeedMbps, data.SpeedGbps)
                port:set_link_status(data.LinkStatus)
                port:set_link_status_numeric(data.LinkStatus)
                port.SpeedMbps = data.SpeedMbps
                port.SpeedGbps = data.SpeedGbps
                port.get_properties_res.LinkStatusFromPldm = true
            end
        end
    end)
    s.on_error:on(function()
        for _, port in pairs(ports) do
            if port.PortID == port_id and port.get_properties_res.LinkStatusFromPldm then
                log:error('%s get link status by pldm on_error', port.NodeId)
                port.get_properties_res.LinkStatusFromPldm = false
            end
        end
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update link status by pldm start, port_id:%s', self.NodeId, port_id)
end

function c_network_adapter:update_all_info_by_pldm_emluex()
    local s = self.pldm_config_obj:AllInfo()
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update all info by pldm start', self.NodeId)
end

function c_network_adapter:update_fw_version_by_pldm()
    local s = self.pldm_config_obj:FirmwareVersion()
    s.on_data_change:on(function(fw_version)
        if not fw_version then
            log:debug("fw_version is nil")
            return
        end
        self.FirmwareVersion = fw_version
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update firmware version by pldm start', self.NodeId)
end

function c_network_adapter:update_serial_number_by_pldm()
    local s = self.pldm_config_obj:SerialNumber()
    s.on_data_change:on(function(serial_number)
        if not serial_number then
            log:debug("serial_number is nil")
            return
        end
        self.SerialNumber = serial_number
        log:notice('%s update serial number to %s by pldm', self.NodeId, serial_number)
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update serial number by pldm start', self.NodeId)
end

function c_network_adapter:update_part_number_by_pldm()
    local s = self.pldm_config_obj:PartNumber()
    s.on_data_change:on(function(part_number)
        if not part_number then
            log:debug("part_number is nil")
            return
        end
        self.PartNumber = part_number
        log:notice('%s update part number to %s by pldm', self.NodeId, part_number)
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update part number by pldm start', self.NodeId)
end

function c_network_adapter:update_chip_temp_by_pldm()
    local s = self.pldm_config_obj:ChipTemp()
    s.on_data_change:on(function(chip_temp)
        if not chip_temp then
            log:debug("chip_temp is nil")
            return
        end
        self.TemperatureStatus = (self.smbios_status == 3 and data >= 0xff) and 1 or 0
        self.TemperatureCelsius = chip_temp
        self.get_properties_res.ChipTempFromPldm = true
    end)
    s.on_error:on(function()
        if self.get_properties_res.ChipTempFromPldm then
            log:error('%s update chip temp by pldm on_error', self.NodeId)
            self.get_properties_res.ChipTempFromPldm = false
        end
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update chip temp by pldm start', self.NodeId)
end

function c_network_adapter:update_link_speed_by_pldm(port_id, ports)
    local link_speed_func = string.format('Port%sLinkSpeed', port_id)
    local port_link_speed = self.pldm_config_obj[link_speed_func](self.pldm_config_obj)
    port_link_speed.on_data_change:on(function(data)
        if not data then
            log:debug('Port%s link speed data is nil', port_id)
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s link speed change to %s', port.NodeId, data)
                port.SpeedGbps = data
                break
            end
        end
    end)
    table.insert(self.pldm_schedulers, port_link_speed)
    port_link_speed:start()
    log:notice('%s update link speed by pldm start, port_id:%s', self.NodeId, port_id)
end

function c_network_adapter:update_link_status_by_pldm(port_id, ports)
    local link_status_func = string.format('Port%sLinkStatus', port_id)
    local s = self.pldm_config_obj[link_status_func](self.pldm_config_obj)
    s.on_data_change:on(function(link_status)
        if not link_status then
            log:debug("port%s link_status is nil", port_id)
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s change link status to %s', port.NodeId, link_status)
                port.LinkStatus = link_status
                port.get_properties_res.LinkStatusFromPldm = true
                break
            end
        end
    end)
    s.on_error:on(function()
        for _, port in pairs(ports) do
            if port.PortID == port_id and port.get_properties_res.LinkStatusFromPldm then
                log:error('%s get link status by pldm on_error', port.NodeId)
                port.get_properties_res.LinkStatusFromPldm = false
            end
        end
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update link status by pldm start, port_id:%s', self.NodeId, port_id)
end

function c_network_adapter:update_wwnn_by_pldm(port_id, ports)
    local port_work_wwnn = string.format('Port%sWorkWWNN', port_id)
    local s = self.pldm_config_obj[port_work_wwnn](self.pldm_config_obj)
    s.on_data_change:on(function(wwnn)
        if not wwnn then
            log:debug("port%s wwnn is nil", port_id)
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s change wwnn to %s', port.NodeId, wwnn)
                port.WWNN = wwnn
                break
            end
        end
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update wwnn by pldm start, port_id:%s', self.NodeId, port_id)
end

function c_network_adapter:update_wwpn_by_pldm(port_id, ports)
    local port_work_wwpn = string.format('Port%sWorkWWPN', port_id)
    local s = self.pldm_config_obj[port_work_wwpn](self.pldm_config_obj)
    s.on_data_change:on(function(wwpn)
        if not wwpn then
            log:debug("port%s wwnn is nil", port_id)
            return
        end
        for _, port in pairs(ports) do
            if port.PortID == port_id then
                log:notice('%s change wwnn to %s', port.NodeId, wwpn)
                port.WWPN = wwpn
                break
            end
        end
    end)
    table.insert(self.pldm_schedulers, s)
    s:start()
    log:notice('%s update wwpn by pldm start, port_id:%s', self.NodeId, port_id)
end

function c_network_adapter:update_pldm_properties(ports)
    if string.sub(self.Model, 1, 3) ~= 'QLE' and string.sub(self.Model, 1, 2) ~= 'XE' then
        self:next_tick(self.update_port_wwpn_by_pldm, self, 0, ports)
        self:next_tick(self.update_port_wwpn_by_pldm, self, 1, ports)
        self:next_tick(self.update_port_wwnn_by_pldm, self, ports)
        self:next_tick(self.update_port_link_status_by_pldm, self, 0, ports)
        self:next_tick(self.update_port_link_status_by_pldm, self, 1, ports)
    else
        -- 适配qlogic新增
        self:next_tick(self.update_all_info_by_pldm_emluex, self)
        self:next_tick(self.update_wwnn_by_pldm, self, 0, ports)
        self:next_tick(self.update_wwnn_by_pldm, self, 1, ports)
        self:next_tick(self.update_wwpn_by_pldm, self, 0, ports)
        self:next_tick(self.update_wwpn_by_pldm, self, 1, ports)
        self:next_tick(self.update_link_status_by_pldm, self, 0, ports)
        self:next_tick(self.update_link_status_by_pldm, self, 1, ports)
        self:next_tick(self.update_serial_number_by_pldm, self)
        self:next_tick(self.update_fw_version_by_pldm, self)
        self:next_tick(self.update_link_speed_by_pldm, self, 0, ports)
        self:next_tick(self.update_link_speed_by_pldm, self, 1, ports)
        self:next_tick(self.update_part_number_by_pldm, self)
        self:next_tick(self.update_chip_temp_by_pldm, self)
    end
end

function c_network_adapter:init_pldm(pldm_config_func, ports)
    local endpoint = self:create_mctp_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_PLDM)
    self.pldm_config_obj = libmgmt_protocol.device_spec_parser(pldm_config_func(endpoint))
    self:update_pldm_properties(ports)
end

function c_network_adapter:disable_hardware_arbittration()
    local cur_package_id = 0
    local ret
    while cur_package_id <= PACKAGE_ID_MAX do
        ret = self.ncsi_config_obj:DisableHardwareArbitration({package_id = cur_package_id}):value()
        if ret then
            self.package_id = cur_package_id
            log:notice('disable hardware arbittration success, set %s package id to %s',
                self.NodeId, cur_package_id)
            break
        end
        cur_package_id = cur_package_id + 1
    end
end

function c_network_adapter:init_ncsi(ncsi_config_func, ports)
    self:check_mpu_status()
    local endpoint = self:create_mctp_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_NCSI)
    self.ncsi_config_obj = libmgmt_protocol.device_spec_parser(ncsi_config_func(endpoint))
    self:update_ncsi_properties(ports)
end

function c_network_adapter:clean_transport_endpoint(msg_type)
    local path_name
    for i, obj in pairs(self.mctp_endpoints) do
        path_name = obj.path:match(".+/(.+)") -- 提取资源树路径斜线分割的最后一部分
        if tonumber(path_name) == msg_type then
            -- 找到了对应的endpoint，删除实例
            table.remove(self.mctp_endpoints, i)
            table.remove(self.transports, i)  -- 新增的时候transport索引和endpoint一样。
            log:notice('clean_transport_endpoint self.mctp_endpoints %s, self.transports %s',
                #self.mctp_endpoints, #self.transports)
        end
    end
end

function c_network_adapter:setup_vdpci_lldp_listener(ports)
    -- 网卡lldp over mctp走的是mctp vdpci(0x7e)通道
    local endpoint = self:create_mctp_endpoint(mctp_lib.MCTP_MESSAGE_TYPE_VDPCI)
    local bus = c_object_manage.get_instance().bus
    local sig =
        MatchRule.signal('MessageReceived', 'bmc.kepler.Systems.Mctp.PCIeEndpoint'):with_path(
            endpoint.path)
    self.lldp_receive_listener = bus:match(sig, function(msg)
        self:next_tick(function()
            local ok, lldp_rsp = pcall(libmgmt_protocol.vdpci_lldp_parser, msg:read())
            if not ok or not lldp_rsp then
                log:debug('unable to parse vdpci lldp message, error: %s', lldp_rsp)
                return
            end
            lldp_rsp.parsed_data = {}
            for _, tlv in ipairs(lldp_rsp.tlvs) do
                local ok, t = pcall(libmgmt_protocol.lldp_tlv_parser, tlv)
                if ok and t then
                    for k, v in pairs(t) do
                        lldp_rsp.parsed_data[k] = v
                    end
                end
            end
            local port = c_network_port.collection:find({
                Position = self:get_position(),
                PortID = lldp_rsp.port_id
            })
            port:update_lldp_receive(lldp_rsp)
        end)
    end)

    -- 必须打开网口lldp开关才能收到lldp信息
    for _, p in ipairs(ports) do
        p:enable_lldp(true)
    end
end

function c_network_adapter:update_net_dev_func_capabilities(ports)
    local func_type = 0
    for _, port in pairs(ports) do
        func_type = func_type | (port.NetDevFuncType or 0)
    end
    self.NetDevFuncCapabilities = func_type
end

local function parse_vid_did_svid_sdid(self, pcie_info)
    local ok, vendor_id, device_id = pcall(self.get_pcie_vid_did_info, self, pcie_info)
    if not ok or not vendor_id then
        return false
    end
    local ret, sub_vendor_id, sub_device_id = pcall(self.get_pcie_sub_vid_did_info, self, pcie_info)
    if not ret or not sub_vendor_id then
        return false
    end
    log:info('name:%s, vendor_id: %s, device_id: %s, sub_vendor_id:%s, sub_device_id: %s', self.NodeId, vendor_id,
        device_id, sub_vendor_id, sub_device_id)
    return true, vendor_id, device_id, sub_vendor_id, sub_device_id
end

function c_network_adapter:verify_vid_did_svid_sdid(pcie_info)
    -- 校验四元组仅使用第一个返回值
    return parse_vid_did_svid_sdid(self, pcie_info)
end

function c_network_adapter:get_vid_did_svid_sdid(pcie_info)
    local ok, vendor_id, device_id, sub_vendor_id, sub_device_id = parse_vid_did_svid_sdid(self, pcie_info)
    if not ok then
        return false
    end
    pcie_info.did = device_id
    pcie_info.vid = vendor_id
    pcie_info.sdid = sub_device_id
    pcie_info.svid = sub_vendor_id
    return true
end

function c_network_adapter:update_1822_port_bdf(ports)
    log:notice("update_1822_port_bdf start, name:%s", self.NodeId)

    local retries = 0
    repeat
        self.tasks:sleep_ms(1000)
        retries = retries + 1
    until (self.DevBus and self.DevBus ~= 0) or retries > 120

    if not self.DevBus or self.DevBus == 0 then
        log:error('update_1822_port_bdf, the value of devbus is invalid')
        return
    end

    local port_num = self.NetworkPortCount
    local count = 0
    local pcie_info, val, ok, flag
    for _, port in pairs(ports) do
        log:notice("update_1822_port_bdf, name:%s, DevBus=%s, DevDevice=%s, port_id=%s, DevFunction=%s",
            self.NodeId, self.DevBus, self.DevDevice, port.PortID, self.DevFunction)
        pcie_info = {
            system_id = 1,
            is_local = false,
            cpu_id = self.SocketId,
            bus_num = self.DevBus,
            device_num = self.DevDevice,
            function_num = self.DevFunction + port.PortID
        }
        flag = self:verify_vid_did_svid_sdid(pcie_info)
        ok, val = pcall(string.format, '0000:%02x:%02x.%01x', pcie_info.bus_num,
            pcie_info.device_num, pcie_info.function_num)
        if flag and ok then
            log:notice("update_1822_port_bdf, name:%s, port = %s, set port BDF val=%s",
                self.NodeId, port.PortID, val)
            if port.WorkloadType ~= 1 then
                port.BDF = val
            end
            count = count + 1
        end
    end
    if count ~= port_num then
        log:error('update port bdf failed, name:%s', self.NodeId)
    end
end

function c_network_adapter:update_port_bdf(ports)
    local port_num = self.NetworkPortCount
    local count = 0
    local pcie_info, ret, val, ok
    if self.Type ~= 1 and (not self.DevBus or self.DevBus == 0) then
        log:notice("update_port_bdf return, DevBus check fail, name:%s", self.NodeId)
        return
    end

    if self.Type == 1 and (not self.Bus or self.Bus == 0) then
        log:notice("update_port_bdf return, Bus check fail, name:%s", self.NodeId)
        return
    end

    for port_id, port in pairs(ports) do
        log:notice("update_port_bdf, name:%s, DevBus=%s, Bus=%s, port_id=%s, Type=%s",
            self.NodeId, self.DevBus, self.Bus, port.PortID, self.Type)
        if self.DevBus == 0 and self.Type == 1 then
            pcie_info = {
                system_id = 1,
                is_local = false,
                cpu_id = self.SocketId,
                bus_num = self.Bus + 1,
                device_num = self.Device,
                function_num = port.PortID
            }
        else
            pcie_info = {
                system_id = 1,
                is_local = false,
                cpu_id = self.SocketId,
                bus_num = self.DevBus,
                device_num = self.DevDevice,
                function_num = port.PortID
            }
        end
        ret = self:verify_vid_did_svid_sdid(pcie_info)
        while not ret do
            skynet.sleep(12000)
            if fructl.get_power_status() == 'ON' then
                ret = self:verify_vid_did_svid_sdid(pcie_info)
            end
            if STOP_INIT then
                break
            end
        end
        ok, val = pcall(string.format, '0000:%02x:%02x.%01x', pcie_info.bus_num,
            pcie_info.device_num, pcie_info.function_num)
        log:info("update_port_bdf, name:%s, val:%s", self.NodeId, val)
        if ret and ok and port.BDF == '' then
            if port.WorkloadType ~= 1 then
                port.BDF = val
            end
            count = count + 1
        end
    end
    if count ~= port_num then
        log:error('update port(name:%s) bdf failed', self.NodeId)
    end
end

function c_network_adapter:update_health()
    local s = self.smbus_config_obj:Health()
    s.on_data_change:on(function(data)
        self.health = data
        if data ~= 0 then
            log:error('card:%s, is not in normal state, health_status: %s', self.NodeId, data)
        end
    end)
    self.smbus_schedulers.health_task = s
    s:start()
end

function c_network_adapter:update_fault_code()
    local s = self.smbus_config_obj:FaultCode()
    s.on_data_change:on(function(data)
        if self.health ~= 0 and not utils.table_compare(self.last_fault_code, data) then
            self.last_fault_code = data
            -- 故障告警只用第一个故障码
            if data and #data > 0 then
                self.FaultState = data[1]
                log:notice('%s update fault state to %s by smbus', self.NodeId, self.FaultState)
            end
            if self.smbus_collect_status == LOG_DUMP_IDLE then
                log:info('netcard fault occur')
                self.smbus_collect_status = LOG_DUMP_BUSY
                self:collect_log_by_smbus_task()
                self.smbus_collect_status = LOG_DUMP_IDLE
                self.smbus_has_collected = true
            end
        end
    end)
    self.smbus_schedulers.fault_code_task = s
    s:start()
    log:notice('%s update fault code by smbus start', self.NodeId)
end

function c_network_adapter:update_chip_temp()
    local s = self.smbus_config_obj:ChipTemp()
    s.on_data_change:on(function(data)
        self.TemperatureCelsius = data
        self.TemperatureStatus = (self.smbios_status == 3 and data >= 32768) and 1 or 0
        self.get_properties_res.ChipTempFromSmbus = true
    end)
    s.on_error:on(function()
        if self.get_properties_res.ChipTempFromSmbus then
            log:error('%s update chip temp by smbus on_error', self.NodeId)
            self.get_properties_res.ChipTempFromSmbus = false
        end
    end)
    self.smbus_schedulers.chip_temp_task = s
    s:start()
    log:notice('%s update chip temp by smbus start', self.NodeId)
end

local function update_ports_property(s, ports, cb)
    local sig = signal.new()
    s.on_data_change:on(function(data)
        for i, val in ipairs(data) do
            sig:emit(i, val)
        end
    end)

    for port_id, port in ipairs(ports) do
        port:connect_signal(sig, function(i, ...)
            if port_id == i then
                cb(port, ...)
            end
        end)
    end
end

function c_network_adapter:update_optical_temp(ports)
    local s = self.smbus_config_obj:OpticalTemp()
    update_ports_property(s, ports, function(port, temp)
        port:set_op_temp(temp)
        log:debug('%s update optical temp to %s by smbus', port.NodeId, temp)
        port.get_properties_res.OpticalTempFromSmbus = true
    end)
    s.on_error:on(function()
        for _, port in pairs(ports) do
            if port.get_properties_res.OpticalTempFromSmbus then
                log:error('%s update optical temp by smbus on_error', port.NodeId)
                port.get_properties_res.OpticalTempFromSmbus = false
            end
        end
    end)
    self.smbus_schedulers.optical_temp_task = s
    s:start()
    log:notice('%s update optical temp by smbus start', self.NodeId)
end

function c_network_adapter:update_smbus_link_status(ports)
    local s = self.smbus_config_obj:LinkStatus()
    update_ports_property(s, ports, function(port, status)
        port:set_link_status(status)
        log:notice('%s update link status to %s by smbus', port.NodeId, status)
        port.get_properties_res.LinkStatusFromSmbus = true
        port:set_link_status_numeric(status)
    end)
    s.on_error:on(function()
        for _, port in pairs(ports) do
            if port.get_properties_res.LinkStatusFromSmbus then
                log:error('%s update link status by smbus on_error', port.NodeId)
                port.get_properties_res.LinkStatusFromSmbus = false
            end
        end
    end)
    self.smbus_schedulers.link_status_task = s
    s:start()
    log:notice('%s update link status by smbus start', self.NodeId)
end

function c_network_adapter:update_smbus_mac_address(ports)
    local s = self.smbus_config_obj:MacAddress()
    update_ports_property(s, ports, function(port, addr)
        port:set_mac_addr(addr)
        port:set_permanent_mac_addr(addr)
        log:notice('%s update mac address to %s by smbus', port.NodeId, addr)
    end)
    self.smbus_schedulers.mac_address_task = s
    s:start()
    log:notice('%s update mac address by smbus start', self.NodeId)
end

function c_network_adapter:update_fw_ver_on_data_change(data, ports)
    if not data then return end
    if self.update_fw_ver_abnormal then
        log:notice('[network_adapter] update FirmwareVersion successfully, name:%s, FirmwareVersion:%s',
            self.NodeId, data)
        self.update_fw_ver_abnormal = false
    end
    self.FirmwareVersion = data
    for _, port in pairs(ports) do
        port:set_firmware_version(data)
    end
end

function c_network_adapter:update_fw_ver_on_error()
    if not self.update_fw_ver_abnormal then
        log:error('[network_adapter] update FirmwareVersion on_error, name:%s', self.NodeId)
        self.update_fw_ver_abnormal = true
    end
end

function c_network_adapter:update_fw_ver(ports)
    local s = self.smbus_config_obj:FirmwareVersion()
    s.on_data_change:on(function(data)
        self:update_fw_ver_on_data_change(data, ports)
    end)
    -- 消息发不通的时候
    s.on_error:on(function()
        self:update_fw_ver_on_error()
    end)

    self.smbus_schedulers.firmware_version_task = s
    s:start()
    log:notice('%s update firmware version by smbus start', self.NodeId)
end

function c_network_adapter:update_smbus_properties(ports)
    self:next_tick(self.update_health, self)
    self:next_tick(self.update_fault_code, self)
    self:next_tick(self.update_chip_temp, self)
    self:next_tick(self.update_smbus_link_status, self, ports)
    self:next_tick(self.update_smbus_mac_address, self, ports)
    self:next_tick(self.update_optical_temp, self, ports)
    self:next_tick(self.update_fw_ver, self, ports)
end

function c_network_adapter:update_ncsi_firmware(ports)
    local s = self.ncsi_config_obj:VendorID({package_id = self.package_id})
    s.on_data_change:on(function(firmware)
        if not firmware then
            log:debug("firmware is nil")
            return
        end

        self.FirmwareVersion = firmware.FirmwareVersion

        -- 仅在四元组无法从CSR获取时使用mctp上报的四元组信息
        if self.VendorID == '' then
            self.VendorID = firmware.VendorID
        end
        if self.DeviceID == '' then
            self.DeviceID = firmware.DeviceID
        end
        if self.SubsystemVendorID == '' then
            self.SubsystemVendorID = firmware.SubsystemVendorID
        end
        if self.SubsystemDeviceID == '' then
            self.SubsystemDeviceID = firmware.SubsystemDeviceID
        end
        -- Hi1822上测试发现网口firmware version与网卡相同
        -- 暂时通过网卡来获取后分发给网口对象，网口不必再去获取
        for _, port in pairs(ports) do
            port:set_firmware_version(firmware.FirmwareVersion)
        end
        log:notice('%s update firmware version to %s by ncsi', self.NodeId, firmware.FirmwareVersion)
    end)
    table.insert(self.ncsi_schedulers, s)
    s:start()
    log:notice('%s update firmware version by ncsi start', self.NodeId)
end

function c_network_adapter:update_chip_temp_by_ncsi()
    local ncsi_failed_count = 0

    local s = self.ncsi_config_obj:ChipTemp({package_id = self.package_id})
    s.on_data_change:on(function(temp)
        self.TemperatureCelsius = temp
        self.TemperatureStatus = (self.smbios_status == 3 and temp >= 32768) and 1 or 0
        self.get_properties_res.ChipTempFromNcsi = true
        ncsi_failed_count = 0 -- 成功时重置计数

        -- NCSI恢复正常，停止备选SMBUS任务
        if self.smbus_schedulers.chip_temp_task and not self.smbus_schedulers.chip_temp_task.is_paused then
            self.smbus_schedulers.chip_temp_task:pause()
            self.smbus_schedulers.chip_temp_task.data = nil
            log:notice('%s ChipTemp: NCSI recovered, stopped SMBUS fallback', self.NodeId)
        end
    end)

    s.on_error:on(function()
        if self.get_properties_res.ChipTempFromNcsi then
            log:error('%s update chip temp by ncsi on_error', self.NodeId)
            self.get_properties_res.ChipTempFromNcsi = false
        end

        -- NCSI失败达到阈值时，启动SMBUS备选
        ncsi_failed_count = ncsi_failed_count + 1
        if ncsi_failed_count >= NCSI_FAILED_THRESHOLD and self.smbus_schedulers.chip_temp_task and
            self.smbus_schedulers.chip_temp_task.is_paused then
            self.smbus_schedulers.chip_temp_task:resume()
            log:notice('%s ChipTemp: SMBUS fallback activated successfully', self.NodeId)
        end
    end)

    table.insert(self.ncsi_schedulers, s)
    s:start()
    log:notice('%s update chip temp by ncsi start', self.NodeId)
end

function c_network_adapter:record_nic_os_status_change(current_os_status, last_os_status)
    if current_os_status == last_os_status then
        return
    end

    log:maintenance(log.MLOG_INFO, log.FC__CARD_MNG_DPU_OS_STATE_CHANGE,
        'Smart Nic Card OS state change from %s to %s',
        (last_os_status > DPU_STATE_NUM) and 'unknow' or DPU_STATE[last_os_status + 1],
        (current_os_status > DPU_STATE_NUM) and 'unknow' or DPU_STATE[current_os_status + 1])
end

function c_network_adapter:fetch_dpu_os_status_by_ncsi()
    if not self.ncsi_config_obj then
        log:error('ncsi_config_obj is nil')
        return
    end
    local s = self.ncsi_config_obj:GetOSStatus()
    s.on_data_change:on(function(status)
        local last_os_status = self.SystemLoadedStatus
        self.SystemLoadedStatus = status
        self:record_nic_os_status_change(self.SystemLoadedStatus, last_os_status)
    end)
    table.insert(self.ncsi_schedulers, s)
    s:start()
    log:notice('%s update dpu os status by ncsi start', self.NodeId)
end

function c_network_adapter:get_dpu_os_status()
    return self.SystemLoadedStatus
end

function c_network_adapter:set_bios_boot_wait_flag(bios_boot_wait_flag)
    self.bios_boot_wait_flag = bios_boot_wait_flag
end

function c_network_adapter:update_max_operating_temp_by_ncsi()
    local s = self.ncsi_config_obj:MaxOpTemp({package_id = self.package_id}):value()
    if not s then
        return
    end
    self.MaxOperatingTemperatureCelsius = s
    log:notice('%s update max operating temperature to %s by ncsi', self.NodeId,
        self.MaxOperatingTemperatureCelsius)
end

function c_network_adapter:update_link_ability_by_ncsi()
    local s = self.ncsi_config_obj:LinkAbility({package_id = self.package_id}):value()
    if not s then
        return
    end
    self.LinkWidthCapability = s.link_width
    self.LinkSpeedCapability = s.link_speed
    log:notice('%s update link capability, LinkWidthCapability:%s, LinkSpeedCapability:%s',
        self.NodeId, self.LinkWidthCapability, self.LinkSpeedCapability)
end

function c_network_adapter:update_link_status_by_ncsi()
    local s = self.ncsi_config_obj:LinkInfo({package_id = self.package_id}):value()
    if not s then
        return
    end
    self.LinkWidth = s.link_width
    self.LinkSpeed = s.link_speed
    log:notice('%s update link status, link_width_status:%s, link_speed_status:%s',
        self.NodeId, self.LinkWidth, self.LinkSpeed)
end

function c_network_adapter:update_error_code_by_ncsi()
    local ncsi_failed_count = 0

    local s = self.ncsi_config_obj:FaultStatCode({package_id = self.package_id})
    s.on_data_change:on(function(data)
        if data.health_status ~= 0 then
            log:error('card:%s, is not in normal state, health_status: %s', self.NodeId,
                data.health_status)
        end
        -- 故障告警只用第一个故障码
        if data.error_codes and #data.error_codes > 0 then
            self.FaultState = data.error_codes[1]
            log:notice('%s update fault state to %s by ncsi', self.NodeId, self.FaultState)
            if self.FaultState ~= 0 and self.smbus_collect_status == LOG_DUMP_IDLE then
                log:info('netcard fault occur')
                self.smbus_collect_status = LOG_DUMP_BUSY
                self:collect_log_by_smbus_task()
                self.smbus_collect_status = LOG_DUMP_IDLE
                self.smbus_has_collected = true
            end
        end
        if not data.error_codes or not next(data.error_codes) then
            -- 恢复告警
            self.FaultState = 0
            log:notice('%s update fault state to 0 by ncsi', self.NodeId)
        end
        log:debug('netcard health is %s, faultcode is :%s', self.Health, self.FaultCode)
        self.get_properties_res.FaultCodeFromNcsi = true
        ncsi_failed_count = 0 -- 成功时重置计数

        -- NCSI恢复正常，停止备选SMBUS任务
        if self.smbus_schedulers.fault_code_task and not self.smbus_schedulers.fault_code_task.is_paused then
            self.smbus_schedulers.fault_code_task:pause()
            self.smbus_schedulers.fault_code_task.data = nil
            log:notice('%s FaultCode: NCSI recovered, stopped SMBUS fallback', self.NodeId)
        end
    end)
    s.on_error:on(function()
        if self.get_properties_res.FaultCodeFromNcsi then
            log:error('%s update fault code by ncsi on_error', self.NodeId)
            self.get_properties_res.FaultCodeFromNcsi = false
        end

        -- NCSI失败达到阈值时，启动SMBUS备选
        ncsi_failed_count = ncsi_failed_count + 1
        if ncsi_failed_count >= NCSI_FAILED_THRESHOLD and self.smbus_schedulers.fault_code_task and
            self.smbus_schedulers.fault_code_task.is_paused then
            self.smbus_schedulers.fault_code_task:resume()
            log:notice('%s FaultCode: SMBUS fallback activated successfully', self.NodeId)
        end
    end)
    table.insert(self.ncsi_schedulers, s)
    s:start()
    log:notice('%s update fault code by ncsi start', self.NodeId)
end

function c_network_adapter:update_network_adapter(ports)
    self:next_tick(self.update_ncsi_firmware, self, ports)
    self:next_tick(self.update_chip_temp_by_ncsi, self)
    self:next_tick(self.update_max_operating_temp_by_ncsi, self)
    self:next_tick(self.update_link_ability_by_ncsi, self)
    self:next_tick(self.update_link_status_by_ncsi, self)
    self:next_tick(self.update_error_code_by_ncsi, self)
    self:next_tick(self.fetch_dpu_os_status_by_ncsi, self)
end

function c_network_adapter:update_ncsi_properties(ports)
    self:sleep_ms(5000)
    if self.Type == OCP_CARD_TYPE or self.Model == 'BF3' then
        self:disable_hardware_arbittration()
    end
    for _, port in pairs(ports) do
        -- 需要先初始化每个网口，才能获取数据。网卡数据默认从端口0获取
        -- 上板测过网卡数据每个网口都能获取到，且数据相同
        -- 这里初始化网口在本协程跑，其余数据在新协程跑
        port:set_ncsi_config_obj(self.ncsi_config_obj)
        -- 同时设置smbus_schedulers给端口和光模块，用于备选机制
        port:set_smbus_schedulers(self.smbus_schedulers)
        if self.Type == OCP_CARD_TYPE or self.Model == 'BF3' then
            port:set_package_id(self.package_id)
        end
        port:initialize_ncsi_channel()
        port:next_tick(port.update_ncsi_properties, port)
    end
    self:update_network_adapter(ports)
end

function c_network_adapter:get_port_count()
    return self.ports_count
end

-- 由于现在光模块告警关联在网卡的health下，需要把网卡的health同步到光模块的health
function c_network_adapter:update_op_health(_, value)
    local ops = c_optical_module.collection:fetch({NetworkAdapterId = self.NodeId})
    for _, op in ipairs(ops) do
        op:update_health(value)
    end
end

function c_network_adapter:synchronize_port_bdf(key, value)
    log:notice('synchronize_port_bdf, name:%s, key:%s, value:%s', self.NodeId, key, value)
    -- 除NIC卡外，其他网卡DevBus不为0
    if value == 0 and key == 'DevBus' and self.Type ~= 1 then
        log:notice('synchronize_port_bdf fail, name:%s, DevBus is 0', self.NodeId)
        return
    end

    if value == 0 and key == 'Bus' and self.Type == 1 then
        log:notice('synchronize_port_bdf fail, name:%s, Bus is 0', self.NodeId)
        return
    end

    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    if self.SpecialPcieCard then
        self:next_tick(self.update_1822_port_bdf, self, ports)
    else
        self:next_tick(self.update_port_bdf, self, ports)
    end
    local ok, hardware_config = pcall(require, string.format('hardware_config.%s', self.Model))
    if not ok or not hardware_config then
        log:info('No specific hardware config file for this network adapter(model: %s)', self.Model)
        return
    end
    if hardware_config.mctp_pldm and self.SupportedMctp then
        self:next_tick(self.init_pldm, self, hardware_config.mctp_pldm, ports)
    end
    if hardware_config.mctp and self.SupportedMctp then
        self:next_tick(self.init_ncsi, self, hardware_config.mctp, ports)
    end
    if hardware_config.lldp and self.SupportedLLDP then
        self:next_tick(self.setup_vdpci_lldp_listener, self, ports)
    end
end

function c_network_adapter:synchronize_port_node_id(_, value)
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    for _, port in pairs(ports) do
        port.NetworkAdapterId = value
    end
end

function c_network_adapter:reset_bma_info()
    self.DisplayName = ''
    self.DriverName = ''
    self.DriverVersion = ''
    if not self.SupportedMctp or not next(self.ncsi_config_obj) or not self.ncsi_config_obj.VendorID then
        self.FirmwareVersion = ''
    end
    log:debug('NetworkAdapter(%s) reset from bma complete', self.NodeId)
end

local function is_virtual_port(type)
    if type == 6 or type == 7 or type == 8 then
        return true
    end
    return false
end

function c_network_adapter:update_smbios_status(value)
    self.smbios_status = value
    log:notice('update smbios_status=%s', self.smbios_status)
    local ops = c_optical_module.collection:fetch({NetworkAdapterId = self.NodeId})
    for _, op in ipairs(ops) do
        op:update_smbios_status(value)
    end
end

function c_network_adapter:register_listen_callback()
    local switch = {
        Health = self.update_op_health,
        DevBus = self.synchronize_port_bdf,
        NodeId = self.synchronize_port_node_id,
        Bus = self.synchronize_port_bdf
    }
    self:connect_signal(self.on_property_changed, function(name, value)
        if switch[name] then
            switch[name](self, name, value)
        end
    end)

    local bus = c_object_manage.get_instance().bus
    if not bus then
        log:error(
            '[network_adapter]register_listen_callback: get bus failed, listen smbios status failed.')
        return
    end

    client:OnSmBiosInterfacesAdded(function (_, _, props)
        self:update_smbios_status(props.SmBiosStatus:value())
    end)

    client:OnSmBiosPropertiesChanged(function (values, _, _)
        if not values.SmBiosStatus then
            return
        end
        self:update_smbios_status(values.SmBiosStatus:value())
    end)
end

function c_network_adapter:check_mpu_status()
    self:listen('MPUBusyStatus', function(name, value)
        if value ~= MPU_IDLE then
            log:notice('MPU become busy')
            self:pause()
            return true
        end
        log:notice('MPU become idle')
        self:resume()
        return true
    end)
    if self.MPUBusyStatus ~= MPU_IDLE then
        log:notice('MPU is busy')
        self:pause()
    end
end

local function fetch_log(obj, log_prop_name, log_file)
    if not obj[log_prop_name] then
        log:info('the log prop %s doesn\'t exist, ignore it', log_prop_name)
        return
    end

    log:info('start to collect %s', log_prop_name)
    local fp_w, err = file_sec.open_s(log_file, 'wb+')
    if not fp_w then
        log:error('open file failed for %s, err: %s', log_prop_name, err)
        return
    end
    utils.safe_close_file(fp_w, function()
        utils_core.chmod_s(log_file, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

        local log_data = obj[log_prop_name](obj):value()
        fp_w:write(log_data)
    end)
    log:info('finish to collect %s', log_prop_name)
end

local NET_CARD_INOF<const> = 'net_card_info'

local function collect_optical_info(bma)
    local parent_port, parent_na, ibma_id
    if not c_optical_module.collection.objects or c_optical_module.collection.count <= 0 then
        log:notice('optical module cnt is 0')
        return ''
    end
    local head_info =
        string.format("Optical module:\n%-32s | %-12s | %-8s | %-12s | %-12s | %-14s | %-18s | %-30s | %-18s\n",
            'Net Card Name', 'Port Name', 'BMA Id', 'Present', 'Vendor Name', 'Serial Number',
            'MAC Addr', 'Actual Mac', 'Link Status')
    for _, optical in ipairs(c_optical_module.collection.objects) do
        parent_port = optical:get_parent()
        if not parent_port then
            log:notice('parent_port is null')
            goto next_optical
        end
        parent_na = parent_port:get_parent()
        if not parent_na then
            log:notice('parent_network_adapter is null')
            goto next_optical
        end
        ibma_id = ''
        -- handles[3]为optical_handle
        for k, v in pairs(bma.handles[3].objects) do
            if optical.NetworkAdapterId == k.NetworkAdapterId and optical.portID == k.portID then
                ibma_id = v
            end
        end
        head_info = head_info .. string.format("%-32s | %-12s | %-8s | %-12s | %-12s | %-14s | %-18s | %-30s | %-18s\n",
            parent_na.Name, parent_port.PortID, ibma_id,
            optical.Presence, optical.Manufacturer, optical.SerialNumber,
            parent_port.PermanentMACAddress, parent_port.MACAddress, parent_port.LinkStatus)
        ::next_optical::
    end
    head_info = head_info .. '\n'
    return head_info
end

local function collection_netcard_info()
    if not c_network_adapter.collection.objects or c_network_adapter.collection.count <= 0 then
        log:notice('netcard cnt is 0')
        return ''
    end
    local head_info = string.format("Network Card:\n%-32s | %-12s | %-8s | %-8s | %-12s | %-12s | %-12s\n",
        'Device Name', 'Position', 'Slot Number', 'Vendor Name', 'Model', 'RootBDF', 'BDF')
    local ok
    local bdf_str = ''
    for _, network_adapter in ipairs(c_network_adapter.collection.objects) do
        ok, bdf_str = pcall(string.format, '0000:%02x:%02x.%01x', network_adapter.DevBus,
            network_adapter.DevDevice, network_adapter.DevFunction)
        if not ok or bdf_str == '0000:00:00.0' then
            bdf_str = ''
        end
        if not is_virtual_port(network_adapter.Type) then
            head_info = head_info .. string.format("%-32s | %-12s | %-8s | %-8s | %-12s | %-12s | %-12s\n",
                network_adapter.Name, network_adapter.Position, network_adapter.SlotNumber,
                network_adapter.Manufacturer, network_adapter.Model,
                network_adapter.RootBDF == '0000:00:00.0' and '' or network_adapter.RootBDF, bdf_str)
        end
    end
    head_info = head_info .. '\n'
    return head_info
end

local function collection_netport_info()
    if not c_network_port.collection.objects or c_network_port.collection.count <= 0 then
        log:notice('netport cnt is 0')
        return ''
    end
    local parent_na
    local head_info = string.format("Network Port:\n%-32s | %-12s | %-8s | %-12s | %-12s\n",
        'Ref Network Card', 'Port Name', 'Port Type', 'MAC Addr', 'Actual Mac')
    local netport_table = {}
    local tmp
    for _, network_port in ipairs(c_network_port.collection.objects) do
        parent_na = network_port:get_parent()
        tmp = {
            PortID = network_port.PortID,
            PermanentMACAddress = network_port.PermanentMACAddress,
            MACAddress = network_port.MACAddress
        }
        if parent_na then
            tmp.RefNetworkCard = parent_na.DeviceLocator
            tmp.MediumType = network_port.MediumType
        else
            parent_na = c_network_adapter.collection:find({
                ID = network_port.NetworkAdapterId
            })
            tmp.RefNetworkCard = parent_na and parent_na.DisplayName or ''
            tmp.MediumType = ''
        end
        table.insert(netport_table, tmp)
        tmp = nil
    end
    table.sort(netport_table, function(a, b)
        if a.RefNetworkCard == b.RefNetworkCard then
            return a.PortID < b.PortID
        else
            return a.RefNetworkCard < b.RefNetworkCard
        end
    end)
    for _, network_port in ipairs(netport_table) do
        head_info = head_info .. string.format("%-32s | %-12s | %-8s | %-12s | %-12s\n",
            network_port.RefNetworkCard or '', network_port.PortID, network_port.MediumType,
            network_port.PermanentMACAddress, network_port.MACAddress)
    end
    return head_info
end

function c_network_adapter.collect_dump_info(dest_path, bma)
    local head_info = ''
    local ok, err = pcall(collect_optical_info, bma)
    if not ok then
        log:notice('Collect optical info failed, err:%s', err)
    end
    head_info = head_info .. (ok and err or '')

    ok, err = pcall(collection_netcard_info)
    if not ok then
        log:notice('Collect netcard info failed, err:%s', err)
    end
    head_info = head_info .. (ok and err or '')

    ok, err = pcall(collection_netport_info)
    if not ok then
        log:notice('Collect netport info failed, err:%s', err)
    end
    head_info = head_info .. (ok and err or '')
    if #head_info == 0 then
        return
    end
    local file_name = dest_path .. '/' .. NET_CARD_INOF
    local file
    file, err = file_sec.open_s(file_name, 'w+')
    if not file then
        log:error('open %s failed, err: %s', file_name, err)
        return
    end
    utils.close(file, pcall(file.write, file, head_info))
    utils_core.chmod_s(file_name, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP)
end

local port_id_table = {
    [0] = 8,
    [2] = 10,
    [4] = 12,
    [6] = 14,
    [9] = 1,
    [11] = 3,
    [13] = 5,
    [15] = 7
}

function c_network_adapter:collect_optical_static_info()
    local parent_port
    local cur_static_text = {}

    local opts = c_optical_module.collection:fetch_by_position(self:get_position())
    table.sort(opts, function(a, b)
        return a:get_parent().PortID < b:get_parent().PortID
    end)
    for _, opt in pairs(opts) do
        parent_port = opt:get_parent()
        if parent_port.NpuID == nil then
            log:notice("network adapter %s port %s failed to detect corresponding npu",
                self.Name, parent_port.PortID)
            goto next_optical
        end
        if opt.Presence == 0 then
            log:notice("network adapter %s port %s failed to detect optical module", self.Name, parent_port.PortID)
            goto next_optical
        end
        if parent_port.PortID and port_id_table[parent_port.PortID] then
            cur_static_text[#cur_static_text + 1] = string.format(
                    "OpticalModuleId(Logic): %s\nSilk: %s\nNetworkPort:\n  %s Port%s - NPU%s\n" ..
                    "  %s Port%s - NPU%s\nVendorName: %s\nSerialNumber: %s\nTransceiverType: %s\n\n",
                    (parent_port.NpuID + 1) // 2, opt.SilkText, self.Name, parent_port.PortID + 1,
                    parent_port.PortID + 1, self.Name, port_id_table[parent_port.PortID] + 1,
                    port_id_table[parent_port.PortID] + 1, opt.Manufacturer, opt.SerialNumber, opt.TransceiverType)
        else
            cur_static_text[#cur_static_text + 1] = string.format(
                    "OpticalModuleId(Logic): %s\nSilk: %s\nVendorName: %s\nSerialNumber: %s\nTransceiverType: %s\n\n",
                    (parent_port.NpuID + 1) // 2, opt.SilkText, opt.Manufacturer, opt.SerialNumber, opt.TransceiverType)

        end
        ::next_optical::
    end
    return table.concat(cur_static_text)
end

function c_network_adapter:collect_log_by_smbus_task()
    local log_dir = log_collector.get_smbus_log_dir(self)
    if not log_dir then
        return
    end
    if not log_collector.create_dir(log_dir) then
        return
    end

    log:notice('start to dump smbus log for netcard, NodeId:%s', self.NodeId)
    local curr_log_files = {}

    -- 获取错误日志
    curr_log_files[#curr_log_files + 1] = 'error_log_' .. log_collector.get_time() .. '.bin'
    local ok, resp = pcall(fetch_log, self.smbus_config_obj, 'ErrorLog',
        log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        log:notice('cannot fetch error log, resp = %s', resp)
    end

    -- 获取临终遗言日志
    curr_log_files[#curr_log_files + 1] = 'last_word_' .. log_collector.get_time() .. '.bin'
    ok, resp = pcall(fetch_log, self.smbus_config_obj, 'LastWord',
        log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        log:notice('cannot fetch last word, resp = %s', resp)
    end

    -- 获取运行日志
    curr_log_files[#curr_log_files + 1] = 'running_log_' .. log_collector.get_time() .. '.bin'
    ok = pcall(fetch_log, self.smbus_config_obj, 'RunningLog',
        log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        log:notice('cannot fetch running log')
    end

    -- 获取操作日志
    curr_log_files[#curr_log_files + 1] = 'operate_log_' .. log_collector.get_time() .. '.bin'
    ok = pcall(fetch_log, self.smbus_config_obj, 'OperateLog',
        log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        log:notice('cannot fetch operate log')
    end

    -- 获取维护日志
    curr_log_files[#curr_log_files + 1] = 'maintenance_log_' .. log_collector.get_time() .. '.bin'
    ok = pcall(fetch_log, self.smbus_config_obj, 'MaintenanceLog',
        log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        log:notice('cannot fetch maintenance log')
    end

    -- 删除老的日志
    log_collector.delete_old_log_file(log_dir, curr_log_files)
    log:notice('finish to dump log for netcard, NodeId:%s', self.NodeId)
end

function c_network_adapter:get_log_by_ncsi(log_type, total_frame, base_offset)
    local rsptext = {}
    local rsp
    local frame_index = 1
    local reqdata = 0
    local req_format = bs.new([[<<
        offset:32,
        length:32
    >>]])
    -- 优先使用新固件0x15命令字，若无响应则使用旧固件命令字0x12
    local sub_cmd = GETLOG_SUBCMD_WITH_NEW_CARD_FW

    -- 通过ncsi获取指定offset日志，每帧长度1024
    while frame_index <= total_frame do
        reqdata = req_format:pack({
            offset = base_offset + 1024 * (frame_index - 1),
            length = 1024
        })
        rsp = self.ncsi_config_obj:GetLog({
            sub_cmd_id = sub_cmd,
            extra_cmd = log_type,
            data = reqdata,
            package_id = self.package_id
        }):value()
        if not rsp then
            if sub_cmd == GETLOG_SUBCMD_WITH_OLD_CARD_FW then -- 旧命令字0x12发不通，则退出
                log:error('get log by ncsi failed')
                return false, table.concat(rsptext)
            end
            -- 新命令字0x15发不通，后续全部采用旧命令字，并重置循环
            sub_cmd = GETLOG_SUBCMD_WITH_OLD_CARD_FW
            rsptext = {}
            frame_index = 1
        else
            rsptext[frame_index] = rsp
            frame_index = frame_index + 1
        end
    end

    return true, table.concat(rsptext)
end

function c_network_adapter:fetch_log_by_ncsi(log_type, total_frame, base_offset, log_file)
    local fp_w, err = file_sec.open_s(log_file, 'wb+')
    if not fp_w then
        log:error('open file failed, err: %s', err)
        return false
    end
    return utils.safe_close_file(fp_w, function()
        utils_core.chmod_s(log_file, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

        local ret, log_data = self:get_log_by_ncsi(log_type, total_frame, base_offset)
        if not ret then
            log:info('collect type %s log failed', log_type)
            return false
        end
        fp_w:write(log_data)
        log:info('finish to collect type %s log', log_type)
        return true
    end)
end

function c_network_adapter:collect_log_by_rmii(log_dir)
    local time = log_collector.get_time()
    local ok = ncsi_core.ncsi_get_log(0, 'eth0', log_dir, time)
    if not ok then
        log:notice('cannot fetch log by RMII Based Transport')
    end
    local curr_log_files_rmii = {}
    curr_log_files_rmii[#curr_log_files_rmii + 1] = 'mpu_ucode_' .. time .. '.bin'
    utils_core.chmod_s((log_dir .. curr_log_files_rmii[#curr_log_files_rmii]),
        utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

    curr_log_files_rmii[#curr_log_files_rmii + 1] = 'last_word_' .. time .. '.bin'
    utils_core.chmod_s((log_dir .. curr_log_files_rmii[#curr_log_files_rmii]),
        utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

    curr_log_files_rmii[#curr_log_files_rmii + 1] = 'counter_' .. time .. '.bin'
    utils_core.chmod_s((log_dir .. curr_log_files_rmii[#curr_log_files_rmii]),
        utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

    curr_log_files_rmii[#curr_log_files_rmii + 1] = 'register_' .. time .. '.bin'
    utils_core.chmod_s((log_dir .. curr_log_files_rmii[#curr_log_files_rmii]),
        utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

    log_collector.delete_old_log_file(log_dir, curr_log_files_rmii)
    log:notice('finish to dump log for netcard by RMII Based Transport, NodeId:%s', self.NodeId)
end

-- SDI6.0收集命令字和逻辑与5.0存在差异
-- 先使用collect_log_by_rmii收集，如果前者失败，再使用该函数收集
function c_network_adapter:collect_log_by_rmii_new(log_dir)
    log:notice('start to collect log by rmii with new command')
    local time = log_collector.get_time()
    local ok = ncsi_core.ncsi_get_log_new(0, 'eth0', log_dir, time)
    if not ok then
        log:notice('cannot fetch log by RMII with new command')
    end

    local curr_log_files_rmii = {
        'type0_' .. time .. '.bin',
        'type1_' .. time .. '.bin',
        'type2_' .. time .. '.bin',
        'type3_' .. time .. '.bin'
    }
    for _, filename in pairs(curr_log_files_rmii) do
        utils_core.chmod_s(log_dir .. filename, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640
    end

    log_collector.delete_old_log_file(log_dir, curr_log_files_rmii)
    log:notice('finish to dump log for netcard by RMII with new command, NodeId:%s', self.NodeId)
    return ok
end

-- 采用重试机制获取日志
local function excute_task_retry(retry_times, title, cb)
    for i = 1, retry_times do
        local ok, ret = pcall(cb)
        if ok then
            return ret
        end
        log:error("excute task(%s) failed, retry times: %d, err: %s", title, i, ret)
        skynet.sleep(10)
    end
end

function c_network_adapter:get_sub_log(req_format, sub_log_len, log_type, fp_w, sub_idx, cur_len)
    local cur_max_len = 0   -- 当前子日志应当获取的最大长度，用于日志补0
    local last_frame = 0    -- 表示当前是否为最后一帧
    local remain_len = 0    -- 需要补0的长度
    cur_max_len = cur_max_len + sub_log_len[sub_idx] * 1024
    local rsptext = {}
    while true do
        local rsp = excute_task_retry(LOG_RETRY_TIMES, 'GetNewLog Data', function ()
            local reqdata = req_format:pack({
                frame_type = 0,
                offset = cur_len,
                length = 1024
            })
            local rsp_data = self.ncsi_config_obj:GetNewLog({
                extra_cmd = log_type,
                data = reqdata,
                package_id = self.package_id
            }):value()
            if not rsp_data then
                error('get log data failed')
            end
            return rsp_data
        end)
        if not rsp then
            return false
        end
        last_frame = string.unpack('<I1', rsp)
        local data = string.sub(rsp, 5)
        rsptext[#rsptext + 1] = data
        if last_frame == 1 then
            -- 最后一帧可能没有1024，需要获取具体长度
            cur_len = cur_len + #data
            if cur_len < cur_max_len then
                remain_len = cur_max_len - cur_len
                cur_len = cur_max_len
            end
            break
        end
        cur_len = cur_len + 1024
    end

    rsptext[#rsptext + 1] = string.rep('\x00', remain_len)
    fp_w:write(table.concat(rsptext))
    return cur_len
end

function c_network_adapter:fetch_log_data(sub_log_num, total_length, sub_log_len, req_format, log_type, fp_w)
    local sub_idx = 1   -- 当前获取的子日志序列
    local cur_len = 0   -- 当前已获取日志长度，单位byte

    while sub_idx <= sub_log_num and cur_len < total_length * 1024 do
        local ret = self:get_sub_log(req_format, sub_log_len, log_type, fp_w, sub_idx, cur_len)
        if not ret then
            return false
        end
        cur_len = ret
        sub_idx = sub_idx + 1
    end
    return true
end

-- 获取指定类型的日志
function c_network_adapter:get_spec_log(log_type, log_file)
    log:notice('start to collect log, log type: %s', log_type)

    local req_format = bs.new([[<<
        frame_type:32,
        offset:32,
        length:32
    >>]])

    -- 发送控制帧，获取子日志个数
    local rsp = excute_task_retry(LOG_RETRY_TIMES, 'GetNewLog Control', function ()
        local reqdata = req_format:pack({
            frame_type = 0x5a5a5a5a,
            offset = 0,
            length = 0
        })
        local rsp_data = self.ncsi_config_obj:GetNewLog({
            extra_cmd = log_type,
            data = reqdata,
            package_id = self.package_id
        }):value()
        if not rsp_data then
            error('send control command failed')
        end
        return rsp_data
    end)

    if not rsp then
        return false
    end

    -- 在控制帧发送成功后再创建文件
    local fp_w, err = file_sec.open_s(log_file, 'wb+')
    if not fp_w then
        log:error('open file failed, err: %s', err)
        return false
    end
    return utils.safe_close_file(fp_w, function()
        utils_core.chmod_s(log_file, utils.S_IRUSR | utils.S_IWUSR | utils.S_IRGRP) -- 文件权限设置为0640

        local total_length, sub_log_num, _, pos = string.unpack('<I4I1B', rsp)
        local sub_log_len = {}
        for _ = 1, sub_log_num do
            sub_log_len[#sub_log_len + 1], pos = string.unpack('<I2', rsp, pos)
        end
    
        -- 发送数据帧，获取日志内容
        if not self:fetch_log_data(sub_log_num, total_length, sub_log_len, req_format, log_type, fp_w) then
            return false
        end
        return true
    end)
end

-- sdi6.0新增命令获取网卡日志，新命令将和BMC解耦
function c_network_adapter:collect_log_by_new_cmd(log_dir, curr_log_files)
    for k, v in pairs(LOG_TYPES) do
        curr_log_files[#curr_log_files + 1] = v()
        if not self:get_spec_log(k, log_dir .. curr_log_files[#curr_log_files]) then
            log:error("collect log by ncsi failed, log_type: %s", k)
            return false
        end
    end
    return true
end

function c_network_adapter:collect_log_by_old_cmd(curr_log_files, log_dir)
    local is_pcie_disconnected = false
    -- 获取mpu_ucode日志
    curr_log_files[#curr_log_files + 1] = 'mpu_ucode_' .. log_collector.get_time() .. '.bin'
    local ok = self:fetch_log_by_ncsi(0, 640, 0, log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        is_pcie_disconnected = true
        log:notice('cannot fetch mpu ucode log')
    end

    -- 获取last_word日志
    curr_log_files[#curr_log_files + 1] = 'last_word_' .. log_collector.get_time() .. '.bin'
    ok = self:fetch_log_by_ncsi(0, 256, 0xA0000, log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        is_pcie_disconnected = true
        log:notice('cannot fetch last word log')
    end

    -- 获取counter日志
    curr_log_files[#curr_log_files + 1] = 'counter_' .. log_collector.get_time() .. '.bin'
    ok = self:fetch_log_by_ncsi(1, 136, 0, log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        is_pcie_disconnected = true
        log:notice('cannot fetch counter log')
    end

    -- 获取register日志
    curr_log_files[#curr_log_files + 1] = 'register_' .. log_collector.get_time() .. '.bin'
    ok = self:fetch_log_by_ncsi(2, 8, 0, log_dir .. curr_log_files[#curr_log_files])
    if not ok then
        is_pcie_disconnected = true
        log:notice('cannot fetch register log')
    end
    return is_pcie_disconnected
end

-- sdi通过ncsi收集日志，与smbus隔离开
function c_network_adapter:collect_log_by_ncsi_task()
    local available = self:wait_mpu_idle()
    if not available then
        return
    end
    local log_dir = log_collector.get_ncsi_log_dir(self)
    if not log_dir then
        return
    end
    if not log_collector.create_dir(log_dir) then
        return
    end

    log:notice('start to dump ncsi log for netcard, NodeId:%s', self.NodeId)
    local curr_log_files = {}
    local is_pcie_disconnected = false

    -- 首先尝试新命令获取日志，如果获取失败，则采用原方式获取
    local ok = self:collect_log_by_new_cmd(log_dir, curr_log_files)
    if not ok then
        curr_log_files = {}
        is_pcie_disconnected = self:collect_log_by_old_cmd(curr_log_files, log_dir)
    end

    -- 删除老的日志
    log_collector.delete_old_log_file(log_dir, curr_log_files)
    log:notice('finish to dump log for netcard, NodeId:%s', self.NodeId)
    if is_pcie_disconnected then
        -- 优先使用新命令获取日志，新命令获取失败使用老命令
        if not self:collect_log_by_rmii_new(log_dir) then
            self:collect_log_by_rmii(log_dir)
        end
    end
end

local t_resource = {
    [0] = 'CPU1',
    [1] = 'CPU2',
    [2] = 'CPU3',
    [3] = 'CPU4',
    [251] = 'CPU1,CPU2,CPU3,CPU4',
    [253] = 'CPU1,CPU2',
    [254] = 'PCH',
    [255] = 'PCIeSwitch'
}

function c_network_adapter:update_root_bdf()
    log:notice('update root bdf, name:%s, BDF:%s:%s.%s, DeviceBDF:%s:%s.%s, type:%s',
        self.NodeId, self.Bus, self.Device, self.Function, self.DevBus, self.DevDevice, self.DevFunction, self.Type)
    local ok, val = pcall(string.format, '0000:%02x:%02x.%01x', self.Bus, self.Device, self.Function)
    if ok then
        self.RootBDF = val
    end
end

function c_network_adapter:properties_synchronizer()
    self:update_root_bdf()
    local resource = t_resource[self.SocketId]
    self.AssociatedResource = resource
    self:listen('DeviceLocator', function(_, value)
        if not param_validator(value) then
            return
        end
        if self.Type == 3 then
            self.ID = string.gsub(self.DeviceLocator, '%s+', '')
            self.NodeId = string.gsub(self.DeviceLocator, '%s+', '')
        else
            self.ID = string.gsub(self.Position, '%s+', '') ..
                          string.gsub(self.DeviceLocator, '%s+', '')
            self.NodeId = string.gsub(self.Position, '%s+', '') ..
                              string.gsub(self.DeviceLocator, '%s+', '')
        end
    end)

    self:listen('SocketId', function(name, value)
        resource = t_resource[value]
        if not resource then
            log:error('Invalid SocketId:%s', value)
            resource = 'CPU1'
        end
        self.AssociatedResource = resource
    end)
    self:listen('Bus', function(_, value)
        self:update_root_bdf()
    end)
    self:listen('Device', function(_, value)
        self:update_root_bdf()
    end)
    self:listen('Function', function(_, value)
        self:update_root_bdf()
    end)
end

function c_network_adapter:set_prop_on_removing(flag)
    if flag then
        self.AttentionHotPlugState = HOTPLUG_STATE.Uninstalling
        self.ReadyToRemove = true
        self.ReadyToRemoveValue = READY_TO_REMOVE_VALUE
    else
        if self.AttentionHotPlugState ~= HOTPLUG_STATE.Uninstalled then
            self.AttentionHotPlugState = HOTPLUG_STATE.Inoperable
        end
    end
end

function c_network_adapter:set_prop_on_removed(flag)
    if flag then
        self.AttentionHotPlugState = HOTPLUG_STATE.Uninstalled
        self.ReadyToRemove = false
    else
        if self.AttentionHotPlugState ~= HOTPLUG_STATE.Uninstalled then
            self.AttentionHotPlugState = HOTPLUG_STATE.Removable
        end
    end
end

-- 对象分发完成后，主动获取bios状态
function c_network_adapter:set_attention_on_complete()
    local bios_obj = client:GetBiosBiosObject()
    self:set_attention_on_bios(bios_obj.SystemStartupState)
end

-- 根据bios的状态设置属性
function c_network_adapter:set_attention_on_bios(bios_state)
    if bios_state == BIOS_STARTUP_POST_STAGE_FINISH then
        if self.CardPowerGood == NETWORK_POWER_OFF then
            self.AttentionHotPlugState = HOTPLUG_STATE.Uninstalled
        else
            self.AttentionHotPlugState = HOTPLUG_STATE.Removable
        end
    else
        self.AttentionHotPlugState = HOTPLUG_STATE.Inoperable
    end
    log:notice('bios_state:%s, set %s AttentionHotPlugState %s success',
        bios_state, self.NodeId, self.AttentionHotPlugState)
end

-- 卸载时停止所有的轮询操作
function c_network_adapter:stop()
    self:stop_ncsi_schedulers()
    self:stop_smbus_schedulers()
    self:stop_pldm_schedulers()
end

function c_network_adapter:stop_ncsi_schedulers()
    for _, s in ipairs(self.ncsi_schedulers) do
        s:deconstruct()
    end
    self.ncsi_schedulers = {}
    -- 停止对应网口的轮询操作
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    for _, port in pairs(ports) do
        port:stop()
    end
end

function c_network_adapter:stop_smbus_schedulers()
    for _, s in pairs(self.smbus_schedulers) do
        s:deconstruct()
    end
    self.smbus_schedulers = {}
-- 暂不需要停止对应网口的轮询操作 目前网口没有smbus_schedulers
end

--MPU恢复空闲则发送一次性信息获取指令
function c_network_adapter:resume_fetch_properties()
    self:next_tick(self.update_link_ability_by_ncsi, self)
    self:next_tick(self.update_link_status_by_ncsi, self)
end

function c_network_adapter:pause_ncsi_schedulers()
    log:notice("pause ncsi schedulers ,size of ncsi schedulers is %s", #self.ncsi_schedulers)
    for _, s in ipairs(self.ncsi_schedulers) do
        s:pause()
    end
end

function c_network_adapter:resume_ncsi_schedulers()
    log:notice("resume ncsi schedulers ,size of ncsi schedulers is %s", #self.ncsi_schedulers)
    for _, s in ipairs(self.ncsi_schedulers) do
        s:resume()
    end
end

-- MPU繁忙时暂定所有的轮询操作
function c_network_adapter:pause()
    self:pause_ncsi_schedulers()
    -- 暂停对应网口的轮询操作
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    for _, port in pairs(ports) do
        port:pause()
    end
end

-- MPU空闲时恢复所有的轮询操作
function c_network_adapter:resume()
    self:resume_ncsi_schedulers()
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    self:resume_fetch_properties()
    -- 恢复对应网口的轮询操作
    for _, port in pairs(ports) do
        port:resume()
    end
end

function c_network_adapter:wait_mpu_idle()
    if self.MPUBusyStatus == MPU_IDLE then
        return true
    end
    local retry_times = 0
    while true do
        skynet.sleep(600)
        if self.MPUBusyStatus == MPU_IDLE then
            return true
        end
        if retry_times > 199 then
            -- 等待20min，MPU始终繁忙则返回超时
            log:notice("waiting for MPU idle timed out")
            return false
        end
        retry_times = retry_times + 1
    end
end

local old_dtor = c_network_adapter.dtor
function c_network_adapter:dtor()
    self:stop()
end

function c_network_adapter:destroy()
    self:dtor()
    old_dtor(self)
end

function c_network_adapter:ctor()
    self.health = 0
    self.last_fault_code = {}
    self.smbus_collect_status = LOG_DUMP_IDLE
    self.ncsi_collect_status = LOG_DUMP_IDLE
    self.smbus_has_collected = false
    self.ncsi_has_collected = false
    self.ports_count = 0
    self.smbios_status = 0
    self.add_smbios_slot = 0
    self.update_smbios_slot = 0
    self.sync_nic_op_temp_slot = 0

    self.smbus_config_obj = {}
    self.update_fw_ver_abnormal = false

    self.mctp_endpoints = {}
    self.transports = {}
    self.pldm_config_obj = {}
    self.ncsi_config_obj = {}
    self.lldp_config_obj = {}
    self.package_id = 0

    self.smbus_schedulers = {}
    self.ncsi_schedulers = {}
    self.pldm_schedulers = {}
    self.tasks = c_tasks.new()
    self.ink_numberic_table = {}

    self.lldp_receive_listener = false
    self.lldp_enabled_listener = false
    self.na_object_name = ''
    self.log_collection = npu_port_link_log_collection.new()

    self.bios_boot_wait_flag = 0

    self.cdr_overtemp_table = {
        npu_id = 255,
        max_temp = 0
    }

    self.children = {}
    self.parent = {}
    self.get_properties_res = {}
end

-- 十六进制字符转ascii字符
local function hex_to_ascii(mac)
    local hex = string.gsub(mac, ":", "")
    -- 非法mac地址置零
    if string.find(hex, "[^%x]") then
        return "\0\0\0\0\0\0"
    end
    local str = {}
    local byte
    -- 十六进制字符转换
    for i = 1, #hex, 2 do
        byte = string.sub(hex, i, i + 1)
        table.insert(str, string.char(tonumber(byte, 16)))
    end
    return table.concat(str)
end

local function get_port_mac_hex(port)
    local str = "\0\0\0\0\0\0"
    if not port then
        return str
    end

    local mac = port.PermanentMACAddress
    -- 非法mac地址置零
    if mac and #mac == 17 then
        str = hex_to_ascii(mac)
    end
    return str
end

-- ascii字符转十六进制字符
local function str_to_hex(str)
    local hex = {}
    for i = 1, #str do
        table.insert(hex,string.format("%02X", string.byte(str, i)))
    end
    return table.concat(hex)
end

local function regenerate_uuid_str(pack_string)
    local partten_mac = bs.new([[<<
        custom_a:48/big,
        custom_b:4,
        custom_c:4,
        custom_d:4,
        custom_e:4,
        custom_f:56/big,
        custom_g:8/big>>]])
    local uuid_part = partten_mac:unpack(pack_string)
    local uuid_param = {
        custom_a = uuid_part.custom_a,
        custom_b = uuid_part.custom_c,
        ver = 8,
        custom_c = uuid_part.custom_e,
        custom_d = uuid_part.custom_b,
        custom_e = uuid_part.custom_d,
        var = 8,
        custom_f = uuid_part.custom_f
    }
    local partten_uuid = bs.new([[<<
        custom_a:48/big,
        custom_b:4,
        ver:4,
        custom_c:4,
        custom_d:4,
        custom_e:4,
        var:4,
        custom_f:56/big>>]])
    return partten_uuid:pack(uuid_param)
end

local function get_uuid_pack_str(ports)
    table.sort(ports, function(a, b)
        return a.PortID < b.PortID
    end)
    local mac1 = get_port_mac_hex(ports[1])
    local mac2 = get_port_mac_hex(ports[2])
    local mac3 = get_port_mac_hex(ports[3])
    local mac4 = get_port_mac_hex(ports[4])
    -- 网口个数小于等于2个时，取两个网口的mac共96位，剩余32位用0补齐
    -- 网口个数大于2个，取第一个网口的mac（48位），后三个网口的低24位共120位，剩余8位用0补齐
    if #ports <= 2 then
        return string.pack("<c6c6c4", mac1, mac2, string.rep('\0', 4))
    end
    return string.pack("<c6c3c3c3c1", mac1, string.sub(mac2, 4, 6),
                string.sub(mac3, 4, 6), string.sub(mac4, 4, 6), '\0')
end

-- 通过mac生成网卡资产信息的uuid
function c_network_adapter:get_uuid_by_mac()
    local ports = c_network_port.collection:fetch_by_position(self:get_position())
    if ports == nil or #ports == 0 then
        return ""
    end
    local uuid_pack_str = get_uuid_pack_str(ports)
    local uuid_str = regenerate_uuid_str(uuid_pack_str)
    local uuid = str_to_hex(uuid_str)
    return uuid
end

function c_network_adapter:update_asset_uuid_info()
    if is_virtual_port(self.Type) then
        return
    end
    self.UUID = self:get_uuid_by_mac()
end

function c_network_adapter:init_id()
    local retry_times = 0
    -- bridge 不通过devicelocator生成ID信息
    if is_virtual_port(self.Type) then
        return
    end

    self.BoardIDHex = string.format('0x%04x', self.BoardID)
    while not param_validator(self.DeviceLocator) and retry_times < 10 do
        retry_times = retry_times + 1
        self.tasks:sleep_ms(5000)
    end

    if self.Type == 3 then
        self.ID = string.gsub(self.DeviceLocator, '%s+', '')
        self.NodeId = string.gsub(self.DeviceLocator, '%s+', '')
    else
        self.ID = string.gsub(self.Position, '%s+', '') ..
                        string.gsub(self.DeviceLocator, '%s+', '')
        self.NodeId = string.gsub(self.Position, '%s+', '') ..
                            string.gsub(self.DeviceLocator, '%s+', '')
    end
    self:register_mdb_objects()
    self:properties_synchronizer()
end

function c_network_adapter:update_asset_name(value)
    self.AssetName = value
end

function c_network_adapter:update_asset_serial_number(value)
    self.InventorySerialNumber = value
end

function c_network_adapter:update_asset_firmware_version(value)
    self.InventoryFirmwareVersion = value
end

function c_network_adapter:update_asset_manufacturer(value)
    self.InventoryManufacturer = value
end

function c_network_adapter:update_asset_slot(value)
    self.Slot = tostring(value)
end

function c_network_adapter:update_asset_pcb_version(value)
    self.InventoryPCBVersion = value
end

function c_network_adapter:update_asset_model(value)
    self.InventoryModel = value
end

function c_network_adapter:register_asset_listen_callback()
    local switch = {
        Name = self.update_asset_name,
        SerialNumber = self.update_asset_serial_number,
        FirmwareVersion = self.update_asset_firmware_version,
        Manufacturer = self.update_asset_manufacturer,
        SlotNumber = self.update_asset_slot,
        PCBVersion = self.update_asset_pcb_version,
        Model = self.update_asset_model
    }
    self:connect_signal(self.on_property_changed, function(name, value)
        if switch[name] then
            switch[name](self, value)
        end
    end)
end

function c_network_adapter:init_asset_data_info()
    if is_virtual_port(self.Type) then
        return
    end
    self.AssetType = 'NetworkAdapter'
    self.AssetName = self.Name
    self.InventorySerialNumber = self.SerialNumber
    self.InventoryFirmwareVersion = self.FirmwareVersion
    self.InventoryPCBVersion = self.PCBVersion
    self.InventoryManufacturer = self.Manufacturer
    self.AssetTag = 'N/A'
    self.PartNumber = 'N/A'
    self.ManufactureDate = 'N/A'
    self.Slot = tostring(self.SlotNumber)
    self.UUID = ''
    self.InventoryModel = self.Model
    self:register_asset_listen_callback()
end

function c_network_adapter:init()
    if self.CreatedByDeviceObject then
        log:notice('c_network_adapter init start, NodeId(%s)', self.NodeId)
        c_network_adapter.super.init(self)
        return
    end
    self:init_asset_data_info()
    self:register_listen_callback() -- 注册监听信号的回调函数
    self:init_id()
    c_network_adapter.super.init(self)
    self.na_object_name = self.path:match('([^/]+)$')
    self:connect_signal(self.on_add_object_complete, function()
        self:start()
    end)

    pcall(function()
        -- 接收到os重启信号后，收集网卡信息
        self:connect_signal(log_collector.log_dump_reset_smbios_sig, function()
            log:notice('collect netcard info after os reboot')
            log_netcard_info.collect_netcard_info(c_network_adapter.collection, c_network_port.collection)
        end)
        -- 接收到实际mac地址变更信号后，收集网卡信息
        self:connect_signal(log_netcard_info.actual_mac_change_sig, function()
            log:notice('collect netcard info after actual MAC address change')
            log_netcard_info.collect_netcard_info(c_network_adapter.collection, c_network_port.collection)
        end)
    end)
end

function c_network_adapter.create_mdb_object(value)
    local app = c_object_manage.get_instance().app
    if value.CreatedByDeviceObject then
        return app:CreateNetworkAdapter(1, value.ObjectName, function (obj)
            log:notice("create network adapter mdb object, NodeId(%s)", value.NodeId)
            obj.ID = value.ID
            obj.NodeId = value.NodeId
            obj.ObjectName = value.ObjectName
            obj.CreatedByDeviceObject = true
        end)
    end
    return app:CreateNetworkAdapter(1, value.ID, function (obj)
        log:debug("create network adapter id(%s), nodeid(%s), type(%d), RootBDF(%s)",
            value.ID, value.NodeId, value.Type, value.RootBDF)
        obj.ID = value.ID
        obj.NodeId = value.NodeId
        obj.Type = value.Type
        obj.RootBDF = value.RootBDF
    end)
end


function c_network_adapter.insert_or_update(na)
    local db_addr = c_network_adapter.collection:find(na)
    if db_addr then
        na = db_addr
    end

    return db_addr or c_network_adapter.__table(na)
end

c_network_adapter.update_port_mac_action = update_port_mac_action

return c_network_adapter
