diff --git a/server.test/CommonTest.cs b/server.test/CommonTest.cs new file mode 100644 index 0000000..a6cb5e5 --- /dev/null +++ b/server.test/CommonTest.cs @@ -0,0 +1,39 @@ +using Common; + +namespace server.test; + +public class CommonTest +{ + [Fact] + public void ReverseBytesTest() + { + var rnd = new Random(); + var bytesLen = 8; + var bytesArray = new byte[bytesLen]; + rnd.NextBytes(bytesArray); + + var rev2Bytes = new byte[] { + bytesArray[1], + bytesArray[0], + bytesArray[3], + bytesArray[2], + bytesArray[5], + bytesArray[4], + bytesArray[7], + bytesArray[6], + }; + Assert.Equal(Number.ReverseBytes(bytesArray, 2).Value, rev2Bytes); + + var rev4Bytes = new byte[] { + bytesArray[3], + bytesArray[2], + bytesArray[1], + bytesArray[0], + bytesArray[7], + bytesArray[6], + bytesArray[5], + bytesArray[4], + }; + Assert.Equal(Number.ReverseBytes(bytesArray, 4).Value, rev4Bytes); + } +} diff --git a/server.test/UDPServerTest.cs b/server.test/UDPServerTest.cs index 11c4aad..5cde6f3 100644 --- a/server.test/UDPServerTest.cs +++ b/server.test/UDPServerTest.cs @@ -5,30 +5,6 @@ using Xunit.Abstractions; namespace server.test; -public sealed class RepeatAttribute : Xunit.Sdk.DataAttribute -{ - private readonly int count; - - public RepeatAttribute(int count) - { - if (count < 1) - { - throw new System.ArgumentOutOfRangeException( - paramName: nameof(count), - message: "Repeat count must be greater than 0." - ); - } - this.count = count; - } - - public override System.Collections.Generic.IEnumerable GetData(System.Reflection.MethodInfo testMethod) - { - foreach (var iterationNumber in Enumerable.Range(start: 1, count: this.count)) - { - yield return new object[] { iterationNumber }; - } - } -} public class UDPServerTest { diff --git a/server/src/Common.cs b/server/src/Common.cs index 7cee773..3d4bb1f 100644 --- a/server/src/Common.cs +++ b/server/src/Common.cs @@ -85,11 +85,11 @@ namespace Common /// /// 比特合并成二进制字节 /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// 第一个比特值 + /// 第一个比特值的长度(位数) + /// 第二个比特值 + /// 第二个比特值的长度(位数) + /// 合并后的二进制字节数组 public static Result MultiBitsToBytes(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len) { return NumberToBytes(MultiBitsToNumber(bits1, bits1Len, bits2, bits2Len).Value, @@ -99,11 +99,11 @@ namespace Common /// /// 比特合并成整型 /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// 第一个比特值 + /// 第一个比特值的长度(位数) + /// 第二个比特值 + /// 第二个比特值的长度(位数) + /// 合并后的整型值 public static Result MultiBitsToNumber(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len) { if (bits1Len + bits2Len > 64) return new(new ArgumentException("Two Bits is more than 64 bits")); @@ -115,11 +115,11 @@ namespace Common /// /// 比特合并成整型 /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// 第一个比特值 + /// 第一个比特值的长度(位数) + /// 第二个比特值 + /// 第二个比特值的长度(位数) + /// 合并后的整型值 public static Result MultiBitsToNumber(uint bits1, uint bits1Len, uint bits2, uint bits2Len) { if (bits1Len + bits2Len > 64) return new(new ArgumentException("Two Bits is more than 64 bits")); @@ -131,10 +131,10 @@ namespace Common /// /// 比特位检查 /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// 源比特值 + /// 目标比特值 + /// 掩码(默认为全1) + /// 检查结果(是否匹配) public static bool BitsCheck(ulong srcBits, ulong dstBits, ulong mask = 0xFFFF_FFFF_FFFF_FFFF) { return (srcBits & mask) == dstBits; @@ -143,10 +143,10 @@ namespace Common /// /// 比特位检查 /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// 源比特值 + /// 目标比特值 + /// 掩码(默认为全1) + /// 检查结果(是否匹配) public static bool BitsCheck(uint srcBits, uint dstBits, uint mask = 0xFFFF_FFFF) { return (srcBits & mask) == dstBits; @@ -156,9 +156,9 @@ namespace Common /// /// 字符串转二进制字节数组 /// - /// [TODO:parameter] - /// [TODO:parameter] - /// [TODO:return] + /// 输入的字符串 + /// 进制(默认为16进制) + /// 转换后的二进制字节数组 public static byte[] StringToBytes(string str, int numBase = 16) { var len = str.Length; @@ -173,10 +173,52 @@ namespace Common return bytes; } + /// + /// 反转字节数组中的子数组 + /// + /// 源字节数组 + /// 子数组的长度(反转的步长) + /// 反转后的字节数组 + public static Result ReverseBytes(byte[] srcBytes, int distance) + { + if (distance <= 0) + return new(new ArgumentException("Distance can't be negetive", nameof(distance))); + + var srcBytesLen = srcBytes.Length; + if (distance > srcBytesLen) + return new(new ArgumentException( + "Distance is larger than bytesArray", nameof(distance))); + if (srcBytesLen % distance != 0) + return new(new ArgumentException( + "The length of bytes can't be divided by 2 without reminder", nameof(distance))); + + var dstBytes = new byte[srcBytesLen]; + var buffer = new byte[distance]; + + for (int i = 0; i < srcBytesLen; i += distance) + { + var end = i + distance; + buffer = srcBytes[i..end]; + Array.Reverse(buffer); + Array.Copy(buffer, 0, dstBytes, i, distance); + } + + return dstBytes; + } + + } + /// + /// 字符串处理工具 + /// public class String { + /// + /// 反转字符串 + /// + /// 输入的字符串 + /// 反转后的字符串 public static string Reverse(string s) { char[] charArray = s.ToCharArray(); diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs index fc6ed81..43a2451 100644 --- a/server/src/Controllers.cs +++ b/server/src/Controllers.cs @@ -323,7 +323,11 @@ public class JtagController : ControllerBase int bytesRead; while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0) { - await memoryStream.WriteAsync(buffer, 0, bytesRead); + var revBuffer = Common.Number.ReverseBytes(buffer, 4); + if (!revBuffer.IsSuccessful) + return TypedResults.InternalServerError(revBuffer.Error); + + await memoryStream.WriteAsync(revBuffer.Value, 0, bytesRead); totalBytesRead += bytesRead; } @@ -360,97 +364,19 @@ public class JtagController : ControllerBase } } +/// +/// 日志控制器 +/// [ApiController] [Route("api/[controller]")] public class Log : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + /// + /// 日志文件路径 + /// private readonly string _logFilePath = Directory.GetFiles(Directory.GetCurrentDirectory())[0]; - [EnableCors("Development")] - [HttpGet] - public async Task Index() - { - if (!System.IO.File.Exists(_logFilePath)) - { - Problem("日志文件不存在"); - } - - // 请求头 - Response.Headers.Add("Content-Type", "text/event-stream"); - Response.Headers.Add("Cache-Control", "no-cache"); - Response.Headers.Add("Connection", "keep-alive"); - - long position = 0; - while (!HttpContext.RequestAborted.IsCancellationRequested) - { - try - { - if (System.IO.File.Exists(_logFilePath)) - { - using (var stream = new FileStream(_logFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - stream.Position = position; - using (var reader = new StreamReader(stream)) - { - string? line; - while ((line = await reader.ReadLineAsync()) != null) - { - // await WriteEvent(line); - await Response.WriteAsync($"data: {line}\n\n"); - await Response.Body.FlushAsync(); - position = stream.Position; - } - if (line is null) break; - } - } - } - else - { - await WriteEvent("日志文件被删除"); - break; - } - } - catch (Exception ex) - { - await WriteEvent($"错误: {ex.Message}"); - } - - await Task.Delay(1000, HttpContext.RequestAborted); // 每秒检查一次 - } - - // 结束 - return new EmptyResult(); - } - - private async Task WriteEvent(string message) - { - await Response.WriteAsync($"data: {message}\n\n"); - await Response.Body.FlushAsync(); - } - - // [EnableCors("Development")] - // [HttpGet("example")] - // public async Task SseExample(HttpContext ctx, ItemService service, CancellationToken ct) - // { - // // 请求头 - // ctx.Response.Headers.Add("Content-Type", "text/event-stream"); - // // Response.Headers.Add("Content-Type", "text/event-stream"); - // // Response.Headers.Add("Cache-Control", "no-cache"); - // // Response.Headers.Add("Connection", "keep-alive"); - // - // while (!ct.IsCancellationRequested) - // { - // var item = await service.WaitForNewItem(); - // - // await ctx.Response.WriteAsync($"data: "); - // await JsonSerializer.SerializeAsync(ctx.Response.Body, item); - // await ctx.Response.WriteAsync($"\n\n"); - // await ctx.Response.Body.FlushAsync(); - // - // service.Reset(); - // } - // } } diff --git a/server/src/JtagClient.cs b/server/src/JtagClient.cs index b4581ff..b270f55 100644 --- a/server/src/JtagClient.cs +++ b/server/src/JtagClient.cs @@ -229,11 +229,11 @@ public class Jtag private IPEndPoint ep; /// - /// Jtag构造函数 + /// Jtag 构造函数 /// - /// 目标IP地址 - /// 目标UDP端口 - /// 超时时间 + /// 目标 IP 地址 + /// 目标 UDP 端口 + /// 超时时间(毫秒) public Jtag(string address, int port, int timeout = 2000) { this.address = address; @@ -276,6 +276,13 @@ public class Jtag return Convert.ToUInt32(Common.Number.BytesToNumber(retPackOpts.Data).Value); } + /// + /// 向指定的 JTAG 设备地址写入数据到 FIFO + /// + /// 目标设备地址 + /// 要写入的数据 + /// 写入后的延迟时间(毫秒) + /// 包含接收数据包的异步结果 public async ValueTask> WriteFIFO(UInt32 devAddr, UInt32 data, UInt32 delayMilliseconds = 0) { var ret = false; @@ -462,16 +469,28 @@ public class Jtag } + /// + /// 清除所有 JTAG 寄存器 + /// + /// 指示清除是否成功的异步结果 async ValueTask> ClearAllRegisters() { return await WriteFIFO(JtagAddr.STATE, 0xFF_FF_FF_FF, 0x01_02_02_02, JtagState.ALL_REG); } + /// + /// 清除 JTAG 写数据寄存器 + /// + /// 指示清除是否成功的异步结果 async ValueTask> ClearWriteDataReg() { return await WriteFIFO(JtagAddr.STATE, 0x00_00_11_00, 0x01_00_02_00, JtagState.WRITE_DATA_FIFO | JtagState.CMD_EXEC_FINISH); } + /// + /// 关闭 JTAG 测试模式 + /// + /// 指示操作是否成功的异步结果 async ValueTask> CloseTest() { return await WriteFIFO( @@ -480,6 +499,10 @@ public class Jtag 0x01_00_00_00, JtagState.CMD_EXEC_FINISH); } + /// + /// 启动 JTAG 测试模式 + /// + /// 指示操作是否成功的异步结果 async ValueTask> RunTest() { return await WriteFIFO( @@ -488,6 +511,11 @@ public class Jtag 0x01_00_00_00, JtagState.CMD_EXEC_FINISH); } + /// + /// 设置 JTAG 空闲延迟时间 + /// + /// 延迟时间(毫秒) + /// 指示操作是否成功的异步结果 async ValueTask> IdleDelay(UInt32 milliseconds) { if (milliseconds > Math.Pow(2, 28)) return new(new Exception("Timespan is over 2^28 milliseconds")); @@ -562,6 +590,10 @@ public class Jtag return new(new Exception("LoadDRCareo Failed!")); } + /// + /// 读取 JTAG 设备的 ID 代码 + /// + /// 包含 ID 代码的异步结果 public async ValueTask> ReadIDCode() { // Clear Data @@ -596,6 +628,10 @@ public class Jtag return retData.Value; } + /// + /// 读取 JTAG 设备的状态寄存器 + /// + /// 包含状态寄存器值的异步结果 public async ValueTask> ReadStatusReg() { // Clear Data @@ -630,6 +666,11 @@ public class Jtag return retData.Value; } + /// + /// 下载比特流到 JTAG 设备 + /// + /// 比特流数据 + /// 指示下载是否成功的异步结果 public async ValueTask> DownloadBitstream(byte[] bitstream) { // Clear Data @@ -663,7 +704,7 @@ public class Jtag logger.Trace("Jtag ready to write bitstream"); - ret = await IdleDelay(75000); + ret = await IdleDelay(100000); if (!ret.IsSuccessful) return new(ret.Error); else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed")); @@ -687,7 +728,7 @@ public class Jtag logger.Trace("Jtag reset device"); - ret = await IdleDelay(1000); + ret = await IdleDelay(10000); if (!ret.IsSuccessful) return new(ret.Error); else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed")); diff --git a/server/src/UdpServer.cs b/server/src/UdpServer.cs index 87278f2..b9e933c 100644 --- a/server/src/UdpServer.cs +++ b/server/src/UdpServer.cs @@ -81,9 +81,13 @@ public class UDPServer /// UDP 服务器的错误代码 public enum ErrorCode { + /// [TODO:description] Success = 0, + /// [TODO:description] GetNoneAfterTimeout, + /// [TODO:description] ResponseWrong, + /// [TODO:description] NotRecvDataPackage, } diff --git a/src/App.vue b/src/App.vue index 908e59e..54e03aa 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,7 @@ + + diff --git a/src/views/JtagTest.vue b/src/views/JtagTest.vue index 8337c9b..7fb5887 100644 --- a/src/views/JtagTest.vue +++ b/src/views/JtagTest.vue @@ -3,44 +3,38 @@
-

{{ logText }}

-