From 51fd1145d876ed6a7f179d6bdd8945ccd9ce02c9 Mon Sep 17 00:00:00 2001 From: SikongJueluo Date: Mon, 5 May 2025 17:24:28 +0800 Subject: [PATCH] feature: add more web api for remote update --- server/src/Controllers.cs | 265 +++++++++++++++++++++++++++---------- server/src/RemoteUpdate.cs | 186 +++++++++++++++----------- 2 files changed, 299 insertions(+), 152 deletions(-) diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs index 80365ed..968ab0a 100644 --- a/server/src/Controllers.cs +++ b/server/src/Controllers.cs @@ -1,6 +1,6 @@ -using System.Buffers.Binary; using System.Net; using Common; +using DotNext; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; @@ -143,6 +143,8 @@ public class JtagController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private const string BITSTREAM_PATH = "bitstream/Jtag"; + /// /// 页面 /// @@ -244,7 +246,7 @@ public class JtagController : ControllerBase // 生成安全的文件名(避免路径遍历攻击) var fileName = Path.GetRandomFileName(); - var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}"); + var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}"); // 如果存在文件,则删除原文件再上传 if (Directory.Exists(uploadsFolder)) @@ -276,7 +278,7 @@ public class JtagController : ControllerBase public async ValueTask DownloadBitstream(string address, int port) { // 检查文件 - var fileDir = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}"); + var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}"); if (!Directory.Exists(fileDir)) return TypedResults.BadRequest("Empty bitstream, Please upload it first"); @@ -306,10 +308,6 @@ public class JtagController : ControllerBase if (!retBuffer.IsSuccessful) return TypedResults.InternalServerError(retBuffer.Error); revBuffer = retBuffer.Value; - // for (int i = 0; i < buffer.Length; i++) - // { - // revBuffer[i] = Common.Number.ReverseBits(revBuffer[i]); - // } await memoryStream.WriteAsync(revBuffer, 0, bytesRead); totalBytesRead += bytesRead; @@ -357,22 +355,36 @@ public class RemoteUpdater : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private const string BITSTREAM_PATH = "bitstream/RemoteUpdate"; + /// /// 上传远程更新比特流文件 /// /// 设备地址 - /// 比特流文件 + /// 黄金比特流文件 + /// 比特流文件1 + /// 比特流文件2 + /// 比特流文件3 + /// 上传结果 [HttpPost("UploadBitstream")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async ValueTask UploadBitstream(string address, IFormFile file) + public async ValueTask UploadBitstreams( + string address, + IFormFile? goldenBitream, + IFormFile? bitstream1, + IFormFile? bitstream2, + IFormFile? bitstream3) { - if (file == null || file.Length == 0) + if ((goldenBitream is null || goldenBitream.Length == 0) && + (bitstream1 is null || bitstream1.Length == 0) && + (bitstream2 is null || bitstream2.Length == 0) && + (bitstream3 is null || bitstream3.Length == 0)) return TypedResults.BadRequest("未选择文件"); // 生成安全的文件名(避免路径遍历攻击) var fileName = Path.GetRandomFileName(); - var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"bitstream/RemoteUpdate/{address}"); + var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}"); // 如果存在文件,则删除原文件再上传 if (Directory.Exists(uploadsFolder)) @@ -381,19 +393,79 @@ public class RemoteUpdater : ControllerBase } Directory.CreateDirectory(uploadsFolder); - var filePath = Path.Combine(uploadsFolder, fileName); - - using (var stream = new FileStream(filePath, FileMode.Create)) + for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++) { - await file.CopyToAsync(stream); + IFormFile file; + if (bitstreamNum == 0 && goldenBitream is not null) + file = goldenBitream; + else if (bitstreamNum == 1 && bitstream1 is not null) + file = bitstream1; + else if (bitstreamNum == 2 && bitstream2 is not null) + file = bitstream2; + else if (bitstreamNum == 3 && bitstream3 is not null) + file = bitstream3; + else continue; + + var fileFolder = Path.Combine(uploadsFolder, bitstreamNum.ToString()); + Directory.CreateDirectory(fileFolder); + + var filePath = Path.Combine(fileFolder, fileName); + + using (var stream = new FileStream(filePath, FileMode.Create)) + { + await file.CopyToAsync(stream); + } } logger.Info($"Device {address} Upload Bitstream Successfully"); return TypedResults.Ok("Bitstream Upload Successfully"); } + private async ValueTask> ProcessBitstream(string filePath) + { + using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open)) + { + if (fileStream is null || fileStream.Length <= 0) + return new(new ArgumentException("Wrong bitstream path")); + + // 定义缓冲区大小: 32KB + byte[] buffer = new byte[32 * 1024]; + byte[] revBuffer = new byte[32 * 1024]; + long totalBytesRead = 0; + + // 使用异步流读取文件 + using (var memoryStream = new MemoryStream()) + { + int bytesRead; + while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + // 反转 32bits + var retBuffer = Common.Number.ReverseBytes(buffer, 4); + if (!retBuffer.IsSuccessful) + return new(retBuffer.Error); + revBuffer = retBuffer.Value; + + await memoryStream.WriteAsync(revBuffer, 0, bytesRead); + totalBytesRead += bytesRead; + } + + // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存) + var restStreamLen = memoryStream.Length % (4 * 1024); + if (restStreamLen != 0) + { + var appendLen = ((int)(4 * 1024 - restStreamLen)); + var bytesAppend = new byte[appendLen]; + Array.Fill(bytesAppend, 0xFF); + await memoryStream.WriteAsync(bytesAppend, 0, appendLen); + } + + return new(memoryStream.ToArray()); + } + } + } + /// - /// 远程更新比特流文件 + /// 远程更新单个比特流文件 /// /// 设备地址 /// 设备端口 @@ -405,7 +477,7 @@ public class RemoteUpdater : ControllerBase public async ValueTask UpdateBitstream(string address, int port, int bitstreamNum) { // 检查文件 - var fileDir = Path.Combine(Environment.CurrentDirectory, $"bitstream/RemoteUpdate/{address}"); + var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}"); if (!Directory.Exists(fileDir)) return TypedResults.BadRequest("Empty bitstream, Please upload it first"); @@ -414,63 +486,22 @@ public class RemoteUpdater : ControllerBase // 读取文件 var filePath = Directory.GetFiles(fileDir)[0]; - using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open)) + var fileBytes = await ProcessBitstream(filePath); + if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error); + + // 下载比特流 + var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port); + var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value); + + if (ret.IsSuccessful) { - if (fileStream is null || fileStream.Length <= 0) - return TypedResults.BadRequest("Wrong bitstream, Please upload it again"); - - // 定义缓冲区大小: 32KB - byte[] buffer = new byte[32 * 1024]; - byte[] revBuffer = new byte[32 * 1024]; - long totalBytesRead = 0; - - // 使用异步流读取文件 - using (var memoryStream = new MemoryStream()) - { - int bytesRead; - while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0) - { - // 反转 32bits - var retBuffer = Common.Number.ReverseBytes(buffer, 4); - if (!retBuffer.IsSuccessful) - return TypedResults.InternalServerError(retBuffer.Error); - revBuffer = retBuffer.Value; - // for (int i = 0; i < buffer.Length; i++) - // { - // revBuffer[i] = Common.Number.ReverseBits(revBuffer[i]); - // } - - await memoryStream.WriteAsync(revBuffer, 0, bytesRead); - totalBytesRead += bytesRead; - } - - // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存) - var restStreamLen = memoryStream.Length % (4 * 1024); - if (restStreamLen != 0) - { - var appendLen = ((int)(4 * 1024 - restStreamLen)); - var bytesAppend = new byte[appendLen]; - Array.Fill(bytesAppend, 0xFF); - await memoryStream.WriteAsync(bytesAppend, 0, appendLen); - } - var fileBytes = memoryStream.ToArray(); - - // 下载比特流 - var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port); - var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes); - - if (ret.IsSuccessful) - { - logger.Info($"Device {address} Update bitstream successfully"); - return TypedResults.Ok(ret.Value); - } - else - { - logger.Error(ret.Error); - return TypedResults.InternalServerError(ret.Error); - } - } - + logger.Info($"Device {address} Update bitstream successfully"); + return TypedResults.Ok(ret.Value); + } + else + { + logger.Error(ret.Error); + return TypedResults.InternalServerError(ret.Error); } } @@ -479,6 +510,94 @@ public class RemoteUpdater : ControllerBase return TypedResults.InternalServerError(error); } } + + + /// + /// 下载多个比特流文件 + /// + /// 设备地址 + /// 设备端口 + /// 比特流编号 + /// 总共上传比特流的数量 + [HttpPost("DownloadMultiBitstreams")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async ValueTask DownloadMultiBitstreams(string address, int port, int? bitstreamNum) + { + // 检查文件 + var bitstreamsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}"); + if (!Directory.Exists(bitstreamsFolder)) + return TypedResults.BadRequest("Empty bitstream, Please upload it first"); + + try + { + var bitstreams = new List() { null, null, null, null }; + int cnt = 0; // 上传比特流数量 + for (int i = 0; i < 4; i++) + { + var bitstreamDir = Path.Combine(bitstreamsFolder, i.ToString()); + if (!Directory.Exists(bitstreamDir)) + continue; + cnt++; + + // 读取文件 + var filePath = Directory.GetFiles(bitstreamDir)[0]; + var fileBytes = await ProcessBitstream(filePath); + if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error); + bitstreams[i] = fileBytes.Value; + } + + // 下载比特流 + var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port); + { + var ret = await remoteUpdater.UploadBitstreams(bitstreams[0], bitstreams[1], bitstreams[2], bitstreams[3]); + if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error); + if (!ret.Value) return TypedResults.InternalServerError("Upload MultiBitstreams failed"); + } + + if (bitstreamNum is not null) + { + var ret = await remoteUpdater.HotResetBitstream(bitstreamNum ?? 0); + if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error); + if (!ret.Value) return TypedResults.InternalServerError("Hot reset failed"); + } + return TypedResults.Ok(cnt); + } + catch (Exception error) + { + return TypedResults.InternalServerError(error); + } + } + + + /// + /// 热复位比特流文件 + /// + /// 设备地址 + /// 设备端口 + /// 比特流编号 + /// 操作结果 + [HttpPost("HotResetBitstream")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async ValueTask HotResetBitstream(string address, int port, int bitstreamNum) + { + var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port); + var ret = await remoteUpdater.HotResetBitstream(bitstreamNum); + + if (ret.IsSuccessful) + { + logger.Info($"Device {address}即可 Update bitstream successfully"); + return TypedResults.Ok(ret.Value); + } + else + { + logger.Error(ret.Error); + return TypedResults.InternalServerError(ret.Error); + } + } } diff --git a/server/src/RemoteUpdate.cs b/server/src/RemoteUpdate.cs index 330f33c..b6db774 100644 --- a/server/src/RemoteUpdate.cs +++ b/server/src/RemoteUpdate.cs @@ -1,4 +1,3 @@ -using System.Buffers.Binary; using System.Net; using DotNext; namespace RemoteUpdate; @@ -187,10 +186,6 @@ public class RemoteUpdateClient var bytesData = new byte[FLASH_SECTOR_LENGTH]; Array.Fill(bytesData, 0xFF); - // bytesData[FLASH_SECTOR_LENGTH - 4] = 0x80; // 0b10000000 - // bytesData[FLASH_SECTOR_LENGTH - 3] = 0xCC; // 0b11001100 - // bytesData[FLASH_SECTOR_LENGTH - 2] = 0xB4; // 0b10110100 - // bytesData[FLASH_SECTOR_LENGTH - 1] = 0x29; // 0b00101001 bytesData[FLASH_SECTOR_LENGTH - 1] = 0x01; bytesData[FLASH_SECTOR_LENGTH - 2] = 0x33; bytesData[FLASH_SECTOR_LENGTH - 3] = 0x2D; @@ -238,7 +233,6 @@ public class RemoteUpdateClient // Init data var bytesData = new byte[FLASH_SECTOR_LENGTH]; for (int i = 3; i < FLASH_SECTOR_LENGTH; i += 4) - // bytesData[i] = Common.Number.ReverseBits((byte)0xA0); bytesData[i] = (byte)0xA0; { @@ -273,54 +267,6 @@ public class RemoteUpdateClient var bytesSrc = new byte[] { 0x0F, 0x00, 0x00, 0x00 }; Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x48, 4); } - // { - // var bytesSrc = new byte[] { 0xAB, 0x00, 0x00, 0x01 }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x04, 4); - // } - // { - // var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x0B }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x08, 4); - // } - // { - // var bytesSrc = new byte[] { 0xAB, 0xC0, 0x00, 0x01 }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x34, 4); - // } - // { - // var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x00 }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x38, 4); - // } - // { - // var bytesSrc = new byte[] { 0xAC, 0x00, 0x00, 0x01 }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x3C, 4); - // } - // { - // var bytesSrc = Common.Number.NumberToBytes(FlashAddr.Bitstream[bitstreamNum], 4).Value; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x40, 4); - // } - // { - // var bytesSrc = new byte[] { 0xA8, 0x80, 0x00, 0x01 }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x44, 4); - // } - // { - // var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x0F }; - // for (int i = 0; i < 4; i++) - // bytesSrc[i] = Common.Number.ReverseBits(bytesSrc[i]); - // Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x48, 4); - // } var ret = await WriteFlash(FlashAddr.Jump[bitstreamNum], 1, bytesData); if (!ret.IsSuccessful) return new(ret.Error); @@ -402,8 +348,8 @@ public class RemoteUpdateClient var remoteCRC = Common.Number.BytesToUInt32(bytes); if (!remoteCRC.IsSuccessful) return new(remoteCRC.Error); - logger.Debug($"Expected CRC: \t 0x{Convert.ToString(checkSum, 16)}"); - logger.Debug($"Received CRC: \t 0x{Convert.ToString(remoteCRC.Value, 16)}"); + logger.Debug($"Bitstream {bitstreamNum} Expected CRC: \t 0x{Convert.ToString(checkSum, 16)}"); + logger.Debug($"Bitstream {bitstreamNum} Received CRC: \t 0x{Convert.ToString(remoteCRC.Value, 16)}"); return remoteCRC.Value == checkSum; } @@ -414,7 +360,7 @@ public class RemoteUpdateClient /// /// [TODO:parameter] /// [TODO:return] - private async ValueTask> HotRest(int bitstreamNum) + private async ValueTask> HotReset(int bitstreamNum) { // Assert if (bitstreamNum < 0 || bitstreamNum > 3) @@ -428,6 +374,81 @@ public class RemoteUpdateClient return ret.Value; } + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> HotResetBitstream(int bitstreamNum) + { + await MsgBus.UDPServer.ClearUDPData(this.address); + logger.Trace("Clear udp data finished"); + + { + var ret = await InitRemoteUpdate(bitstreamNum); + if (!ret.IsSuccessful) return new(ret.Error); + if (!ret.Value) return new(new Exception("Init remote update failed")); + } + + { + var ret = await HotReset(bitstreamNum); + if (!ret.IsSuccessful) return new(ret.Error); + return ret.Value; + } + } + + /// + /// [TODO:description] + /// + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:return] + public async ValueTask> UploadBitstreams( + byte[]? goldenBitream, + byte[]? bitstream1, + byte[]? bitstream2, + byte[]? bitstream3) + { + await MsgBus.UDPServer.ClearUDPData(this.address); + logger.Trace("Clear udp data finished"); + + for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++) + { + byte[] bytesData; + if (bitstreamNum == 0 && goldenBitream is not null) + bytesData = goldenBitream; + else if (bitstreamNum == 1 && bitstream1 is not null) + bytesData = bitstream1; + else if (bitstreamNum == 2 && bitstream2 is not null) + bytesData = bitstream2; + else if (bitstreamNum == 3 && bitstream3 is not null) + bytesData = bitstream3; + else continue; + + var bitstreamBlockNum = bytesData.Length / (4 * 1024); + + { + var ret = await WriteFlash(FlashAddr.Bitstream[bitstreamNum], bitstreamBlockNum, bytesData); + if (!ret.IsSuccessful) return new(ret.Error); + if (!ret.Value) return new(new Exception("Write bitstream failed")); + } + + { + var crc = Honoo.IO.Hashing.Crc.Create(Honoo.IO.Hashing.CrcName.CRC32_MPEG_2); + var checkSum = crc.ComputeFinal(bytesData); + var ret = await CheckBitstreamCRC(bitstreamNum, bitstreamBlockNum, checkSum.ToUInt32()); + if (!ret.IsSuccessful) return new(ret.Error); + if (!ret.Value) logger.Warn($"Bitstream {bitstreamNum} CRC32 not correct!"); + else logger.Info($"Bitstream {bitstreamNum} CRC32 calibration passed"); + } + + } + + return true; + } + /// /// [TODO:description] /// @@ -466,7 +487,7 @@ public class RemoteUpdateClient } { - var ret = await HotRest(bitstreamNum); + var ret = await HotReset(bitstreamNum); if (!ret.IsSuccessful) return new(ret.Error); return ret.Value; } @@ -475,33 +496,40 @@ public class RemoteUpdateClient /// /// [TODO:description] /// - /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] + /// [TODO:parameter] /// [TODO:return] - public async ValueTask> HotResetBitstream(int bitstreamNum) - { - await MsgBus.UDPServer.ClearUDPData(this.address); - logger.Trace("Clear udp data finished"); - - { - var ret = await InitRemoteUpdate(bitstreamNum); - if (!ret.IsSuccessful) return new(ret.Error); - if (!ret.Value) return new(new Exception("Init remote update failed")); - } - - { - var ret = await HotRest(bitstreamNum); - if (!ret.IsSuccessful) return new(ret.Error); - return ret.Value; - } - } - - public async ValueTask> UploadBitstreams( + public async ValueTask> UpdateBitstreams( int enableBitstreamNum, byte[]? goldenBitream, byte[]? bitstream1, byte[]? bitstream2, byte[]? bitstream3) { - return true; + // Assert + if (goldenBitream is null && bitstream1 is null && bitstream2 is null && bitstream3 is null) + return new(new ArgumentException( + $"At least one bitstream should not be empty")); + if ((enableBitstreamNum == 0 && goldenBitream is null) || + (enableBitstreamNum == 1 && bitstream1 is null) || + (enableBitstreamNum == 2 && bitstream2 is null) || + (enableBitstreamNum == 3 && bitstream3 is null)) + return new(new ArgumentException($"Bitstream {enableBitstreamNum} shouldn't be empty")); + + { + var ret = await UploadBitstreams(goldenBitream, bitstream1, bitstream2, bitstream3); + if (!ret.IsSuccessful) return new(ret.Error); + if (!ret.Value) return false; + } + + { + var ret = await HotResetBitstream(enableBitstreamNum); + if (!ret.IsSuccessful) return new(ret.Error); + return ret.Value; + } } + }