local posix = require('posix') local time = require('posix.time') local siganl = require('posix.signal') --local sem = require('posix.sem') local bit = require('bit') local cjson = require('cjson') local base64 = require('base64') local dyutils = require('dyutils') local lpack = require('lua_pack') local band, bor, bxor, bnot = bit.band, bit.bor, bit.bxor, bit.bnot local lshift, rshift, rol = bit.lshift, bit.rshift, bit.rol local bpack = string.pack local bunpack = string.unpack local TIMEOUT_MS = 3000 -- 注意:firmware_info.mode = 1 -- 升级模式: 并行升级 由lua脚本自行通过http post等方式推送升级文件给下挂设备,严禁调用send_command()\receive_data()等串行升级接口 -- 并行升级参数可在脚本中定义,也可在扩展参数中获取 local firmware_info = {} local extend_info = {} -- log Level local LOG_EMERG = 0 --/* system is unusable */ local LOG_ALERT = 1 --/* action must be taken immediately */ local LOG_CRIT = 2 --/* critical conditions */ local LOG_ERR = 3 --/* error conditions */ local LOG_WARNING = 4 --/* warning conditions */ local LOG_NOTICE = 5 --/* normal but significant condition */ local LOG_INFO = 6 --/* informational */ local LOG_DEBUG = 7 --/* debug-level messages */ local pipes_table = { ["command"] = {fd = -1, path = "/tmp/command"}, -- Lua → C ["response"] = {fd = -1, path = "/tmp/response"},-- C → Lua ["progress"] = {fd = -1, path = "/tmp/progress"},-- Lua → C ["log"] = {fd = -1, path = "/tmp/log"}, -- Lua → C } --local mysem, err = sem.open("/otasem") local function open_pipes() local flags = bor(posix.O_RDONLY, posix.O_NONBLOCK) pipes_table["command"].fd = posix.open(pipes_table["command"].path, posix.O_WRONLY) pipes_table["progress"].fd = posix.open(pipes_table["progress"].path, posix.O_WRONLY) pipes_table["log"].fd = posix.open(pipes_table["log"].path, posix.O_WRONLY) pipes_table["response"].fd = posix.open(pipes_table["response"].path, flags) end local update_log local function close_pipes(pipes) for name, pipe_info in pairs(pipes) do if pipe_info.fd ~= -1 then local ok, err = pcall(posix.close, pipe_info.fd) if ok then pipe_info.fd = -1 update_log(LOG_INFO, string.format("success closed fd=%d for pipe '%s'", pipe_info.fd, name)) else update_log(LOG_ERR, string.format("fail to close fd=%d for pipe '%s': %s", pipe_info.fd, name, err)) end end end end -- 接收C返回LUA的响应帧(带超时时间ms,返回0 此帧无应答 返回 1 正常解析数据) local function receive_data() if firmware_info.mode == 1 then return end local fd_Id = pipes_table["response"].fd if fd_Id == -1 then update_log(LOG_ERR, "fd_Id is -1 (already closed)") return -1, nil end local fds = { [fd_Id] = { events = { IN = true, -- 数据可读 HUP = true, -- 挂断事件 ERR = true -- 错误事件 } } } local poll = require 'posix.poll' local ret = poll.poll(fds, TIMEOUT_MS) if ret == 0 then update_log(LOG_INFO, "timeout, no data received") return 0, nil elseif ret == -1 then update_log(LOG_WARNING, "poll call failed, reason: " .. tostring(posix.errno())) return -1, nil else for fd in pairs(fds) do if fds[fd].revents.IN then local data, err = posix.read(fd, 1024) if data then --update_log(LOG_INFO, string.format("RX[%d]: %s", #data, string.toHex(data," "))) return 1, data else return -1, nil end elseif fds[fd].revents.HUP then update_log(LOG_ERR, "pipe closed (HUP)") posix.close(fd) -- 关闭 fd fd = -1 -- 标记为已关闭 pipes_table["response"].fd = -1 return -2, nil elseif fds[fd].revents.ERR then update_log(LOG_ERR, "pipe error (ERR)") posix.close(fd) fd = -1 pipes_table["response"].fd = -1 return -3, nil end end end end -- 发送命令/固件包 --(每包数据可指定超时时间) local function send_command(timeout_ms, data) if firmware_info.mode == 1 then return end --mysem:wait() local block_pkt = bpack("C BIN update_log(LOG_INFO, string.format("[convert] HEX -> BIN: %s", bin_file)) local ret = os.execute(string.format("objcopy -I ihex -O binary %s %s", hex_file, bin_file)) if ret ~= 0 then update_log(LOG_ERR, "Failed to convert HEX to BIN!!!") return nil end -- 2. 转换 HEX -> ELF update_log(LOG_INFO, string.format("[convert] HEX -> ELF: %s", elf_file)) ret = os.execute(string.format("objcopy -I ihex -O elf32-little %s %s", hex_file, elf_file)) if ret ~= 0 then update_log(LOG_ERR, "Failed to convert HEX to ELF!!!") return nil end -- 3. 提取起始地址(第一个 LOAD 段的 vaddr) local handle = io.popen(string.format("LANG=C readelf -S --wide %s 2>/dev/null | grep -m 1 'PROGBITS' | awk '{print $5}'", elf_file)) local start_addr = handle:read("*a"):gsub("%s+", "") handle:close() -- 4. 提取入口地址(Entry Point) handle = io.popen(string.format("LANG=C readelf -h %s 2>/dev/null | grep 'Entry' | awk '{print $5}'", elf_file)) local entry_addr = handle:read("*a"):gsub("%s+", "") handle:close() local result = { name = hex_file:match("^.+/(.+)$"):gsub("%..+$", ""), hex_file = hex_file, bin_file = bin_file, elf_file = elf_file, start_addr = start_addr ~= "" and string.format("0x%s", start_addr) or nil, --entry_addr = entry_addr ~= "" and string.format("0x%s", entry_addr) or nil } result.size, result.bin_data = read_file_with_alignment(bin_file, false) if not result.size then return nil end update_log(LOG_INFO, string.format("File name: %s", result.name)) update_log(LOG_INFO, string.format("Output BIN file: %s", result.bin_file)) update_log(LOG_INFO, string.format("Output ELF file: %s", result.elf_file)) update_log(LOG_INFO, string.format("Start address (LOAD): %s", result.start_addr)) --update_log(LOG_INFO, string.format("Entry address: %s", result.entry_addr)) return result end -- posix.sleep(1) -- 秒级 1s -- time.nanosleep({tv_sec=0, tv_nsec=500000000}) -- 纳秒级 500ms local BMS_ID = 0 local function update_result(success) if success then send_update_success() update_log(LOG_INFO, "Firmware upgrade completed success") else send_update_fail() update_log(LOG_INFO, "Firmware upgrade failed!!!") end end local function handle_error(log_level, message) update_log(log_level, message) update_result(false) return false end -- 切换固件,不确认其具体意义 local function swap_firmware() local Offset = BMS_ID * 0x30 local can_id = 0x141 + Offset local data = bpack("C7", BMS_ID, 0x00, 0x13, 0x00, 0x00, 0x00, 0x01) local can_frame = pack_can_frame(can_id, data) send_command(2000, can_frame) local _, response = receive_data() if not response then return handle_error(LOG_ERR, "Timeout waiting for swap response!!!") end local frdup = unpack_can_frame(response) if not frdup or not frdup.data[8] then return handle_error(LOG_ERR, "Invalid SWAP response format") end if frdup.data[8] ~= 0x00 then -- IF ERROR return handle_error(LOG_WARNING, "swap firmware ack error!!!") end return true end local function send_eof() local can_id = 0x143 local data = bpack("C6", 0X05, 0x00, 0x00, 0x00, 0x00, 0x00) local can_frame = pack_can_frame(can_id, data) send_command(2000, can_frame) local _, response = receive_data() if not response then return handle_error(LOG_ERR, "Timeout waiting for EOF response!!!") end local frdup = unpack_can_frame(response) if not frdup or not frdup.data[6] then return handle_error(LOG_ERR, "Invalid EOF response format") end if frdup.data[6] == 0x04 then -- NACK swap_firmware() return handle_error(LOG_ERR, "EOF, NACK") elseif frdup.data[6] ~= 0x02 then return handle_error(LOG_WARNING, string.format("Unexpected EOF response %02x", frdup.data[6])) end return true end local function _send_eof() local can_id = 0x143 local data = bpack("C6", 0X05, 0x00, 0x00, 0x00, 0x00, 0x00) local can_frame = pack_can_frame(can_id, data) send_command(2000, can_frame) local _, response = receive_data() if not response then return handle_error(LOG_ERR, "Timeout waiting for EOF response!!!") end local frdup = unpack_can_frame(response) if not frdup or not frdup.data[6] then return handle_error(LOG_ERR, "Invalid EOF response format") end if frdup.data[6] == 0x04 then -- NACK return handle_error(LOG_ERR, "EOF, NACK") elseif frdup.data[6] ~= 0x02 then return handle_error(LOG_WARNING, string.format("Unexpected EOF response %02x", frdup.data[6])) end return true end local function send_file_name(param) local index = 0 local chunk_size = 7 -- 每包7个字符 local can_id = 0x173 for i = 1, #param.name, chunk_size do local chunk = param.name:sub(i, i + chunk_size - 1) local data = bpack("CA", index, chunk) local can_frame = pack_can_frame(can_id, data) send_command(50, can_frame) index = index + 1 if index >= 256 then index = 0 end time.nanosleep({tv_sec=0, tv_nsec=100000000}) -- 纳秒级 100ms end -- send EOF if not _send_eof() then return handle_error(LOG_ERR, "Send EOF failed") end return true end -- CRC16-ARC LUA实现 local function CRC16_ARC(data) local crc = 0x0000 -- 初始值 local poly = 0xA001 -- 反转后的多项式(0x8005 的反转) for i = 1, #data do local byte = string.byte(data, i) crc = bit.bxor(crc, byte) for _ = 1, 8 do if bit.band(crc, 0x0001) ~= 0 then crc = bit.rshift(crc, 1) crc = bit.bxor(crc, poly) else crc = bit.rshift(crc, 1) end end end crc = bit.band(crc, 0xFFFF) -- 确保结果在 16 位范围内 -- 返回小端格式(低字节在前) return bit.bor( bit.band(bit.rshift(crc, 8), 0xFF), bit.band(bit.lshift(crc, 8), 0xFF00) ) end -- 写FALSH指令 local function write_flash(param) local can_id = 0x143 local data = bpack("C= param.size) then break end local block_data = string.sub(param.bin_data, offset + 1, offset + block_size) if not block_data then update_log(LOG_ERR, "read data failed") break end local data = bpack("CA", index, block_data) local can_frame = pack_can_frame(can_id, data) send_command(50, can_frame) offset = offset + #block_data local progress = math.modf((offset * 100)/param.size) if (progress ~= last_progress) then send_progress(progress) update_log(LOG_INFO, string.format("Current upgrade progress::::::::::::: %f", progress)) last_progress = progress end index = index + 1 if index >= 256 then index = 0 end time.nanosleep({tv_sec=0, tv_nsec=100000000}) -- 纳秒级 100ms end if offset ~= param.size then return false -- 升级失败 end update_log(LOG_INFO, string.format("payload data send complete %d", offset)) -- 2. 发送校验 --local crc = dyutils.CRC16(block_pkt, #block_pkt) -- CRC-16/MODBUS 小端格式 标准接口支持:CRC-16/MODBUS 等 -- send CRC can_id = 0x143 -- CRC-16/ARC local crc = CRC16_ARC(param.bin_data) local data = bpack("C = << local can_frame = pack_can_frame(can_id, data) send_command(2000, can_frame) local _, response = receive_data() if not response then return handle_error(LOG_ERR, "Timeout waiting for CRC-16/ARC response!!!") end local frdup = unpack_can_frame(response) if not frdup or not frdup.data[6] then return handle_error(LOG_ERR, "Invalid CRC-16/ARC response format") end if frdup.data[6] == 0x04 then -- NACK return handle_error(LOG_ERR, "CRC-16/ARC NACK") end return true end -- 单个固件升级流程 local function upgrade_firmware(param) if not param then return false end -- 1. send EOF if not _send_eof() then return handle_error(LOG_ERR, "Send EOF failed") end -- 2. send start addr local can_id = 0x143 local data = bpack("C