-- 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.

-- Description: sdr lifecycle management.
local skynet = require 'skynet'
local ipmi_req = require 'sensor.ipmi.ipmi'
local ipmi_enums = require 'ipmi.enums'
local common = require 'sdr.sdr_common'
local log = require 'mc.logging'
local mc_utils = require 'mc.utils'
local cc = require 'sdr.sdr_const'
local utils = require 'sensor_utils'
local err = require 'sensor_error'
local oper = require 'sensor_operation'
local client = require 'sensor.client'
local sdr_grp = require 'sdr.sdr_group'

local ct = ipmi_enums.ChannelType
local sf = string.format

local sdr_management = {}
sdr_management.__index = sdr_management

local sdr_device_id = nil

function sdr_management.new(sdr_sigs)
    return setmetatable({
        sdr_sigs = sdr_sigs,
        reserve_id = cc.SDR_RESERVE_ID_INIT,
        sdr_groups = {},
        sdr_count = 0,
        population_time = os.time(),
        luns = {},
        update_owner = {},
        lun_flag = 0,
        add_timestamp = 0,
        erase_timestamp = 0,
        sdr_state = cc.SDRDEV_NORMAL_MODE,
        sdr_owner = {}
    }, sdr_management)
end

function sdr_management:sdr_clear_all()
    for i = 1, cc.SENSOR_LUN_NUM do
        self.luns[i] = 0
    end

    for _, group in pairs(self.sdr_groups) do
        group:sdr_clear_all()
    end

    self.sdr_count = 0
    self.population_time = os.time()
    self.lun_flag = 0
end

function sdr_management:initialize()
    self:sdr_clear_all()
    self.sdr_owner.chan = 0
    self.sdr_owner.src_addr = 0
    self.sdr_owner.src_lun = 0

    -- 开始监听SDR的更新
    self.sdr_sigs.update:on(function(...) return self:update_sdr(...) end)
end

function sdr_management:update_sdr(clz, obj, host_id)
    if not self.sdr_groups[host_id] then
        log:notice('there is no sdr group of host %s', host_id)
        return
    end

    self.sdr_groups[host_id]:update_sdr(clz, obj)
end

function sdr_management:register_ipmi(cb)
    cb(ipmi_req.GetSDRRepoInfo, function(...) return self:ipmi_get_sdr_repo_info(...) end)
    cb(ipmi_req.GetSDRReserveId, function(...) return self:ipmi_get_sdr_reserve_id(...) end)
    cb(ipmi_req.GetSDR, function(...) return self:ipmi_get_sdr(...) end)
    cb(ipmi_req.ClearSDR, function(...) return self:ipmi_clear_sdr(...) end)
    cb(ipmi_req.AddPartialSDR, function(...) return self:ipmi_add_partial_sdr(...) end)
    cb(ipmi_req.AddSDR, function(...) return self:ipmi_add_sdr(...) end)
    cb(ipmi_req.EnterSDRRepoUpdate, function(...) return self:ipmi_enter_sdr_repo_update(...) end)
    cb(ipmi_req.ExitSDRRepoUpdate, function(...) return self:ipmi_exit_sdr_repo_update(...) end)
    cb(ipmi_req.GetDeviceSDRInfo, function(...) return self:ipmi_get_device_sdr_info(...) end)
    cb(ipmi_req.GetDeviceSDR, function(...) return self:ipmi_get_device_sdr(...) end)
    cb(ipmi_req.ReserveDeviceSDR, function(...) return self:ipmi_reserve_device_sdr(...) end)
    cb(ipmi_req.GetSDRRepoAllocInfo, function(...) return self:ipmi_get_sdr_repo_alloc(...) end)
    cb(ipmi_req.RunSDRInitAgent, function(...) return self:ipmi_run_initialization_agent(...) end)
    cb(ipmi_req.GetDeviceLocatorRecordId, function(...) return self:ipmi_get_device_locator_record_id(...) end)
end

local function is_support_fru_id(fruid)
    local is_support = false
    local objs = client:GetFruObjects()
    for _, obj in pairs(objs) do
        if obj['FruId'] == fruid then
            is_support = true
        end
    end
    if not is_support then
        log:error('fruid invalid')
        error(err.ipmi_error_map(err.ERR_OUT_OF_RANGE))
    end

    is_support = false
    objs = client:GetOverviewObjects()
    for _, obj in pairs(objs) do
        if obj['FruId'] == fruid then
            is_support = true
        end
    end
    if not is_support then
        log:error('fruid invalid')
        error(err.ipmi_error_map(err.ERR_DES_UNAVAILABLE))
    end
end

function sdr_management:ipmi_get_device_locator_record_id(req)
    local fruid = req.FruDeviceId
    is_support_fru_id(fruid)

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.PicmgIdentifier = fruid

    if not sdr_device_id then
        log:error('get sdr device id failed')
    end

    if fruid == 0 and sdr_device_id then
        rsp.RecordIdLs = sdr_device_id & 0xff
        rsp.RecordIdMs = (sdr_device_id >> 8) & 0xff
    else
        rsp.RecordIdLs = 0
        rsp.RecordIdMs = 0
    end

    return rsp
end

function sdr_management:ipmi_run_initialization_agent(req)
    if req.RunFlag > 1 then
        log:error('get sdr ipmi request is invalid')
        error(err.ipmi_error_map(err.ERR_INVALID_FIELD))
    end

    local rsp = {CompletionCode = 0x00}
    if req.RunFlag & 0x01 ~= 0 then
        self.population_time = os.time()
        self.lun_flag = 0
        self.reserve_id = cc.SDR_RESERVE_ID_INIT
        rsp.Progress = 0x00 -- 正在处理中
    else
        rsp.Progress = 0x01 -- 完成处理
    end
    return rsp
end

function sdr_management:ipmi_get_sdr_repo_alloc()
    local free = cc.SDR_MAX_NUMBER - self.sdr_count
    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.UnitCntL = cc.SDR_MAX_NUMBER & 0xFF
    rsp.UnitCntH = (cc.SDR_MAX_NUMBER >> 8) & 0xFF
    rsp.UnitSizeL = cc.SDR_MAX_LEN & 0xFF
    rsp.UnitSizeH = (cc.SDR_MAX_LEN >> 8) & 0xFF
    rsp.FreeUnitL = free & 0xFF
    rsp.FreeUnitH = (free >> 8) & 0xFF
    rsp.FreeBlockL = free & 0xFF
    rsp.FreeBlockH = (free >> 8) & 0xFF
    rsp.MaxUnitSize = free
    return rsp
end

function sdr_management:check_update_owner(ctx)
    if self.sdr_state ~= cc.SDRDEV_UPDATE_MODE then
        return cc.SDR_NO_UPDATING
    end

    if self.sdr_owner.chan ~= ctx.chan_num then
        return cc.SDR_UPDATING_BY_OTHER
    end
    if self.sdr_owner.src_addr ~= ctx.src_addr then
        return cc.SDR_UPDATING_BY_OTHER
    end
    if self.sdr_owner.src_lun ~= ctx.src_lun then
        return cc.SDR_UPDATING_BY_OTHER
    end
    return cc.SDR_UPDATING_BY_MYSELF
end

function sdr_management:ipmi_exit_sdr_repo_update(_, ctx)
    if self:check_update_owner(ctx) == cc.SDR_UPDATING_BY_OTHER then
        log:error('current sdr is already in update mode')
        oper.log(ctx, oper.SDR_UPDATE_EX, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_SDR_IN_UPDATE_MODE))
    end

    self.sdr_owner.chan = 0
    self.sdr_owner.src_addr = 0
    self.sdr_owner.src_lun = 0
    self.sdr_state = cc.SDRDEV_NORMAL_MODE

    if ctx.chan_num and ctx.src_addr and ctx.src_lun then
        local input = sf('%d-%d-%d', ctx.chan_num, ctx.src_addr, ctx.src_lun)
        log.info('[%s] exit sdr repository update mode successfully', input)
    else
        log.info('exit sdr repository update mode successfully')
    end
    oper.log(ctx, oper.SDR_UPDATE_EX, oper.SUCCESS)

    local rsp = {}
    rsp.CompletionCode = 0x0
    return rsp
end

function sdr_management:ipmi_enter_sdr_repo_update(_, ctx)
    if self.sdr_state == cc.SDRDEV_UPDATE_MODE then
        log:error('current sdr is already in update mode')
        oper.log(ctx, oper.SDR_UPDATE_EN, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_SDR_IN_UPDATE_MODE))
    end

    self.sdr_owner.chan = ctx.chan_num
    self.sdr_owner.src_addr = ctx.src_addr
    self.sdr_owner.src_lun = ctx.src_lun
    self.sdr_state = cc.SDRDEV_UPDATE_MODE

    if ctx.chan_num and ctx.src_addr and ctx.src_lun then
        local input = sf('%d-%d-%d', ctx.chan_num, ctx.src_addr, ctx.src_lun)
        log.info('[%s] enter sdr repository update mode successfully', input)
    else
        log.info('enter sdr repository update mode successfully')
    end
    oper.log(ctx, oper.SDR_UPDATE_EN, oper.SUCCESS)

    local rsp = {}
    rsp.CompletionCode = 0x0
    return rsp
end

function sdr_management:ipmi_get_sdr_repo_time()
    local rsp = {}
    local t = os.time()
    rsp.CompletionCode = 0x00
    rsp.Timestamp0 = t & 0xFF
    rsp.Timestamp1 = (t >> 8) & 0xFF
    rsp.Timestamp2 = (t >> 16) & 0xFF
    rsp.Timestamp3 = (t >> 24) & 0xFF
    return rsp
end

function sdr_management:ipmi_reserve_device_sdr()
    return self:ipmi_get_sdr_reserve_id()
end

local function get_sdr_record(self, recid, ctx)
    -- 区分带内带外查询
    local sdr_groups, sdr_count = {}, 0
    if ctx.ChanType == ct.CT_HOST:value() then
        table.insert(sdr_groups, self.sdr_groups[cc.PUBLIC_SYSTEM_ID])
        table.insert(sdr_groups, self.sdr_groups[ctx.HostId])
        for _, group in ipairs(sdr_groups) do
            sdr_count = sdr_count + group.sdr_count
        end
    else
        for _, group in pairs(self.sdr_groups) do
            table.insert(sdr_groups, group)
        end
        table.sort(sdr_groups, function (a, b)
            return a.host_id < b.host_id
        end)
        sdr_count = self.sdr_count
    end

    if recid == 0 then recid = 1 end
    if recid == 0xFFFF then recid = sdr_count end
    if recid ~= 0xFFFF and recid > sdr_count then
        log:error('get sdr ipmi request record id %x is out of range', recid)
        error(err.ipmi_error_map(err.ERR_OUT_OF_RANGE))
    end

    local next_recid = (recid < sdr_count and recid + 1 or 0xFFFF)
    local sdr_rec
    for _, group in ipairs(sdr_groups) do
        if recid <= group.sdr_count then
            sdr_rec = group:get_sdr_record(recid)
            break
        end
        recid = recid - group.sdr_count
    end
    return sdr_rec, next_recid
end

function sdr_management:ipmi_get_device_sdr(req, ctx)
    local resid = (req.ReserveIdH << 8) | req.ReserveIdL
    if resid ~= self.reserve_id and (req.Offset ~= 0 or resid ~= 0) then
        log:error('get sdr ipmi request is not match the rule')
        error(err.ipmi_error_map(err.ERR_INVALID_RSVID))
    end

    local recid = (req.RecordIdH << 8) | req.RecordIdL
    local sdr_rec, next_recid = get_sdr_record(self, recid, ctx)

    local read_len = req.Length
    if req.Length == 0xFF then
        read_len = #sdr_rec
    end
    if read_len + req.Offset > #sdr_rec and req.Offset < #sdr_rec then
        read_len = #sdr_rec - req.Offset
    end

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.RecordIdL = next_recid & 0xFF
    rsp.RecordIdH = (next_recid >> 8) & 0xFF
    rsp.Datas = sdr_rec:sub(req.Offset + 1, req.Offset + read_len)
    return rsp
end

function sdr_management:ipmi_get_device_sdr_info(req, ctx)
    if #req.Datas > 1 then
        log:error('get device sdr ipmi request length is invalid')
        error(err.ipmi_error_map(err.ERR_LENGTH_LARGE))
    end

    local flag = false
    if #req.Datas == 1 then
        flag = (req.Datas:byte() & 0x01) == 1
    end

    local count = 0
    if ctx.dest_lun and ctx.dest_lun < cc.SENSOR_LUN_NUM then
        count = self.luns[1 + ctx.dest_lun]
    end

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.Count = flag and self.sdr_count or count
    rsp.DynamicSensor = ctx.ChanType == ct.CT_SMM:value() and cc.SDR_POPULATION_DYNAMIC or cc.SDR_POPULATION_STATIC
    rsp.Reserved = 0
    rsp.Lun = self.lun_flag
    rsp.Population = self.population_time
    return rsp
end

function sdr_management:ipmi_get_sdr_repo_info()
    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.SDRVersion = cc.SDR_VERSION
    rsp.RecordCountL = self.sdr_count & 0xFF
    rsp.RecordCountH = (self.sdr_count >> 8) & 0xFF

    local free = (cc.SDR_MAX_NUMBER - self.sdr_count) * cc.SDR_MAX_LEN
    rsp.FreeL = free & 0xFF
    rsp.FreeH = (free >> 8) & 0xFF
    rsp.AddTime0 = self.add_timestamp & 0xFF
    rsp.AddTime1 = (self.add_timestamp >> 8) & 0xFF
    rsp.AddTime2 = (self.add_timestamp >> 16) & 0xFF
    rsp.AddTime3 = (self.add_timestamp >> 24) & 0xFF
    rsp.DelTime0 = self.erase_timestamp & 0xFF
    rsp.DelTime1 = (self.erase_timestamp >> 8) & 0xFF
    rsp.DelTime2 = (self.erase_timestamp >> 16) & 0xFF
    rsp.DelTime3 = (self.erase_timestamp >> 24) & 0xFF
    rsp.OpSupport = common.get_sdr_oper_support()
    return rsp
end

local function refresh_reservation_id(rid)
    local reservation_id = rid + 1
    if reservation_id > 0xFFFF then
        reservation_id = 1
        log:notice('sdr reservation id reverse to 1')
    end
    return reservation_id
end

function sdr_management:ipmi_get_sdr_reserve_id()
    self.reserve_id = refresh_reservation_id(self.reserve_id)

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.ReserveIdL = self.reserve_id & 0xFF
    rsp.ReserveIdH = (self.reserve_id >> 8) & 0xFF
    return rsp
end

function sdr_management:ipmi_get_sdr(req, ctx)
    if self:check_update_owner(ctx) == cc.SDR_UPDATING_BY_OTHER then
        log:error('current sdr is already in update mode')
        error(err.ipmi_error_map(err.ERR_SDR_IN_UPDATE_MODE))
    end

    return self:ipmi_get_device_sdr(req, ctx)
end

function sdr_management:ipmi_clear_sdr(req, ctx)
    if self.sdr_state == cc.SDRDEV_UPDATE_MODE then
        log:error('clear sdr failed because current is already in update mode')
        oper.log(ctx, oper.SDR_CLEAR, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_CANNOT_SUPPORT))
    end

    if self:check_update_owner(ctx) == cc.SDR_UPDATING_BY_OTHER then
        log:error('current sdr cannot be updated by other')
        oper.log(ctx, oper.SDR_CLEAR, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_SDR_IN_UPDATE_MODE))
    end

    local flag = string.char(req.FlagC, req.FlagL, req.FlagR)
    if flag ~= 'CLR' then
        log:error('clear sdr ipmi request flag is invalid')
        oper.log(ctx, oper.SDR_CLEAR, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_INVALID_FIELD))
    end

    if req.Operation ~= cc.SDR_ERASE_INITIATE and req.Operation ~= cc.SDR_ERASE_GET_STATE then
        log:error('clear sdr ipmi request operation is invalid')
        oper.log(ctx, oper.SDR_CLEAR, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_INVALID_FIELD))
    end

    if req.Operation == cc.SDR_ERASE_INITIATE then
        self:sdr_clear_all()
        self.erase_timestamp = os.time()
    end

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.Status = 0x01
    rsp.Reserved = 0
    oper.log(ctx, oper.SDR_CLEAR, oper.SUCCESS)
    return rsp
end

local function get_sdr_group(self, ctx, op)
    if self.sdr_state ~= cc.SDRDEV_UPDATE_MODE then
        log:error('add sdr failed because current is not in update mode')
        oper.log(ctx, op, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_CANNOT_SUPPORT))
    end

    if self:check_update_owner(ctx) == cc.SDR_UPDATING_BY_OTHER then
        log:error('current sdr cannot be updated by other')
        oper.log(ctx, op, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_SDR_IN_UPDATE_MODE))
    end

    if self.sdr_count >= cc.SDR_MAX_NUMBER then
        log:error('sdr max count has been reached')
        oper.log(ctx, op, oper.FAILED)
        error(err.ipmi_error_map(err.ERR_OUT_OF_SPACE))
    end

    -- 非host通道添加到公共表中
    local host_id = cc.PUBLIC_SYSTEM_ID
    if ctx.ChanType == ct.CT_HOST:value() then
        host_id = ctx.HostId or cc.PUBLIC_SYSTEM_ID
    end
    if not self.sdr_groups[host_id] then
        self.sdr_groups[host_id] = sdr_grp.new(host_id)
    end
    return self.sdr_groups[host_id]
end

function sdr_management:ipmi_add_sdr(req, ctx)
    local ret = get_sdr_group(self, ctx, oper.SDR_ADD_DATA):add_sdr(req.Datas)

    if ret ~= err.SUCCESS then
        log:error('add sdr failed, ret: %s', ret)
        oper.log(ctx, oper.SDR_ADD_DATA, oper.FAILED)
        error(err.ipmi_error_map(ret))
    end

    self.sdr_count = self.sdr_count + 1
    oper.log(ctx, oper.SDR_ADD_DATA, oper.SUCCESS, self.sdr_count)

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.RecordIdL = self.sdr_count & 0xFF
    rsp.RecordIdH = (self.sdr_count >> 8) & 0xFF
    return rsp
end

function sdr_management:ipmi_add_partial_sdr(req, ctx)

    local ret = get_sdr_group(self, ctx, oper.SDR_ADD):add_partial_sdr(req.Offset, req.Progress, req.Datas)
    if ret ~= err.SUCCESS then
        log:error('add partial sdr failed, ret: %d', ret)
        oper.log(ctx, oper.SDR_ADD, oper.FAILED)
        error(err.ipmi_error_map(ret))
    end

    local rid = self.sdr_count + 1
    if req.Progress == cc.SDR_PARTIAL_ADD_FINISHED then
        self.sdr_count = self.sdr_count + 1
    end

    local req_rid = (req.RecordIdH << 8) | req.RecordIdL
    oper.log(ctx, oper.SDR_ADD, oper.SUCCESS, req_rid)

    local rsp = {}
    rsp.CompletionCode = 0x00
    rsp.RecordIdL = rid & 0xFF
    rsp.RecordIdH = (rid >> 8) & 0xFF
    return rsp
end

function sdr_management:update_lun(lun, abs)
    local inner_lun = 1 + lun
    if inner_lun > cc.SENSOR_LUN_NUM then
        log:error('current lun %d is out of range.', lun)
    end

    if not abs then
        -- 对应的 LUN 添加 SDR 标记
        self.lun_flag = self.lun_flag | (1 << lun)
        self.luns[inner_lun] = 1 + self.luns[inner_lun]
    else
        -- 对应的 LUN 核减 SDR 标记，如果已经为 0 则记录日志并且不核减
        self.lun_flag = self.lun_flag & (~(1 << lun))
        if self.luns[inner_lun] == 0 then
            log:warn('current lun [%d] is already 0', inner_lun)
        else
            self.luns[inner_lun] = self.luns[inner_lun] - 1
        end
    end
end

function sdr_management:register_fru(mode, obj)
    if self.sdr_count >= cc.SDR_MAX_NUMBER then
        log:error('current sdr count %d is reached the limit %d', self.sdr_count, cc.SDR_MAX_NUMBER)
        return
    end

    local id, name, host_id, path
    if mode == utils.FRU_SDR_BY_OBJ then
        if not obj or not obj.FruDataId or #obj.FruDataId == 0 then
            log:error('fru sdr by obj no need to process.')
            return
        end
        -- 目前multihost环境除CpuBoard外都是公共Fru，host_id取0
        id, name, host_id, path = obj.FruId, obj.FruName, string.find(obj.FruName, 'CpuBoard') and obj.extra_params.SystemId or 0, obj.path
    else
        id, name, host_id, path = obj.id, obj.name, string.find(obj.name, 'CpuBoard') and utils.format_system_id(obj.path) or 0, obj.path
    end

    if not self.sdr_groups[host_id] then
        self.sdr_groups[host_id] = sdr_grp.new(host_id)
    end

    if self.sdr_groups[host_id]:register_fru(mode, id, name, path) then
        self.sdr_count = self.sdr_count + 1
    end
end

function sdr_management:unregister_fru(id, name, path)
    local host_id = utils.format_system_id(path)
    if not self.sdr_groups[host_id] then
        log:notice('there is no sdr group of host %s', host_id)
        return
    end

    if self.sdr_groups[host_id]:unregister_fru(id, name, path) then
        self.sdr_count = self.sdr_count - 1
    end
end

function sdr_management:register(clz, obj)
    if self.sdr_count >= cc.SDR_MAX_NUMBER then
        log:error('current sdr count %d is reached the limit %d', self.sdr_count, cc.SDR_MAX_NUMBER)
        return
    end

    local host_id = obj.BelongsToSystem and obj:get_system_id() or 0
    if not self.sdr_groups[host_id] then
        self.sdr_groups[host_id] = sdr_grp.new(host_id)
    end

    if self.sdr_groups[host_id]:register(clz, obj) then
        self.sdr_count = self.sdr_count + 1

        if clz:match('Sensor') then
            self:update_lun(obj.OwnerLun)
        elseif clz == 'MCDLSDR' then -- 记录 mcdl 的 device id
            sdr_device_id = self.sdr_count
        end
    end
end

function sdr_management:unregister(obj)
    local host_id = obj.BelongsToSystem and obj:get_system_id() or 0
    if not self.sdr_groups[host_id] then
        log:notice('there is no sdr group of host %s', host_id)
        return
    end

    if self.sdr_groups[host_id]:unregister(obj) then
        self:update_lun(obj.OwnerLun)
        self.sdr_count = self.sdr_count - 1
    end
end

function sdr_management:dump_sdr_list(path)
    local f, res = require('utils.file').open_s(path, 'w')
    if not f then
        log:error('[dump sdr] open or check file failed, err: %s', res)
        return
    end
    mc_utils.chmod(path, mc_utils.S_IRUSR | mc_utils.S_IWUSR | mc_utils.S_IRGRP)
    for _, group in pairs(self.sdr_groups) do
        for i = 1, group.sdr_count do
            f:write(group:get_sdr_record(i))
            if i % utils.DUMP_SLEEP_PERIOD == 0 then skynet.sleep(utils.DUMP_SLEEP_TIME) end
        end
    end
    f:close()
end

function sdr_management:get_sdr_list()
    -- 当前接口仅为DumpSDR使用，因此查询时需要根据SDR的操作返回对应的ReservationId
    self.reserve_id = refresh_reservation_id(self.reserve_id)
    local datas, count = {}, 0
    for sid, group in pairs(self.sdr_groups) do
        count = count + group.sdr_count
        local items = {}
        for rid, item in pairs(group.repos) do
            items[#items + 1] = {RecordId = rid, Content = item.sdr_record}
        end
        datas[#datas + 1] = {SystemId = sid, SDRItems = items}
    end
    return self.reserve_id, count, datas
end

return sdr_management