-- 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 c_object = require 'mc.orm.object'
local log = require 'mc.logging'
local valve_enums = require 'valve_enums'
local _, skynet_queue = pcall(require, 'skynet.queue')
local queue = (type(skynet_queue) == 'function') and skynet_queue()

local valve_obj_manager = c_object('Valve')

function valve_obj_manager:get_opening_degree()
    return self.OpeningDegree
end

function valve_obj_manager:get_id()
    return self.Id
end

function valve_obj_manager:get_slot()
    return self.Slot
end

function valve_obj_manager:get_standard_opening_degree()
    return self.StandardOpeningDegree
end

function valve_obj_manager:get_mode()
    return self.Mode
end

function valve_obj_manager:is_present()
    return self.Presence == 1
end

function valve_obj_manager:set_opening_degree(degree, mode)
    if mode == nil or degree == nil then
        return false
    end

    -- ipmi模式下允许设置开度为0，巡检过程中不允许设置开度为0
    if mode == valve_enums.INSPECTION and degree == valve_enums.OPENING_DEGREE_MIN then
        log:error("[thermal_mgmt] set_opening_degree failed. Degree is not allowed to 0 during the inspection process")
        return false
    end

    -- 允许设置的默认开度范围[0~1000]
    if degree < valve_enums.OPENING_DEGREE_MIN or degree > valve_enums.OPENING_DEGREE_MAX then
        log:error('[thermal_mgmt] set_opening_degree failed. StandardOpeningDegree = %d, degree = %d', self.StandardOpeningDegree, degree)
        return false
    end

    -- 当开度设置为950 ~ 1000时，开度固定为950
    if degree >= valve_enums.OPENING_DEGREE_AVAILABLE and degree <= valve_enums.OPENING_DEGREE_MAX then
        log:notice("[thermal_mgmt] %s exceeds 950, set opening degree to 950", degree)
        degree = 950
    end

    self.OpeningDegree = degree
    return true
end

function valve_obj_manager:set_opening_degree_state(state)
    self.OpeningDegreeState = state
end

function valve_obj_manager:set_patrol_result(result)
    self.PatrolResult = result
end

-- 校验current_degree开度值是否在target_degree允许的误差范围
function valve_obj_manager:check_opening_degree(current_degree, target_degree, error_range)
    -- 节流阀最大只能设置到95%的开度，因为目标开度不可能大于95%
    target_degree = target_degree < 950 and target_degree or 950
    log:notice('target_degree is %s, current_degree is %s', target_degree, current_degree)

    if current_degree > valve_enums.OPENING_DEGREE_MAX then
        return false
    end

    if current_degree >= target_degree - error_range and current_degree <= target_degree + error_range then
        return true
    end

    return false
end

function valve_obj_manager:single_patrol_action(standard_opening_degree, patrol_para)
    -- 巡检标准：巡检值为标定值+13%开度，排除3%开度误差，判断标准为10%开度（最小调节单位）
    if self:check_opening_degree(self.OpeningDegree, standard_opening_degree + 130, 30) then
        -- 如果一致，则下发命令回到原来的位置，并判断是否与标定值一致，延时保证查询时阀门已经到位
        self:set_opening_degree(standard_opening_degree, valve_enums.INSPECTION)
        self:sleep_ms(20 * 1000)
        log:notice('[thermal_mgmt] back rotation stage current opening degree = %s', self.OpeningDegree)
        if self:check_opening_degree(self.OpeningDegree, standard_opening_degree, 30) then
            -- 如果回退一致，则完成该次巡检，退出流程，否则认为该组巡检失败，开始下组巡检
            patrol_para.opening_degree_state = valve_enums.Opening_Degree_State.Normal
            return true
        else
            patrol_para.opening_degree_state = valve_enums.Opening_Degree_State.Abnormal
            log:error('inspect the valve(slot-%s) failed, retry count = %s', self.Slot, patrol_para.cnt)
            patrol_para.cnt = patrol_para.cnt + 1
            return false
        end
    else
        -- 该组巡检已失败，执行回退，开始下组巡检
        self:set_opening_degree(standard_opening_degree, valve_enums.INSPECTION)
        self:sleep_ms(20 * 1000)
        patrol_para.opening_degree_state = valve_enums.Opening_Degree_State.Abnormal
        log:error('inspect the valve(slot-%s) failed, retry count = %s', self.Slot, patrol_para.cnt)
        patrol_para.cnt = patrol_para.cnt + 1
        return false
    end
end

function valve_obj_manager:do_patrol()
    log:notice('[thermal_mgmt] valve patrol:id-%d, slot-%s', self.Id, self.Slot)

    local standard_opening_degree = self:get_standard_opening_degree()

    -- 标定开度未进行出厂配置或超出范围，巡检异常告警
    if standard_opening_degree > valve_enums.OPENING_DEGREE_MAX then
        log:error('[thermal_mgmt] invalid standard opening degree(%s)', standard_opening_degree)
        -- 根据巡检异常结果记录运行日志
        log:running(log.RLOG_INFO, 'Failed to inspect the valve(slot-%s)', self.Slot)
        return valve_enums.Patrol_Result.Failed
    end

    -- 巡检流程
    local patrol_para = {
        opening_degree_state = valve_enums.Opening_Degree_State.Normal,
        cnt = 0
    }
    local RETRY_COUNT = 2

    log:notice('[thermal_mgmt] standard opening degree = %s', standard_opening_degree)
    queue(function ()
        while patrol_para.cnt < RETRY_COUNT do
            -- 设置目标开度，延时保证查询时阀门已经到位
            log:notice('[thermal_mgmt] before rotation stage current opening degree = %s', self.OpeningDegree)
            self:set_opening_degree(standard_opening_degree + 130, valve_enums.INSPECTION)
            self:sleep_ms(5 * 1000)
            log:notice('[thermal_mgmt] forward rotation stage current opening degree = %s', self.OpeningDegree)
            -- 执行单次巡检
            if self:single_patrol_action(standard_opening_degree, patrol_para) then
                break
            end
        end
    end)

    -- 更新开度状态
    self:set_opening_degree_state(patrol_para.opening_degree_state)

    -- 根据开度状态巡检结果记录运行日志
    if patrol_para.opening_degree_state == valve_enums.Opening_Degree_State.Normal then
        log:running(log.RLOG_INFO, 'Successfully inspect the valve(slot-%s)', self.Slot)
    else
        log:running(log.RLOG_INFO, 'Failed to inspect the valve(slot-%s)', self.Slot)
    end

    return valve_enums.Patrol_Result.Success
end

function valve_obj_manager:init()
    self:connect_signal(self.on_add_object, function()
    end)
    self:connect_signal(self.on_add_object_complete, function()
        log:notice('[thermal_mgmt] valve on_add_object_complete id: %d, slot: %s',
            self.Id, self.Slot)
    end)
    valve_obj_manager.super.init(self)
end

return valve_obj_manager
