-- Copyright (c) 2025 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 ncsi_standard = require 'libmgmt_protocol.protocol.ncsi_standard'
local class = require 'mc.class'
local bs = require 'mc.bitstring'

-- 此文件提供一个demo，如何基于ncsi标准协议自定义不同oem的协议解析方法
local ncsi_demo = class(ncsi_standard)

-- ncsi oem packet type 0x50(0xD0)
local ncsi_oem_request_packet_type<const> = 0x50
local ncsi_oem_response_packet_type<const> = 0xD0
local ncsi_demo_manu_id<const> = 0x6666

-----------------------------------
--         response_header
--| byte1 | byte2 | byte3 | byte4 |
--|    rsp_code   |  reason_code  |
--|            manu_id            |
--|  cmd1 | cmd2  |  cmd3 | cmd4  |
--|              data             |  
-----------------------------------

-- 此bitstring用于解析ncsi报文返回结构，格式参考上图
-- data不做解析，data由各配置文件里自定义的解析函数来解析
local response_bs<const> = bs.new([[<<
    rsp_code:16/big,
    reason:16/big,
    manu_id:32/big,
    cmd1:8,
    cmd2:8,
    cmd3:8,
    cmd4:8,
    data/string
>>]])

-----------------------------------
--         request_header
--| byte1 | byte2 | byte3 | byte4 |
--|            manu_id            |
--|  cmd1 | cmd2  |  cmd3 | cmd4  |
-----------------------------------

-- 此bitstring用于封装ncsi报文请求结构，格式参考上图
-- 这里不含 data,因为data可以直接拼接即可。
local request_bs<const> = bs.new([[<<
    manu_id:32/big,
    cmd1:8,
    cmd2:8,
    cmd3:8,
    cmd4:8
>>]])

-- 此结构体用于检查请求体配置参数是否完备
local request_params_template<const> = {
    channel_id = true,  -- 必选
    package_id = true,  -- 必选
    cmd1 = true,        -- 根据oem协议确定此字段是否可为空
    cmd2 = true,        -- 根据oem协议确定此字段是否可为空
    cmd3 = true,        -- 根据oem协议确定此字段是否可为空
    cmd4 = true,        -- 根据oem协议确定此字段是否可为空
    data = true         -- 根据oem协议确定此字段是否可为空
}

-- 重载请求协议体封装函数
function ncsi_demo:construct_request_data(ctx, request)
    
    ----------------------
    -- ctx用于收到响应后进行header校验，如果不需要可以跳过
    ctx.manufacture_id = ncsi_demo_manu_id
    ctx.cmd1 = request.cmd1
    ctx.cmd2 = request.cmd2
    ctx.cmd3 = request.cmd3
    ctx.cmd4 = request.cmd4
    ----------------------

    -- 拼装请求请求
    local data = request_bs:pack({
        manu_id = ncsi_demo_manu_id,
        cmd1 = request.cmd1,
        cmd2 = request.cmd2,
        cmd3 = request.cmd3,
        cmd4 = request.cmd4
    })

    if request.data then
        data = data .. request.data
    end

    -- 调用ncsi_standard父函数进行最终封装
    return ncsi_demo.super.construct_request_data(self, ctx, {
        packet_type = ncsi_oem_request_packet_type,
        expect_rsp_packet_type = ncsi_oem_response_packet_type,
        channel_id = request.channel_id,
        package_id = request.package_id,
        data = data
    })
end

-- 重载响应协议体解析函数
function ncsi_demo:unpack_response_data(ctx, rsp_bin)
    -- 此处rsp_bin是返回的原始二进制，因此首先先解析header
    -- 注意：此处调用unpack第二参数一定要是true，因为不是完全解析数据体
    local rsp = response_bs:unpack(rsp_bin, true)

    if not rsp then
        -- 解析不出来，说明有问题，返回nil中止协议解析
        return nil
    end
    if rsp.rsp_code ~= 0 then
        -- 协议错误code非0，说明有问题，返回nil中止协议解析
        return nil
    end

    ----------------------
    -- ctx用于收到响应后进行header校验，如果请求没搞就需要跳过
    if rsp.manu_id and rsp.manu_id ~= ctx.manufacture_id then
        -- 协议header不匹配，说明有问题，返回nil中止协议解析
        return nil
    end
    ----------------------

    -- header解析完毕，返回二进制裸数据，由配置文件中的解析函数进行解析
    return rsp.data
end

function ncsi_demo:ctor()
    -- 协议名，用于日志打印
    self.name = 'ncsi_demo'
    -- 请求输入格式检查，覆盖父函数的模板，不需要检查的跳过
    self.request_params_template = request_params_template
end

return ncsi_demo

