maybe fix bug: download bitstream failed

This commit is contained in:
SikongJueluo 2025-04-23 20:22:49 +08:00
parent 5ea541ef4b
commit 4e752d4c9e
No known key found for this signature in database
5 changed files with 229 additions and 54 deletions

View File

@ -3,20 +3,35 @@
pwd pwd
echo echo
# 清空构建文件
clean: clean:
rm -rf "server/bin" rm -rf "server/bin"
rm -rf "server/obj" rm -rf "server/obj"
rm -rf "server.test/bin" rm -rf "server.test/bin"
rm -rf "server.test/obj" rm -rf "server.test/obj"
rm -rf "dist"
# 生成Restful API到网页客户端
gen-api:
cd server && dotnet run &
npx nswag openapi2tsclient /input:http://localhost:5000/swagger/v1/swagger.json /output:src/APIClient.ts
pkill server
# 构建服务器包含win与linux平台
[working-directory: "server"] [working-directory: "server"]
publish: _show-dir build-server: _show-dir
dotnet publish --self-contained false -t:PublishAllRids dotnet publish --self-contained false -t:PublishAllRids
# 运行服务器
[working-directory: "server"] [working-directory: "server"]
run-server: _show-dir run-server: _show-dir
dotnet run dotnet run
# 运行网页客户端
run-web:
npm run dev
# 运行测试用例测试服务器
[working-directory: "server.test"] [working-directory: "server.test"]
test-server: _show-dir test-server: _show-dir
dotnet test --logger "console;verbosity=detailed" dotnet test --logger "console;verbosity=detailed"

View File

@ -251,46 +251,150 @@ public class JtagController : ControllerBase
{ {
// 读取文件 // 读取文件
var filePath = Directory.GetFiles(fileDir)[0]; 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 using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
byte[] buffer = new byte[32 * 1024];
long totalBytesRead = 0;
// 使用异步流读取文件
using (var memoryStream = new MemoryStream())
{ {
int bytesRead; if (fileStream is null || fileStream.Length <= 0)
while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.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())
{ {
await memoryStream.WriteAsync(buffer, 0, bytesRead); int bytesRead;
totalBytesRead += bytesRead; 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)
{
logger.Info($"Device {address} dowload bitstream successfully");
return TypedResults.Ok(ret.Value);
}
else
{
logger.Error(ret.Error);
return TypedResults.InternalServerError(ret.Error);
}
} }
// 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
var fileBytes = memoryStream.ToArray();
// 下载比特流
var jtagCtrl = new JtagClient.Jtag(address, port);
var ret = await jtagCtrl.DownloadBitstream(fileBytes);
if (ret.IsSuccessful)
{
logger.Info($"Device {address} dowload bitstream successfully");
return TypedResults.Ok(ret.Value);
}
else
{
logger.Error(ret.Error);
return TypedResults.InternalServerError(ret.Error);
}
} }
} }
catch (Exception error) catch (Exception error)
{ {
return TypedResults.InternalServerError(error); return TypedResults.InternalServerError(error);
} }
finally
{
}
} }
} }
[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];
[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();
}
[HttpGet("example")]
public async Task<IActionResult> SseExample()
{
// 请求头
Response.Headers.Add("Content-Type", "text/event-stream");
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
// 假设不断得到新数据
for (int i = 0; i < 10000; i++)
{
// 每个数据参照 SSE 返回数据格式进行组装
var content = "event: message\n"
+ "data: {\"color\":\"66ccff\"}\n\n";
// 立刻写入响应
await Response.WriteAsync(content);
await Response.Body.FlushAsync();
}
// 结束
return new EmptyResult();
}
}

View File

@ -220,6 +220,8 @@ public class Jtag
{ {
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private const int CLOCK_FREQ = 50; // MHz
readonly int timeout; readonly int timeout;
readonly int port; readonly int port;
@ -274,7 +276,7 @@ public class Jtag
return Convert.ToUInt32(Common.Number.BytesToNumber(retPackOpts.Data).Value); return Convert.ToUInt32(Common.Number.BytesToNumber(retPackOpts.Data).Value);
} }
public async ValueTask<Result<RecvDataPackage>> WriteFIFO(UInt32 devAddr, UInt32 data) public async ValueTask<Result<RecvDataPackage>> WriteFIFO(UInt32 devAddr, UInt32 data, UInt32 delayMilliseconds = 0)
{ {
var ret = false; var ret = false;
var opts = new SendAddrPackOptions(); var opts = new SendAddrPackOptions();
@ -303,6 +305,9 @@ public class Jtag
else if (!udpWriteAck.Value.IsSuccessful) else if (!udpWriteAck.Value.IsSuccessful)
return new(new Exception("Send address package failed")); return new(new Exception("Send address package failed"));
// Delay some time before read register
await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds));
// Read Jtag State Register // Read Jtag State Register
opts.IsWrite = false; opts.IsWrite = false;
opts.Address = JtagAddr.STATE; opts.Address = JtagAddr.STATE;
@ -317,7 +322,7 @@ public class Jtag
return udpDataResp.Value; return udpDataResp.Value;
} }
async ValueTask<Result<RecvDataPackage>> WriteFIFO(UInt32 devAddr, byte[] dataArray) async ValueTask<Result<RecvDataPackage>> WriteFIFO(UInt32 devAddr, byte[] dataArray, UInt32 delayMilliseconds = 0)
{ {
var ret = false; var ret = false;
var opts = new SendAddrPackOptions(); var opts = new SendAddrPackOptions();
@ -334,20 +339,21 @@ public class Jtag
var writeTimes = dataArray.Length / (256 * (32 / 8)) + 1; var writeTimes = dataArray.Length / (256 * (32 / 8)) + 1;
for (var i = 0; i < writeTimes; i++) for (var i = 0; i < writeTimes; i++)
{ {
// Sperate Data Array
var isLastData = i == writeTimes - 1; var isLastData = i == writeTimes - 1;
var sendDataArray =
isLastData ?
dataArray[(i * (256 * (32 / 8)))..] :
dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)))];
// Write Jtag State Register // Write Jtag State Register
opts.IsWrite = true; opts.IsWrite = true;
opts.BurstLength = isLastData ? (byte)(dataArray.Length - i * (256 * (32 / 8)) - 1) : (byte)255; opts.BurstLength = ((byte)(sendDataArray.Length / 4 - 1));
ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts)); ret = await UDPClientPool.SendAddrPackAsync(ep, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send 1st address package failed!")); if (!ret) return new(new Exception("Send 1st address package failed!"));
// Send Data Package // Send Data Package
ret = await UDPClientPool.SendDataPackAsync(ep, ret = await UDPClientPool.SendDataPackAsync(ep, new SendDataPackage(sendDataArray));
new SendDataPackage(
isLastData ?
dataArray[(i * (256 * (32 / 8)))..] :
dataArray[(i * (256 * (32 / 8)))..((i + 1) * (256 * (32 / 8)) - 1)]
)
);
if (!ret) return new(new Exception("Send data package failed!")); if (!ret) return new(new Exception("Send data package failed!"));
// Wait for Write Ack // Wait for Write Ack
@ -357,6 +363,9 @@ public class Jtag
return new(new Exception("Send address package failed")); return new(new Exception("Send address package failed"));
} }
// Delay some time before read register
await Task.Delay(TimeSpan.FromMilliseconds(delayMilliseconds));
// Read Jtag State Register // Read Jtag State Register
opts.IsWrite = false; opts.IsWrite = false;
opts.BurstLength = 0; opts.BurstLength = 0;
@ -373,10 +382,10 @@ public class Jtag
} }
async ValueTask<Result<bool>> WriteFIFO async ValueTask<Result<bool>> WriteFIFO
(UInt32 devAddr, UInt32 data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF) (UInt32 devAddr, UInt32 data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 delayMilliseconds = 0)
{ {
var ret = false; var ret = false;
var retPack = await WriteFIFO(devAddr, data); var retPack = await WriteFIFO(devAddr, data, delayMilliseconds);
if (!retPack.IsSuccessful) return new(retPack.Error); if (!retPack.IsSuccessful) return new(retPack.Error);
if (retPack.Value.Options.Data is null) if (retPack.Value.Options.Data is null)
@ -394,10 +403,10 @@ public class Jtag
} }
async ValueTask<Result<bool>> WriteFIFO async ValueTask<Result<bool>> WriteFIFO
(UInt32 devAddr, byte[] data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF) (UInt32 devAddr, byte[] data, UInt32 result, UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 delayMilliseconds = 0)
{ {
var ret = false; var ret = false;
var retPack = await WriteFIFO(devAddr, data); var retPack = await WriteFIFO(devAddr, data, delayMilliseconds);
if (retPack.Value.Options.Data is null) if (retPack.Value.Options.Data is null)
return new(new Exception($"Data is Null, package: {retPack.Value.Options.ToString()}")); return new(new Exception($"Data is Null, package: {retPack.Value.Options.ToString()}"));
@ -413,6 +422,45 @@ public class Jtag
return ret; return ret;
} }
async ValueTask<Result<bool>> WaitForWriteFIFO
(UInt32 devAddr, byte[] data, UInt32 result,
UInt32 resultMask = 0xFF_FF_FF_FF, UInt32 timeout = 10_000, UInt32 cycle = 500)
{
{
var wrRet = await WriteFIFO(devAddr, data, result, resultMask);
if (!wrRet.IsSuccessful) return new(wrRet.Error);
if (wrRet.Value) return true;
}
// Wait some time
var ret = false;
var startTime = DateTime.Now;
var isTimeout = false;
var timeleft = TimeSpan.FromMilliseconds(timeout);
while (!isTimeout)
{
// Check whether timeout
var elapsed = DateTime.Now - startTime;
isTimeout = elapsed >= TimeSpan.FromMilliseconds(timeout);
if (isTimeout) break;
timeleft = TimeSpan.FromMilliseconds(timeout) - elapsed;
// Check FIFO
var retPack = await ReadFIFO(JtagAddr.STATE);
if (Common.Number.BitsCheck(retPack.Value, result, resultMask))
{
ret = true;
break;
}
// Wait
await Task.Delay(TimeSpan.FromMilliseconds(cycle));
}
return ret;
}
async ValueTask<Result<bool>> ClearAllRegisters() async ValueTask<Result<bool>> ClearAllRegisters()
{ {
@ -447,7 +495,7 @@ public class Jtag
return await WriteFIFO( return await WriteFIFO(
JtagAddr.WRITE_CMD, JtagAddr.WRITE_CMD,
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_IDLE_DELAY, JtagCmd.LEN_CMD_JTAG, milliseconds, 28).Value, Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_IDLE_DELAY, JtagCmd.LEN_CMD_JTAG, milliseconds, 28).Value,
0x01_00_00_00, JtagState.CMD_EXEC_FINISH); 0x01_00_00_00, JtagState.CMD_EXEC_FINISH, (milliseconds / CLOCK_FREQ) + 100);
} }
async ValueTask<Result<bool>> ExecRDCmd(uint command) async ValueTask<Result<bool>> ExecRDCmd(uint command)
@ -472,7 +520,7 @@ public class Jtag
return await ClearWriteDataReg(); return await ClearWriteDataReg();
} }
async ValueTask<Result<bool>> LoadDRCareInput(byte[] bytesArray) async ValueTask<Result<bool>> LoadDRCareInput(byte[] bytesArray, UInt32 timeout = 10_000, UInt32 cycle = 500)
{ {
var bytesLen = ((uint)(bytesArray.Length * 8)); var bytesLen = ((uint)(bytesArray.Length * 8));
if (bytesLen > Math.Pow(2, 28)) return new(new Exception("Length is over 2^(28 - 3)")); if (bytesLen > Math.Pow(2, 28)) return new(new Exception("Length is over 2^(28 - 3)"));
@ -481,13 +529,17 @@ public class Jtag
var ret = await WriteFIFO( var ret = await WriteFIFO(
JtagAddr.WRITE_CMD, JtagAddr.WRITE_CMD,
Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREI, JtagCmd.LEN_CMD_JTAG, bytesLen, 28).Value, Common.Number.MultiBitsToNumber(JtagCmd.CMD_JTAG_LOAD_DR_CAREI, JtagCmd.LEN_CMD_JTAG, bytesLen, 28).Value,
0x01_00_00_00, JtagState.CMD_EXEC_FINISH); 0, 0);
if (!ret.IsSuccessful) return new(ret.Error); if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Write CMD_JTAG_LOAD_DR_CAREI Failed")); else if (!ret.Value) return new(new Exception("Write CMD_JTAG_LOAD_DR_CAREI Failed"));
} }
{ {
var ret = await WriteFIFO(JtagAddr.WRITE_DATA, bytesArray, 0x01_00_00_00, JtagState.CMD_EXEC_FINISH); var ret = await WaitForWriteFIFO(
JtagAddr.WRITE_DATA,
bytesArray, 0x01_00_00_00,
JtagState.CMD_EXEC_FINISH,
timeout, cycle);
if (!ret.IsSuccessful) return new(ret.Error); if (!ret.IsSuccessful) return new(ret.Error);
else if (!ret.Value) return new(new Exception("Write Data Failed")); else if (!ret.Value) return new(new Exception("Write Data Failed"));

View File

@ -116,6 +116,7 @@ public class UDPServer
/// </summary> /// </summary>
/// <param name="ipAddr"> 目标IP地址 </param> /// <param name="ipAddr"> 目标IP地址 </param>
/// <param name="timeout">超时时间</param> /// <param name="timeout">超时时间</param>
/// <param name="cycle">延迟时间</param>
/// <param name="callerName">调用函数名称</param> /// <param name="callerName">调用函数名称</param>
/// <param name="callerLineNum">调用函数位置</param> /// <param name="callerLineNum">调用函数位置</param>
/// <returns> /// <returns>
@ -124,7 +125,7 @@ public class UDPServer
/// Optional 存在时,为最先收到的数据 /// Optional 存在时,为最先收到的数据
/// </returns> /// </returns>
public async ValueTask<Optional<UDPData>> FindDataAsync( public async ValueTask<Optional<UDPData>> FindDataAsync(
string ipAddr, int timeout = 1000, string ipAddr, int timeout = 1000, int cycle = 0,
[CallerMemberName] string callerName = "", [CallerMemberName] string callerName = "",
[CallerLineNumber] int callerLineNum = 0) [CallerLineNumber] int callerLineNum = 0)
{ {
@ -153,6 +154,8 @@ public class UDPServer
break; break;
} }
} }
await Task.Delay(cycle);
} }
if (data is null) if (data is null)
@ -171,8 +174,9 @@ public class UDPServer
/// </summary> /// </summary>
/// <param name="ipAddr">IP地址</param> /// <param name="ipAddr">IP地址</param>
/// <param name="timeout">超时时间</param> /// <param name="timeout">超时时间</param>
/// <param name="cycle">延迟时间</param>
/// <returns>数据列表</returns> /// <returns>数据列表</returns>
public async ValueTask<Optional<List<UDPData>>> GetDataArrayAsync(string ipAddr, int timeout = 1000) public async ValueTask<Optional<List<UDPData>>> GetDataArrayAsync(string ipAddr, int timeout = 1000, int cycle = 0)
{ {
List<UDPData>? data = null; List<UDPData>? data = null;

View File

@ -20,10 +20,10 @@ namespace WebProtocol
/// <summary> Package Burst Type </summary> /// <summary> Package Burst Type </summary>
public enum BurstType public enum BurstType
{ {
/// <summary> Extended Type </summary>
ExtendBurst = 0b00,
/// <summary> Fixed Type </summary> /// <summary> Fixed Type </summary>
FixedBurst = 0b01, FixedBurst = 0b00,
/// <summary> Extended Type </summary>
ExtendBurst = 0b01,
} }