From 98a5dbe7f3a3937a63602fb8760d37e5d15adf7e Mon Sep 17 00:00:00 2001
From: SikongJueluo <selfconfusion@gmail.com>
Date: Tue, 22 Apr 2025 17:24:46 +0800
Subject: [PATCH] try to fix server bug: couldn't download bitstream

---
 server.test/.gitignore    |   1 +
 server/.gitignore         |   1 +
 server/Program.cs         |   6 +-
 server/src/Controllers.cs |  94 +++++++++++++-----
 server/src/JtagClient.cs  | 203 +++++++++++++++++++-------------------
 server/src/UdpServer.cs   |  28 +++---
 6 files changed, 192 insertions(+), 141 deletions(-)

diff --git a/server.test/.gitignore b/server.test/.gitignore
index 1746e32..d676866 100644
--- a/server.test/.gitignore
+++ b/server.test/.gitignore
@@ -1,2 +1,3 @@
 bin
 obj
+bitstream
diff --git a/server/.gitignore b/server/.gitignore
index 501ec83..096eec5 100644
--- a/server/.gitignore
+++ b/server/.gitignore
@@ -1,3 +1,4 @@
 obj
 bin
+bitstream
 
diff --git a/server/Program.cs b/server/Program.cs
index 2a3902a..9e111eb 100644
--- a/server/Program.cs
+++ b/server/Program.cs
@@ -65,7 +65,11 @@ try
     // Application Settings
     var app = builder.Build();
     // Configure the HTTP request pipeline.
-    app.UseExceptionHandler("/Home/Error");
+    // app.UseExceptionHandler(new ExceptionHandlerOptions()
+    // {
+    //     AllowStatusCode404Response = true,
+    //     ExceptionHandlingPath = "/error"
+    // });
     // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
     app.UseHsts();
     app.UseHttpsRedirection();
diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs
index ecd5411..399e7c6 100644
--- a/server/src/Controllers.cs
+++ b/server/src/Controllers.cs
@@ -139,6 +139,8 @@ public class UDPController : ControllerBase
 [Route("api/[controller]")]
 public class JtagController : ControllerBase
 {
+    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
     /// <summary>
     /// 页面
     /// </summary>
@@ -184,48 +186,92 @@ public class JtagController : ControllerBase
         else { return TypedResults.InternalServerError(ret.Error); }
     }
 
+    /// <summary>
+    /// 上传比特流文件
+    /// </summary>
+    /// <param name="address"> 设备地址 </param>
+    /// <param name="file">比特流文件</param>
+    [HttpPost("UploadBitstream")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    public async ValueTask<IResult> UploadBitstream(string address, IFormFile file)
+    {
+        if (file == null || file.Length == 0)
+            return TypedResults.BadRequest("未选择文件");
+
+        // 生成安全的文件名(避免路径遍历攻击)
+        var fileName = Path.GetRandomFileName();
+        var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}");
+
+        // 如果存在文件,则删除原文件再上传
+        if (Directory.Exists(uploadsFolder))
+        {
+            Directory.Delete(uploadsFolder, true);
+        }
+        Directory.CreateDirectory(uploadsFolder);
+
+        var filePath = Path.Combine(uploadsFolder, fileName);
+
+        using (var stream = new FileStream(filePath, FileMode.Create))
+        {
+            await file.CopyToAsync(stream);
+        }
+
+        return TypedResults.Ok("文件上传成功");
+    }
+
     /// <summary>
     /// 通过Jtag下载比特流文件
     /// </summary>
     /// <param name="address"> 设备地址 </param>
     /// <param name="port"> 设备端口 </param>
-    /// <param name="file">比特流文件(最大32MB)</param>
-    [HttpGet("DownloadBitstream")]
+    [HttpPost("DownloadBitstream")]
     [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
     [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-    public async ValueTask<IResult> DownloadBitstream(string address, int port, IFormFile file)
+    public async ValueTask<IResult> DownloadBitstream(string address, int port)
     {
         // 检查文件
-        if (file is null || file.Length <= 0)
-            throw new ArgumentException("Empty file", nameof(file));
+        var fileDir = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}");
+        if (!Directory.Exists(fileDir))
+            return TypedResults.BadRequest("Empty bitstream, Please upload it first");
 
-        // 定义缓冲区大小(例如:1MB)
-        const int bufferSize = 1024 * 1024; // 1MB
-        byte[] buffer = new byte[bufferSize];
-        long totalBytesRead = 0;
-
-        // 使用异步流读取文件
-        using (var memoryStream = new MemoryStream())
+        try
         {
-            using (var stream = file.OpenReadStream())
+            // 读取文件
+            var filePath = Directory.GetFiles(fileDir)[0];
+            var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open);
+            if (fileStream is null || fileStream.Length <= 0)
+                return TypedResults.BadRequest("Wrong bitstream, Please upload it again");
+
+            // 定义缓冲区大小: 32KB
+            byte[] buffer = new byte[32 * 1024];
+            long totalBytesRead = 0;
+
+            // 使用异步流读取文件
+            using (var memoryStream = new MemoryStream())
             {
                 int bytesRead;
-                while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
+                while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                 {
                     await memoryStream.WriteAsync(buffer, 0, bytesRead);
                     totalBytesRead += bytesRead;
                 }
+
+                // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
+                var fileBytes = memoryStream.ToArray();
+
+                // 下载比特流
+                var jtagCtrl = new JtagClient.Jtag(address, port);
+                var ret = await jtagCtrl.DownloadBitstream(fileBytes);
+
+                if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); }
+                else { return TypedResults.InternalServerError(ret.Error); }
             }
-
-            // 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
-            byte[] fileBytes = memoryStream.ToArray();
-
-            // 下载比特流
-            var jtagCtrl = new JtagClient.Jtag(address, port);
-            var ret = await jtagCtrl.DownloadBitstream(fileBytes);
-
-            if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); }
-            else { return TypedResults.InternalServerError(ret.Error); }
+        }
+        catch (Exception error)
+        {
+            return TypedResults.InternalServerError(error);
         }
     }
 }
diff --git a/server/src/JtagClient.cs b/server/src/JtagClient.cs
index 9119f25..a724b18 100644
--- a/server/src/JtagClient.cs
+++ b/server/src/JtagClient.cs
@@ -287,11 +287,10 @@ class Jtag
         if (!MsgBus.IsRunning)
             throw new Exception("Message bus not working!");
         // Wait for Write Ack
-        {
-            var udpResp = await MsgBus.UDPServer.WaitForAckAsync(address, port);
-            if (!udpResp.IsSuccessful || !udpResp.Value.IsSuccessful)
-                throw new Exception("Send address package failed");
-        }
+        var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(address, port);
+        if (!udpWriteAck.IsSuccessful) throw udpWriteAck.Error;
+        else if (!udpWriteAck.Value.IsSuccessful)
+            throw new Exception("Send address package failed");
 
         // Read Jtag State Register
         opts.IsWrite = false;
@@ -299,13 +298,12 @@ class Jtag
         ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts));
         if (!ret) throw new Exception("Send 2rd Address Package Failed!");
         // Wait for Read Data
-        {
-            var udpResp = await MsgBus.UDPServer.WaitForDataAsync(address, port);
-            if (!udpResp.IsSuccessful || !udpResp.Value.IsSuccessful)
-                throw new Exception("Send address package failed");
+        var udpDataResp = await MsgBus.UDPServer.WaitForDataAsync(address, port);
+        if (!udpDataResp.IsSuccessful) throw udpDataResp.Error;
+        else if (!udpDataResp.Value.IsSuccessful)
+            throw new Exception("Send address package failed");
 
-            return udpResp.Value;
-        }
+        return udpDataResp.Value;
     }
 
     public async ValueTask<Result<RecvDataPackage>> WriteFIFO(UInt32 devAddr, byte[] dataArray)
@@ -342,11 +340,10 @@ class Jtag
             if (!ret) throw new Exception("Send data package failed!");
 
             // Wait for Write Ack
-            {
-                var udpResp = await MsgBus.UDPServer.WaitForAckAsync(address, port);
-                if (!udpResp.IsSuccessful || !udpResp.Value.IsSuccessful)
-                    throw new Exception("Send address package failed");
-            }
+            var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(address, port);
+            if (!udpWriteAck.IsSuccessful) throw udpWriteAck.Error;
+            else if (!udpWriteAck.Value.IsSuccessful)
+                throw new Exception("Send address package failed");
         }
 
         // Read Jtag State Register
@@ -356,13 +353,12 @@ class Jtag
         ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts));
         if (!ret) throw new Exception("Send 2rd Address Package Failed!");
         // Wait for Read Data
-        {
-            var udpResp = await MsgBus.UDPServer.WaitForDataAsync(address, port);
-            if (!udpResp.IsSuccessful || !udpResp.Value.IsSuccessful)
-                throw new Exception("Send address package failed");
+        var udpDataResp = await MsgBus.UDPServer.WaitForDataAsync(address, port);
+        if (!udpDataResp.IsSuccessful) throw udpDataResp.Error;
+        else if (!udpDataResp.Value.IsSuccessful)
+            throw new Exception("Send address package failed");
 
-            return udpResp.Value;
-        }
+        return udpDataResp.Value;
     }
 
     public async ValueTask<Result<bool>> WriteFIFO
@@ -370,6 +366,7 @@ class Jtag
     {
         var ret = false;
         var retPack = await WriteFIFO(devAddr, data);
+        if (!retPack.IsSuccessful) throw retPack.Error;
 
         if (retPack.Value.Options.Data is null)
             throw new Exception($"Data is Null, package: {retPack.Value.Options.ToString()}");
@@ -507,27 +504,31 @@ class Jtag
         // Clear Data
         await MsgBus.UDPServer.ClearUDPData(this.address);
 
+        Result<bool> ret;
+
+        ret = await ClearAllRegisters();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Clear All Registers Failed");
+
+        ret = await RunTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
+
+        ret = await ExecRDCmd(JtagCmd.JTAG_DR_IDCODE);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_IDCODE Failed");
+
+        ret = await ClearWriteDataReg();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Clear Write Registers Failed");
+
+        var retData = await LoadDRCareOutput(4);
+        if (!retData.IsSuccessful)
         {
-            var ret = await ClearAllRegisters();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Clear All Registers Failed");
+            throw new Exception("Get ID Code Failed");
         }
-        {
-            var ret = await RunTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
-        }
-        {
-            var ret = await ExecRDCmd(JtagCmd.JTAG_DR_IDCODE);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_IDCODE Failed");
-        }
-        {
-            var ret = await ClearWriteDataReg();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Clear Write Registers Failed");
-        }
-        return (await LoadDRCareOutput(1)).Value;
+
+        return retData.Value;
     }
 
     public async ValueTask<Result<bool>> DownloadBitstream(byte[] bitstream)
@@ -535,66 +536,66 @@ class Jtag
         // Clear Data
         await MsgBus.UDPServer.ClearUDPData(this.address);
 
-        {
-            var ret = await CloseTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Close Test Failed");
-        }
-        {
-            var ret = await RunTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
-        }
-        {
-            var ret = await ExecRDCmd(JtagCmd.JTAG_DR_JRST);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_JRST Failed");
-        }
-        {
-            var ret = await RunTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
-        }
-        {
-            var ret = await ExecRDCmd(JtagCmd.JTAG_DR_CFGI);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_CFGI Failed");
-        }
-        {
-            var ret = await IdleDelay(75000);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag IDLE Delay Failed");
-        }
-        {
-            var ret = await LoadDRCareInput(bitstream);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Load Data Failed");
-        }
-        {
-            var ret = await CloseTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Close Test Failed");
-        }
-        {
-            var ret = await RunTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
-        }
-        {
-            var ret = await ExecRDCmd(JtagCmd.JTAG_DR_JWAKEUP);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_JWAKEUP Failed");
-        }
-        {
-            var ret = await IdleDelay(1000);
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag IDLE Delay Failed");
-        }
-        {
-            var ret = await CloseTest();
-            if (!ret.IsSuccessful) throw ret.Error;
-            else if (!ret.Value) throw new Exception("Jtag Close Test Failed");
-        }
+        Result<bool> ret;
+
+        ret = await CloseTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Close Test Failed");
+
+
+        ret = await RunTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
+
+
+        ret = await ExecRDCmd(JtagCmd.JTAG_DR_JRST);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_JRST Failed");
+
+
+        ret = await RunTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
+
+
+        ret = await ExecRDCmd(JtagCmd.JTAG_DR_CFGI);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_CFGI Failed");
+
+
+        ret = await IdleDelay(75000);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag IDLE Delay Failed");
+
+
+        ret = await LoadDRCareInput(bitstream);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Load Data Failed");
+
+
+        ret = await CloseTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Close Test Failed");
+
+
+        ret = await RunTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Run Test Failed");
+
+
+        ret = await ExecRDCmd(JtagCmd.JTAG_DR_JWAKEUP);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Execute Command JTAG_DR_JWAKEUP Failed");
+
+
+        ret = await IdleDelay(1000);
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag IDLE Delay Failed");
+
+
+        ret = await CloseTest();
+        if (!ret.IsSuccessful) throw ret.Error;
+        else if (!ret.Value) throw new Exception("Jtag Close Test Failed");
 
         return true;
     }
diff --git a/server/src/UdpServer.cs b/server/src/UdpServer.cs
index 1295bbe..e5f15f2 100644
--- a/server/src/UdpServer.cs
+++ b/server/src/UdpServer.cs
@@ -136,11 +136,11 @@ public class UDPServer
 
             using (await udpData.AcquireWriteLockAsync(timeleft))
             {
-                if (udpData.TryGetValue(ipAddr, out var dataQueue) && dataQueue != null && dataQueue.Count > 0)
+                if (udpData.ContainsKey(ipAddr) &&
+                    udpData.TryGetValue(ipAddr, out var dataQueue) &&
+                    dataQueue.Count > 0)
                 {
                     data = dataQueue.Dequeue();
-                    // data = dataList[0].DeepClone();
-                    // dataList.RemoveAt(0);
                     logger.Debug($"Find UDP Data: {data.ToString()}");
                     break;
                 }
@@ -150,7 +150,7 @@ public class UDPServer
         if (data is null)
         {
             logger.Trace("Get nothing even after time out");
-            return Optional.None<UDPData>();
+            return Optional<UDPData>.None;
         }
         else
         {
@@ -179,7 +179,9 @@ public class UDPServer
 
             using (await udpData.AcquireReadLockAsync(timeleft))
             {
-                if (udpData.TryGetValue(ipAddr, out var dataQueue) && dataQueue != null && dataQueue.Count > 0)
+                if (udpData.ContainsKey(ipAddr) &&
+                    udpData.TryGetValue(ipAddr, out var dataQueue) &&
+                    dataQueue.Count > 0)
                 {
                     data = dataQueue.ToList();
                     logger.Debug($"Find UDP Data Array: {JsonConvert.SerializeObject(data)}");
@@ -191,7 +193,7 @@ public class UDPServer
         if (data is null)
         {
             logger.Trace("Get nothing even after time out");
-            return Optional.None<List<UDPData>>();
+            return Optional<List<UDPData>>.None;
         }
         else
         {
@@ -214,7 +216,7 @@ public class UDPServer
             throw new Exception("Get None even after time out!");
 
         var recvData = data.Value;
-        if (recvData.Address != address || (port >= 0 && recvData.Port != port))
+        if (recvData.Address != address || (port > 0 && recvData.Port != port))
             throw new Exception("Receive Data From Wrong Board!");
 
         var retPack = WebProtocol.RecvRespPackage.FromBytes(recvData.Data);
@@ -305,19 +307,13 @@ public class UDPServer
         using (udpData.AcquireWriteLock())
         {
             // Record UDP Receive Data
-            // if (udpData.ContainsKey(remoteAddress))
-            if (udpData.TryGetValue(remoteAddress, out var dataQueue))
+            if (udpData.ContainsKey(remoteAddress) && udpData.TryGetValue(remoteAddress, out var dataQueue))
             {
-                // var listData = udpData[remoteAddress];
-                // listData.Add(data);
                 dataQueue.Enqueue(data);
                 logger.Trace("Receive data from old client");
             }
             else
             {
-                // var list = new List<UDPData>();
-                // list.Add(data);
-                // udpData.Add(remoteAddress, list);
                 var queue = new Queue<UDPData>();
                 queue.Enqueue(data);
                 udpData.Add(remoteAddress, queue);
@@ -401,7 +397,9 @@ public class UDPServer
     {
         using (await udpData.AcquireWriteLockAsync())
         {
-            if (udpData.TryGetValue(ipAddr, out var dataQueue) && dataQueue.Count > 0)
+            if (udpData.ContainsKey(ipAddr) &&
+                udpData.TryGetValue(ipAddr, out var dataQueue) &&
+                dataQueue.Count > 0)
             {
                 dataQueue.Clear();
             }