-- 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: DO NOT EDIT; Code generated by "message.lua.mako"
local class = require 'mc.class'
local log = require 'mc.logging'
local skynet = require 'skynet'
local mdb = require 'mc.mdb'
local c_tasks = require 'mc.orm.tasks'
local client = require 'power_mgmt.client'
local ctx = require 'mc.context'
local ipmi_req = require 'power_mgmt.ipmi.ipmi'
local msg = require 'power_mgmt.ipmi.ipmi_message'
local ipmi = require 'ipmi'
local comp_code = ipmi.types.Cc
local c_psu_object = require 'device.psu'

local manufacture_app = class()
local UART_PATH<const> = '/bmc/kepler/Managers/1/Uart'
local UART_LOG_PATH = '/bmc/kepler/Managers/1/UartCircularLog'

local STATUS_COMPLETE = "Complete"
local STATUS_TESTING = "Testing"
local STATUS_UNSTART = "Unstart"

local RESULT_SUCCEED = "Succeed"
local RESULT_FAILED = "Failed"
local RESULT_NON = "Non"

local CANBUS_CHIP_PATH <const> = '/bmc/kepler/Chip/CanbusChip'
local BLOCK_INTERFACE <const> = "bmc.kepler.Chip.BlockIO"
local CAN_EXTENDED_FRAME <const> = 4
local CAN_POWER_PROTOCOL <const> = 0x3f

local session_type<const> = {NONE = -1, IPMI = 0, CLI = 1}
local UART_BAUD = 115200
local sol_dest_coms = {}
local UART_PORT = {
    sol_enable = 1,
    session_type = session_type,
    sol_type = session_type.NONE,
    os_uart_index = 5,
    sol_uart_index = 5,
    max_recv_once = 255,
    uart_baud = UART_BAUD,
    sol_dest_coms = sol_dest_coms,
    sol_encrypt = 1
}

local uart = require 'libsoc_adapter.uart'

local function read_data(drv)
    -- 阻塞读一秒后返回
    local data = drv:select_read(UART_PORT.os_uart_index, UART_PORT.max_recv_once)
    if #data > 0 then
        data = data .. read_data(drv)
    end
    return data
end

function manufacture_app:send_data_can_chan(data)
    local get_canbus_chips = function ()
        local ok, resp = pcall(mdb.get_sub_objects, self.app.bus, CANBUS_CHIP_PATH, BLOCK_INTERFACE)
        if not ok then
            log:debug('res: %s', resp)
        end
        return ok and resp
    end
    local send_data = function (chips)
        chips:fold(function (obj)
            local ok, ret = pcall(function ()
                return obj:WriteRead(ctx.new(), data, 0)
            end)
            log:debug('ok: %s, ret: %s', ok, ret)
        end)
    end
    local chips
    while self.can_check_flag do
        if not chips then
            chips = get_canbus_chips()
        end
        if chips then
            send_data(chips)
        end
        skynet.sleep(2)
    end
end

function manufacture_app.new(app)
    return setmetatable({app = app}, manufacture_app)
end

function manufacture_app:init()
    self:register_rpc_methods()
    self:registe_object()
    self.check_result = RESULT_NON
end

function manufacture_app:register_rpc_methods()
    self.app:ImplDftPowerSupplyManufactureStart(function (obj, ...)
        return self:DftPowerSupplyStart(obj)
    end)
    self.app:ImplDftPowerSupplyManufactureStop(function (obj, ...)
        return self:DftPowerSupplyStop(obj)
    end)
    self.app:ImplDftPowerSupplyManufactureGetResult(function (obj, ...)
        return self:DftPowerSupplyGetResult(obj)
    end)

    self.app:ImplDftCanChannelManufactureStart(function (obj, ...)
        return self:DftCanChannelStart(obj)
    end)
    self.app:ImplDftCanChannelManufactureStop(function (obj, ...)
        return self:DftCanChannelStop(obj)
    end)
    self.app:ImplDftCanChannelManufactureGetResult(function (obj, ...)
        return self:DftCanChannelGetResult(obj)
    end)
end

function manufacture_app:registe_object()
    self.uart_mdb = nil
    skynet.fork(function ()
        while not self.uart_mdb do
            self.uart_mdb = client:GetUartObjects()[UART_PATH]
            skynet.sleep(50) -- 每50*10ms检查一次soctrl组件的串口管理对象是否存在
        end
    end)
end

function manufacture_app:DftCanChannelStart(obj)
    if obj.Status == STATUS_TESTING then
        return
    end
    obj.Status = STATUS_TESTING
    self.can_check_flag = true
    skynet.fork(function ()
        while self.can_check_flag do
            skynet.sleep(50)
            if not self.uart_mdb then
                goto continue
            end
            self.uart_mdb:UartConnectPort(ctx.new(), 5, 2)
            local drv = uart.new()
            local ok, res = pcall(drv.open_port, drv, UART_PORT.os_uart_index, UART_BAUD)
            if not ok then
                log:error('open err: %s', res)
                goto continue
            end
            ok, res = pcall(read_data, drv)
            if not ok then
                log:error('read uart data failed: %s', res)
            end
            log:debug('read data: %s, data_len: %d', res, string.len(res))
            if #res > 0 then
                self.check_result = RESULT_SUCCEED
                self.can_check_flag = false
                break
            end
            ok, res = pcall(drv.close, drv)
            if not ok then
                log:error('close err: %s', res)
            end
            ::continue::
        end
        obj.Status = STATUS_COMPLETE
    end)
    skynet.fork(function ()
        self:send_data_can_chan('12345678')
    end)
end

function manufacture_app:DftCanChannelStop(obj)
    obj.Status = STATUS_UNSTART
    self.can_check_flag = false
end

function manufacture_app:DftCanChannelGetResult(obj)
    return self.check_result, ""
end

function manufacture_app:DftPowerSupplyStart(obj)
    obj.Status = STATUS_TESTING
end

function manufacture_app:DftPowerSupplyStop(obj)
    obj.Status = STATUS_UNSTART
end

function manufacture_app:DftPowerSupplyGetResult(obj)
    local status
    local description = ""
    obj.Status = STATUS_COMPLETE
    local psu_objs = c_psu_object.collection:fetch({})
    if not psu_objs or not next(psu_objs) then
        status = RESULT_FAILED
        description = "PS DFT FAIL"
        return status, description
    end

    table.sort(
        psu_objs,
        function (obj1, obj2)
            return obj1.ps_id < obj2.ps_id
        end
    )
    for id, psu in pairs(psu_objs) do
        local sn = psu.SerialNumber
        local physical_interface = psu.PhysicalInterface
        local device_locator = psu.DeviceLocator
        -- 支持SMC协议的电源砖无法获取到SN码，返回false，跳过此项检查
        if not sn or #sn == 0 and
            (physical_interface ~= 'smc' or not string.match(device_locator, 'PowerConverter')) then
                status = RESULT_FAILED
                description = "PS DFT FAIL"
                return status, description
        end
        local vin = psu.InputVoltage
        if not vin or vin == 0 then
            status = RESULT_FAILED
            description = "PS DFT FAIL"
            return status, description
        end
        description = description ..
            string.format("SLOT:%u-VIN:%0.2f", id, vin)
    end
    status = RESULT_SUCCEED
    return status, description
end

return manufacture_app