Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
|
b8bb4f6b5e | |
|
c76dabfdb7 | |
|
4e752d4c9e |
17
.justfile
17
.justfile
|
@ -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"
|
||||||
|
|
|
@ -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;
|
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
|
public class UDPServerTest
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,18 @@ try
|
||||||
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add CORS policy
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("Development", policy =>
|
||||||
|
{
|
||||||
|
policy
|
||||||
|
.AllowAnyOrigin()
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Add Swagger
|
// Add Swagger
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddOpenApiDocument(options =>
|
builder.Services.AddOpenApiDocument(options =>
|
||||||
|
@ -75,6 +87,7 @@ try
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
app.UseCors();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
// if (app.Environment.IsDevelopment())
|
// if (app.Environment.IsDevelopment())
|
||||||
|
|
|
@ -85,11 +85,11 @@ namespace Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 比特合并成二进制字节
|
/// 比特合并成二进制字节
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bits1">[TODO:parameter]</param>
|
/// <param name="bits1">第一个比特值</param>
|
||||||
/// <param name="bits1Len">[TODO:parameter]</param>
|
/// <param name="bits1Len">第一个比特值的长度(位数)</param>
|
||||||
/// <param name="bits2">[TODO:parameter]</param>
|
/// <param name="bits2">第二个比特值</param>
|
||||||
/// <param name="bits2Len">[TODO:parameter]</param>
|
/// <param name="bits2Len">第二个比特值的长度(位数)</param>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>合并后的二进制字节数组</returns>
|
||||||
public static Result<byte[]> MultiBitsToBytes(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len)
|
public static Result<byte[]> MultiBitsToBytes(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len)
|
||||||
{
|
{
|
||||||
return NumberToBytes(MultiBitsToNumber(bits1, bits1Len, bits2, bits2Len).Value,
|
return NumberToBytes(MultiBitsToNumber(bits1, bits1Len, bits2, bits2Len).Value,
|
||||||
|
@ -99,11 +99,11 @@ namespace Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 比特合并成整型
|
/// 比特合并成整型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bits1">[TODO:parameter]</param>
|
/// <param name="bits1">第一个比特值</param>
|
||||||
/// <param name="bits1Len">[TODO:parameter]</param>
|
/// <param name="bits1Len">第一个比特值的长度(位数)</param>
|
||||||
/// <param name="bits2">[TODO:parameter]</param>
|
/// <param name="bits2">第二个比特值</param>
|
||||||
/// <param name="bits2Len">[TODO:parameter]</param>
|
/// <param name="bits2Len">第二个比特值的长度(位数)</param>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>合并后的整型值</returns>
|
||||||
public static Result<ulong> MultiBitsToNumber(ulong bits1, uint bits1Len, ulong bits2, uint bits2Len)
|
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"));
|
if (bits1Len + bits2Len > 64) return new(new ArgumentException("Two Bits is more than 64 bits"));
|
||||||
|
@ -115,11 +115,11 @@ namespace Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 比特合并成整型
|
/// 比特合并成整型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bits1">[TODO:parameter]</param>
|
/// <param name="bits1">第一个比特值</param>
|
||||||
/// <param name="bits1Len">[TODO:parameter]</param>
|
/// <param name="bits1Len">第一个比特值的长度(位数)</param>
|
||||||
/// <param name="bits2">[TODO:parameter]</param>
|
/// <param name="bits2">第二个比特值</param>
|
||||||
/// <param name="bits2Len">[TODO:parameter]</param>
|
/// <param name="bits2Len">第二个比特值的长度(位数)</param>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>合并后的整型值</returns>
|
||||||
public static Result<uint> MultiBitsToNumber(uint bits1, uint bits1Len, uint bits2, uint bits2Len)
|
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"));
|
if (bits1Len + bits2Len > 64) return new(new ArgumentException("Two Bits is more than 64 bits"));
|
||||||
|
@ -131,10 +131,10 @@ namespace Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 比特位检查
|
/// 比特位检查
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="srcBits">[TODO:parameter]</param>
|
/// <param name="srcBits">源比特值</param>
|
||||||
/// <param name="dstBits">[TODO:parameter]</param>
|
/// <param name="dstBits">目标比特值</param>
|
||||||
/// <param name="mask">[TODO:parameter]</param>
|
/// <param name="mask">掩码(默认为全1)</param>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>检查结果(是否匹配)</returns>
|
||||||
public static bool BitsCheck(ulong srcBits, ulong dstBits, ulong mask = 0xFFFF_FFFF_FFFF_FFFF)
|
public static bool BitsCheck(ulong srcBits, ulong dstBits, ulong mask = 0xFFFF_FFFF_FFFF_FFFF)
|
||||||
{
|
{
|
||||||
return (srcBits & mask) == dstBits;
|
return (srcBits & mask) == dstBits;
|
||||||
|
@ -143,10 +143,10 @@ namespace Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 比特位检查
|
/// 比特位检查
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="srcBits">[TODO:parameter]</param>
|
/// <param name="srcBits">源比特值</param>
|
||||||
/// <param name="dstBits">[TODO:parameter]</param>
|
/// <param name="dstBits">目标比特值</param>
|
||||||
/// <param name="mask">[TODO:parameter]</param>
|
/// <param name="mask">掩码(默认为全1)</param>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>检查结果(是否匹配)</returns>
|
||||||
public static bool BitsCheck(uint srcBits, uint dstBits, uint mask = 0xFFFF_FFFF)
|
public static bool BitsCheck(uint srcBits, uint dstBits, uint mask = 0xFFFF_FFFF)
|
||||||
{
|
{
|
||||||
return (srcBits & mask) == dstBits;
|
return (srcBits & mask) == dstBits;
|
||||||
|
@ -156,9 +156,9 @@ namespace Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 字符串转二进制字节数组
|
/// 字符串转二进制字节数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="str">[TODO:parameter]</param>
|
/// <param name="str">输入的字符串</param>
|
||||||
/// <param name="numBase">[TODO:parameter]</param>
|
/// <param name="numBase">进制(默认为16进制)</param>
|
||||||
/// <returns>[TODO:return]</returns>
|
/// <returns>转换后的二进制字节数组</returns>
|
||||||
public static byte[] StringToBytes(string str, int numBase = 16)
|
public static byte[] StringToBytes(string str, int numBase = 16)
|
||||||
{
|
{
|
||||||
var len = str.Length;
|
var len = str.Length;
|
||||||
|
@ -173,6 +173,59 @@ namespace Common
|
||||||
return bytes;
|
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();
|
||||||
|
Array.Reverse(charArray);
|
||||||
|
return new string(charArray);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Common;
|
using Common;
|
||||||
using DotNext;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using WebProtocol;
|
using WebProtocol;
|
||||||
|
@ -196,6 +196,62 @@ public class JtagController : ControllerBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取状态寄存器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address"> 设备地址 </param>
|
||||||
|
/// <param name="port"> 设备端口 </param>
|
||||||
|
[HttpGet("ReadStatusReg")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
public async ValueTask<IResult> ReadStatusReg(string address, int port)
|
||||||
|
{
|
||||||
|
var jtagCtrl = new JtagClient.Jtag(address, port);
|
||||||
|
var ret = await jtagCtrl.ReadStatusReg();
|
||||||
|
|
||||||
|
if (ret.IsSuccessful)
|
||||||
|
{
|
||||||
|
var binaryValue = Common.String.Reverse(Convert.ToString(ret.Value, 2).PadLeft(32, '0'));
|
||||||
|
logger.Info($"Read device {address} Status Register: 0b{binaryValue}");
|
||||||
|
return TypedResults.Ok(new
|
||||||
|
{
|
||||||
|
original = ret.Value,
|
||||||
|
binary = binaryValue,
|
||||||
|
id_err = binaryValue[0],
|
||||||
|
crc_err = binaryValue[1],
|
||||||
|
aut_err = binaryValue[2],
|
||||||
|
rbcrc_err = binaryValue[3],
|
||||||
|
timeout = binaryValue[4],
|
||||||
|
wakeup_over = binaryValue[5],
|
||||||
|
wakedown_over = binaryValue[6],
|
||||||
|
m = Common.String.Reverse(binaryValue[7..10]),
|
||||||
|
init_complete = binaryValue[10],
|
||||||
|
init_n = binaryValue[11],
|
||||||
|
done = binaryValue[12],
|
||||||
|
done_i = binaryValue[13],
|
||||||
|
glogen = binaryValue[14],
|
||||||
|
glogen_fb = binaryValue[15],
|
||||||
|
gouten = binaryValue[16],
|
||||||
|
grsn = binaryValue[17],
|
||||||
|
gwen = binaryValue[18],
|
||||||
|
pll_lock = binaryValue[19],
|
||||||
|
fallback = binaryValue[21],
|
||||||
|
ipal_m = Common.String.Reverse(binaryValue[22..24]),
|
||||||
|
flg_x8 = binaryValue[24],
|
||||||
|
flg_x16 = binaryValue[25],
|
||||||
|
flg_x32 = binaryValue[26],
|
||||||
|
over_temp = binaryValue[27],
|
||||||
|
prcfg_err = binaryValue[28],
|
||||||
|
prcfg_over = binaryValue[29],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error(ret.Error);
|
||||||
|
return TypedResults.InternalServerError(ret.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传比特流文件
|
/// 上传比特流文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -251,46 +307,76 @@ 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)
|
||||||
|
{
|
||||||
|
var revBuffer = Common.Number.ReverseBytes(buffer, 4);
|
||||||
|
if (!revBuffer.IsSuccessful)
|
||||||
|
return TypedResults.InternalServerError(revBuffer.Error);
|
||||||
|
|
||||||
|
await memoryStream.WriteAsync(revBuffer.Value, 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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -227,11 +229,11 @@ public class Jtag
|
||||||
private IPEndPoint ep;
|
private IPEndPoint ep;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Jtag构造函数
|
/// Jtag 构造函数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="address">目标IP地址</param>
|
/// <param name="address">目标 IP 地址</param>
|
||||||
/// <param name="port">目标UDP端口</param>
|
/// <param name="port">目标 UDP 端口</param>
|
||||||
/// <param name="timeout">超时时间</param>
|
/// <param name="timeout">超时时间(毫秒)</param>
|
||||||
public Jtag(string address, int port, int timeout = 2000)
|
public Jtag(string address, int port, int timeout = 2000)
|
||||||
{
|
{
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
@ -274,7 +276,14 @@ 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)
|
/// <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;
|
var ret = false;
|
||||||
var opts = new SendAddrPackOptions();
|
var opts = new SendAddrPackOptions();
|
||||||
|
@ -303,6 +312,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 +329,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 +346,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 +370,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 +389,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 +410,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,17 +429,68 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清除所有 JTAG 寄存器
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>指示清除是否成功的异步结果</returns>
|
||||||
async ValueTask<Result<bool>> ClearAllRegisters()
|
async ValueTask<Result<bool>> ClearAllRegisters()
|
||||||
{
|
{
|
||||||
return await WriteFIFO(JtagAddr.STATE, 0xFF_FF_FF_FF, 0x01_02_02_02, JtagState.ALL_REG);
|
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()
|
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);
|
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()
|
async ValueTask<Result<bool>> CloseTest()
|
||||||
{
|
{
|
||||||
return await WriteFIFO(
|
return await WriteFIFO(
|
||||||
|
@ -432,6 +499,10 @@ public class Jtag
|
||||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动 JTAG 测试模式
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>指示操作是否成功的异步结果</returns>
|
||||||
async ValueTask<Result<bool>> RunTest()
|
async ValueTask<Result<bool>> RunTest()
|
||||||
{
|
{
|
||||||
return await WriteFIFO(
|
return await WriteFIFO(
|
||||||
|
@ -440,6 +511,11 @@ public class Jtag
|
||||||
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
0x01_00_00_00, JtagState.CMD_EXEC_FINISH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置 JTAG 空闲延迟时间
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="milliseconds">延迟时间(毫秒)</param>
|
||||||
|
/// <returns>指示操作是否成功的异步结果</returns>
|
||||||
async ValueTask<Result<bool>> IdleDelay(UInt32 milliseconds)
|
async ValueTask<Result<bool>> IdleDelay(UInt32 milliseconds)
|
||||||
{
|
{
|
||||||
if (milliseconds > Math.Pow(2, 28)) return new(new Exception("Timespan is over 2^28 milliseconds"));
|
if (milliseconds > Math.Pow(2, 28)) return new(new Exception("Timespan is over 2^28 milliseconds"));
|
||||||
|
@ -447,7 +523,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 +548,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 +557,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"));
|
||||||
|
@ -510,6 +590,10 @@ public class Jtag
|
||||||
return new(new Exception("LoadDRCareo Failed!"));
|
return new(new Exception("LoadDRCareo Failed!"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取 JTAG 设备的 ID 代码
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>包含 ID 代码的异步结果</returns>
|
||||||
public async ValueTask<Result<uint>> ReadIDCode()
|
public async ValueTask<Result<uint>> ReadIDCode()
|
||||||
{
|
{
|
||||||
// Clear Data
|
// Clear Data
|
||||||
|
@ -544,6 +628,49 @@ public class Jtag
|
||||||
return retData.Value;
|
return retData.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 读取 JTAG 设备的状态寄存器
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>包含状态寄存器值的异步结果</returns>
|
||||||
|
public async ValueTask<Result<uint>> ReadStatusReg()
|
||||||
|
{
|
||||||
|
// Clear Data
|
||||||
|
await MsgBus.UDPServer.ClearUDPData(this.address);
|
||||||
|
|
||||||
|
logger.Trace($"Clear up udp server {this.address} receive data");
|
||||||
|
|
||||||
|
Result<bool> ret;
|
||||||
|
|
||||||
|
ret = await ClearAllRegisters();
|
||||||
|
if (!ret.IsSuccessful) return new(ret.Error);
|
||||||
|
else if (!ret.Value) return new(new Exception("Jtag Clear All Registers Failed"));
|
||||||
|
|
||||||
|
ret = await RunTest();
|
||||||
|
if (!ret.IsSuccessful) return new(ret.Error);
|
||||||
|
else if (!ret.Value) return new(new Exception("Jtag Run Test Failed"));
|
||||||
|
|
||||||
|
ret = await ExecRDCmd(JtagCmd.JTAG_DR_RDSR);
|
||||||
|
if (!ret.IsSuccessful) return new(ret.Error);
|
||||||
|
else if (!ret.Value) return new(new Exception("Jtag Execute Command JTAG_DR_RDSR Failed"));
|
||||||
|
|
||||||
|
ret = await ClearWriteDataReg();
|
||||||
|
if (!ret.IsSuccessful) return new(ret.Error);
|
||||||
|
else if (!ret.Value) return new(new Exception("Jtag Clear Write Registers Failed"));
|
||||||
|
|
||||||
|
var retData = await LoadDRCareOutput(4);
|
||||||
|
if (!retData.IsSuccessful)
|
||||||
|
{
|
||||||
|
return new(new Exception("Read Status Reg Failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retData.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载比特流到 JTAG 设备
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bitstream">比特流数据</param>
|
||||||
|
/// <returns>指示下载是否成功的异步结果</returns>
|
||||||
public async ValueTask<Result<bool>> DownloadBitstream(byte[] bitstream)
|
public async ValueTask<Result<bool>> DownloadBitstream(byte[] bitstream)
|
||||||
{
|
{
|
||||||
// Clear Data
|
// Clear Data
|
||||||
|
@ -577,7 +704,7 @@ public class Jtag
|
||||||
|
|
||||||
logger.Trace("Jtag ready to write bitstream");
|
logger.Trace("Jtag ready to write bitstream");
|
||||||
|
|
||||||
ret = await IdleDelay(75000);
|
ret = await IdleDelay(100000);
|
||||||
if (!ret.IsSuccessful) return new(ret.Error);
|
if (!ret.IsSuccessful) return new(ret.Error);
|
||||||
else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed"));
|
else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed"));
|
||||||
|
|
||||||
|
@ -601,7 +728,7 @@ public class Jtag
|
||||||
|
|
||||||
logger.Trace("Jtag reset device");
|
logger.Trace("Jtag reset device");
|
||||||
|
|
||||||
ret = await IdleDelay(1000);
|
ret = await IdleDelay(10000);
|
||||||
if (!ret.IsSuccessful) return new(ret.Error);
|
if (!ret.IsSuccessful) return new(ret.Error);
|
||||||
else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed"));
|
else if (!ret.Value) return new(new Exception("Jtag IDLE Delay Failed"));
|
||||||
|
|
||||||
|
|
|
@ -81,9 +81,13 @@ public class UDPServer
|
||||||
/// <summary> UDP 服务器的错误代码 </summary>
|
/// <summary> UDP 服务器的错误代码 </summary>
|
||||||
public enum ErrorCode
|
public enum ErrorCode
|
||||||
{
|
{
|
||||||
|
/// <summary> [TODO:description] </summary>
|
||||||
Success = 0,
|
Success = 0,
|
||||||
|
/// <summary> [TODO:description] </summary>
|
||||||
GetNoneAfterTimeout,
|
GetNoneAfterTimeout,
|
||||||
|
/// <summary> [TODO:description] </summary>
|
||||||
ResponseWrong,
|
ResponseWrong,
|
||||||
|
/// <summary> [TODO:description] </summary>
|
||||||
NotRecvDataPackage,
|
NotRecvDataPackage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +120,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 +129,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 +158,8 @@ public class UDPServer
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Task.Delay(cycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data is null)
|
if (data is null)
|
||||||
|
@ -171,8 +178,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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
10
src/App.vue
10
src/App.vue
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import iconMenu from "./assets/menu.svg";
|
import iconMenu from "./assets/menu.svg";
|
||||||
import Sidebar from "./components/Sidebar.vue";
|
import Sidebar from "./components/Sidebar.vue";
|
||||||
|
import Navbar from "./components/Navbar.vue";
|
||||||
import { useThemeStore } from "./stores/theme";
|
import { useThemeStore } from "./stores/theme";
|
||||||
|
|
||||||
const theme = useThemeStore();
|
const theme = useThemeStore();
|
||||||
|
@ -14,14 +15,21 @@ const items = [
|
||||||
<template>
|
<template>
|
||||||
<div :data-theme="theme.currentTheme">
|
<div :data-theme="theme.currentTheme">
|
||||||
<header class="relative">
|
<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" />
|
<Sidebar :items="items" />
|
||||||
</div>
|
</div>
|
||||||
|
<Navbar></Navbar>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</main>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="navbar bg-base-100 shadow-sm">
|
<div class="navbar bg-base-100 shadow-sm">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<div class="dropdown">
|
<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">
|
<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" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16" />
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -19,25 +19,13 @@
|
||||||
<li><a>Item 3</a></li>
|
<li><a>Item 3</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-ghost text-xl">daisyUI</a>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-center hidden lg:flex">
|
<div class="navbar-center lg:flex">
|
||||||
<ul class="menu menu-horizontal px-1">
|
<a class="btn btn-ghost text-xl">FPGA Web Lab</a>
|
||||||
<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>
|
</div>
|
||||||
<div class="navbar-end">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,13 +3,14 @@ import LoginView from "../views/LoginView.vue";
|
||||||
import UserView from "../views/UserView.vue";
|
import UserView from "../views/UserView.vue";
|
||||||
import TestView from "../views/TestView.vue";
|
import TestView from "../views/TestView.vue";
|
||||||
import JtagTest from "../views/JtagTest.vue";
|
import JtagTest from "../views/JtagTest.vue";
|
||||||
|
import HomeView from "@/views/HomeView.vue";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: "/", redirect: "/user" },
|
{ path: "/", name: "Home", component: HomeView },
|
||||||
{ path: "/login", name: "Login", component: LoginView },
|
{ path: "/login", name: "Login", component: LoginView },
|
||||||
{ path: "/user", name: "User", component: UserView },
|
{ path: "/user", name: "User", component: UserView },
|
||||||
{ path: "/test", name: "Test", component: TestView },
|
{ path: "/test", name: "Test", component: TestView },
|
||||||
{ path: "/test/jtag", name:"JtagTest", component: JtagTest}
|
{ path: "/test/jtag", name: "JtagTest", component: JtagTest }
|
||||||
];
|
];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
|
|
@ -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,13 +3,39 @@
|
||||||
<div class="h-full w-32"></div>
|
<div class="h-full w-32"></div>
|
||||||
|
|
||||||
<div class="h-full w-[70%] shadow-2xl flex">
|
<div class="h-full w-[70%] shadow-2xl flex">
|
||||||
|
|
||||||
<button class="btn btn-primary h-10 w-30">获取ID Code</button>
|
<button class="btn btn-primary h-10 w-30">获取ID Code</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup></script>
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
|
|
||||||
|
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.value.onerror = (error) => {
|
||||||
|
console.error("SSE 连接出错:", error);
|
||||||
|
eventSource.value!.close();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initSource();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (eventSource) {
|
||||||
|
eventSource.value?.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<style scoped lang="postcss">
|
<style scoped lang="postcss">
|
||||||
@import "../assets/main.css";
|
@import "../assets/main.css";
|
||||||
|
|
Loading…
Reference in New Issue