-- 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 cjson = require 'cjson'
local utils = require 'mc.utils'
local log = require 'mc.logging'
local ENERGY_SAVING_SCENE_PATH <const> = '/bmc/kepler/Chassis/1/EnergySavingScene'
local ENERGY_SAVING_SCENE_INTERFACE <const> = 'bmc.kepler.Chassis.EnergySavingScene'
local SESSION_PATH = "/bmc/kepler/SessionService/Sessions"
local SESSION_INTF = "bmc.kepler.SessionService.Session"

local m = {}

local subject_name_table = {
    [0] = 'CPU',                          -- 0x00：CPU
    [1] = 'Memory',                       -- 0x01：内存
    [2] = 'Disk',                         -- 0x02：硬盘
    [3] = 'PSU',                          -- 0x03：电源
    [4] = 'Fan',                          -- 0x04：风扇
    [5] = 'Disk Backplane',               -- 0x05：硬盘背板
    [6] = 'RAID Card',                    -- 0x06：RAID扣卡
    [7] = 'UNKNOWN',                      -- 0x07：未定义（历史未知原因未定义）
    [8] = 'PCIe Card',                    -- 0x08：PCIe标卡
    [9] = 'AMC',                          -- 0x09：AMC板
    [10] = 'HBA',                         -- 0x0A：HBA卡
    [11] = 'Mezz Card',                   -- 0x0B：Mezz扣卡
    [12] = 'UNKNOWN',                     -- 0x0C：未定义（历史未知原因未定义）
    [13] = 'NIC',                         -- 0x0D：网卡扣卡
    [14] = 'Memory Board',                -- 0x0E：内存板
    [15] = 'PCIe Riser',                  -- 0x0F：PCIe Riser卡
    [16] = 'Mainboard',                   -- 0x10：主板
    [17] = 'LCD',                         -- 0x11：LCD
    [18] = 'Chassis',                     -- 0x12：机箱
    [19] = 'NCM',                         -- 0x13：NC板
    [20] = 'Switch Module',               -- 0x14：交换板
    [21] = 'Storage Board',               -- 0x15：存储板
    [22] = 'Chassis Backplane',           -- 0x16：机框背板
    [23] = 'HMM/CMC',                     -- 0x17：SMM板/CMC
    [24] = 'Fan Backplane',               -- 0x18：风扇背板
    [25] = 'Power Adapter Board',         -- 0x19：电源转接板
    [26] = 'BMC',                         -- 0x1A：BMC/BMC板
    [27] = 'MM/MMC',                      -- 0x1B：MMC(MM)板
    [28] = 'Twin Node Backplane',         -- 0x1C：双胞胎底板
    [29] = 'Base Plane',                  -- 0x1D：Base (交换平面)
    [30] = 'Fabric Plane',                -- 0x1E：Fabric (交换平面)
    [31] = 'Switch Mezz',                 -- 0x1F：交换扣卡(如FC GW扣卡/FCoE/M8PA)
    [32] = 'LED',                         -- 0x20：LED
    [33] = 'SD Card',                     -- 0x21：SD卡
    [34] = 'Security Module',             -- 0x22：TPM/TCM卡
    [35] = 'I/O Board',                   -- 0x23：IO板
    [36] = 'CPU Board',                   -- 0x24：处理器板
    [37] = 'RMC',                         -- 0x25：RMC板
    [38] = 'PCIe Adapter',                -- 0x26：PCIE SSD盘转接卡
    [39] = 'PCH',                         -- 0x27：PCH(南桥)
    [40] = 'Cable',                       -- 0x28：线缆/内部连接
    [41] = 'Port',                        -- 0x29：端口
    [42] = 'LSW',                         -- 0x2A：LSW
    [43] = 'PHY',                         -- 0x2B：PHY
    [44] = 'System',                      -- 0x2C：业务系统/产品
    [45] = 'M.2 Transfer Card',           -- 0x2D：M.2转接卡
    [46] = 'LED Board',                   -- 0x2E：LED转接板
    [47] = 'LPM',                         -- 0x2F：LPM(也叫PBI板，即PCH、BMC、IO组合板)
    [48] = 'PIC Card',                    -- 0x30：PIC卡（灵活插卡）
    [49] = 'Button',                      -- 0x31：按钮
    [50] = 'Expander',                    -- 0x32：Expander
    [51] = 'CPI',                         -- 0x33：CPI(集中控制分区单元，Central Partition Interconnect Module )
    [52] = 'ACM',                         -- 0x34：ACM(系统时钟板，Advanced Clock Module)
    [53] = 'CIM',                         -- 0x35：CIM(中心接口模块，Central Interface Module)
    [54] = 'PFM',                         -- 0x36：PFM(电源风扇合一模块)
    [55] = 'KPAR',                        -- 0x37：KPAR(多分区系统)
    [56] = 'JC',                          -- 0x38：JC芯片
    [57] = 'SCM',                         -- 0x39：SCM(系统计算模块，指CPU+内存的计算单元)
    [58] = 'Minisas HD channel',          -- 0x3A：MINISAS HD通道
    [59] = 'SATA DOM channel',            -- 0x3B：SATA DOM通道
    [60] = 'GE channel',                  -- 0x3C：GE通道
    [61] = 'XGE channel',                 -- 0x3D：XGE通道
    [62] = 'PCIe Switch',                 -- 0x3E：PCIe Switch
    [63] = 'Interface Device',            -- 0x3F：接口器件
    [64] = 'xPU Carrier Board',           -- 0x40：xPU载板
    [65] = 'Disk BaseBoard',              -- 0x41：硬盘底板（CH222V3的SESA）
    [66] = 'VGA Interface Card',          -- 0x42：VGA接口卡
    [67] = 'Pass-Through Card',           -- 0x43：直通卡(SAS直通卡等)
    [68] = 'Logical Driver',              -- 0x44：逻辑盘
    [69] = 'PCIe Retimer',                -- 0x45：PCIe Retimer
    [70] = 'PCIe Repeater',               -- 0x46：PCIe Reperter
    [71] = 'SAS',                         -- 0x47：SAS
    [72] = 'Memory Channel',              -- 0x48：内存通道
    [73] = 'BMA',                         -- 0x49：BMA
    [74] = 'LOM',                         -- 0x4A：板载网卡
    [75] = 'Signal Adapter Board',        -- 0x4B：信号转接板
    [76] = 'Horizontal Connection Board', -- 0x4C：水平转接板
    [77] = 'Node',                        -- 0x4D：节点
    [78] = 'Asset Locate Board',          -- 0x4E：资产管理控制板
    [79] = 'Unit',                        -- 0x4F：机柜U位
    [80] = 'RMM',                         -- 0x50：机柜管理模块
    [81] = 'Rack',                        -- 0x51：机柜
    [82] = 'BBU',                         -- 0x52：备电
    [83] = 'OCP Card',                    -- 0x53：OCP扣卡
    [84] = 'Leakage Detection Card',      -- 0x54：漏液检测卡
    [85] = 'MESH Card',                   -- 0x55：MESH卡
    [86] = 'NPU',                         -- 0x56：神经网络计算芯片
    [87] = 'CIC Card',                    -- 0x57：融合接口卡
    [88] = 'Expansion Module',            -- 0x58：Expansion Module
    [89] = 'Fan Module',                  -- 0x59：风扇模块
    [90] = 'AR Card',                     -- 0x5A：AR卡
    [91] = 'Converge Board',              -- 0x5B：汇聚板
    [92] = 'SoC Board',                   -- 0x5C：SoC主板
    [93] = 'Expand Board',                -- 0x5D：扩展板
    [94] = 'HiAM',                        -- 0x5E:HiAM
    [95] = 'OpticalModule',               -- 0x5F：光模块
    [96] = 'HDU',                         -- 0x60:HDU
    [98] = 'LCN',                         -- 0x62: 区域计算网络
    [192] = 'BCU',                        -- 0xC0：基础计算组件
    [193] = 'EXU',                        -- 0xC1：系统扩展组件
    [194] = 'SEU',                        -- 0xC2：存储扩展组件
    [195] = 'IEU',                        -- 0xC3：IO扩展组件
    [196] = 'CLU'                         -- 0xC4：散热组件
}

function m.if_unknown_to_null(str)
    local unknown = {
        ['N/A'] = true,
        ['NA'] = true,
        ['Unknown'] = true
    }
    if unknown[str] or str == nil or string.len(str) == 0 then
        return cjson.null
    end

    return str
end

function m.get_trust_module_interface_type(protocol, protocol_version)
    protocol = m.if_unknown_to_null(protocol)
    protocol_version = m.if_unknown_to_null(protocol_version)
    if protocol == cjson.null or protocol_version == cjson.null then
        return cjson.null
    end

    if protocol == 'TPM' and (protocol_version == '1.2' or protocol_version == '2.0') then
        return protocol .. string.gsub(protocol_version, '.', '_') -- 将'.'替换成'_'
    end
    if protocol == 'TCM' and protocol_version == '1.0' then
        return 'TCM1_0'
    end

    return cjson.null
end

function m.get_trust_module_health(health)
    if health == 0 then
        health = 1
    elseif health == 1 then
        health = 0
    end

    local code = {
        [0] = 'OK',
        [1] = 'Warning',
        [2] = 'Warning',
        [3] = 'Critical'
    }
    return code[health] or cjson.null
end

function m.get_trust_module_severity(health)
    if health == 0 then
        health = 1
    elseif health == 1 then
        health = 0
    end

    local code = {
        [0] = 'Informational',
        [1] = 'Minor',
        [2] = 'Major',
        [3] = 'Critical'
    }
    return code[health] or cjson.null
end

function m.get_trust_module_self_test_result(health)
    if health == 255 then -- 无效数据
        return cjson.null
    end

    if health == 1 then -- Enable
        return 'OK'
    end

    return 'Critical'
end

local function get_health_status(health, count)
    local code = {
        [0] = 'OK',
        [1] = 'Warning',
        [2] = 'Warning',
        [3] = 'Critical'
    }

    if count > 0 then
        return code[health] or cjson.null
    end

    return 'OK'
end

local function get_severity_status(health, count)
    local code = {
        [0] = 'Informational',
        [1] = 'Minor',
        [2] = 'Major',
        [3] = 'Critical'
    }

    if count > 0 then
        return code[health] or cjson.null
    end

    return cjson.null
end

function m.get_processor_summary_info(members, power_state)
    local count = 0
    local health = 0
    local model = cjson.null

    for i, v in ipairs(members) do
        if v.Presence == 1 then
            health = health >= v.Health and health or v.Health
            count = count + 1
            if i == 1 then
                model = m.if_unknown_to_null(v.Model)
            end
        end
    end

    if power_state ~= 'ON' then
        -- 没有上电时，修改health为无效数据
        health = 255
    end

    return {
        Count = count,
        Model = model,
        Severity = get_severity_status(health, count),
        HealthRollup = get_health_status(health, count)
    }
end

function m.get_cpu_total_cores(members)
    local total = 0
    for i, v in ipairs(members) do
        if v.Presence == 1 then
            total = total + v.TotalCores
        end
    end

    return total
end

function m.get_memory_summary_info(members, power_state, bma_total)
    local count = 0
    local total_memory = 0
    local health = 0

    for i, v in ipairs(members) do
        if v.Presence == 1 then
            count = count + 1
            total_memory = total_memory + v.CapacityMiB
            health = health >= v.Health and health or v.Health
        end
    end

    if power_state ~= 'ON' then
        -- 没有上电时，修改health为无效数据
        health = 255
    end

    if bma_total and bma_total > 0 then
        total_memory = bma_total
    else
        total_memory = total_memory // 1024
    end

    return {
        MemoryCount = count,
        TotalSystemMemoryGiB = total_memory,
        Severity = get_severity_status(health, count),
        HealthRollup = get_health_status(health, count)
    }
end

function m.get_pcie_devices_id(pcie_cards, ocp_cards)
    local ids = {}
    local ok, obj
    if pcie_cards ~= nil then
        for _, path in ipairs(pcie_cards) do
            ok, obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.Systems.PCIeDevices.PCIeCard')
            if ok then
                ids[#ids + 1] = 'PCIeCard' .. obj.SlotID
            end
        end
    end

    if ocp_cards ~= nil then
        for _, path in ipairs(ocp_cards) do
            ok, obj = pcall(mdb.get_object, bus, path, 'bmc.kepler.Systems.PCIeDevices.PCIeCard')
            if ok then
                ids[#ids + 1] = 'OCPCard' .. obj.SlotID
            end
        end
    end

    return ids
end

local BOARD_TYPE = {
    SWITCH    = 1,
    BLADE     = 2,
    OTHER     = 3,
    MM        = 4,
    RACK      = 5,
    HMM       = 6,
    RM        = 7
}

local CHASSIS_TYPE = {
    RACK        = 0,
    X_SERIAL    = 1,
    BLADE       = 2,
    SWITCH      = 3,
    MM          = 4
}

-- 元素的第一个数字代表chassisType，第二个代表softwareType
local SOFTWARE_TYPE = {
    EM          = {5, 0},
    PANGEA_MGNT = {5, 3},
    EM_X86_BMC  = {5, 2},
    RM          = {6, 0}
}

local HOSTINGROLE_BACK<const>       = "Switch"
local HOSTINGROLE_NO_BACK<const>    = "ApplicationServer"

-- 获取单板x86使能状态
local function get_x86_enabled()
    return false
end

-- 比较管理软件类型是否相等
local function equal_soft_type(chassisType, softwareType, target)
    if chassisType == target[1] and softwareType == target[2] then
        return true
    end
    return false
end

function m.get_hosting_role(chassisType, softwareType)
    local result = {}
    local board_type

    -- 判断机架类型
    if chassisType == CHASSIS_TYPE.SWITCH then
        board_type = BOARD_TYPE.SWITCH
    elseif chassisType == CHASSIS_TYPE.BLADE or chassisType == CHASSIS_TYPE.X_SERIAL then
        board_type = BOARD_TYPE.BLADE
    elseif chassisType == CHASSIS_TYPE.MM then
        board_type = BOARD_TYPE.MM
    elseif chassisType == CHASSIS_TYPE.RACK then
        board_type = BOARD_TYPE.RACK
    else
        board_type = BOARD_TYPE.OTHER
    end

    -- 判断软件类型
    if equal_soft_type(chassisType, softwareType, SOFTWARE_TYPE.EM) or
        equal_soft_type(chassisType, softwareType, SOFTWARE_TYPE.PANGEA_MGNT) then
        board_type = BOARD_TYPE.HMM
    elseif equal_soft_type(chassisType, softwareType, SOFTWARE_TYPE.EM_X86_BMC) then
        board_type = BOARD_TYPE.BLADE
    elseif equal_soft_type(chassisType, softwareType, SOFTWARE_TYPE.RM) then
        board_type = BOARD_TYPE.RM
    end

    if board_type == BOARD_TYPE.SWITCH then
        table.insert(result, HOSTINGROLE_BACK)
    elseif not get_x86_enabled() then
        table.insert(result, HOSTINGROLE_NO_BACK)
    end
    return result
end

function m.get_HealthEvent(processObj, eventsInfo, str)
    local data_source = processObj.EventList
    local major_v = eventsInfo.MajorVersion
    local minor_v = eventsInfo.MinorVersion
    local eventList = m.format_event_list(data_source)
    local healthEvent = cjson.json_object_new_array()
    local level = {
        ['Normal'] = 'Informational',
        ['Minor'] = 'Minor',
        ['Major'] = 'Major',
        ['Critical'] = 'Critical'
    }

    local severity = {
        ['Normal'] = 'OK',
        ['Minor'] = 'Warning',
        ['Major'] = 'Warning',
        ['Critical'] = 'Critical'
    }

    if type(eventList) == 'userdata' or #eventList == 0 then
        return healthEvent
    end
    for i, _ in ipairs(eventList) do
        healthEvent[i] = cjson.json_object_new_object()
        healthEvent[i].EventType = 'Alert'
        healthEvent[i].EntryType = 'Event'
        healthEvent[i].EventSubject = eventList[i].ComponentName
        healthEvent[i].Created = utils.date_format(eventList[i].Timestamp, '%Y-%m-%dT%H:%M:%S', true)
        healthEvent[i].Severity = severity[eventList[i].Severity]
        healthEvent[i].Level = level[eventList[i].Severity]
        healthEvent[i].Message = eventList[i].Description
        healthEvent[i].HandlingSuggestion = eventList[i].Suggestion
        healthEvent[i].EventId = eventList[i].EventCode
        healthEvent[i].MessageId = string.format('%sEvents.%s.%s.%s', str, major_v, minor_v, eventList[i].EventName)
        local ok, messgage_args = pcall(function ()
            return cjson.decode(eventList[i].MessageArgs)
        end)
        if ok then
            local tmp_table = cjson.json_object_new_array()
            for _, value in pairs(messgage_args) do
                tmp_table[#tmp_table + 1] = value
            end
            healthEvent[i].MessageArgs = tmp_table
        else
            healthEvent[i].MessageArgs = cjson.json_object_new_array()
        end
    end
    return healthEvent
end

function m.get_sel_log_parameters(total, start_id, count)
    local entries_parameter = {}
    if start_id > total then
        entries_parameter.StartEntryId = start_id
        entries_parameter.EntriesCount = count
        return entries_parameter
    end

    local index = total - count - start_id + 2
    if index <= 0 then
        index = 1
        count = total - start_id + 1
    end

    entries_parameter.StartEntryId = index
    entries_parameter.EntriesCount = count

    return entries_parameter
end

function m.is_start_id_valid(max_count, start_id)
    if start_id > max_count then
        error(base_messages.ActionParameterValueFormatError(
            start_id,
            'StartEntryId',
            'Oem/{{OemIdentifier}}/LogService.QuerySelLogEntries'
        ))
    end
    return true
end

-- 事件的格式变为kv类型的结构体数组
function m.format_event_list(event_list)
    if #event_list == 0 then
        return {}
    end
    -- 计算结构体参数个数
    local num = 0
    local end_flag = event_list[1].MappingTable[1].Key
    for key, value in pairs(event_list) do
        if value.MappingTable[1].Key == end_flag and key ~= 1 then
            break
        end
        num = num + 1
    end
    -- 格式化结构体,num个结构体构成一条Sel
    local count = 0
    local res = {}
    local event = {}
    for _, v in pairs(event_list) do
        event[v.MappingTable[1].Key] = v.MappingTable[1].Value
        count = count + 1
        if count % num == 0 then
            local temp = event
            event = {}
            res[#res + 1] = temp
        end
    end
    return res
end

function m.get_sel_log(event_list, total)
    local selLogEntries = cjson.json_object_new_array()
    local level = {
        ['Normal'] = '0',
        ['Minor'] = '1',
        ['Major'] = '2',
        ['Critical'] = '3'
    }
    local res = m.format_event_list(event_list)
    for _, event in pairs(res) do
        local event_obj = cjson.json_object_new_object()
        event_obj.level = level[event.Severity]
        event_obj.eventid = event.RecordId
        event_obj.subjecttype = event.SubjectType
        event_obj.eventdesc = event.Description
        event_obj.trigmode = event.TriggerMode
        event_obj.alerttime = utils.date_format(event.Timestamp, '%Y-%m-%d %H:%M:%S', false)
        event_obj.status = event.State
        event_obj.eventcode = event.EventCode
        event_obj.oldeventcode = event.OldEventCode
        event_obj.eventsubject = event.ComponentName
        event_obj.eventsugg = event.Suggestion
        selLogEntries[#selLogEntries + 1] = event_obj
    end

    local count_obj = cjson.json_object_new_object()
    count_obj.number = total
    selLogEntries[#selLogEntries + 1] = count_obj

    return selLogEntries
end

function m.get_MfgDate(MfgDate)
    local MANUFACTURE_DATE_LEN = 23
    if type(MfgDate) == 'userdata' or #MfgDate ~= MANUFACTURE_DATE_LEN then
        return ''
    end
    local matchData = string.match(MfgDate, '^%d%d%d%d/%d%d/%d%d %a%a%a %d%d:%d%d:%d%d$')
    if type(matchData) == 'userdata' or #matchData ~= MANUFACTURE_DATE_LEN then
        return ''
    end
    local MfgDateRegexStr = '(%d+)/(%d+)/(%d+) (%a+) (%d+):(%d+):(%d+)'
    local year, month, day, _, hour, minute, second = string.match(matchData, MfgDateRegexStr)
    return utils.get_timestamp(year, month, day, hour, minute, second)
end

local function get_health_system(health)
    local code = {
        [0] = 'OK',
        [1] = 'Minor',
        [2] = 'Major',
        [3] = 'Critical'
    }
    return code[health] or cjson.null
end

function m.get_storage_health(paths)
    local health = 0
    for _, v in ipairs(paths) do
        local obj = mdb.get_object(bus, v, 'bmc.kepler.Systems.Storage.Controller.ControllerStatus')
        if obj.Health and obj.Health > health then
            health = obj.Health
        end
    end
    return get_health_system(health)
end
function m.get_volume_health(paths)
    local health = 0
    for _, v in ipairs(paths) do
        local obj = mdb.get_object(bus, v, 'bmc.kepler.Systems.Storage.Volume')
        local state = obj.State
        local health_from_state_table_v2 = {
            3,
            2,
            2,
            0,
            3,
            2,
            2,
            2,
            2,
            2,
            2,
            2,
            2,
            2,
            3,
            2,
            2,
            0,
            0,
            2,
            2,
            0,
            0,
            0,
            3,
            0,
            0,
            3,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            0
        }
        local obj_health = health_from_state_table_v2[state + 1]
        if obj_health and obj_health > health then
            health = obj_health
        end
    end
    return get_health_system(health)
end

local function get_real_spans(ref_disk_array_list)
    local span_1880 = {}
    local span_broadcom = {}
    for _, v in pairs(ref_disk_array_list) do
        table.insert((v >= 0x8000) and span_1880 or span_broadcom, v)
    end

    return #span_1880 == 0 and span_broadcom or span_1880
end

function m.get_spans_path(ref_span_ids, controller_spans_list)
    local ref_spans_path = {}
    local real_spans = get_real_spans(ref_span_ids)
    for _, itemId in ipairs(real_spans) do
        for _, v in ipairs(controller_spans_list) do
            local obj = mdb.get_object(bus, v, 'bmc.kepler.Systems.Storage.DiskArray')
            if obj.Id == itemId then
                ref_spans_path[#ref_spans_path + 1] = v
            end
        end
    end
    return ref_spans_path
end

function m.get_present_drives(drive_list)
    local present_drives = {}
    for _, v in ipairs(drive_list) do
        if v.Presence == 1 then
            present_drives[#present_drives + 1] = v
        end
    end
    return present_drives
end

function m.get_drives_path(ref_drive_names, drives_list)
    local ref_drives_path = {}
    for _, itemName in ipairs(ref_drive_names) do
        for _, v in ipairs(drives_list) do
            local obj = mdb.get_object(bus, v, 'bmc.kepler.Systems.Storage.Drive')
            if obj.Presence == 1 and obj.Name == itemName then
                ref_drives_path[#ref_drives_path + 1] = v
            end
        end
    end
    return ref_drives_path
end

function m.get_controller_drives_path(controllerid, drives_list)
    local ref_drives_path = {}
    for _, v in ipairs(drives_list) do
        local obj = mdb.get_object(bus, v, 'bmc.kepler.Systems.Storage.Drive')
        if obj.Presence == 1 and obj.RefControllerId == controllerid and #obj.RefVolumeList == 0 then
            ref_drives_path[#ref_drives_path + 1] = v
        end
    end
    return ref_drives_path
end

function m.get_history_from_jsonstr(jsonstr)
    return cjson.decode(jsonstr)
end

-- 格式化过滤参数
function m.get_select_param(level, subject_type, start_time, end_time, search_word)
    local param = {}
    -- redfish不会过滤语言，默认英语
    param[#param+1] = {'Lang','en'}
    if level and #level ~= 0 then
        level = level == 'Informational' and 'Normal' or level
        param[#param+1] = {'SelLevel', level}
    end
    -- 主体类型需要根据ipmi规范进行转换
    if subject_type and #subject_type ~= 0 then
        local is_find = false
        for s_type, s_name in pairs(subject_name_table) do
            if s_name == subject_type then
                subject_type = tostring(s_type)
                is_find = true
                break
            end
        end
        subject_type = is_find and subject_type or '-1' -- 查询不到即可
        param[#param+1] = {'SelObjectType', subject_type}
    end
    if start_time and #start_time ~= 0 then
        param[#param+1] = {'SelBeginTime', start_time}
    end
    if end_time and #end_time ~= 0 then
        param[#param+1] = {'SelEndTime', end_time}
    end
    if search_word and #search_word ~= 0 then
        param[#param+1] = {'SelSearchString', search_word}
    end
    return param
end

function m.get_bootup_sequence(bootorder)
    local switch_table = {
        Hdd = 'HardDiskDrive',
        Cd = 'DVDROMDrive',
        Pxe = 'PXE',
        Others = 'Others'
    }
    local req_table = {
        BootTypeOrder0 = switch_table[bootorder[1]] or bootorder[1],
        BootTypeOrder1 = switch_table[bootorder[2]] or bootorder[2],
        BootTypeOrder2 = switch_table[bootorder[3]] or bootorder[3],
        BootTypeOrder3 = switch_table[bootorder[4]] or bootorder[4]
    }
    if bootorder[5] ~= nil then
        req_table.BootTypeOrder4 = bootorder[5]
    end
    return cjson.encode(req_table)
end

---获取在位内存的集合
local MEMORY_MDB_INTF <const> = "bmc.kepler.Systems.Memory"
function m.get_current_memory(memory_paths)
    local res = {}
    for _, path in ipairs(memory_paths) do
        local obj = mdb.get_object(bus, path, MEMORY_MDB_INTF)
        if obj.Presence == 1 then
            local position = obj.Position ~= '' and obj.Position or 'mainboard'
            local dimm_name = obj.DimmName
            res[#res + 1] = position .. dimm_name
        end
    end
    return res
end

function m.get_fru_serial_num(sys_sn, paths, host_type)
    -- 非multihost环境返回fru0的product sn
    if not host_type or host_type ~= 'Multihost' then
        return sys_sn
    end

    local path = nil
    local tmp_id = 65535
    local obj, fru_name, fru_type, id
    for _, value in pairs(paths) do
        obj = mdb.get_object(bus, value, 'bmc.kepler.Systems.FruData.Overview')
        fru_type = obj.FruType
        if fru_type == '36' then
            fru_name = obj.FruName
            id = tonumber(string.match(fru_name, '%d+$') or '', 10) -- 防止string返回nil导致tonumber报错
            if id and id < tmp_id then
                tmp_id = id
                path = value
            end
        end
    end

    if not path then
        return nil
    end

    local bcu_obj = mdb.get_object(bus, path, 'bmc.kepler.Systems.FruData.Product')
    return bcu_obj.ProductSerialNumber
end

-- 使用循环遍历
function m.get_alarm_count(eventList, host, host_type)
    eventList = m.format_event_list(eventList)
    -- host已校验
    local counts = {Critical = 0, Major = 0, Minor = 0, Normal = 0}
    if #eventList == 0 then
        return counts
    end
    local system_id
    local is_multihost = host_type == 'Multihost'
    for i, _ in ipairs(eventList) do
        system_id = eventList[i].SystemId
        if not is_multihost or (system_id == '' or system_id == '0' or system_id == host) then
            counts[eventList[i].Severity] = counts[eventList[i].Severity] + 1
        end
    end
    return counts
end

function m.count_to_severity(counts)
    if counts.Critical > 0 then
        return 'Critical'
    elseif counts.Major > 0 then
        return 'Major'
    elseif counts.Minor > 0 then
        return 'Minor'
    else
        return 'Informational'
    end
end

function m.is_power_mode_supported()
    local ok, obj = pcall(mdb.get_object, bus, ENERGY_SAVING_SCENE_PATH, ENERGY_SAVING_SCENE_INTERFACE)
    if ok and obj.IsPowerModeSupported then
        return true
    end
    local err = custom_messages.PropertyModificationNotSupported('PowerMode')
    err.RelatedProperties = {'#/PowerMode'}
    error(err)
end

function m.get_number_of_active_sessions_and_type(system_id)
    local res = {
        Count = 0,
        ActivatedType = cjson.null
    }
    local session_type
    local kvm_session_type
    local ok, sessions = pcall(mdb.get_sub_objects, bus, SESSION_PATH, SESSION_INTF)
    if not ok then
        sessions = {}
    end
    for _, session in pairs(sessions) do
        session_type = string.lower(session.SessionType)
        if session.SystemId == tonumber(system_id) and string.match(session_type, 'kvm') then
            kvm_session_type = session_type
            res.Count = res.Count + 1
        end
    end
    if res.Count == 0 then
        return res
    else
        res.ActivatedType = string.match(kvm_session_type, 'private') and 'Private' or 'Shared'
        return res
    end
end

function m.get_number_of_active_sessions(system_id, activated_id)
    local session_type
    local count = 0
    if activated_id ~= cjson.null and activated_id ~= tonumber(system_id) then
        return count
    end
    local ok, sessions = pcall(mdb.get_sub_objects, bus, SESSION_PATH, SESSION_INTF)
    if not ok then
        sessions = {}
    end
    for _, session in pairs(sessions) do
        session_type = string.lower(session.SessionType)
        if string.match(session_type, 'vnc') then
            count = count + 1
        end
    end
    return count
end

return m
