add home view and reverse 4 bytes order to send
This commit is contained in:
		
							
								
								
									
										39
									
								
								server.test/CommonTest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								server.test/CommonTest.cs
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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<object[]> GetData(System.Reflection.MethodInfo testMethod)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var iterationNumber in Enumerable.Range(start: 1, count: this.count))
 | 
			
		||||
        {
 | 
			
		||||
            yield return new object[] { iterationNumber };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class UDPServerTest
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -85,11 +85,11 @@ namespace Common
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 比特合并成二进制字节
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="bits1">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits1Len">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits2">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits2Len">[TODO:parameter]</param>
 | 
			
		||||
        /// <returns>[TODO:return]</returns>
 | 
			
		||||
        /// <param name="bits1">第一个比特值</param>
 | 
			
		||||
        /// <param name="bits1Len">第一个比特值的长度(位数)</param>
 | 
			
		||||
        /// <param name="bits2">第二个比特值</param>
 | 
			
		||||
        /// <param name="bits2Len">第二个比特值的长度(位数)</param>
 | 
			
		||||
        /// <returns>合并后的二进制字节数组</returns>
 | 
			
		||||
        public static Result<byte[]> MultiBitsToBytes(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len)
 | 
			
		||||
        {
 | 
			
		||||
            return NumberToBytes(MultiBitsToNumber(bits1, bits1Len, bits2, bits2Len).Value,
 | 
			
		||||
@@ -99,11 +99,11 @@ namespace Common
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 比特合并成整型
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="bits1">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits1Len">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits2">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits2Len">[TODO:parameter]</param>
 | 
			
		||||
        /// <returns>[TODO:return]</returns>
 | 
			
		||||
        /// <param name="bits1">第一个比特值</param>
 | 
			
		||||
        /// <param name="bits1Len">第一个比特值的长度(位数)</param>
 | 
			
		||||
        /// <param name="bits2">第二个比特值</param>
 | 
			
		||||
        /// <param name="bits2Len">第二个比特值的长度(位数)</param>
 | 
			
		||||
        /// <returns>合并后的整型值</returns>
 | 
			
		||||
        public static Result<ulong> 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
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 比特合并成整型
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="bits1">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits1Len">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits2">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="bits2Len">[TODO:parameter]</param>
 | 
			
		||||
        /// <returns>[TODO:return]</returns>
 | 
			
		||||
        /// <param name="bits1">第一个比特值</param>
 | 
			
		||||
        /// <param name="bits1Len">第一个比特值的长度(位数)</param>
 | 
			
		||||
        /// <param name="bits2">第二个比特值</param>
 | 
			
		||||
        /// <param name="bits2Len">第二个比特值的长度(位数)</param>
 | 
			
		||||
        /// <returns>合并后的整型值</returns>
 | 
			
		||||
        public static Result<uint> 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
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 比特位检查
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="srcBits">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="dstBits">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="mask">[TODO:parameter]</param>
 | 
			
		||||
        /// <returns>[TODO:return]</returns>
 | 
			
		||||
        /// <param name="srcBits">源比特值</param>
 | 
			
		||||
        /// <param name="dstBits">目标比特值</param>
 | 
			
		||||
        /// <param name="mask">掩码(默认为全1)</param>
 | 
			
		||||
        /// <returns>检查结果(是否匹配)</returns>
 | 
			
		||||
        public static bool BitsCheck(ulong srcBits, ulong dstBits, ulong mask = 0xFFFF_FFFF_FFFF_FFFF)
 | 
			
		||||
        {
 | 
			
		||||
            return (srcBits & mask) == dstBits;
 | 
			
		||||
@@ -143,10 +143,10 @@ namespace Common
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 比特位检查
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="srcBits">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="dstBits">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="mask">[TODO:parameter]</param>
 | 
			
		||||
        /// <returns>[TODO:return]</returns>
 | 
			
		||||
        /// <param name="srcBits">源比特值</param>
 | 
			
		||||
        /// <param name="dstBits">目标比特值</param>
 | 
			
		||||
        /// <param name="mask">掩码(默认为全1)</param>
 | 
			
		||||
        /// <returns>检查结果(是否匹配)</returns>
 | 
			
		||||
        public static bool BitsCheck(uint srcBits, uint dstBits, uint mask = 0xFFFF_FFFF)
 | 
			
		||||
        {
 | 
			
		||||
            return (srcBits & mask) == dstBits;
 | 
			
		||||
@@ -156,9 +156,9 @@ namespace Common
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 字符串转二进制字节数组
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="str">[TODO:parameter]</param>
 | 
			
		||||
        /// <param name="numBase">[TODO:parameter]</param>
 | 
			
		||||
        /// <returns>[TODO:return]</returns>
 | 
			
		||||
        /// <param name="str">输入的字符串</param>
 | 
			
		||||
        /// <param name="numBase">进制(默认为16进制)</param>
 | 
			
		||||
        /// <returns>转换后的二进制字节数组</returns>
 | 
			
		||||
        public static byte[] StringToBytes(string str, int numBase = 16)
 | 
			
		||||
        {
 | 
			
		||||
            var len = str.Length;
 | 
			
		||||
@@ -173,10 +173,52 @@ namespace Common
 | 
			
		||||
            return bytes;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 反转字节数组中的子数组
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="srcBytes">源字节数组</param>
 | 
			
		||||
        /// <param name="distance">子数组的长度(反转的步长)</param>
 | 
			
		||||
        /// <returns>反转后的字节数组</returns>
 | 
			
		||||
        public static Result<byte[]> 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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 字符串处理工具
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class String
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 反转字符串
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="s">输入的字符串</param>
 | 
			
		||||
        /// <returns>反转后的字符串</returns>
 | 
			
		||||
        public static string Reverse(string s)
 | 
			
		||||
        {
 | 
			
		||||
            char[] charArray = s.ToCharArray();
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 日志控制器
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
public class Log : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 日志文件路径
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly string _logFilePath = Directory.GetFiles(Directory.GetCurrentDirectory())[0];
 | 
			
		||||
 | 
			
		||||
    [EnableCors("Development")]
 | 
			
		||||
    [HttpGet]
 | 
			
		||||
    public async Task<IActionResult> 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<IActionResult> 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();
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -229,11 +229,11 @@ public class Jtag
 | 
			
		||||
    private IPEndPoint ep;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Jtag构造函数
 | 
			
		||||
    /// Jtag 构造函数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="address">目标IP地址</param>
 | 
			
		||||
    /// <param name="port">目标UDP端口</param>
 | 
			
		||||
    /// <param name="timeout">超时时间</param>
 | 
			
		||||
    /// <param name="address">目标 IP 地址</param>
 | 
			
		||||
    /// <param name="port">目标 UDP 端口</param>
 | 
			
		||||
    /// <param name="timeout">超时时间(毫秒)</param>
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 向指定的 JTAG 设备地址写入数据到 FIFO
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="devAddr">目标设备地址</param>
 | 
			
		||||
    /// <param name="data">要写入的数据</param>
 | 
			
		||||
    /// <param name="delayMilliseconds">写入后的延迟时间(毫秒)</param>
 | 
			
		||||
    /// <returns>包含接收数据包的异步结果</returns>
 | 
			
		||||
    public async ValueTask<Result<RecvDataPackage>> WriteFIFO(UInt32 devAddr, UInt32 data, UInt32 delayMilliseconds = 0)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = false;
 | 
			
		||||
@@ -462,16 +469,28 @@ public class Jtag
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 清除所有 JTAG 寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>指示清除是否成功的异步结果</returns>
 | 
			
		||||
    async ValueTask<Result<bool>> ClearAllRegisters()
 | 
			
		||||
    {
 | 
			
		||||
        return await WriteFIFO(JtagAddr.STATE, 0xFF_FF_FF_FF, 0x01_02_02_02, JtagState.ALL_REG);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 清除 JTAG 写数据寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>指示清除是否成功的异步结果</returns>
 | 
			
		||||
    async ValueTask<Result<bool>> ClearWriteDataReg()
 | 
			
		||||
    {
 | 
			
		||||
        return await WriteFIFO(JtagAddr.STATE, 0x00_00_11_00, 0x01_00_02_00, JtagState.WRITE_DATA_FIFO | JtagState.CMD_EXEC_FINISH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 关闭 JTAG 测试模式
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>指示操作是否成功的异步结果</returns>
 | 
			
		||||
    async ValueTask<Result<bool>> CloseTest()
 | 
			
		||||
    {
 | 
			
		||||
        return await WriteFIFO(
 | 
			
		||||
@@ -480,6 +499,10 @@ public class Jtag
 | 
			
		||||
                0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启动 JTAG 测试模式
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>指示操作是否成功的异步结果</returns>
 | 
			
		||||
    async ValueTask<Result<bool>> RunTest()
 | 
			
		||||
    {
 | 
			
		||||
        return await WriteFIFO(
 | 
			
		||||
@@ -488,6 +511,11 @@ public class Jtag
 | 
			
		||||
                0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置 JTAG 空闲延迟时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="milliseconds">延迟时间(毫秒)</param>
 | 
			
		||||
    /// <returns>指示操作是否成功的异步结果</returns>
 | 
			
		||||
    async ValueTask<Result<bool>> 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!"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取 JTAG 设备的 ID 代码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>包含 ID 代码的异步结果</returns>
 | 
			
		||||
    public async ValueTask<Result<uint>> ReadIDCode()
 | 
			
		||||
    {
 | 
			
		||||
        // Clear Data
 | 
			
		||||
@@ -596,6 +628,10 @@ public class Jtag
 | 
			
		||||
        return retData.Value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取 JTAG 设备的状态寄存器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>包含状态寄存器值的异步结果</returns>
 | 
			
		||||
    public async ValueTask<Result<uint>> ReadStatusReg()
 | 
			
		||||
    {
 | 
			
		||||
        // Clear Data
 | 
			
		||||
@@ -630,6 +666,11 @@ public class Jtag
 | 
			
		||||
        return retData.Value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 下载比特流到 JTAG 设备
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="bitstream">比特流数据</param>
 | 
			
		||||
    /// <returns>指示下载是否成功的异步结果</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> 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"));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -81,9 +81,13 @@ public class UDPServer
 | 
			
		||||
    /// <summary> UDP 服务器的错误代码 </summary>
 | 
			
		||||
    public enum ErrorCode
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary> [TODO:description] </summary>
 | 
			
		||||
        Success = 0,
 | 
			
		||||
        /// <summary> [TODO:description] </summary>
 | 
			
		||||
        GetNoneAfterTimeout,
 | 
			
		||||
        /// <summary> [TODO:description] </summary>
 | 
			
		||||
        ResponseWrong,
 | 
			
		||||
        /// <summary> [TODO:description] </summary>
 | 
			
		||||
        NotRecvDataPackage,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/App.vue
									
									
									
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import iconMenu from "./assets/menu.svg";
 | 
			
		||||
import Sidebar from "./components/Sidebar.vue";
 | 
			
		||||
import Navbar from "./components/Navbar.vue";
 | 
			
		||||
import { useThemeStore } from "./stores/theme";
 | 
			
		||||
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
@@ -14,14 +15,21 @@ const items = [
 | 
			
		||||
<template>
 | 
			
		||||
  <div :data-theme="theme.currentTheme">
 | 
			
		||||
    <header class="relative">
 | 
			
		||||
      <div class="fixed left-0 top-0 z-50">
 | 
			
		||||
      <div class="fixed left-0 top-0 z-50 hidden">
 | 
			
		||||
        <Sidebar :items="items" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <Navbar></Navbar>
 | 
			
		||||
    </header>
 | 
			
		||||
 | 
			
		||||
    <main>
 | 
			
		||||
      <RouterView />
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <footer class="footer footer-center p-4 bg-base-300 text-base-content">
 | 
			
		||||
      <div>
 | 
			
		||||
        <p>Copyright © 2023 - All right reserved by OurEDA</p>
 | 
			
		||||
      </div>
 | 
			
		||||
    </footer>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
  <div class="navbar bg-base-100 shadow-sm">
 | 
			
		||||
    <div class="navbar-start">
 | 
			
		||||
      <div class="dropdown">
 | 
			
		||||
        <div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
 | 
			
		||||
        <div tabindex="0" role="button" class="btn btn-ghost hidden">
 | 
			
		||||
          <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 | 
			
		||||
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" />
 | 
			
		||||
          </svg>
 | 
			
		||||
@@ -19,25 +19,13 @@
 | 
			
		||||
          <li><a>Item 3</a></li>
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
      <a class="btn btn-ghost text-xl">daisyUI</a>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="navbar-center hidden lg:flex">
 | 
			
		||||
      <ul class="menu menu-horizontal px-1">
 | 
			
		||||
        <li><a>Item 1</a></li>
 | 
			
		||||
        <li>
 | 
			
		||||
          <details>
 | 
			
		||||
            <summary>Parent</summary>
 | 
			
		||||
            <ul class="p-2">
 | 
			
		||||
              <li><a>Submenu 1</a></li>
 | 
			
		||||
              <li><a>Submenu 2</a></li>
 | 
			
		||||
            </ul>
 | 
			
		||||
          </details>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li><a>Item 3</a></li>
 | 
			
		||||
      </ul>
 | 
			
		||||
    <div class="navbar-center lg:flex">
 | 
			
		||||
      <a class="btn btn-ghost text-xl">FPGA Web Lab</a>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="navbar-end">
 | 
			
		||||
      <a class="btn">Button</a>
 | 
			
		||||
      <a class="btn btn-soft w-20 mx-10">注册</a>
 | 
			
		||||
      <a class="btn btn-primary w-25">登录</a>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,14 @@ import LoginView from "../views/LoginView.vue";
 | 
			
		||||
import UserView from "../views/UserView.vue";
 | 
			
		||||
import TestView from "../views/TestView.vue";
 | 
			
		||||
import JtagTest from "../views/JtagTest.vue";
 | 
			
		||||
import HomeView from "@/views/HomeView.vue";
 | 
			
		||||
 | 
			
		||||
const routes = [
 | 
			
		||||
  { path: "/", redirect: "/user" },
 | 
			
		||||
  { path: "/", name: "Home", component: HomeView },
 | 
			
		||||
  { path: "/login", name: "Login", component: LoginView },
 | 
			
		||||
  { path: "/user", name: "User", component: UserView },
 | 
			
		||||
  { path: "/test", name: "Test", component: TestView },
 | 
			
		||||
  { path: "/test/jtag", name:"JtagTest", component: JtagTest}
 | 
			
		||||
  { path: "/test/jtag", name: "JtagTest", component: JtagTest }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const router = createRouter({
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/views/HomeView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/views/HomeView.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="bg-base-200 min-h-screen">
 | 
			
		||||
    <main class="hero min-h-screen bg-base-200">
 | 
			
		||||
      <div class="hero-content flex-col lg:flex-row-reverse">
 | 
			
		||||
        <img src="https://placehold.co/600x400" class="max-w-sm rounded-lg shadow-2xl" />
 | 
			
		||||
        <div>
 | 
			
		||||
          <h1 class="text-5xl font-bold">Welcome to FPGA Web Lab!</h1>
 | 
			
		||||
          <p class="py-6">
 | 
			
		||||
            Prototype and simulate electronic circuits in your browser.
 | 
			
		||||
          </p>
 | 
			
		||||
          <button class="btn btn-primary">Get Started</button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </main>
 | 
			
		||||
 | 
			
		||||
    <div class="fixed bottom-10 right-10 btn btn-circle">
 | 
			
		||||
      <ThemeControlButton class=""></ThemeControlButton>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import ThemeControlButton from "@/components/ThemeControlButton.vue";
 | 
			
		||||
import "@/router";
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
@import "../assets/main.css";
 | 
			
		||||
</style>
 | 
			
		||||
@@ -3,44 +3,38 @@
 | 
			
		||||
    <div class="h-full w-32"></div>
 | 
			
		||||
 | 
			
		||||
    <div class="h-full w-[70%] shadow-2xl flex">
 | 
			
		||||
      <p>{{ logText }}</p>
 | 
			
		||||
 | 
			
		||||
      <button class="btn btn-primary h-10 w-30">获取ID Code</button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ref } from "vue";
 | 
			
		||||
const eventSource = new EventSource("http://localhost:5000/api/log");
 | 
			
		||||
const logText = ref("");
 | 
			
		||||
import { ref, onMounted, onUnmounted } from "vue";
 | 
			
		||||
 | 
			
		||||
// 启动SSE连接
 | 
			
		||||
function startStream() {
 | 
			
		||||
  eventSource.onmessage = function (e) {
 | 
			
		||||
    logText.value = e.data;
 | 
			
		||||
const eventSource = ref<EventSource>();
 | 
			
		||||
 | 
			
		||||
const initSource = () => {
 | 
			
		||||
  eventSource.value = new EventSource("http://localhost:500/api/log/example");
 | 
			
		||||
 | 
			
		||||
  eventSource.value.onmessage = (event) => {
 | 
			
		||||
    console.log("收到消息内容是:", event.data);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  eventSource.onerror = function (err) {
 | 
			
		||||
    console.error("Error:", err);
 | 
			
		||||
    eventSource.close();
 | 
			
		||||
  eventSource.value.onerror = (error) => {
 | 
			
		||||
    console.error("SSE 连接出错:", error);
 | 
			
		||||
    eventSource.value!.close();
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
  eventSource.onopen = function () {
 | 
			
		||||
    console.log("Connection opened");
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  initSource();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 停止SSE连接
 | 
			
		||||
function stopStream() {
 | 
			
		||||
onUnmounted(() => {
 | 
			
		||||
  if (eventSource) {
 | 
			
		||||
    eventSource.close();
 | 
			
		||||
    console.log("Connection closed");
 | 
			
		||||
    eventSource.value?.close();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 页面加载时自动启动
 | 
			
		||||
window.onload = startStream;
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped lang="postcss">
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user