-- 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 log = require 'mc.logging'
local ctx = require 'mc.context'
local bs = require 'mc.bitstring'
local skynet = require 'skynet'
local utils_vos = require 'utils.vos'
local utils = require 'power_mgmt_utils'
local enums = require 'macros.power_mgmt_enums'
local crc16 = require 'mc.crc16'
local upgrade = require 'device_tree.adapters.power_mgmt.protocol.upgrade.canbus_upgrade'
local fw_def = require 'device_tree.adapters.power_mgmt.protocol.upgrade.fw_def'
local class = require 'mc.class'

local canbus = class()

local MAX_BLACK_BOX_BLOCK_NUMBER<const> = 0x1D
local BLACK_BOX_MAX_LEN_CANBUS<const> = 2048
local CANBUS_RETRY_COUNT <const> = 3
local CAN_EXTENDED_FRAME <const> = 4
local CAN_POWER_PROTOCOL <const> = 0x3f
local BIT_M_CNT <const> = 1    -- 发送帧的主设置
local BIT_S_CNT <const> = 0    -- 发送帧的从设置
local CAN_SET_BYTE <const> = 2               -- can协议设置1byte字节的数据域位置
local CAN_OFFSET_BYTE <const> = 0
local CAN_SET_UINT16_VALUE_INDEX <const> = 1 -- can协议设置2byte字节的数据域位置
local CAN_OFFSET_UINT16 <const> = 1
local CAN_SET_UINT32_VALUE_INDEX <const> = 3 -- can协议设置4byte字节的数据域位置
local CAN_OFFSET_UINT32 <const> = 3
local CANBUS_QUERY_DATA_SIZE <const> = 8
local CAN_BATCH_QUERY_SIZE <const> = 12
local BASIC_INFO_SIZE <const> = 6
local TOTAL_BASIC_INFO_LEN <const> = 64
local ALTERNATING_CURRENT <const> = 0
local HIGH_VOLT_DIRECT_CURRENT <const> = 1
local LOW_VOLT_DIRECT_CURRENT <const> = 2
local PROPERTY_DC_MODE <const> = 0
local PROPERTY_AC_MODE <const> = 1
local PROPERTY_ACDC_MODE_UNKNOWN <const> = 255
local CANBUS_POWER_COFFICIENT <const> = 1024
local CAN_VERSION1_OFFSET <const> = 3 -- can协议版本1数据域位置，对电源是DCDC固件版本号，对电池是主版本号
local CAN_VERSION2_OFFSET <const> = 5 -- can协议版本1数据域位置，对电源是PFC固件版本号，对电池是次版本号
-- canbus电源升级
local INIT_SLIDE_WIN_LEN <const> = 0x40
local E_OK = nil -- 函数执行成功返回nil
local E_FAILED = '' -- 空错误信息
local FRAME_DATA_LEN <const> = 0x08

-- 电源寄存器地址
canbus.cmd = {
    CMD_BATCH_QUERY_PERIOD_DATA         =  0x40,    -- 批量查询需要周期获取的信息
    CMD_BATCH_QUERY                     =  0x50,    -- 批量查询命令
    CMD_SET                             =  0x81,    -- 自定义按字节配置
    CMD_QUERY                           =  0x82,    -- 自定义按字节查询
    CMD_CANBUS_READ_ELABEL              =  0xD2,    -- CANBUS模块读电子标签命令
    CMD_DOWNLOAD_START                  =  0xD3,    -- 启动在线升级
    CMD_DOWNLOAD_TRANSEFER              =  0xD4,    -- 在线加载过程中，数据帧传送
    CMD_DOWNLOAD_ACK                    =  0xD5,    -- 在线加载过程中，达到滑窗帧数时，下发一帧确认帧
    CMD_DOWNLOAD_END                    =  0xD6,    -- 在线加载结束
    CMD_PSU_GET_BLACK_BOX               =  0xD9,    -- 获取黑匣子信息
    CANBUS_CMD_PSU_SET_TIME             =  0x117,  -- psu模块设置时间
    CANBUS_CMD_INPUT_VOLT_TYPE          =  0x12F,  -- 输入电压类型
    CANBUS_CMD_PS_RATE                  =  0x19E,  -- 模块的额定功率
    CANBUS_CMD_IOUT_RATE                =  0x188,  -- 模块额定输出电流
    CANBUS_CMD_INPUT_VOLTAGE_STATUS     =  0x12A,  -- 输入电压过欠压、掉电状态
    CANBUS_CMD_TOTAL_RUNNING_TIME       =  0x10E,  -- 模块总运行时间
    CANBUS_CMD_INPUTPOWERWATTS          =  0x170,  -- 输入功率
    CANBUS_CMD_INPUTFREQUENCY           =  0x171,  -- 输入频率
    CANBUS_CMD_INPUTCURRENTAMPS         =  0x172,  -- 输入电流
    CANBUS_CMD_OUTPUTPOWERWATTS         =  0x173,  -- 输出功率
    CANBUS_CMD_VOUT                     =  0x175,  -- 输出电压
    CANBUS_CMD_VIN                      =  0x178,  -- 输入电压
    CANBUS_CMD_OUTLETTEMP               =  0x17F,  -- 出风口温度
    CANBUS_CMD_OUTCURRENTAMPS           =  0x182,  -- 输出电流
    CANBUS_CMD_ALARM_STATUS             =  0x183,  -- 模块告警信息
    CANBUS_CMD_PRE_ALARM_STATUS         =  0x184,  -- 模块预告警信息
}

-- CAN读取读取多帧信息时的错误类型
local canbus_response_error_code = {
    CAN_RESP_CODE_OK                            = 0,      -- 正常响应
    CAN_RESP_CODE_ADDR_ERROR                    = 1,      -- 节点地址无效
    CAN_RESP_CODE_CMD_ERROR                     = 2,      -- 命令无效
    CAN_RESP_CODE_ADDR_IS_BEING_IDENTIFIED      = 3,      -- 地址识别过程中
    CAN_RESP_CODE_ELABLE_IS_NOT_WRITTN          = 4,      -- 电子标签没写入
    CAN_RESP_CODE_HARDWARE_IS_ERROR             = 5,      -- 电子标签读取硬件故障
    CAN_RESP_CODE_MODULE_LOAD_INTERRUPTED       = 6,      -- 模块加载中断
    CAN_RESP_CODE_DURING_MODULE_SELF_REGULATION = 7,      -- 模块自调压过程中
    CAN_RESP_CODE_CABINET_ADDR_CONFLICT         = 8,      -- 机柜地址冲突
    CAN_RESP_CODE_ELABLE_INIT_ERROR             = 9,      -- 初始化失败
}

local can_data_info = bs.new([[<<
    cnt:1,
    reserve:6,
    ms:1,
    cmd:8,
    addr:7,
    protocol:6,
    frame_type:3,
    sigid_and_error:16/big,
    data/string
>>]])

local can_frame_info = bs.new([[<<
    cnt:1,
    reserve:6,
    ms:1,
    cmd:8,
    addr:7,
    protocol:6,
    frame_type:3,
    canbus_data/string
>>]])

local can_frame_data = bs.new([[<<
    sigid_and_error:16/big,
    data/string
>>]])

function canbus:ctor(psu_slot, protocol_name)
    self.ps_id = psu_slot['SlotNumber']
    self.psu_chip = psu_slot['PsuChip']
    self.slot_addr = psu_slot['SlotI2cAddr']
    -- 电源信息表
    self.canbusinfo = {}
    -- 电源电子标签信息表
    self.canbus_elable_info = {}
    self.utils = utils.get_instance()
    self.protocol_name = protocol_name
end

function canbus:chip_write_read(canbus_send_data)
    log:info('chip_write_read start, cmd %d, sigid_and_error %d, psid %d', canbus_send_data.cmd,
             canbus_send_data.sigid_and_error, self.ps_id)
    for i = 1, CANBUS_RETRY_COUNT do
        -- write data
        local ok, value = pcall(function()
            return self.psu_chip:WriteRead(ctx.new(), can_data_info:pack(canbus_send_data), 0)
        end)
        -- read data
        if ok and value then
            return value
        end
        log:info('chip_write_read ok %s value %s retry_count %d psid %d', ok, value, i, self.ps_id)
        skynet.sleep(10 * i)  -- 最长600ms
    end
    local err_str = table.concat({ '[canbus]commad(0x', string.format('%02x', canbus_send_data.cmd), ') failed' })
    log:info(err_str)
    return nil
end

-- 自定义根据canbus sigid查询
function canbus:can_send_get_data_info(canbus_sigid)
    local canbus_req_data = string.rep(string.char(0), 6)
    local canbus_error = 0
    local recv_data = self:chip_write_read({
        cnt = 0,
        reserve = self.slot_addr,
        ms = BIT_M_CNT,
        cmd = self.cmd.CMD_QUERY,
        addr = self.slot_addr,
        protocol = CAN_POWER_PROTOCOL,
        frame_type = CAN_EXTENDED_FRAME,
        sigid_and_error = canbus_sigid | (canbus_error << 12),
        data = canbus_req_data,
    })
    if recv_data == nil then
        log:info('get canbus sigid data failed, psid %d', self.ps_id)
        error('get canbus sigid data failed')
    end
    return recv_data
end

function canbus:get_signal_data(canbus_sigid)
    log:info('get_signal_data start, psid %d', self.ps_id)
    for _ = 1, CANBUS_RETRY_COUNT do
        local ok, recv_data = pcall(function()
            local recv_data = can_data_info:unpack(self:can_send_get_data_info(canbus_sigid))
            if recv_data == nil then
                error('get canbus signal data failed, recv_data is nil')
            end
            local canbus_error = ((recv_data.sigid_and_error) & 0xf000) >> 12
            local sigid = ((recv_data.sigid_and_error) & 0x0fff)
            log:info('get_signal_data sigid %s canbus_error %s psid %d', sigid, canbus_error, self.ps_id)
            if canbus_error == canbus_response_error_code.CAN_RESP_CODE_OK then
                return recv_data
            end
            error('get canbus signal data failed')
        end)
        if ok then
            return recv_data.data
        else
            log:info('get_signal_data failed %s recv_data %s psid %d', ok, recv_data, self.ps_id)
        end
    end
    return nil
end

-- get_rate 额定功率，属性Rate
function canbus:get_rate()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_PS_RATE
    log:info('get_rate start, psid %d', self.ps_id)
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:info('get_rate failed, psid %d', self.ps_id)
        error('get_rate failed')
    end
    local rate = self:can_data_swap_32(string.unpack('I', data:sub(CAN_SET_UINT32_VALUE_INDEX,
                                       CAN_OFFSET_UINT32 + CAN_SET_UINT32_VALUE_INDEX)))
    rate = rate / CANBUS_POWER_COFFICIENT
    log:info('get_rate successed, rate %s, psid %d', rate, self.ps_id)
    return rate
end

-- get_rated_current_amps 额定输出电流，属性RatedCurrentAmps
function canbus:get_rated_current_amps()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_IOUT_RATE
    log:info('get_rated_current_amps start, psid %d', self.ps_id)
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:info('get_rated_current_amps failed, psid %d', self.ps_id)
        error('get_rated_current_amps failed')
    end
    local iout_rate = self:can_data_swap_32(string.unpack('I', data:sub(CAN_SET_UINT32_VALUE_INDEX,
                                            CAN_OFFSET_UINT32 + CAN_SET_UINT32_VALUE_INDEX)))
    iout_rate = iout_rate // CANBUS_POWER_COFFICIENT -- 额定电流为小数，但是属性类型为整数，所以使用整除
    log:info('get_rated_current_amps successed, iout_rate %s, psid %d', iout_rate, self.ps_id)
    return iout_rate
end

-- get_input_voltage_status 输入电压过欠压、掉电状态，canbus特有，属性InputVoltageStatus，0：表示正常 1：表示过压 2：表示欠压 3：表示掉电 redfish查询获取
function canbus:get_input_voltage_status()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_INPUT_VOLTAGE_STATUS
    log:info('get_input_voltage_status start, psid %d', self.ps_id)
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:info('get_input_voltage_status failed, psid %d', self.ps_id)
        error('get_input_voltage_status failed')
    end
    local input_status_a = string.unpack('B', data:sub(CAN_SET_BYTE, CAN_SET_BYTE + CAN_OFFSET_BYTE))
    log:info('get_input_voltage_status succedssed, input_status_a %s, psid %d', input_status_a, self.ps_id)
    return input_status_a
end

-- 0x40批量查询需要周期获取的信息
function canbus:can_get_batch_query_data_info()
    local canbus_error = 0
    local canbus_sigid = 0
    local canbus_addr = self.slot_addr
    local canbus_req_data = string.rep(string.char(0), 6)
    local value = self:chip_write_read({
        cnt = 0,
        reserve = canbus_addr,
        ms = BIT_M_CNT,
        cmd = self.cmd.CMD_BATCH_QUERY_PERIOD_DATA,
        addr = canbus_addr,
        protocol = CAN_POWER_PROTOCOL,
        frame_type = CAN_EXTENDED_FRAME,
        sigid_and_error = canbus_sigid | (canbus_error << 12),
        data = canbus_req_data,
    })
    if value == nil then
        log:info('get canbus batch query data info failed, psid %d', self.ps_id)
        error('get canbus batch query data info failed')
    end
    return value
end

function canbus:get_batch_query_data()
    local ok, recv_data = pcall(function()
        local recv_data = can_frame_info:unpack(self:can_get_batch_query_data_info())
        return recv_data
    end)
    if not ok or recv_data == nil then
        log:info('get batch query data info failed, recv_data is nil, psid %d', self.ps_id)
        return nil
    end

    local query_data = {}
    local valid_frame_count = 0
    for i = 1, CAN_BATCH_QUERY_SIZE do
        local candata = recv_data.canbus_data:sub((i - 1) * CANBUS_QUERY_DATA_SIZE + 1, i * CANBUS_QUERY_DATA_SIZE)
        table.insert(
            query_data,
            can_frame_data:unpack(candata)
        )
        local canbus_error = ((query_data[i].sigid_and_error) & 0xf000) >> 12
        local sigid = ((query_data[i].sigid_and_error) & 0x0fff)
        log:info('get batch query data info sigid %s canbus_error %s, psid %d', sigid, canbus_error, self.ps_id)
        if sigid ~= 0 then
            valid_frame_count = valid_frame_count + 1
        end
    end
    if valid_frame_count ~= CAN_BATCH_QUERY_SIZE then
        log:info('get batch query data info failed, valid_frame_count is error, valid_frame_count %d, psid %d',
                 valid_frame_count, self.ps_id)
        return nil
    end
    log:info('get_batch_query_data successed, psid %d', self.ps_id)
    return query_data
end

--[[  
    根据能源提供的PSU软件通信协议，canbus 0x40 批量查询数据，返回格式如下：
    解析时需严格按照返回顺序，扩展时建议在最后追加：
    模块总运行时间
    电源模块的供电类型
    输入功率
    输入频率
    输入电流
    直流输出功率
    直流输出电压测量值
    相位号&综合输入电压
    出风口温度
    输出电流显示值
    模块当前告警/状态
    模块预告警信息
--]]

local batch_query_map = {
    [1] = 'TotalRunningHours',
    [2] = {'PowerSupplyType', function (data)
        local input_type = string.unpack('B', data:sub(CAN_SET_BYTE, CAN_SET_BYTE + CAN_OFFSET_BYTE))
        local power_supply_type = PROPERTY_ACDC_MODE_UNKNOWN
        if input_type == ALTERNATING_CURRENT then
            power_supply_type = PROPERTY_AC_MODE
        elseif input_type == HIGH_VOLT_DIRECT_CURRENT or input_type == LOW_VOLT_DIRECT_CURRENT then
            power_supply_type = PROPERTY_DC_MODE
        end
        return power_supply_type
    end},
    [3] = 'InputPowerWatts',
    [4] = 'InputFrequencyHz',
    [5] = 'InputCurrentAmps',
    [6] = 'OutputPowerWatts',
    [7] = 'OutputVoltage',
    [8] = 'InputVoltage',
    [9] = 'InnerTemperatureCelsius',
    [10] = 'OutputCurrentAmps',
    [11] = 'AlarmStatus',
    [12] = 'PreAlarmStatus'
}
function canbus:refresh_batch_query_info()
    log:info('refresh_canbus_batch_query_info start, psid %d', self.ps_id)
    local batch_query_data = self:get_batch_query_data()
    if not batch_query_data then
        log:info('refresh_canbus_batch_query_info failed, psid %d', self.ps_id)
        error('refresh_canbus_batch_query_info failed')
    end
    for index, value in pairs(batch_query_map) do
        if type(value) == 'string' then
            self.canbusinfo[value] = self:can_data_swap_32(
                string.unpack('I', batch_query_data[index].data:sub(
                        CAN_SET_UINT32_VALUE_INDEX,
                        CAN_SET_UINT32_VALUE_INDEX + CAN_OFFSET_UINT32
                    )
                )
            )
        elseif type(value) == 'table' then
            self.canbusinfo[value[1]] = value[2](batch_query_data[index].data)
        end
    end

    self.canbusinfo['InputPowerWatts'] = self.canbusinfo['InputPowerWatts'] // 1024
    self.canbusinfo['InputFrequencyHz'] = self.canbusinfo['InputFrequencyHz'] / 1024
    self.canbusinfo['InputCurrentAmps'] = self.canbusinfo['InputCurrentAmps'] / 1024
    self.canbusinfo['OutputPowerWatts'] = self.canbusinfo['OutputPowerWatts'] / 1024
    self.canbusinfo['OutputVoltage'] = self.canbusinfo['OutputVoltage'] // 1024
    self.canbusinfo['InputVoltage'] = self.canbusinfo['InputVoltage']  // 1024
    self.canbusinfo['InnerTemperatureCelsius'] = self.canbusinfo['InnerTemperatureCelsius'] / 1024
    self.canbusinfo['OutputCurrentAmps'] = self.canbusinfo['OutputCurrentAmps'] / 1024

    log:info('refresh canbus info, psid %d, TotalRunningHours %s PowerSupplyType %s InputPowerWatts %s ' ..
        'InputFrequencyHz %s InputCurrentAmps %s OutputPowerWatts %s OutputVoltage %s InputVoltage %s ' ..
        'InnerTemperatureCelsius %s OutputCurrentAmps %s AlarmStatus %s PreAlarmStatus %s',
        self.ps_id, self.canbusinfo['TotalRunningHours'], self.canbusinfo['PowerSupplyType'],
        self.canbusinfo['InputPowerWatts'], self.canbusinfo['InputFrequencyHz'], self.canbusinfo['InputCurrentAmps'],
        self.canbusinfo['OutputPowerWatts'], self.canbusinfo['OutputVoltage'], self.canbusinfo['InputVoltage'],
        self.canbusinfo['InnerTemperatureCelsius'], self.canbusinfo['OutputCurrentAmps'],
        self.canbusinfo['AlarmStatus'], self.canbusinfo['PreAlarmStatus']
    )
    return self.canbusinfo
end

-- get_total_running_hours 模块总运行时间，TotalRunningHours
function canbus:get_total_running_hours()
    log:info('get_total_running_hours %d, psid %d', self.canbusinfo['TotalRunningHours'], self.ps_id)
    if self.canbusinfo['TotalRunningHours'] == nil then
        error('get_total_running_hours failed')
    end
    return self.canbusinfo['TotalRunningHours']
end

-- get_power_supply_type 交直流模式，PowerSupplyType
-- 电源模块的供电类型  0:DC; 1:AC; 3: Unknow
function canbus:get_power_supply_type()
    local power_supply_type = PROPERTY_ACDC_MODE_UNKNOWN
    if self.canbusinfo['PowerSupplyType'] == nil then
        error('get_power_supply_type failed')
    end
    local input_type = self.canbusinfo['PowerSupplyType']
    if input_type == ALTERNATING_CURRENT then
        power_supply_type = PROPERTY_AC_MODE
    elseif input_type == HIGH_VOLT_DIRECT_CURRENT or input_type == LOW_VOLT_DIRECT_CURRENT then
        power_supply_type = PROPERTY_DC_MODE
    end
    log:info('get_power_supply_type %d, psid %d', power_supply_type, self.ps_id)
    return power_supply_type
end

-- get_input_frequency_hz 输入频率，InputFrequencyHz
function canbus:get_input_frequency_hz()
    log:info('get_input_frequency_hz %f, psid %d', self.canbusinfo['InputFrequencyHz'], self.ps_id)
    if self.canbusinfo['InputFrequencyHz'] == nil then
        error('get_input_frequency_hz failed')
    end
    return self.canbusinfo['InputFrequencyHz']
end

-- get_input_current_amps 输入电流，InputCurrentAmps
function canbus:get_input_current_amps()
    log:info('get_input_current_amps %f, psid %d', self.canbusinfo['InputCurrentAmps'], self.ps_id)
    if self.canbusinfo['InputCurrentAmps'] == nil then
        error('get_input_current_amps failed')
    end
    return self.canbusinfo['InputCurrentAmps']
end

-- get_output_current_amps 输出电流，OutputCurrentAmps
function canbus:get_output_current_amps()
    log:info('get_output_current_amps %f, psid %d', self.canbusinfo['OutputCurrentAmps'], self.ps_id)
    if self.canbusinfo['OutputCurrentAmps'] == nil then
        error('get_output_current_amps failed')
    end
    return self.canbusinfo['OutputCurrentAmps']
end

-- get_input_voltage 输入电压，InputVoltage
function canbus:get_input_voltage()
    log:info('get_input_voltage %f, psid %d', self.canbusinfo['InputVoltage'], self.ps_id)
    if self.canbusinfo['InputVoltage'] == nil then
        error('get_input_voltage failed')
    end
    return self.canbusinfo['InputVoltage']
end

-- get_output_voltage 输出电压，OutputVoltage
function canbus:get_output_voltage()
    log:info('get_output_voltage %f, psid %d', self.canbusinfo['OutputVoltage'], self.ps_id)
    if self.canbusinfo['OutputVoltage'] == nil then
        error('get_output_voltage failed')
    end
    return self.canbusinfo['OutputVoltage']
end

-- get_input_power_watts 输入功率，InputPowerWatts
function canbus:get_input_power_watts()
    log:info('get_input_power_watts %f, psid %d', self.canbusinfo['InputPowerWatts'], self.ps_id)
    if self.canbusinfo['InputPowerWatts'] == nil then
        error('get_input_power_watts failed')
    end
    return self.canbusinfo['InputPowerWatts']
end

-- get_output_power_watts 输出功率，OutputPowerWatts
function canbus:get_output_power_watts()
    log:info('get_output_power_watts %f, psid %d', self.canbusinfo['OutputPowerWatts'], self.ps_id)
    if self.canbusinfo['OutputPowerWatts'] == nil then
        error('get_output_power_watts failed')
    end
    return self.canbusinfo['OutputPowerWatts']
end

-- get_inner_temperature_celsius 出风口温度，InnerTemperatureCelsius
function canbus:get_inner_temperature_celsius()
    log:info('get_inner_temperature_celsius %f, psid %d', self.canbusinfo['InnerTemperatureCelsius'], self.ps_id)
    if self.canbusinfo['InnerTemperatureCelsius'] == nil then
        error('get_inner_temperature_celsius failed')
    end
    return self.canbusinfo['InnerTemperatureCelsius']
end

-- get_alarm_status 模块告警信息，AlarmStatus，切换主备电路（无）和升级前需要检查
function canbus:get_alarm_status()
    log:info('get_alarm_status %d, psid %d', self.canbusinfo['AlarmStatus'], self.ps_id)
    if self.canbusinfo['AlarmStatus'] == nil then
        error('get_alarm_status failed')
    end
    return self.canbusinfo['AlarmStatus']
end

-- get_pre_alarm_status 模块预告警信息，PreAlarmStatus
function canbus:get_pre_alarm_status()
    log:info('get_pre_alarm_status %d, psid %d', self.canbusinfo['PreAlarmStatus'], self.ps_id)
    if self.canbusinfo['PreAlarmStatus'] == nil then
        error('get_pre_alarm_status failed')
    end
    return self.canbusinfo['PreAlarmStatus']
end

-- 0x50批量查询命令
function canbus:can_get_canbus_basic_info()
    local canbus_error = 0
    local canbus_sigid = 0
    local canbus_req_data = string.rep(string.char(0), 6)
    local value = self:chip_write_read({
        cnt = 0,
        reserve = self.slot_addr,
        ms = BIT_M_CNT,
        cmd = self.cmd.CMD_BATCH_QUERY,
        addr = self.slot_addr,
        protocol = CAN_POWER_PROTOCOL,
        frame_type = CAN_EXTENDED_FRAME,
        sigid_and_error = canbus_sigid | (canbus_error << 12),
        data = canbus_req_data,
    })
    if value == nil then
        log:info('can_get_canbus_basic_info failed, value is nil, psid %d', self.ps_id)
        error('can_get_canbus_basic_info failed, value is nil')
    end
    return value
end

function canbus:get_canbus_basic_info()
    local ok, recv_data = pcall(function()
        local recv_data = can_frame_info:unpack(self:can_get_canbus_basic_info())
        return recv_data
    end)
    if ok then
        return recv_data
    end
    self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
                    'get_canbus_basic_info failed, ps_id: %s, error info: %s', self.ps_id, recv_data)
    return nil
end

function canbus:refresh_basic_info()
    local ver1 = 0
    local ver2 = 0
    local cur_presence = 0
    log:info('refresh_basic_info start, psid %d', self.ps_id)
    local recv_data = self:get_canbus_basic_info()
    if recv_data == nil then
        log:info('refresh_basic_info failed, recv_data is nil, psid %d', self.ps_id)
        error('refresh_basic_info failed, recv_data is nil')
    else
        cur_presence, ver1, ver2 = self:parse_canbus_basic_info(recv_data)
    end

    if cur_presence == 1 then
        self:refresh_canbus_device_version_systime(ver1, ver2)
    end
    log:info('refresh_basic_info successed, psid %s', self.ps_id)
    return self.canbusinfo['FirmwareVersion']
end

function canbus:parse_canbus_basic_info(recv_data)
    local presence = 0
    local ver1 = 0
    local ver2 = 0
    local query_data = {}
    if recv_data.canbus_data == nil then
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
            'parse_canbus_basic_info basic info data is nil, psid %s', self.ps_id)
        return presence, ver1, ver2
    end
    if #(recv_data.canbus_data) < TOTAL_BASIC_INFO_LEN then
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
            'parse_canbus_basic_info basic info len(%s) is wrong, psid %s', #(recv_data.canbus_data), self.ps_id)
        return presence, ver1, ver2
    end
    for i = 1, BASIC_INFO_SIZE do
        -- canbus数据段为8字节
        local candata = recv_data.canbus_data:sub((i - 1) * CANBUS_QUERY_DATA_SIZE + 1, i * CANBUS_QUERY_DATA_SIZE)
        table.insert(
            query_data,
            can_frame_data:unpack(candata)
        )
    end
    if not query_data[1] or not query_data[5] then
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
            'parse_canbus_basic_info basic info query_data is nil, psid %s', self.ps_id)
        return presence, ver1, ver2
    end
    local canbus_error = ((query_data[1].sigid_and_error) & 0xf000) >> 12
    local sigid = ((query_data[1].sigid_and_error) & 0x0fff)
    log:info('parse_canbus_basic_info sigid %s canbus_error %s psid %d', sigid, canbus_error, self.ps_id)
    -- 只有响应OK或者处于加载中才认为在位，其余情况均不在位
    if canbus_error ~= canbus_response_error_code.CAN_RESP_CODE_OK and
       canbus_error ~= canbus_response_error_code.CAN_RESP_CODE_MODULE_LOAD_INTERRUPTED then
        presence = 0
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
            'parse_canbus_basic_info canbus_error: %s, psid %s', canbus_error, self.ps_id)
        return presence, ver1, ver2
    end
    presence = 1
    -- 未处于升级时，需要截取第五帧的数据获取版本信息
    local version1 = query_data[5].data:sub(CAN_VERSION1_OFFSET, CAN_VERSION1_OFFSET + CAN_OFFSET_UINT16)
    ver1 = self:can_data_swap_16(string.unpack('H', version1))
    local version2 = query_data[5].data:sub(CAN_VERSION2_OFFSET, CAN_VERSION2_OFFSET + CAN_OFFSET_UINT16)
    ver2 = self:can_data_swap_16(string.unpack('H', version2))
    log:info('parse_canbus_basic_info successed, ver1 %02x ver2 %02x psid %d', ver1, ver2, self.ps_id)
    return presence, ver1, ver2
end

function canbus:can_data_swap_16(data)
    local data1 = (data & 0xff00) >> 8
    local data2 = (data & 0x00ff) << 8
    return (data2 | data1)
end

function canbus:can_data_swap_32(data)
    local data1 = (data & 0xff000000) >> 24
    local data2 = (data & 0x00ff0000) >> 8
    local data3 = (data & 0x0000ff00) << 8
    local data4 = (data & 0x000000ff) << 24
    return (data1 | data2 | data3 | data4)
end

function canbus:refresh_canbus_device_version_systime(ver1, ver2)
    local sys_time_second = utils_vos.vos_get_cur_time_stamp()
    local send_sys_time_second = 0
    local canbus_sigid = self.cmd.CANBUS_CMD_PSU_SET_TIME

    if sys_time_second >= 0xffffffff then
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
            'sys_time_second is error, psid %d', self.ps_id)
        return
    end
    send_sys_time_second = self:can_data_swap_32(sys_time_second)
    local canbus_req_data = table.concat { string.char(0), string.char(0), string.pack('I', send_sys_time_second)}
    local ok, recv_data = pcall(self.set_signal_data, self, canbus_sigid, canbus_req_data)
    if not ok or recv_data == nil then
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60,
            'set_signal_data failed, psid: %d, error info: %s', self.ps_id, recv_data)
    end

    local version_format = string.format('DC:%02x PFC:%02x', ver1, ver2)
    self.canbusinfo['FirmwareVersion'] = version_format
    log:info('refresh_canbus_device_version_systime successed, psid %d, FirmwareVersion %s', self.ps_id,
             self.canbusinfo['FirmwareVersion'])
end

-- 自定义按canbus sigid配置
function canbus:power_send_set_data_info(canbus_sigid, canbus_req_data)
    local canbus_error = 0
    local recv_data = self:chip_write_read({
        cnt = 0,
        reserve = self.slot_addr,
        ms = BIT_M_CNT,
        cmd = self.cmd.CMD_SET,
        addr = self.slot_addr,
        protocol = CAN_POWER_PROTOCOL,
        frame_type = CAN_EXTENDED_FRAME,
        sigid_and_error = canbus_sigid | (canbus_error << 12),
        data = canbus_req_data,
    })
    if recv_data == nil then
        log:info('power_send_set_data_info, recv_data is nil, psid %d', self.ps_id)
        error('power_send_set_data_info, recv_data is nil')
    end
    return recv_data
end

function canbus:set_signal_data(canbus_sigid, canbus_req_data)
    for _ = 1, CANBUS_RETRY_COUNT do
        local ok, recv_data = pcall(function()
            local recv_data =
                  can_data_info:unpack(self:power_send_set_data_info(canbus_sigid, canbus_req_data))
            if recv_data == nil then
                error('set canbus signal data failed, recv_data is nil')
            end
            local canbus_error = ((recv_data.sigid_and_error) & 0xf000) >> 12
            local sigid = ((recv_data.sigid_and_error) & 0x0fff)
            log:info('set_signal_data recv_data sigid %s canbus_error %s, psid %d', sigid, canbus_error, self.ps_id)
            if canbus_error ~= canbus_response_error_code.CAN_RESP_CODE_OK then
                error('canbus set signal data failed, canbus_error')
            end
            if canbus_req_data ~= recv_data.data then
                error('canbus set signal data failed, canbus_req_data is not same as recv_data.data')
            end
            return recv_data
        end)
        if ok then
            return recv_data.data
        end
        log:info('canbus set signal data failed, ok %s recv_data %s, psid %d', ok, recv_data, self.ps_id)
        skynet.sleep(2)  -- 延时20ms
    end
    return nil
end

-- get_firmware_version 电源版本，FirmwareVersion
function canbus:get_firmware_version()
    log:info('get_firmware_version %s, psid %d', self.canbusinfo['FirmwareVersion'], self.ps_id)
    if not self.canbusinfo['FirmwareVersion'] then
        self.utils:frequency_limit_log(enums.LOG_LEVEL.ERROR, 60, 'get_firmware_version failed, psid %s', self.ps_id)
        error('get firmware version failed')
    end
    return self.canbusinfo['FirmwareVersion']
end

--[[
    canbus电子标签 
    ELABEL_ATTR_BOARD_TYPE = 1,
    ELABEL_ATTR_BAR_CODE = 2,
    ELABEL_ATTR_ITEM = 3,
    ELABEL_ATTR_DESCRIPTION = 4,
    ELABEL_ATTR_MANUFACTURED = 5,
    ELABEL_ATTR_VENDOR_NAME = 6,
    ELABEL_ATTR_ISSUE_NUMBER = 7,
    ELABEL_ATTR_CLEI_CODE = 8,
    ELABEL_ATTR_BOMCODE = 9,
    ELABEL_ATTR_MODULE = 10,
]]--

local label_attr_nameList = {
    "BoardType",
    "BarCode",
    "Item",
    "Description",
    "Manufactured",
    "VendorName",
    "IssueNumber",
    "CLEICode",
    "BOM",
    "Model",
}

local canbus_label_attr_num = {
    ELABEL_ATTR_BAR_CODE = 2,
    ELABEL_ATTR_ITEM = 3,
    ELABEL_ATTR_MANUFACTURED = 5,
    ELABEL_ATTR_VENDOR_NAME = 6,
    ELABEL_ATTR_MODULE = 10,
}

local canbus_label_attr_name = {
    ELABEL_ATTR_BAR_CODE = 'SerialNumber',
    ELABEL_ATTR_ITEM = 'PartNumber',
    ELABEL_ATTR_MANUFACTURED = 'ProductionDate',
    ELABEL_ATTR_VENDOR_NAME = 'Manufacturer',
    ELABEL_ATTR_MODULE = 'Model',
}

-- 获取指定类型电子标签
function canbus:update_elabel_info()
    local recv_buff = self:read_elabel_data_by_canbus()
    if not recv_buff then
        for k, v in pairs(canbus_label_attr_num) do
            self.canbus_elable_info[canbus_label_attr_name[k]] = ''
        end
        log:info('update_elabel_info failed, psid %d', self.ps_id)
        error('update_elabel_info failed')
    end
    for k, v in pairs(canbus_label_attr_num) do
        local elabel_value = self:parse_elabel_info_by_type(recv_buff, v)
        if elabel_value ~= nil then
            self.canbus_elable_info[canbus_label_attr_name[k]] = elabel_value
            log:info('update_elabel_info successed, attr_name %s attr_value %s, psid %d',
                     canbus_label_attr_name[k], elabel_value, self.ps_id)
        end
    end
    return self.canbus_elable_info
end

function canbus:get_fru_data()
    local frudata = self:update_elabel_info()
    return {
        ManufacturerName = frudata.Manufacturer,
        ProductName = frudata.Model,
        ProductDate = frudata.ProductionDate,
        ProductSerialNumber = frudata.SerialNumber,
        ProductPartNumber = frudata.PartNumber,
    }
end

function canbus:parse_elabel_info_by_type(recv_buff, elable_type)
    local start = 1
    local separate_char = '\r\n'
    local equal_char = '='
    local elable_value_substr = nil

    -- 若没有找到分隔字符"\r\n",直接退出
    if not string.find(recv_buff, separate_char, start, true) then
        log:info('elabel info dose not find separate char, psid %d', self.ps_id)
        return nil
    end

    while true do
        local pos = string.find(recv_buff, separate_char, start, true)
        if not pos then
            break
        end
        local substr = string.sub(recv_buff, start, pos - 1)
        if not string.find(substr, label_attr_nameList[elable_type], 1, true) then
            start = pos + string.len(separate_char)
            goto continue
        end
        -- 若没有找到"=",直接退出
        local subpos = string.find(substr, equal_char, 1, true)
        if not subpos then
            log:info('elabel info not find =')
            return nil
        end
        -- 去掉=号
        subpos = subpos + 1
        -- value值为'\0'
        if substr[subpos] == '\0' then
            log:info('elabel info is nil')
            return nil
        else
            elable_value_substr = string.sub(substr, subpos, #substr)
            start = pos + string.len(separate_char)
            log:info('parse_elabel_info_by_type attr_name %s elable_value_substr %s',
                     label_attr_nameList[elable_type], elable_value_substr)
            break
        end
        ::continue::
    end
    return elable_value_substr
end

function canbus:read_elabel_data_by_canbus()
    local num = 1
    local out_data = ''
    local canbus_addr = self.slot_addr

    local ok, recv_data = pcall(function()
        local recv_data = can_frame_info:unpack(self:send_read_elabel_data(canbus_addr))
        return recv_data
    end)
    if not ok or not recv_data then
        log:info('read_elabel_data_by_canbus failed, ok %s recv_data %s, psid %d', ok, recv_data, self.ps_id)
        return nil
    end
    local len = #(recv_data.canbus_data) / CANBUS_QUERY_DATA_SIZE
    if len == 0 then
        return nil
    end
    for i = 1, len do
        -- canbus数据段为8字节
        local data = recv_data.canbus_data:sub((i - 1) * CANBUS_QUERY_DATA_SIZE + 1, i * CANBUS_QUERY_DATA_SIZE)
        local elabel_frame_data = can_frame_data:unpack(data)
        if elabel_frame_data == nil then
            log:info('read elabel data failed, elabel_frame_data is nil')
            return nil
        end
        local canbus_error = ((elabel_frame_data.sigid_and_error) & 0xf000) >> 12
        local sigid = ((elabel_frame_data.sigid_and_error) & 0x0fff)
        log:info('read elabel data recv_data sigid %s canbus_error %s, psid %d', sigid, canbus_error, self.ps_id)
        -- 验证帧的完整性
        if sigid ~= num then
            log:info('read elabel data failed, sigid is wrong, sigid %d, num %d, psid %d', sigid, num, self.ps_id)
            return nil
        end
        num = num + 1
        if canbus_error ~= canbus_response_error_code.CAN_RESP_CODE_OK then
            log:info('read elabel data failed, canbus_error %d, psid %d', canbus_error, self.ps_id)
            return nil
        end
        out_data = out_data .. elabel_frame_data.data
    end
    return out_data
end

function canbus:send_read_elabel_data(canbus_addr)
    local canbus_error = 0
    local canbus_sigid = 0
    local canbus_req_data = string.rep(string.char(0), 6)
    local value = self:chip_write_read({
        cnt = 0,
        reserve = canbus_addr,
        ms = BIT_M_CNT,
        cmd = self.cmd.CMD_CANBUS_READ_ELABEL,
        addr = canbus_addr,
        protocol = CAN_POWER_PROTOCOL,
        frame_type = CAN_EXTENDED_FRAME,
        sigid_and_error = canbus_sigid | (canbus_error << 12),
        data = canbus_req_data,
    })
    if value == nil then
        log:info('send_read_elabel_data, value is nil, psid %d', self.ps_id)
        error('read elabel data failed')
    end
    return value
end

-- get_sn SN条码 --> 序列号，SerialNumber
function canbus:get_serial_number()
    log:info('get_serial_number SerialNumber %s, psid %d', self.canbus_elable_info['SerialNumber'], self.ps_id)
    if self.canbus_elable_info['SerialNumber'] == nil then
        error('read elabel data SerialNumber failed')
    end
    return self.canbus_elable_info['SerialNumber']
end

-- get_manufacturer 厂家名称 --> 制造厂商，Manufacturer
function canbus:get_manufacturer()
    log:info('get_serial_number Manufacturer %s, psid %d', self.canbus_elable_info['Manufacturer'], self.ps_id)
    if self.canbus_elable_info['Manufacturer'] == nil then
        error('read elabel data Manufacturer failed')
    end
    return self.canbus_elable_info['Manufacturer']
end

-- get_model 对外型号 --> 电源型号，Model
function canbus:get_model()
    log:info('get_serial_number Model %s, psid %d', self.canbus_elable_info['Model'], self.ps_id)
    if self.canbus_elable_info['Model'] == nil then
        error('read elabel data Model failed')
    end
    return self.canbus_elable_info['Model']
end

-- get_part_number BOM编码 --> 电源部件号，PartNumber
function canbus:get_part_number()
    log:info('get_serial_number PartNumber %s, psid %d', self.canbus_elable_info['PartNumber'], self.ps_id)
    if self.canbus_elable_info['PartNumber'] == nil then
        error('read elabel data PartNumber failed')
    end
    return self.canbus_elable_info['PartNumber']
end

-- get_production_date 生产日期，ProductionDate
function canbus:get_production_date()
    log:info('get_serial_number ProductionDate %s, psid %d', self.canbus_elable_info['ProductionDate'], self.ps_id)
    if self.canbus_elable_info['ProductionDate'] == nil then
        error('read elabel data ProductionDate failed')
    end
    return self.canbus_elable_info['ProductionDate']
end

-- canbus 黑匣子接口
local MAX_BLACK_BOX_FRAME_NUMBER_CANBUS <const> = 0x0B

function canbus:get_black_box_data(block_id)
    local canbus_error = 0
    local canbus_sigid = 0
    local canbus_req_data = string.char(0) .. string.char(block_id) .. string.rep(string.char(0), 4)
    local value = self:chip_write_read({
        cnt = 0,
        reserve = self.slot_addr,
        ms = BIT_M_CNT,
        cmd = self.cmd.CMD_PSU_GET_BLACK_BOX,
        addr = self.slot_addr,
        protocol = CAN_POWER_PROTOCOL,
        frame_type = CAN_EXTENDED_FRAME,
        sigid_and_error = canbus_sigid | (canbus_error << 12),
        data = canbus_req_data,
    })
    if value == nil then
        log:error('canbus_get_black_box_data failed, value is nil, psid %d, block_id %d', self.ps_id, block_id)
        error('canbus_get_black_box_data failed, value is nil')
    end
    local block_data = self:handle_black_box_block_data(can_frame_info:unpack(value))
    if block_data == nil then
        log:error('canbus_get_black_box_data handle data failed, psid %d, block_id %d', self.ps_id, block_id)
        error('canbus_get_black_box_data handle data failed')
    end
    return block_data
end

function canbus:handle_black_box_block_data(recv_data)
    local out_data_table = {}
    local num = 1
    local data
    local frame_data
    local canbus_error
    local sigid
    for i = 1, MAX_BLACK_BOX_FRAME_NUMBER_CANBUS do
        -- canbus数据段为8字节
        data = recv_data.canbus_data:sub((i - 1) * CANBUS_QUERY_DATA_SIZE + 1, i * CANBUS_QUERY_DATA_SIZE)
        frame_data = can_frame_data:unpack(data)
        if frame_data == nil then
            log:error('handle_black_box_block_data failed, frame_data is nil')
            return nil
        end
        canbus_error = ((frame_data.sigid_and_error) & 0xf000) >> 12
        sigid = ((frame_data.sigid_and_error) & 0x0fff)
        -- 验证帧的完整性
        if sigid ~= num then
            log:error('handle_black_box_block_data failed, sigid is wrong, sigid %d, num %d, psid %d', sigid, num, self.ps_id)
            return nil
        end
        num = num + 1
        if canbus_error ~= canbus_response_error_code.CAN_RESP_CODE_OK then
            log:error('handle_black_box_block_data failed, canbus_error %d, psid %d', canbus_error, self.ps_id)
            return nil
        end
        table.insert(out_data_table, frame_data.data)
    end
    return table.concat(out_data_table)
end

-- canbus电源升级接口
local can_upgrade_start_frame_info = bs.new([[<<
    cnt:1,
    reserve:6,
    ms:1,
    cmd:8,
    addr:7,
    protocol:6,
    frame_type:3,
    total_frame:32/big,
    crc:16/big,
    slide_window_size:16/big
>>]])

local can_upgrade_end_frame_info = bs.new([[<<
    cnt:1,
    reserve1:6,
    ms:1,
    cmd:8,
    addr:7,
    protocol:6,
    frame_type:3,
    total_frame:32/big,
    crc:16/big,
    reserve2:16/big
>>]])

-- canbus升级写读接口
function canbus:canbus_upgrade_chip_write_read(canbus_send_data)
    log:notice('canbus_upgrade_chip_write_read start, psid %d', self.ps_id)
    for i = 1, CANBUS_RETRY_COUNT do
        -- write data
        local ok, value = pcall(function()
            return self.psu_chip:WriteRead(ctx.new(), canbus_send_data, 0)
        end)
        -- read data
        if ok and value then
            return value
        end
        log:notice('canbus_upgrade_chip_write_read failed, ok %s value %s retry_count %d psid %d', ok, value, i, self.ps_id)
        skynet.sleep(15 * i)  -- 最长900ms
    end
    return nil
end

-- canbus电源升级，启动在线加载
function canbus:send_download_start_frame(total_crc, total_frame_num)
    log:info('send_download_start_frame start, psid %d', self.ps_id)
    local recv_data
    skynet.sleep(2)  -- 延时20ms
    for i = 1, 10 do  -- 重试10次
        recv_data = self:canbus_upgrade_chip_write_read(can_upgrade_start_frame_info:pack{
            cnt = 0,
            reserve = self.slot_addr,
            ms = BIT_M_CNT,
            cmd = self.cmd.CMD_DOWNLOAD_START,
            addr = self.slot_addr,
            protocol = CAN_POWER_PROTOCOL,
            frame_type = CAN_EXTENDED_FRAME,
            total_frame = total_frame_num,
            crc = total_crc,
            slide_window_size = INIT_SLIDE_WIN_LEN,
        })
        if recv_data == nil then
            log:notice('send_download_start_frame nil, psid %d', self.ps_id)
            skynet.sleep(2)
            goto continue
        end
        local frame_data = can_upgrade_start_frame_info:unpack(recv_data)
        if frame_data.crc ~= total_crc then
            log:notice('send_download_start_frame crc check failed, psid %d', self.ps_id)
            recv_data = nil
            skynet.sleep(2)
            goto continue
        end
        if frame_data.slide_window_size == 0 then
            log:notice('send_download_start_frame window size is 0, psid %d', self.ps_id)
            recv_data = nil
            skynet.sleep(2)
            goto continue
        end
        break
        ::continue::
    end
    return recv_data
end

-- canbus电源升级，在线加载结束
function canbus:send_download_end_frame(total_crc, total_frame_num)
    log:info('send_download_end_frame start, psid %d', self.ps_id)
    local recv_data
    skynet.sleep(2)  -- 延时20ms
    for i = 1, 10 do    -- 重试10次
        recv_data = self:canbus_upgrade_chip_write_read(can_upgrade_end_frame_info:pack{
            cnt = 0,
            reserve1 = self.slot_addr,
            ms = BIT_M_CNT,
            cmd = self.cmd.CMD_DOWNLOAD_END,
            addr = self.slot_addr,
            protocol = CAN_POWER_PROTOCOL,
            frame_type = CAN_EXTENDED_FRAME,
            total_frame = total_frame_num,
            crc = total_crc,
            reserve2 = 0,
        })
        if recv_data == nil then
            skynet.sleep(2)  -- 间隔20ms
            goto continue
        end
        local frame_data = can_upgrade_end_frame_info:unpack(recv_data)
        if frame_data.crc ~= total_crc then
            log:notice('send_download_end_frame crc check failed, psid %d', self.ps_id)
            recv_data = nil
            skynet.sleep(2)
            goto continue
        end
        break
        ::continue::
    end
    return recv_data
end

function canbus:load_canbus_soft_bin_data_to_chip(data, len)
    local ret = self:can_load_firmware(data, len)
    if ret ~= E_OK then
        return E_FAILED
    end
    return ret
end

-- canbus电源升级，在线加载过程中，达到滑窗帧数时，下发一帧确认帧
function canbus:send_download_ack_frame(window_crc, sended_frame)
    log:info('send_download_ack_frame start')
    local recv_data
    for i = 1, 2 do  -- CRC校验失败的重试2次即可
        recv_data = self:canbus_upgrade_chip_write_read(can_upgrade_end_frame_info:pack{
            cnt = 0,
            reserve1 = self.slot_addr,
            ms = BIT_M_CNT,
            cmd = self.cmd.CMD_DOWNLOAD_ACK,
            addr = self.slot_addr,
            protocol = CAN_POWER_PROTOCOL,
            frame_type = CAN_EXTENDED_FRAME,
            total_frame = sended_frame,
            crc = window_crc,
            reserve2 = 0,
        })
        if recv_data == nil then
            skynet.sleep(2)  -- 间隔20ms
            goto continue
        end
        local frame_data = can_upgrade_end_frame_info:unpack(recv_data)
        if frame_data.crc ~= window_crc then
            log:notice('send_download_ack_frame crc check failed, %d %d', frame_data.crc, window_crc)
            recv_data = nil
            skynet.sleep(2)  -- 间隔20ms
            goto continue
        end
        break
        ::continue::
    end
    return recv_data
end

-- canbus电源升级，电源固件滑窗发送
function canbus:can_load_firmware_by_window(block_id, block_num, can_window_size, data, len, sended_frame)
    log:info('can_load_firmware_by_window start')
    local frame_num  -- block窗口有多少frame帧
    local real_window_size -- block窗口有多少字节数据
    if block_id == (block_num - 1) then -- 需要判断最后一个block有多少frame帧和字节数据
        -- frame个数需要向上取整，但是实际电源固件bin文件数据都是8byte的倍数
        frame_num = (len - block_id * can_window_size + FRAME_DATA_LEN - 1) // FRAME_DATA_LEN
        real_window_size = len - block_id * can_window_size
    else
        frame_num = INIT_SLIDE_WIN_LEN
        real_window_size = can_window_size
    end
    local subdata = data:sub(0 , block_id * can_window_size + real_window_size)
    local window_crc = self:calc_crc16_table(subdata)
    local block_data = data:sub(block_id * can_window_size + 1, block_id * can_window_size + real_window_size)
    self:canbus_load_firmware_by_frame_plugin(block_id, frame_num, block_data, len)
    sended_frame = sended_frame + frame_num
    skynet.sleep(2)  -- 延时20ms
    local frame_data
    for retry = 1, 10 do  -- 电源回复ack报文较慢，特别第一次发送ack报文以后，电源会重启需要8秒左右，重试10次
        frame_data = self:send_download_ack_frame(window_crc, sended_frame)
        if frame_data == nil then
            log:notice('send_download_ack_frame failed, retry %d', retry)
            skynet.sleep(100) -- 间隔1s
            goto continue
        end
        break
        ::continue::
    end
    if frame_data == nil then
        log:notice('send_download_ack_frame failed nil')
        return E_FAILED
    end
    return E_OK
end

function canbus:canbus_load_firmware_by_frame_plugin(block_id, frame_num, block_data, len)
    local context = ctx.new()
    -- 设置超时时间20s
    context.Timeout = 20
    self.psu_chip:PluginRequest(context, 'power_mgmt', 'canbus_load_firmware_by_frame',
        skynet.packstring(block_id, frame_num, block_data, len, self.slot_addr))
end

-- canbus电源升级，canbus块写功能，写之后返回模块反馈的数据
function canbus:can_load_firmware(data, len)
    log:info('can_load_firmware start')
    local can_window_size = FRAME_DATA_LEN * INIT_SLIDE_WIN_LEN;  -- 每个block窗口的总字节数
    local block_num = (len + can_window_size - 1) // can_window_size;  -- block个数需要向上取整
    local ret
    local sended_frame = 0
    for block_id = 0, block_num - 1 do
        for retry = 1, 30 do  -- 每个block窗口发送重试30次
            ret = self:can_load_firmware_by_window(block_id, block_num, can_window_size, data, len, sended_frame)
            if ret == E_OK then
                log:notice('load_firmware_by_window block_id %d successed', block_id)
                sended_frame = sended_frame + INIT_SLIDE_WIN_LEN
                skynet.sleep(2)  -- 20ms延时
                break
            else
                log:notice('load_firmware_by_window block_id %d failed, retry %d', block_id, retry)
                sended_frame = block_id * INIT_SLIDE_WIN_LEN
            end
            skynet.sleep(2)  -- 20ms延时
        end
        if ret ~= E_OK then
            log:notice('load_firmware_by_window block_id %d failed', block_id)
            break
        end
    end
    return ret
end

function canbus:get_event_log()
    local black_box_data_table = {}
    local ok, data = pcall(function ()
        for i = 0, MAX_BLACK_BOX_BLOCK_NUMBER - 1 do
            table.insert(black_box_data_table, self:get_black_box_data(i))
            skynet.sleep(100)
        end
    end)
    if not ok then
        log:error('[pmbus]ps%d get event log failed, msg: %s', self.ps_id, data)
    end
    return BLACK_BOX_MAX_LEN_CANBUS, table.concat(black_box_data_table)
end

local CRC16Lookup = {
    0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad,
    0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a,
    0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
    0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
    0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861,
    0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
    0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87,
    0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
    0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
    0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3,
    0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290,
    0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
    0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e,
    0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f,
    0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
    0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
    0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83,
    0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
    0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
  }

-- 计算crc码，init初始值
function canbus:calc_crc16_table(data)
    local crcvalue = 0
    for i = 1, #data do
        -- crc16校验算法，沿用V2
        local tmp = string.byte(data, i, i)
        local a = (((crcvalue << 8) & 0xFFFF) | tmp)
        local b = CRC16Lookup[((crcvalue >> 8) & 0xFF) + 1]
        crcvalue = a ~ b
    end
    return tonumber(crcvalue)
end

function canbus:upgrade(upgrade_path, upgrade_process)
    local obj = upgrade(self).new(upgrade_process)
    local upgrade_ret = obj:process(upgrade_path)

    if upgrade_ret ~= fw_def.SUPPLY_0 then
        log:error('upgrade ps%u(protocol_name: %s) failed', self.ps_id, self.protocol_name)
    else
        log:notice('upgrade ps%u successfully', self.ps_id)
    end
    return upgrade_ret
end

return canbus

