-- Copyright (c) 2026 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 class = require 'mc.class'
local log = require 'mc.logging'
local cmn = require 'common'

---@class LogicSelfTest: Unit @逻辑器件自检
local c_logic_self_test = class()

function c_logic_self_test:ctor(mds_obj, position)
    self.mds_obj = mds_obj -- model.json:LogicSelfTest
    self.logic_failcount = 0
    self.write_length = 0
    self.stop_flag = false
end

function c_logic_self_test:update_stop_flag(value)
    self.stop_flag = value
end

function c_logic_self_test.update_failcount(self, value)
    if not value then
        self.logic_failcount = 0
    else
        self.logic_failcount = (self.logic_failcount >= self.mds_obj.DebounceCount)
            and self.mds_obj.DebounceCount or (self.logic_failcount + 1)
    end
end

function c_logic_self_test.update_logic_health_status(self)
    local ok = false
    if self.logic_failcount >= self.mds_obj.DebounceCount then
        -- 对应位设置为1,用于告警
        ok = pcall(function()
            if self.mds_obj.RefChip.HealthStatus ~= 1 then
                self.mds_obj.RefChip.HealthStatus = 1
                log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, '%s logic_self_test alarm on', self.mds_obj.name)
            end
        end)
    else
        ok = pcall(function()
            if self.mds_obj.RefChip.HealthStatus ~= 0 then
                self.mds_obj.RefChip.HealthStatus = 0
                log:maintenance(log.MLOG_INFO, log.FC__PUBLIC_OK, '%s logic_self_test alarm clear', self.mds_obj.name)
            end
        end)
    end
    if not ok then
        log:error('set %s HealthStatus to %s fail', self.mds_obj.name, self.mds_obj.RefChip.HealthStatus)
    end
    log:debug('set %s health_status = %s  failcount:%s debounce_cnt:%s', 
        self.mds_obj.name, self.mds_obj.RefChip.HealthStatus, self.logic_failcount, self.mds_obj.DebounceCount)
end

function c_logic_self_test:logic_self_test_process(period)
    local ok, check_value = true, 0
    -- 遍历 WriteVal 中配置的值进行写入和读取校验
    for i = 1, self.write_length do
        if self.stop_flag then
            return
        end

        ok = pcall(function() -- write 环节
            self.mds_obj.WriteReg = self.mds_obj.WriteVal[i]
        end)
        if not ok then
            log:error('set WriteReg object fail, error: %s', self.mds_obj.name)
            self.update_failcount(self, true)
            goto continue_label
        end
        cmn.skynet.sleep(50) -- 延时500ms防止写入没有生效

        ok, check_value = pcall(function() -- read 环节
            return self.mds_obj.ReadReg
        end)
        if not ok or not check_value then
            log:error('get ReadReg object fail, error: %s', self.mds_obj.name)
            self.update_failcount(self, true)
            goto continue_label
        end
        log:debug('%s i:%s failcnt:%s debounce:%s write:%s read:%s expect:%s ',
            self.mds_obj.name, i, self.logic_failcount, self.mds_obj.DebounceCount,
            self.mds_obj.WriteVal[i], check_value, self.mds_obj.ReadVal[i])

        if check_value == self.mds_obj.ReadVal[i] then -- check 环节
            self.update_failcount(self, false)
        else
            self.update_failcount(self, true)
            log:error('%s failed once i:%s failcnt:%s debounce:%s write:%s read:%s expect:%s ',
                self.mds_obj.name, i, self.logic_failcount, self.mds_obj.DebounceCount,
                self.mds_obj.WriteVal[i], check_value, self.mds_obj.ReadVal[i])
        end
        ::continue_label::
        self.update_logic_health_status(self)
        cmn.skynet.sleep(period)
    end
end

function c_logic_self_test:logic_self_test_check_length()
    local read_length = #self.mds_obj.ReadVal
    self.write_length = #self.mds_obj.WriteVal

    if self.write_length ~= read_length then
        log:error('write and read length not match fail, error: %s %s %s',
            self.mds_obj.name, self.write_length, read_length)
        return false
    end
    log:debug('init success:%s write and read length %s', self.mds_obj.name, read_length)
    return true
end

-- 串行逻辑器件自检
function c_logic_self_test:start_logic_self_test_status_task()
    log:notice('start logic self test %s', self.mds_obj.name)
    cmn.skynet.sleep(18000) -- 3min

    local ok = self:logic_self_test_check_length()
    if not ok then
        return
    end

    local period = self.mds_obj.PeriodMs // 10 -- 每轮检测间隔 单位是10ms
    while not self.stop_flag do
        self:logic_self_test_process(period)
    end
    log:notice('end logic self test %s', self.mds_obj.name)
end

return c_logic_self_test