-- 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 file_sec = require 'utils.file'
local canbus_base = require 'device_tree.adapters.power_mgmt.protocol.canbus'
local canbus = class(canbus_base)

local E_OK = nil -- 函数执行成功返回nil
local E_FAILED = '' -- 空错误信息
local NO_OPERATION<const> = 0
local UNLOCK<const> = 1
local NO_LATCH<const> = 0
local LATCH<const> = 1
local VOLTAGE_UPPER_LIMIT<const> = 54.5
local VOLTAGE_LOWER_LIMIT<const> = 54.5
local CANBUS_CAN0_CHANNEL <const>  = 0
local CANBUS_CAN1_CHANNEL <const> = 1
local CANBUS_RETRY_COUNT <const> = 3
local SUPPLY_CIRCUIT_A <const>  = 0
local SUPPLY_CIRCUIT_B <const>  = 1
local SUPPLY_CIRCUIT_A_CMD <const>  = 0x55
local SUPPLY_CIRCUIT_B_CMD <const>  = 0xAA
local DEBOUNCE_MILLISECONDS_MIN <const> = 1
local DEBOUNCE_MILLISECONDS_MAX <const> = 1000
local DEPTH_OF_DISCHARGE_MIN <const> = 60
local RATE<const> = 6600
local OUTPUT_LIMIT_WATTS_MAX <const> = 1
local OUTPUT_LIMIT_WATTS_MIN <const> = 0.2
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_data = bs.new([[<<
    sigid_and_error:16/big,
    data/string
>>]])

local SLEEP_MODE_VALUE = {
    [0x55] = 'DeepSleep',
    [0xAA] = 'Normal'
}

local RESET_TYPE = {
    ['ForceOff'] = 0x01,
    ['On'] = 0x00
}

canbus.tpsu_cmd = {
    CANBUS_CMD_CAPACITOR_HARDWARE_VER                    = 0x512, -- 电容硬件版本号
    CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_ALARM_THRESHOLD   = 0x513, -- Turbo模组电容电压告警门限LEVEL1
    CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_RELEASE_THRESHOLD = 0x514, -- Turbo模组电容电压放开输入限流门限LEVEL2
    CANBUS_CMD_TURBO_CAPACITOR_CTRL                      = 0x51C, -- TURBO开关控制
    CANBUS_CMD_CAPACITY_MICROFARADS                      = 0x51B, -- Turbo模组电容容量
    CANBUS_CMD_RELAY_SWITCHED_COUNT                      = 0x504,  -- 双输入继电器切换次数上报
    CANBUS_CMD_CAPACITOR_CALIBRATE                       = 0x51E,   -- Turbo电容充放电校准 0x00:不充放电 0x01执行一次电容充放电校准
    CANBUS_CMD_CAPACITOR_CALIBRATE_TIME                  = 0x51F,   -- Turbo电容充放电校准命令下发时间
    CANBUS_CMD_OUTPUT_LIMIT_POINT                        = 0x105,   -- 输出限功率点设定值
    CANBUS_CMD_LIFE_PREDICTION_LEVEL                     = 0x515,   -- Turbo电容寿命预测档位
    CANBUS_CMD_LIFE_PREDICTION_COUNT                     = 0x516,    -- Turbo电容寿命预测次数
    CANBUS_CMD_CIRCUIT_A_INPUT_VOLTAGE_TYPE              = 0x12D,    -- A路输入电压状态
    CANBUS_CMD_CIRCUIT_B_INPUT_VOLTAGE_TYPE              = 0x12E,    -- B路输入电压状态
    CANBUS_CMD_CIRCUIT_A_INPUT_VOLTAGE                   = 0x19C,    -- A路输入电压
    CANBUS_CMD_CIRCUIT_B_INPUT_VOLTAGE                   = 0x19D     -- B路输入电压
}

function canbus:ctor(psu_slot, protocol_name)
    if not psu_slot['PsuChip'][enums.PSU_CHIP_OBJECT.BLOCKIO] or
       not psu_slot['PsuBackupChip'][enums.PSU_CHIP_OBJECT.BLOCKIO] then
        log:error('canbus:ctor PsuChip error') --主备路都需要配置
        return
    end
    --cabbus的协议信息
    canbus.protocol = {
        CANBUS_FRAME_DATA_SIZE = 8, --canbus协议一帧内容8byte
        CANBUS_BASIC_INFO_SIZE = 10,  --0x50的消息长度
        CANBUS_QUERY_DATA_SIZE = 17, --0X40的消息长度
        CANBUS_BLACK_BOX_FRAME_SIZE = 0x15, --黑匣子一帧的长度
        CANBUS_BLACK_START_ADDR = 2, --黑匣子内容起始地址，从黑匣子读出来内容后有效内容的起始地址
        CANBUS_BLACK_BOX_MAX_LEN = 3600, --支持的黑匣子最大长度，20（帧）*6（byte)*30(序列号)=3600
        CANBUS_MAX_BLACK_BOX_BLOCK_NUMBER = 0x1D,
        CANBUS_BLACK_BOX_DELAY = 20, --每个序列号黑匣子读取延时间隔，按照电源建议TPSU延时200MS
    }

    self.canbus_chip = {
        current_channel = (self.ps_id % 2 == 0) and CANBUS_CAN1_CHANNEL or CANBUS_CAN0_CHANNEL,--奇数槽是使用can0，偶数槽使用can1
        main_channle_fail_nums = 0, --主通道访问失败次数
        upgrade_channel_switch_flag = 0, --非升级槽位在升级槽位升级期间是否发送通道切换
        can0_chip = psu_slot['PsuChip'][enums.PSU_CHIP_OBJECT.BLOCKIO], --can0设备句柄存储
        can1_chip = psu_slot['PsuBackupChip'][enums.PSU_CHIP_OBJECT.BLOCKIO], --can1设备句柄存储
        upgrading_channle_fail_nums = 0, --正在升级通道的失败次数。
        upgrading_change_channel_flag = 0, --指示升级过程中是否发生过通道切换
    }
    self.canbus_chip.main_channel = self.canbus_chip.current_channel --初始化阶段和current_channel一致
    self.canbus_chip.psu_chip = (self.canbus_chip.main_channel == CANBUS_CAN1_CHANNEL) and
                                    self.canbus_chip.can1_chip or self.canbus_chip.can0_chip --psu_chip存储当前使用的can设备句柄
    self.canbus_chip.upgrade_channle = (self.canbus_chip.main_channel == CANBUS_CAN1_CHANNEL) and
                                CANBUS_CAN0_CHANNEL or CANBUS_CAN1_CHANNEL --默认的升级通道和主通道相反
    self.canbus_chip.upgrade_chip = (self.canbus_chip.main_channel == CANBUS_CAN1_CHANNEL) and
                                                self.canbus_chip.can0_chip or self.canbus_chip.can1_chip
    log:notice('psid: %s main_channel %s', self.ps_id, self.canbus_chip.main_channel)
end

function canbus:canbus_ms_channel_switch(flag)
    if flag == 0 then --flag定义同 QUERY_INFO_OK
        self.canbus_chip.main_channle_fail_nums = 0 --本次轮询访问成功，失败次数清0
        return
    end
    self.canbus_chip.main_channle_fail_nums = self.canbus_chip.main_channle_fail_nums + 1
    self.utils:frequency_limit_log(enums.LOG_LEVEL.NOTICE, 60,
            'psid %s canbus_ms_channel_switch %s', self.ps_id, self.canbus_chip.main_channle_fail_nums)
    if self.canbus_chip.main_channle_fail_nums <= 12 then
        return --失败次数小于13次，不处理,TPSU10秒钟会自愈一次，BMC这里处理大于10秒就行
    end
    if self.canbus_chip.current_channel ~= self.canbus_chip.main_channel then
        return --通道已经切换到备用通道不处理
    end
    self.canbus_chip.current_channel = (self.canbus_chip.main_channel == CANBUS_CAN0_CHANNEL) and
                                                            CANBUS_CAN1_CHANNEL or CANBUS_CAN0_CHANNEL
    log:notice('psid: %s change channel to %s', self.ps_id, self.canbus_chip.current_channel)
    return
end

function canbus:canbus_check_ms_cahnnel(channel)
    local canbus_send_data = {
        cnt = 0,
        reserve = self.slot_addr,
        ms = 1,
        cmd = self.cmd.CMD_QUERY,
        addr = self.slot_addr,
        protocol = 0x3f,
        frame_type = 4,
        sigid_and_error = 0x5 | (0 << 12), --固件查询0x5版本号
        data = string.rep(string.char(0), 6),
    }
    for i = 1, CANBUS_RETRY_COUNT do
        -- write data
        local ok, value = pcall(function()
            if channel == CANBUS_CAN0_CHANNEL then
                return self.canbus_chip.can0_chip:WriteRead(ctx.new(), can_data_info:pack(canbus_send_data), 0)
            end
            return self.canbus_chip.can1_chip:WriteRead(ctx.new(), can_data_info:pack(canbus_send_data), 0)
        end)
        -- read data
        if ok and value then
            return E_OK
        end
        skynet.sleep(10 * i)  -- 最长600ms
    end
    return E_FAILED
end

function canbus:canbus_recover_to_main_channel()
    if self.upgrade_flag == 1 then
        return --有板子升级固件期间，该任务停止。避免该板子住通道通信回复，切换到该通道使用升级通道
    end
    if self.canbus_chip.current_channel == self.canbus_chip.main_channel then
        return
    end
    if self:canbus_check_ms_cahnnel(self.canbus_chip.main_channel) == E_OK then
        self.canbus_chip.current_channel = self.canbus_chip.main_channel
        self.canbus_chip.main_channle_fail_nums = 0
        log:notice('psid: %s recover to main channel %s', self.ps_id, self.canbus_chip.current_channel)
    end
    return
end

--升级板子获取升级通道
function canbus:get_upgrade_channel()
    local upgrade_channel = self.canbus_chip.current_channel
    self.canbus_chip.upgrading_channle_fail_nums = 0 --正在升级通道的失败次数清空
    self.canbus_chip.upgrading_change_channel_flag = 0
    if self.canbus_chip.current_channel == self.canbus_chip.main_channel then
        local slave_channel = (self.canbus_chip.main_channel == CANBUS_CAN0_CHANNEL) and
                                                    CANBUS_CAN1_CHANNEL or CANBUS_CAN0_CHANNEL
        if self:canbus_check_ms_cahnnel(slave_channel) == E_OK then
            upgrade_channel = slave_channel --备用通道可用，则使用备用通道作为升级通道
        end
    end
    log:notice('psid: %s cur_channel:%s upgrade_channel:%s',
                self.ps_id, self.canbus_chip.current_channel, upgrade_channel)
    self.canbus_chip.upgrade_channle = upgrade_channel
    self.canbus_chip.upgrade_chip = (upgrade_channel == CANBUS_CAN1_CHANNEL) and
                        self.canbus_chip.can1_chip or self.canbus_chip.can0_chip
    return upgrade_channel
end

--非升级板子根据当前升级板子切换轮询通道到非升级通道
function canbus:change_polling_channel(upgrade_channel)
    self.canbus_chip.upgrade_channel_switch_flag = 0
    self.upgrade_flag = 1 --非升级板子置上有板子正在升级标记
    if self.canbus_chip.current_channel ~= upgrade_channel then
       return --当前使用通道和升级通道不一致，无须切换
    end
    if self.canbus_chip.current_channel ~= self.canbus_chip.main_channel then
        return --当前通道为备用通道，说明主用通道已经损坏，无须切换
    end
    local slave_channel = (self.canbus_chip.current_channel == CANBUS_CAN0_CHANNEL) and
                                                CANBUS_CAN1_CHANNEL or CANBUS_CAN0_CHANNEL
    if self:canbus_check_ms_cahnnel(slave_channel) == E_OK then
        log:notice('psid: %s cur_channel:%s change polling channel:%s',
                    self.ps_id, self.canbus_chip.current_channel, slave_channel)
        self.canbus_chip.current_channel = slave_channel --备用通道可用，则使用备用通道作为轮询
        self.canbus_chip.upgrade_channel_switch_flag = 1
        self.canbus_chip.main_channle_fail_nums = 0
    end
    return
end

--非升级板子在升级后恢复原来轮询通道
function canbus:recover_polling_channel()
    self.upgrade_flag = 0 --非升级板子取消有板子正在升级标记
    if self.canbus_chip.upgrade_channel_switch_flag == 0 then
        return
    end
    self.canbus_chip.current_channel = (self.canbus_chip.current_channel == CANBUS_CAN0_CHANNEL) and
                                                                CANBUS_CAN1_CHANNEL or CANBUS_CAN0_CHANNEL
    log:notice('psid: %s recover_polling_channel %s', self.ps_id, self.canbus_chip.current_channel)
    return
end

function canbus:chip_write_read(canbus_send_data)
    log:info('chip_write_read start, cmd %d, sigid_and_error %d, psid: %s', canbus_send_data.cmd,
             canbus_send_data.sigid_and_error, self.ps_id)
    self.canbus_chip.psu_chip = (self.canbus_chip.current_channel == CANBUS_CAN1_CHANNEL) and
                                    self.canbus_chip.can1_chip or self.canbus_chip.can0_chip
    if self.canbus_chip.psu_chip == nil then
        return nil
    end
    for i = 1, CANBUS_RETRY_COUNT do
        -- write data
        local ok, value = pcall(function()
            return self.canbus_chip.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: %s', 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

function canbus:canbus_set_firmware_version(query_data)
    --canbus协议电源DC的版本号位于0x5命令字的byte4~byte5,byte4是第5个字节，前面解析时候已经将前面两个特征值去掉，所以起始地址是3
    local version = query_data[5].data:sub(3, 4)
    local ver_dc = self:can_data_swap_16(string.unpack('H', version))
    --canbus协议电源PFC的版本号位于0x5命令字的byte6~byte7
    version = query_data[5].data:sub(5, 6)
    local ver_pfc = self:can_data_swap_16(string.unpack('H', version))
    --canbus协议电源turbo模组的版本号位于0x220命令字的byte4~byte5
    version = query_data[9].data:sub(3, 4)
    local ver_turbo_model = self:can_data_swap_16(string.unpack('H', version))
    --canbus协议电源can的版本号位于0x220命令字的byte6~byte7
    version = query_data[9].data:sub(5, 6)
    local ver_can = self:can_data_swap_16(string.unpack('H', version))
    --canbus协议电源turbo采样芯片的版本号位于0x51d命令字的byte2~byte3
    version = query_data[10].data:sub(1, 2)
    local ver_turbo_chip = self:can_data_swap_16(string.unpack('H', version))
    local version_format = string.format('DC:%02x PFC:%02x TURBO_MODEL:%02x CAN:%02x TURBO_CHIP:%02x',
                                                ver_dc, ver_pfc, ver_turbo_model, ver_can, ver_turbo_chip)
    self.canbusinfo['FirmwareVersion'] = version_format
    log:info('refresh_canbus_device_version_systime successed, psid: %s, FirmwareVersion %s',
                                                                self.ps_id, self.canbusinfo['FirmwareVersion'])
end

function canbus:canbus_get_property_info()
    --[[  
        根据能源提供的PSU软件通信协议，canbus 0x40 批量查询数据，返回格式如下：
        解析时需严格按照返回顺序，扩展时建议在最后追加：
        模块总运行时间
        电源模块的供电类型
        输入功率
        输入频率
        输入电流
        直流输出功率
        直流输出电压测量值
        输出实际限流点
        相位号&综合输入电压
        出风口温度
        电源进风口环境温度
        输出电流显示值
        模块当前告警/状态
        A路输入电压
        B路输入电压
        Turbo电容电压(Moni)
        模块预告警信息
    --]]
    local property_tpsu_info = {
        [0x010E] = {'TotalRunningHours',       'int',       1},
        [0x012F] = {'PowerSupplyType',         'char',    255},
        [0x0170] = {'InputPowerWatts',         'int',    1024},
        [0x0171] = {'InputFrequencyHz',        'double', 1024},
        [0x0172] = {'InputCurrentAmps',        'double', 1024},
        [0x0173] = {'OutputPowerWatts',        'int',    1024},
        [0x0175] = {'OutputVoltage',           'int',    1024},
        [0x0176] = {'OutputThrottling',        'double', 1024},
        [0x0178] = {'InputVoltage',            'int',    1024},
        [0x017F] = {'InnerTemperatureCelsius','double',  1024},
        [0x0180] = {'InletTemperatureCelsius', 'double', 1024},
        [0x0182] = {'OutputCurrentAmps',       'double', 1024},
        [0x0183] = {'AlarmStatus',             'int',       1},
        [0x019c] = {'AInputVoltage',           'int',    1024},
        [0x019d] = {'BInputVoltage',           'int',    1024},
        [0x0510] = {'TruboMoni',               'double', 1024},
        [0x0520] = {'PreAlarmStatus',          'int',       1}
    }
    return property_tpsu_info
end

-- 写psu风扇转速，单位r/min
function canbus:set_psu_fan_speed_rpm(speed_rpm)
    local speed = self:can_data_swap_16(speed_rpm)
    local canbus_req_data = string.pack('H', speed) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_PSU_SET_FAN_SPEED, canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set_psu_fan_speed_rpm failed, psid: %s, error info: %s', self.ps_id, recv_data)
        error('set psu fan speed rpm failed')
    end
end

function canbus:get_psu_fan_speed_rpm()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_PSU_SET_FAN_SPEED
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('get_psu_fan_speed_rpm, psid: %s', self.ps_id)
        return 0
    end
    return self:can_data_swap_16(string.unpack('H', data:sub(1, 2)))
end

function canbus:get_output_power_limit_watts()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_OUTPUT_LIMIT_POINT)
    if not data then
        log:error('get output limit point watts  value failed, psid %s', self.ps_id)
        error('get output limit point watts value failed')
    end
    local output_limit_point_watts = string.unpack('>I4', data:sub(3, #data))
    if not output_limit_point_watts then
        log:error('parsing output limit point value failed, psid %s', self.ps_id)
        error('get output limit point watts value failed')
    end
    local result = math.floor(output_limit_point_watts / 1024 * RATE)
    return result
end

function canbus:set_output_limit_point_watts(output_power_limit_watts)
    local limit_point = output_power_limit_watts / RATE
    if output_power_limit_watts < OUTPUT_LIMIT_WATTS_MIN or 
        math.floor(limit_point) > OUTPUT_LIMIT_WATTS_MAX then
        log:error('OutputPowerLimitWatts is out of range')
        error('OutputPowerLimitWatts is out of range')
    end
    local canbus_req_data = string.rep(string.char(0), 2) .. string.pack('>I4', limit_point * 1024)
    local ok, recv_data = pcall(self.set_signal_data, self,
        canbus.tpsu_cmd.CANBUS_CMD_OUTPUT_LIMIT_POINT, canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set_output_limit_point_watts failed, psid: %s, error info: %s', self.ps_id, recv_data)
        error('set output limit watts failed')
    end
end

function canbus:set_psu_lockout_protection(lock_status)
    if lock_status ~= UNLOCK and lock_status ~= NO_OPERATION then
        log:error('Invalid lock status: %s', lock_status)
        error('Invalid lock status')
    end
    local canbus_req_data = string.char(0) .. string.char(lock_status) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_SET_LOCKOUT_PROTECTION,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set psu lockout protection(%s) failed, psid: %s, error info: %s', lock_status, self.ps_id, recv_data)
        error('set psu lockout protection failed')
    end
end

local POWER_DEEP_SLEEP_MODE <const> = 'DeepSleep'
local POWER_NORMAL_MODE <const> = 'Normal'
-- set_sleep_mode 设置单个电源睡眠模式
function canbus:set_sleep_mode(sleep_mode)
    local control
    if sleep_mode == POWER_DEEP_SLEEP_MODE then
        log:notice('Power%s Mode will change to DeepSleep', self.ps_id)
        control = 0x55
    elseif sleep_mode == POWER_NORMAL_MODE then
        log:notice('Power%s will change to Normal', self.ps_id)
        control = 0xaa
    end
    local canbus_req_data = string.char(0) .. string.char(control) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_SET_SLEEP_MODE, canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set_sleep_mode failed, psid: %s, error info: %s', self.ps_id, recv_data)
        error('set sleep mode failed')
    end
end

function canbus:get_sleep_mode()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_SET_SLEEP_MODE
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('canbus get sleep mode failed, psid %s', self.ps_id)
        self.canbusinfo['SleepMode'] = ''
    else
        self.canbusinfo['SleepMode'] = SLEEP_MODE_VALUE[string.byte(data, 2)] or ''
    end
    return self.canbusinfo['SleepMode']
end

function canbus:send_heart_beat()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_HEART_BEAT
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('canbus send heart beat failed, psid %s', self.ps_id)
    end
end

function canbus:get_hardware_version()
    local canbus_sigid = canbus.tpsu_cmd.CANBUS_CMD_CAPACITOR_HARDWARE_VER
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('canbus get capacitor hardware version failed, psid %s', self.ps_id)
        self.canbusinfo['HardwareVersion'] = ''
    else
        self.canbusinfo['HardwareVersion'] = tostring(self:can_data_swap_16(string.unpack('H', data:sub(1, 2))))
    end
    return self.canbusinfo['HardwareVersion']
end

function canbus:get_capacity_microfarads()
    local canbus_sigid = canbus.tpsu_cmd.CANBUS_CMD_CAPACITY_MICROFARADS
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('canbus get capacitor failed, psid %s', self.ps_id)
        self.canbusinfo['CapacityMicrofarads'] = 0
        return 0
    end
    local value = string.unpack('>I4', data:sub(3, #data))
    if not value then
        log:error('parsing capacitor failed, psid %s', self.ps_id)
        self.canbusinfo['CapacityMicrofarads'] = 0
        return 0
    end
    self.canbusinfo['CapacityMicrofarads'] = math.floor(value / 1024 + 0.5)
    return self.canbusinfo['CapacityMicrofarads']
end

function canbus:get_relay_switched_count()
    local canbus_sigid = canbus.tpsu_cmd.CANBUS_CMD_RELAY_SWITCHED_COUNT
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('canbus get relay switched count failed, psid %s', self.ps_id)
        self.canbusinfo['RelaySwitchedCount'] = 0
        return 0
    end
    local value = string.unpack('H', data:sub(1, 2))
    if not value then
        log:error('parsing relay switched count failed, psid %s', self.ps_id)
        self.canbusinfo['RelaySwitchedCount'] = 0
        return 0
    end
    self.canbusinfo['RelaySwitchedCount'] = self:can_data_swap_16(value)
    return self.canbusinfo['RelaySwitchedCount']
end

local POWER_ACTIVE_MODE <const> = 0 -- 主用模式
local POWER_STANDBY_MODE <const> = 1 -- 备用模式
function canbus:set_power_mode(mode)
    local control
    if mode == POWER_ACTIVE_MODE then
        log:notice('Power%d will change to active mode', self.ps_id)
        control = 0x55
    elseif mode == POWER_STANDBY_MODE then
        log:notice('Power%d will change to standby mode', self.ps_id)
        control = 0xaa
    end
    local canbus_req_data = string.char(0) .. string.char(control) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_SWITCH_MS, canbus_req_data)
    if not ok or recv_data == nil then
        -- 两路输入带载能力不相同时，电源不执行切换指令，会失败
        log:error('set_power_mode failed, psid: %s, error info: %s', self.ps_id, recv_data)
        error('set power mode failed')
    end
end

local upgrade_dcdc_file = "PS_DCDC.bin"
local upgrade_pfc_file = "PS_PFC.bin"
local upgrade_turbo_file = "PS_TURBO.bin"
local upgrade_turbo_sample_file = "PS_TURBOSAMPLE.bin"
local upgrade_hd_file = "PS_HD.bin"
local upgrade_zy_file = "PS_ZY.bin"
local canbus_upgrade_tpsu_bin_table = {
    upgrade_hd_file,
    upgrade_zy_file,
    upgrade_dcdc_file,
    upgrade_pfc_file,
    upgrade_turbo_file,
    upgrade_turbo_sample_file,
}
function canbus:canbus_get_upgrade_num()
    local bin_count = 0
    for _ in pairs(canbus_upgrade_tpsu_bin_table) do 
        bin_count = bin_count + 1 
    end
    return bin_count
end

function canbus:canbus_get_upgrade_bin_table()
    return canbus_upgrade_tpsu_bin_table
end

function canbus:canbus_change_upgrading_channle()
    self.canbus_chip.upgrading_channle_fail_nums = self.canbus_chip.upgrading_channle_fail_nums + 1
    if self.canbus_chip.upgrading_channle_fail_nums <= 7 then --连续失败3次才进来1次，所以是连续失败21次才有问题
        log:notice('upgrading channel fail ps:%s, count:%s',
                self.ps_id, self.canbus_chip.upgrading_channle_fail_nums)
        return
    end
    if self.canbus_chip.upgrade_channle == self.canbus_chip.current_channel then
        return --如果升级通道和当前通道一致，另外一路已经损坏，不再切换
    end
    if self.canbus_chip.upgrading_change_channel_flag == 1 then
        return --已经切换过通道
    end
    self.canbus_chip.upgrading_channle_fail_nums = 0
    self.canbus_chip.upgrading_change_channel_flag = 1
    log:notice('upgrading channel change ps:%s, cur_channel:%s change to channel:%s',
                self.ps_id, self.canbus_chip.upgrade_channle, self.canbus_chip.current_channel)
    self.canbus_chip.upgrade_channle = (self.canbus_chip.upgrade_channle == CANBUS_CAN1_CHANNEL) and
                                CANBUS_CAN0_CHANNEL or CANBUS_CAN1_CHANNEL --默认的升级通道和主通道相反
    self.canbus_chip.upgrade_chip = (self.canbus_chip.upgrade_channle == CANBUS_CAN1_CHANNEL) and
                                                self.canbus_chip.can1_chip or self.canbus_chip.can0_chip
    return
end

-- canbus升级写读接口
function canbus:canbus_upgrade_chip_write_read(canbus_send_data)
    log:info('canbus_upgrade_chip_write_read start, psid: %s', self.ps_id)
    for i = 1, CANBUS_RETRY_COUNT do
        -- write data
        local ok, value = pcall(function()
            return self.canbus_chip.upgrade_chip:WriteRead(ctx.new(), canbus_send_data, 0)
        end)
        -- read data
        if ok and value then
            self.canbus_chip.upgrading_channle_fail_nums = 0
            return value
        end
        log:info('canbus_upgrade_chip_write_read failed, ok %s value %s retry_count %d psid: %s',
                                                                        ok, value, i, self.ps_id)
        skynet.sleep(15 * i) -- 最长900ms
    end
    self:canbus_change_upgrading_channle()
    return nil
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.canbus_chip.upgrade_chip:PluginRequest(context, 'power_mgmt', 'canbus_load_firmware_by_frame_tpsu',
        skynet.packstring(block_id, frame_num, block_data, len, self.slot_addr))
end

function canbus:canbus_get_factory_type()
    local recv_data = self:get_canbus_basic_info()
    if not recv_data then
        log:error('canbus_get_factory_type failed, psid: %s', self.ps_id)
        return E_FAILED, -1
    end
    --硬件特征值在批量命令0x50的第一个位置，在HD或者ZY的APP被擦除情况下，只能0x50可以读取硬件特征值
    local canbus_data = recv_data.canbus_data:sub(1, self.protocol.CANBUS_FRAME_DATA_SIZE)
    canbus_data = can_frame_data:unpack(canbus_data)
    --40-43为多元化场景bit域
    local version1 = canbus_data.data:sub(1, 1)  --对应协议的byte2的低4bit，byte0-byte1解包时已经去掉
    local ver2 = string.unpack('B', version1)
    return E_OK, (ver2 & 0xf)
end

function canbus:canbus_upload_file_check(file_path, filename)
    local res = file_sec.check_real_path_s(file_path)
    if res ~= 0 then
        log:error('check_real_path_s failed, filename:%s', filename)
        return E_FAILED
    end
    local ok, version = self:canbus_get_factory_type()
    if (filename == upgrade_zy_file) and ((ok == E_FAILED) or (version == 0)) then --版本为0是hd或者获取不到版本，zy不加载
        log:notice('filename:%s not need to load, version is %d', filename, version)
        return E_FAILED
    end
    if (filename == upgrade_hd_file) and ((ok == E_FAILED) or (version == 1)) then
        log:notice('filename:%s not need to load, version is %d', filename, version)
        return E_FAILED
    end
    return E_OK
end

function canbus:canbus_get_powerSupplyChannel()
    local canbus_sigid = canbus.cmd.CANBUS_CMD_PSU_GET_SUPPLYCHANNEL
    local data = self:get_signal_data(canbus_sigid)
    if not data then
        log:error('canbus get powerSupplyChannel fail, psid: %s', self.ps_id)
        self.canbusinfo['PowerSupplyChannel'] = 0 --获取失败默认为主线路
        return
    end
    local channel = string.unpack('B', data:sub(2, 2)) --1byte位于第2个位置
    self.canbusinfo['PowerSupplyChannel'] = channel
    log:info('refresh_canbus_device_version_systime successed, psid: %s, PowerSupplyChannel %s',
    self.ps_id, self.canbusinfo['PowerSupplyChannel'])
    return
end

local power_latch_num = 0
function canbus:set_power_latch()
    local cmd = self.cmd.CMD_QUERY
    local canbus_sigid = canbus.cmd.CANBUS_CMD_SET_POWER_LATCH
    power_latch_num = power_latch_num + 1 --功率锁存需要下发锁存index
    if power_latch_num > 1000 then
        power_latch_num = 1
    end
    local canbus_req_data = string.pack('H', power_latch_num) .. string.rep(string.char(0), 4)
    self:canbus_broadcast_cmd(canbus_sigid, canbus_req_data, cmd)
end

function canbus:set_alarm_latch(switch_status)
    if switch_status ~= LATCH and switch_status ~= NO_LATCH then
        log:error('invalid switch status: %s', switch_status)
        error('invalid switch status')
    end
    local canbus_req_data = string.char(0) .. string.char(switch_status) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_SET_ALARM_LATCH,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set alarm latch(%s) failed, psid: %s, error info: %s', switch_status, self.ps_id, recv_data)
        error('set alarm latch failed')
    end
end

function canbus:get_vout_debounce_milliseconds()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_ALARM_THRESHOLD)
    if not data then
        log:error('get output voltage debounce millisecond failed')
        error('get output voltage debounce millisecond failed')
    end
    local debounce_milliseconds = string.unpack('>H', data:sub(1, 2))
    if not debounce_milliseconds then
        log:error('parsing output voltage debounce millisecond failed, psid %s', self.ps_id)
        error('get output voltage debounce millisecond failed')
    end
    self.canbusinfo['VOUTDebounceMilliseconds'] = debounce_milliseconds
    return self.canbusinfo['VOUTDebounceMilliseconds']
end

function canbus:get_depth_of_discharge_volts()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_ALARM_THRESHOLD)
    if not data then
        log:error('get depth of discharge voltage failed')
        error('get depth of discharge voltage failed')
    end
    local depth_of_discharge_volts = string.unpack('>I4', data:sub(3, #data))
    if not depth_of_discharge_volts then
        log:error('parsing depth of discharge voltage failed, psid %s', self.ps_id)
        error('get depth of discharge voltage failed')
    end
    self.canbusinfo['DepthOfDischargeVolts'] = math.floor(depth_of_discharge_volts / 1024 + 0.5)
    return self.canbusinfo['DepthOfDischargeVolts']
end

function canbus:get_turbo_input_current_limit()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_RELEASE_THRESHOLD)
    if not data then
        log:error('get turbo input current limit failed')
        error('get turbo input current limit failed')
    end
    local input_current_limit = string.unpack('>I4', data:sub(3, #data))
    if not input_current_limit then
        log:error('parsing input current limit failed, psid %s', self.ps_id)
        error('get dinput current limit failed')
    end
    self.canbusinfo['InputCurrentLimit'] = math.floor(input_current_limit / 1024 + 0.5)
    return self.canbusinfo['InputCurrentLimit']
end

function canbus:set_vout_debounce_milliseconds(debounce_milliseconds)
    if debounce_milliseconds < DEBOUNCE_MILLISECONDS_MIN or debounce_milliseconds > DEBOUNCE_MILLISECONDS_MAX then
        log:error('debounce millisecond is out of range')
        error('debounce millisecond is out of range')
    end
    local ret, depth_of_discharge_volts = pcall(self.get_depth_of_discharge_volts, self)
    if not ret then
        error('set output voltage debounce millisecond failed')
    end
    local canbus_req_data = string.pack('>H', debounce_milliseconds) ..
        string.pack('>I4', depth_of_discharge_volts * 1024)
    local ok, recv_data = pcall(self.set_signal_data, self,
        canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_ALARM_THRESHOLD, canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set output voltafe debounce millisecond(%s) failed, psid: %s, error info: %s',
            debounce_milliseconds, self.ps_id, recv_data)
        error('set output voltage debounce millisecond failed')
    end
end

function canbus:set_depth_of_discharge_volts(depth_of_discharge_volts)
    local limit_ret, input_current_limit = pcall(self.get_turbo_input_current_limit, self)
    if not limit_ret then
        error('set depth of dischrage voltage failed')
    end
    if depth_of_discharge_volts < DEPTH_OF_DISCHARGE_MIN or depth_of_discharge_volts > input_current_limit then
        log:error('depth of discharge voltage is out of range, max is %s', input_current_limit)
        error('depth of discharge voltage is out of range')
    end
    local debounce_millisecond_ret, debounce_milliseconds = pcall(self.get_vout_debounce_milliseconds, self)
    if not debounce_millisecond_ret then
        error('set depth of dischrage voltage failed')
    end
    local canbus_req_data = string.pack('>H', debounce_milliseconds) ..
        string.pack('>I4', depth_of_discharge_volts * 1024)
    local ok, recv_data = pcall(self.set_signal_data, self,
        canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_VOLTAGE_ALARM_THRESHOLD, canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set depth of discharge voltage(%s) failed, psid: %s, error info: %s',
            debounce_milliseconds, self.ps_id, recv_data)
            error('set depth of dischrage voltage failed')
    end
end

function canbus:get_output_voltage_set_value()
    local data = self:get_signal_data(canbus.cmd.CANBUS_CMD_SET_PSU_VOLTAGE)
    if not data then
        log:error('get output voltage set value failed, psid %s', self.ps_id)
        return
    end
    local voltage = string.unpack('>I4', data:sub(3, #data))
    if not voltage then
        log:error('parsing output voltage set value failed, psid %s', self.ps_id)
        return
    end
    log:notice('get output voltage set value(%s) successfully, psid %s', voltage / 1024, self.ps_id)
    return voltage / 1024
end

function canbus:set_output_voltage(voltage)
    if voltage < VOLTAGE_LOWER_LIMIT and voltage > VOLTAGE_UPPER_LIMIT then
        log:error('invalid output voltage: %s, voltage lower limit: %s, voltage upper limit: %s', voltage,
            VOLTAGE_LOWER_LIMIT, VOLTAGE_UPPER_LIMIT)
        error('invalid output voltage')
    end
    local canbus_req_data = string.rep(string.char(0), 2) .. string.pack('>I4', math.floor(voltage * 1024 + 0.5))
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_SET_PSU_VOLTAGE,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set output voltage(%s) failed, psid: %s, error info: %s', voltage, self.ps_id, recv_data)
        error('set output voltage failed')
    end
    log:notice('set output voltage(%s) successfully, psid: %s', voltage, self.ps_id)
end

function canbus:set_power_supply_circuit(circuit)
    if circuit ~= SUPPLY_CIRCUIT_A and circuit ~= SUPPLY_CIRCUIT_B then
        log:error('invalid supply circuit: %s', circuit)
        error('invalid supply circuit')
    end
    local canbus_req_data = string.char(0) .. string.char(circuit == SUPPLY_CIRCUIT_A and SUPPLY_CIRCUIT_A_CMD or
        SUPPLY_CIRCUIT_B_CMD) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_SET_SUPPLYCIRCUIT,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set supply circuit(%s) failed, psid: %s, error info: %s', circuit, self.ps_id, recv_data)
        error('set supply circuit failed')
    end
    log:notice('set supply circuit(%s) successfully, psid: %s', circuit, self.ps_id)
end

function canbus:get_main_circuit_vin_status()
    local data = self:get_signal_data(canbus.cmd.CANBUS_CMD_CIRCUIT_A_STATUS)
    if not data then
        log:error('canbus get power supply circuit a status fail, psid: %s', self.ps_id)
        error('get power supply circuit a status fail')
    end
    return string.unpack('B', data:sub(2, 2))
end

function canbus:get_backup_circuit_vin_status()
    local data = self:get_signal_data(canbus.cmd.CANBUS_CMD_CIRCUIT_B_STATUS)
    if not data then
        log:error('canbus get power supply circuit b status fail, psid: %s', self.ps_id)
        error('get power supply circuit b status fail')
    end
    return string.unpack('B', data:sub(2, 2))
end

function canbus:set_power_capacitor_enable(enable)
    local flag = enable == 'On' and 0 or 1 --协议0为开启
    local canbus_req_data = string.char(0) .. string.char(flag) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_CTRL,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set psu capacitor enable(%s) failed, psid: %s, error info: %s', enable, self.ps_id, recv_data)
        error('set psu capacitor enable failed')
    end
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:get_power_capacitor_calibrate_time()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_CAPACITOR_CALIBRATE_TIME)
    if not data then
        log:error('canbus get psu capacitor calibrate time failed, psid: %s', self.ps_id)
        error('get psu capacitor calibrate time failed')
    end
    return canbus:can_data_swap_32(string.unpack('I4', data:sub(3, 6)))
end

function canbus:get_power_capacitor_calibrate()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_CAPACITOR_CALIBRATE)
    if not data then
        log:error('canbus get psu capacitor calibrate failed, psid: %s', self.ps_id)
        error('get psu capacitor calibrate failed')
    end
    return string.unpack('B', data:sub(2, 2))
end

function canbus:set_power_capacitor_calibrate()
    local calibrate_enabled = 0x01
    local canbus_req_data = string.char(0) .. string.char(calibrate_enabled) .. string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, canbus.tpsu_cmd.CANBUS_CMD_CAPACITOR_CALIBRATE,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set psu capacitor calibrate failed, psid: %s, error info: %s', self.ps_id, recv_data)
        error('set psu capacitor calibrate failed')
    end
end

function canbus:get_power_state()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_TURBO_CAPACITOR_CTRL)
    if not data then
        log:error('canbus get power capacitor state fail, psid: %s', self.ps_id)
        return
    end
    return string.unpack('B', data:sub(2, 2)) == 0 and 'On' or 'Off'
end

function canbus:reset(reset_type)
    if not RESET_TYPE[reset_type] then
        log:error('invalid reset type: %s', reset_type)
        error('invalid reset type')
    end

    local canbus_req_data = string.char(0) .. string.char(RESET_TYPE[reset_type]) .. string.pack('>I4', 0)
    -- 关机动作需要连续下发3次
    local send_count = reset_type == 'ForceOff' and 3 or 1
    local ok, recv_data
    for _ = 1, send_count, 1 do
        ok, recv_data = pcall(self.set_signal_data, self, self.cmd.CANBUS_CMD_RESET,
        canbus_req_data)
        if not ok or recv_data == nil then
            log:error('reset psu(%s) failed, psid: %s, error info: %s', reset_type, self.ps_id, recv_data)
            error('set output voltage failed')
        end
        skynet.sleep(100)
    end

    log:notice('reset psu(%s) successfully, psid: %s', reset_type, self.ps_id)
end

function canbus:set_retransfer_delay_seconds(seconds_number)
    local canbus_req_data = string.rep(string.char(0), 2) .. string.pack('>I4',seconds_number)
    local ok, recv_data = pcall(self.set_signal_data, self, canbus.cmd.CANBUS_CMD_RETRANSFER_DELAY_SECONDS,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set pretransfer delay seconds(%s) failed, psid: %s,  error info: %s',
            seconds_number, self.ps_id, recv_data)
        error('set set retransfer delay seconds failed')
    end
end

function canbus:get_retransfer_delay_seconds()
    local data = self:get_signal_data(canbus.cmd.CANBUS_CMD_RETRANSFER_DELAY_SECONDS)
    if not data then
        log:error('canbus get pretransfer delay seconds fail, psid: %s', self.ps_id)
        return
    end
    return string.unpack('>I4',data:sub(3, #data))
end

function canbus:get_life_prediction_level()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_LIFE_PREDICTION_LEVEL)
    if not data then
        log:error('canbus get life prediction level fail, psid: %s', self.ps_id)
        return
    end
    return string.unpack('>H', data:sub(1, 2))
end

function canbus:get_life_prediction_count()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_LIFE_PREDICTION_COUNT)
    if not data then
        log:error('canbus get life prediction count fail, psid: %s', self.ps_id)
        return
    end
    return string.unpack('>H>I4', data:sub(1, #data))
end

function canbus:set_life_prediction_count_index(dod_level, vout_level, current_level)
    local canbus_req_data = string.pack('>H', dod_level << 8 | vout_level << 4 | current_level) ..
        string.rep(string.char(0), 4)
    local ok, recv_data = pcall(self.set_signal_data, self, canbus.tpsu_cmd.CANBUS_CMD_LIFE_PREDICTION_COUNT,
        canbus_req_data)
    if not ok or recv_data == nil then
        log:error('set life prediction count index(dod_level: %s, vout_level: %s, current_level: %s) failed, ' ..
            'psid: %s, error info: %s', dod_level, vout_level, current_level, self.ps_id, recv_data)
        error('set life prediction count index failed')
    end
end

function canbus:get_main_circuit_input_voltage_type()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_CIRCUIT_A_INPUT_VOLTAGE_TYPE)
    if not data then
        log:error('canbus get power supply circuit a input voltage type fail, psid: %s', self.ps_id)
        error('get power supply circuit a input voltage type fail')
    end
    return string.unpack('B', data:sub(2, 2))
end

function canbus:get_backup_circuit_input_voltage_type()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_CIRCUIT_B_INPUT_VOLTAGE_TYPE)
    if not data then
        log:error('canbus get power supply circuit b input voltage type fail, psid: %s', self.ps_id)
        error('get power supply circuit b input voltage type fail')
    end
    return string.unpack('B', data:sub(2, 2))
end

function canbus:get_main_circuit_input_voltage()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_CIRCUIT_A_INPUT_VOLTAGE)
    if not data then
        log:error('canbus get power supply circuit a input voltage fail, psid: %s', self.ps_id)
        error('get power supply circuit a input voltage fail')
    end
    return math.floor(string.unpack('>I4', data:sub(3, #data)) / 1024 + 0.5)
end

function canbus:get_backup_circuit_input_voltage()
    local data = self:get_signal_data(canbus.tpsu_cmd.CANBUS_CMD_CIRCUIT_B_INPUT_VOLTAGE)
    if not data then
        log:error('canbus get power supply circuit b input voltage fail, psid: %s', self.ps_id)
        error('get power supply circuit b input voltage fail')
    end
    return math.floor(string.unpack('>I4', data:sub(3, #data)) / 1024 + 0.5)
end

return canbus