using DotNext; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; namespace server.Controllers; /// /// 远程更新 /// [ApiController] [Route("api/[controller]")] public class RemoteUpdateController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private const string BITSTREAM_PATH = "bitstream/RemoteUpdate"; /// /// 上传远程更新比特流文件 /// /// 设备地址 /// 黄金比特流文件 /// 比特流文件1 /// 比特流文件2 /// 比特流文件3 /// 上传结果 [Authorize("Admin")] [HttpPost("UploadBitstream")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] public async ValueTask UploadBitstreams( string address, IFormFile? goldenBitream, IFormFile? bitstream1, IFormFile? bitstream2, IFormFile? bitstream3) { 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_PATH}/{address}"); // 如果存在文件,则删除原文件再上传 if (Directory.Exists(uploadsFolder)) { Directory.Delete(uploadsFolder, true); } Directory.CreateDirectory(uploadsFolder); for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++) { 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(true); } 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()); } } } /// /// 远程更新单个比特流文件 /// /// 设备地址 /// 设备端口 /// 比特流位号 [Authorize("Admin")] [HttpPost("DownloadBitstream")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] public async ValueTask UpdateBitstream(string address, int port, int bitstreamNum) { // 检查文件 var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}"); if (!Directory.Exists(fileDir)) return TypedResults.BadRequest("Empty bitstream, Please upload it first"); try { // 读取文件 var filePath = Directory.GetFiles(fileDir)[0]; var fileBytes = await ProcessBitstream(filePath); if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error); // 下载比特流 var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(address, port); var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value); 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); } } catch (Exception error) { return TypedResults.InternalServerError(error); } } /// /// 下载多个比特流文件 /// /// 设备地址 /// 设备端口 /// 比特流编号 /// 总共上传比特流的数量 [Authorize("Admin")] [HttpPost("DownloadMultiBitstreams")] [EnableCors("Users")] [ProducesResponseType(typeof(int), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(Exception), 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 Peripherals.RemoteUpdateClient.RemoteUpdater(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); } } /// /// 热复位比特流文件 /// /// 设备地址 /// 设备端口 /// 比特流编号 /// 操作结果 [Authorize("Admin")] [HttpPost("HotResetBitstream")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] public async ValueTask HotResetBitstream(string address, int port, int bitstreamNum) { var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(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); } } /// /// [TODO:description] /// /// [TODO:parameter] /// [TODO:parameter] /// [TODO:return] [Authorize("Admin")] [HttpPost("GetFirmwareVersion")] [EnableCors("Users")] [ProducesResponseType(typeof(UInt32), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] public async ValueTask GetFirmwareVersion(string address, int port) { var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(address, port); var ret = await remoteUpdater.GetVersion(); if (ret.IsSuccessful) { logger.Info($"Device {address} get firmware version successfully"); return TypedResults.Ok(ret.Value); } else { logger.Error(ret.Error); return TypedResults.InternalServerError(ret.Error); } } }