Compare commits

..

No commits in common. "master" and "dpp" have entirely different histories.
master ... dpp

12 changed files with 42 additions and 1368 deletions

View File

@ -22,7 +22,6 @@
sqlite
sqls
sql-studio
zlib
# Backend
(dotnetCorePackages.combinePackages [
dotnetCorePackages.sdk_9_0
@ -38,8 +37,7 @@
typescript-language-server
];
shellHook = ''
export PATH=$PATH:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${pkgs.zlib}/lib
export PATH=$PATH:$HOME/.bun/bin
'';
};

View File

@ -1,4 +1,3 @@
using System.Buffers.Binary;
using Common;
namespace server.test;
@ -41,21 +40,9 @@ public class CommonTest
[Fact]
public void ToBitTest()
{
Assert.Equal(true, Number.ToBit(0xFF, 3).Value);
Assert.Equal(false, Number.ToBit(0x00, 3).Value);
Assert.Equal(true, Number.ToBit(0xAA, 3).Value);
Assert.Equal(false, Number.ToBit(0xAA, 2).Value);
}
[Fact]
public void ReverseBits()
{
Assert.Equal((byte)0x05, Common.Number.ReverseBits((byte)0xA0));
var bytesSrc = new byte[] { 0xAB, 0x00, 0x00, 0x01 };
var bytes = new byte[4];
for (int i = 0; i < 4; i++)
bytes[i] = Common.Number.ReverseBits(bytesSrc[i]);
Assert.Equal(new byte[] { 0xD5, 0x00, 0x00, 0x80 }, bytes);
Assert.Equal(Number.ToBit(0xFF, 3).Value, true);
Assert.Equal(Number.ToBit(0x00, 3).Value, false);
Assert.Equal(Number.ToBit(0xAA, 3).Value, true);
Assert.Equal(Number.ToBit(0xAA, 2).Value, false);
}
}

View File

@ -92,7 +92,7 @@ public class UDPServerTest
Assert.True(ret.HasValue);
var data = ret.Value;
Assert.Equal(address, data.Address);
Assert.Equal(number, Number.BytesToUInt32(data.Data).Value);
Assert.Equal(number, Number.BytesToNumber(data.Data));
}
}
@ -112,7 +112,7 @@ public class UDPServerTest
Assert.True(ret.IsSuccessful);
var data = ret.Value;
Assert.True(data.IsSuccessful);
Assert.Equal(number, Number.BytesToUInt32(data.ToBytes()).Value);
Assert.Equal(number, Number.BytesToNumber(data.ToBytes()));
}
}
@ -132,7 +132,7 @@ public class UDPServerTest
Assert.True(ret.IsSuccessful);
var data = ret.Value;
Assert.True(data.IsSuccessful);
Assert.Equal((UInt64)number, Number.BytesToUInt64(data.ToBytes()).Value);
Assert.Equal(number, Number.BytesToNumber(data.ToBytes()));
}
}
}

View File

@ -37,17 +37,16 @@ try
});
// Add CORS policy
if (builder.Environment.IsDevelopment())
builder.Services.AddCors(options =>
{
builder.Services.AddCors(options =>
options.AddPolicy("Development", policy =>
{
options.AddPolicy("Development", policy => policy
policy
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
.AllowAnyHeader();
});
}
});
// Add Swagger
builder.Services.AddControllers();

View File

@ -13,15 +13,12 @@
<ItemGroup>
<PackageReference Include="DotNext" Version="5.19.1" />
<PackageReference Include="DotNext.Threading" Version="5.19.1" />
<PackageReference Include="Honoo.IO.Hashing.Crc" Version="1.3.3" />
<PackageReference Include="linq2db.AspNet" Version="5.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.4" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.23" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.4.0" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" />
<PackageReference Include="NSwag.AspNetCore" Version="14.3.0" />
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" />
</ItemGroup>
</Project>

View File

@ -7,48 +7,14 @@ namespace Common
/// </summary>
public class Number
{
private static readonly byte[] BitReverseTable = new byte[] {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
/// <summary>
/// 整数转成二进制字节数组
/// </summary>
/// <param name="num">整数</param>
/// <param name="length">整数长度</param>
/// <param name="isLowNumHigh">是否高位在数组索引低</param>
/// <param name="isRightHigh">是否高位在右边</param>
/// <returns>二进制字节数组</returns>
public static Result<byte[]> NumberToBytes(ulong num, uint length, bool isLowNumHigh = false)
public static Result<byte[]> NumberToBytes(ulong num, uint length, bool isRightHigh = false)
{
if (length > 8)
{
@ -60,7 +26,7 @@ namespace Common
var arr = new byte[length];
if (isLowNumHigh)
if (isRightHigh)
{
for (var i = 0; i < length; i++)
{
@ -78,12 +44,12 @@ namespace Common
}
/// <summary>
/// 二进制字节数组转成64bits整数
/// 二进制字节数组转成整数
/// </summary>
/// <param name="bytes">二进制字节数组</param>
/// <param name="isLowNumHigh">是否高位在数组索引低</param>
/// <param name="isRightHigh">是否高位在右边</param>
/// <returns>整数</returns>
public static Result<UInt64> BytesToUInt64(byte[] bytes, bool isLowNumHigh = false)
public static Result<ulong> BytesToNumber(byte[] bytes, bool isRightHigh = false)
{
if (bytes.Length > 8)
{
@ -93,78 +59,27 @@ namespace Common
));
}
UInt64 num = 0;
ulong num = 0;
int len = bytes.Length;
try
if (isRightHigh)
{
if (isLowNumHigh)
for (var i = 0; i < len; i++)
{
for (var i = 0; i < len; i++)
{
num += Convert.ToUInt64((UInt64)bytes[len - 1 - i] << (i << 3));
}
num += Convert.ToUInt64((UInt64)bytes[len - 1 - i] << (i << 3));
}
else
}
else
{
for (var i = 0; i < len; i++)
{
for (var i = 0; i < len; i++)
{
num += Convert.ToUInt64((UInt64)bytes[i] << ((int)(len - 1 - i) << 3));
}
num += Convert.ToUInt64((UInt64)bytes[i] << ((int)(len - 1 - i) << 3));
}
}
return num;
}
catch (Exception error)
{
return new(error);
}
return num;
}
/// <summary>
/// 二进制字节数组转成32bits整数
/// </summary>
/// <param name="bytes">二进制字节数组</param>
/// <param name="isLowNumHigh">是否高位在数组索引低</param>
/// <returns>整数</returns>
public static Result<UInt32> BytesToUInt32(byte[] bytes, bool isLowNumHigh = false)
{
if (bytes.Length > 4)
{
return new(new ArgumentException(
"Unsigned long number can't over 4 bytes(32 bits).",
nameof(bytes)
));
}
UInt32 num = 0;
int len = bytes.Length;
try
{
if (isLowNumHigh)
{
for (var i = 0; i < len; i++)
{
num += Convert.ToUInt32((UInt32)bytes[len - 1 - i] << (i << 3));
}
}
else
{
for (var i = 0; i < len; i++)
{
num += Convert.ToUInt32((UInt32)bytes[i] << ((int)(len - 1 - i) << 3));
}
}
return num;
}
catch (Exception error)
{
return new(error);
}
}
/// <summary>
@ -237,12 +152,6 @@ namespace Common
return (srcBits & mask) == dstBits;
}
/// <summary>
/// 获取整型对应位置的比特
/// </summary>
/// <param name="srcBits">整型数字</param>
/// <param name="location">位置</param>
/// <returns>比特</returns>
public static Result<bool> ToBit(UInt32 srcBits, int location)
{
if (location < 0)
@ -290,7 +199,7 @@ namespace Common
"Distance is larger than bytesArray", nameof(distance)));
if (srcBytesLen % distance != 0)
return new(new ArgumentException(
"The length of bytes can't be divided by distance without reminder", nameof(distance)));
"The length of bytes can't be divided by 2 without reminder", nameof(distance)));
var dstBytes = new byte[srcBytesLen];
var buffer = new byte[distance];
@ -306,31 +215,7 @@ namespace Common
return dstBytes;
}
/// <summary>
/// 反转字节内比特顺序(使用查找表的方法)
/// </summary>
/// <param name="srcByte">字节</param>
/// <returns>反转后的字节</returns>
public static byte ReverseBits(byte srcByte)
{
return BitReverseTable[srcByte];
}
/// <summary>
/// 反转字节数组的字节内比特顺序(使用查找表的方法)
/// </summary>
/// <param name="srcBytes">字节数组</param>
/// <returns>反转后的字节字节数组</returns>
public static byte[] ReverseBits(byte[] srcBytes)
{
var bytesLen = srcBytes.Length;
var dstBytes = new byte[bytesLen];
for (int i = 0; i < bytesLen; i++)
{
dstBytes[i] = BitReverseTable[srcBytes[i]];
}
return dstBytes;
}
}
/// <summary>

View File

@ -1,7 +1,6 @@
using System.Buffers.Binary;
using System.Net;
using Common;
using DotNext;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using WebProtocol;
@ -143,8 +142,6 @@ public class JtagController : ControllerBase
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private const string BITSTREAM_PATH = "bitstream/Jtag";
/// <summary>
/// 页面
/// </summary>
@ -246,7 +243,7 @@ public class JtagController : ControllerBase
// 生成安全的文件名(避免路径遍历攻击)
var fileName = Path.GetRandomFileName();
var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}");
// 如果存在文件,则删除原文件再上传
if (Directory.Exists(uploadsFolder))
@ -278,7 +275,7 @@ public class JtagController : ControllerBase
public async ValueTask<IResult> DownloadBitstream(string address, int port)
{
// 检查文件
var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
var fileDir = Path.Combine(Environment.CurrentDirectory, $"bitstream/{address}");
if (!Directory.Exists(fileDir))
return TypedResults.BadRequest("Empty bitstream, Please upload it first");
@ -308,6 +305,10 @@ public class JtagController : ControllerBase
if (!retBuffer.IsSuccessful)
return TypedResults.InternalServerError(retBuffer.Error);
revBuffer = retBuffer.Value;
for (int i = 0; i < buffer.Length; i++)
{
revBuffer[i] = BinaryPrimitives.ReverseEndianness(revBuffer[i]);
}
await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
totalBytesRead += bytesRead;
@ -346,325 +347,6 @@ public class JtagController : ControllerBase
}
}
/// <summary>
/// 远程更新
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class RemoteUpdater : ControllerBase
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private const string BITSTREAM_PATH = "bitstream/RemoteUpdate";
/// <summary>
/// 上传远程更新比特流文件
/// </summary>
/// <param name="address"> 设备地址 </param>
/// <param name="goldenBitream">黄金比特流文件</param>
/// <param name="bitstream1">比特流文件1</param>
/// <param name="bitstream2">比特流文件2</param>
/// <param name="bitstream3">比特流文件3</param>
/// <returns>上传结果</returns>
[HttpPost("UploadBitstream")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async ValueTask<IResult> UploadBitstreams(
string address,
IFormFile? goldenBitream,
IFormFile? bitstream1,
IFormFile? bitstream2,
IFormFile? bitstream3)
{
if ((goldenBitream is null || goldenBitream.Length == 0) &&
(bitstream1 is null || bitstream1.Length == 0) &&
(bitstream2 is null || bitstream2.Length == 0) &&
(bitstream3 is null || bitstream3.Length == 0))
return TypedResults.BadRequest("未选择文件");
// 生成安全的文件名(避免路径遍历攻击)
var fileName = Path.GetRandomFileName();
var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
// 如果存在文件,则删除原文件再上传
if (Directory.Exists(uploadsFolder))
{
Directory.Delete(uploadsFolder, true);
}
Directory.CreateDirectory(uploadsFolder);
for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
{
IFormFile file;
if (bitstreamNum == 0 && goldenBitream is not null)
file = goldenBitream;
else if (bitstreamNum == 1 && bitstream1 is not null)
file = bitstream1;
else if (bitstreamNum == 2 && bitstream2 is not null)
file = bitstream2;
else if (bitstreamNum == 3 && bitstream3 is not null)
file = bitstream3;
else continue;
var fileFolder = Path.Combine(uploadsFolder, bitstreamNum.ToString());
Directory.CreateDirectory(fileFolder);
var filePath = Path.Combine(fileFolder, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
logger.Info($"Device {address} Upload Bitstream Successfully");
return TypedResults.Ok("Bitstream Upload Successfully");
}
private async ValueTask<Result<byte[]>> ProcessBitstream(string filePath)
{
using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
{
if (fileStream is null || fileStream.Length <= 0)
return new(new ArgumentException("Wrong bitstream path"));
// 定义缓冲区大小: 32KB
byte[] buffer = new byte[32 * 1024];
byte[] revBuffer = new byte[32 * 1024];
long totalBytesRead = 0;
// 使用异步流读取文件
using (var memoryStream = new MemoryStream())
{
int bytesRead;
while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
// 反转 32bits
var retBuffer = Common.Number.ReverseBytes(buffer, 4);
if (!retBuffer.IsSuccessful)
return new(retBuffer.Error);
revBuffer = retBuffer.Value;
await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
totalBytesRead += bytesRead;
}
// 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
var restStreamLen = memoryStream.Length % (4 * 1024);
if (restStreamLen != 0)
{
var appendLen = ((int)(4 * 1024 - restStreamLen));
var bytesAppend = new byte[appendLen];
Array.Fill<byte>(bytesAppend, 0xFF);
await memoryStream.WriteAsync(bytesAppend, 0, appendLen);
}
return new(memoryStream.ToArray());
}
}
}
/// <summary>
/// 远程更新单个比特流文件
/// </summary>
/// <param name="address"> 设备地址 </param>
/// <param name="port"> 设备端口 </param>
/// <param name="bitstreamNum"> 比特流位号 </param>
[HttpPost("DownloadBitstream")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> UpdateBitstream(string address, int port, int bitstreamNum)
{
// 检查文件
var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}");
if (!Directory.Exists(fileDir))
return TypedResults.BadRequest("Empty bitstream, Please upload it first");
try
{
// 读取文件
var filePath = Directory.GetFiles(fileDir)[0];
var fileBytes = await ProcessBitstream(filePath);
if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
// 下载比特流
var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port);
var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value);
if (ret.IsSuccessful)
{
logger.Info($"Device {address} Update bitstream successfully");
return TypedResults.Ok(ret.Value);
}
else
{
logger.Error(ret.Error);
return TypedResults.InternalServerError(ret.Error);
}
}
catch (Exception error)
{
return TypedResults.InternalServerError(error);
}
}
/// <summary>
/// 下载多个比特流文件
/// </summary>
/// <param name="address">设备地址</param>
/// <param name="port">设备端口</param>
/// <param name="bitstreamNum">比特流编号</param>
/// <returns>总共上传比特流的数量</returns>
[HttpPost("DownloadMultiBitstreams")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> DownloadMultiBitstreams(string address, int port, int? bitstreamNum)
{
// 检查文件
var bitstreamsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
if (!Directory.Exists(bitstreamsFolder))
return TypedResults.BadRequest("Empty bitstream, Please upload it first");
try
{
var bitstreams = new List<byte[]?>() { null, null, null, null };
int cnt = 0; // 上传比特流数量
for (int i = 0; i < 4; i++)
{
var bitstreamDir = Path.Combine(bitstreamsFolder, i.ToString());
if (!Directory.Exists(bitstreamDir))
continue;
cnt++;
// 读取文件
var filePath = Directory.GetFiles(bitstreamDir)[0];
var fileBytes = await ProcessBitstream(filePath);
if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
bitstreams[i] = fileBytes.Value;
}
// 下载比特流
var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port);
{
var ret = await remoteUpdater.UploadBitstreams(bitstreams[0], bitstreams[1], bitstreams[2], bitstreams[3]);
if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
if (!ret.Value) return TypedResults.InternalServerError("Upload MultiBitstreams failed");
}
if (bitstreamNum is not null)
{
var ret = await remoteUpdater.HotResetBitstream(bitstreamNum ?? 0);
if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
if (!ret.Value) return TypedResults.InternalServerError("Hot reset failed");
}
return TypedResults.Ok(cnt);
}
catch (Exception error)
{
return TypedResults.InternalServerError(error);
}
}
/// <summary>
/// 热复位比特流文件
/// </summary>
/// <param name="address">设备地址</param>
/// <param name="port">设备端口</param>
/// <param name="bitstreamNum">比特流编号</param>
/// <returns>操作结果</returns>
[HttpPost("HotResetBitstream")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> HotResetBitstream(string address, int port, int bitstreamNum)
{
var remoteUpdater = new RemoteUpdate.RemoteUpdateClient(address, port);
var ret = await remoteUpdater.HotResetBitstream(bitstreamNum);
if (ret.IsSuccessful)
{
logger.Info($"Device {address}即可 Update bitstream successfully");
return TypedResults.Ok(ret.Value);
}
else
{
logger.Error(ret.Error);
return TypedResults.InternalServerError(ret.Error);
}
}
}
/// <summary>
/// 数据控制器
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class Data : ControllerBase
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
/// <summary>
/// 创建数据库表
/// </summary>
/// <returns>插入的记录数</returns>
[EnableCors("Development")]
[HttpPost("CreateTable")]
public IResult CreateTables()
{
using var db = new Database.AppDataConnection();
db.CreateAllTables();
return TypedResults.Ok();
}
/// <summary>
/// 删除数据库表
/// </summary>
/// <returns>插入的记录数</returns>
[EnableCors("Development")]
[HttpDelete("DropTables")]
public IResult DropTables()
{
using var db = new Database.AppDataConnection();
db.DropAllTables();
return TypedResults.Ok();
}
/// <summary>
/// 获取所有用户
/// </summary>
/// <returns>用户列表</returns>
[HttpGet("AllUsers")]
public IResult AllUsers()
{
using var db = new Database.AppDataConnection();
var ret = db.User.ToList();
return TypedResults.Ok(ret);
}
/// <summary>
/// 注册新用户
/// </summary>
/// <param name="name">用户名</param>
/// <returns>操作结果</returns>
[HttpPost("SignUpUser")]
public IResult SignUpUser(string name)
{
if (name.Length > 255)
return TypedResults.BadRequest("Name Couln't over 255 characters");
using var db = new Database.AppDataConnection();
var ret = db.AddUser(name);
return TypedResults.Ok(ret);
}
}
/// <summary>
/// 日志控制器
/// </summary>

View File

@ -1,113 +0,0 @@
using LinqToDB;
using LinqToDB.Data;
using LinqToDB.Mapping;
namespace Database;
/// <summary>
/// 用户类,表示用户信息
/// </summary>
public class User
{
/// <summary>
/// 用户的唯一标识符
/// </summary>
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
/// <summary>
/// 用户的名称
/// </summary>
[NotNull]
public required string Name { get; set; }
}
/// <summary>
/// FPGA 板子类,表示板子信息
/// </summary>
public class Board
{
/// <summary>
/// FPGA 板子的唯一标识符
/// </summary>
[PrimaryKey]
public Guid Id { get; set; } = Guid.NewGuid();
/// <summary>
/// FPGA 板子的名称
/// </summary>
[NotNull]
public required string BoardName { get; set; }
}
/// <summary>
/// 应用程序数据连接类,用于与数据库交互
/// </summary>
public class AppDataConnection : DataConnection
{
static readonly LinqToDB.DataOptions options =
new LinqToDB.DataOptions()
.UseSQLite($"Data Source={Environment.CurrentDirectory}/Database.sqlite");
/// <summary>
/// 初始化应用程序数据连接
/// </summary>
public AppDataConnection() : base(options) { }
/// <summary>
/// 创建所有数据库表
/// </summary>
public void CreateAllTables()
{
this.CreateTable<User>();
this.CreateTable<Board>();
}
/// <summary>
/// 删除所有数据库表
/// </summary>
public void DropAllTables()
{
this.DropTable<User>();
this.DropTable<Board>();
}
/// <summary>
/// 添加一个新的用户到数据库
/// </summary>
/// <param name="name">用户的名称</param>
/// <returns>插入的记录数</returns>
public int AddUser(string name)
{
var user = new User()
{
Name = name
};
return this.Insert(user);
}
/// <summary>
/// 添加一块新的 FPGA 板子到数据库
/// </summary>
/// <param name="name">FPGA 板子的名称</param>
/// <returns>插入的记录数</returns>
public int AddBoard(string name)
{
var board = new Board()
{
BoardName = name
};
return this.Insert(board);
}
/// <summary>
/// 用户表
/// </summary>
public ITable<User> User => this.GetTable<User>();
/// <summary>
/// FPGA 板子表
/// </summary>
public ITable<Board> Board => this.GetTable<Board>();
}

View File

@ -428,7 +428,7 @@ public class Jtag
if (retPackLen != 4)
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
return Convert.ToUInt32(Common.Number.BytesToUInt64(retPackOpts.Data).Value);
return Convert.ToUInt32(Common.Number.BytesToNumber(retPackOpts.Data).Value);
}
/// <summary>
@ -558,7 +558,7 @@ public class Jtag
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
if (Common.Number.BitsCheck(
Common.Number.BytesToUInt64(retPack.Value.Options.Data).Value, result, resultMask))
Common.Number.BytesToNumber(retPack.Value.Options.Data).Value, result, resultMask))
ret = true;
return ret;
@ -578,7 +578,7 @@ public class Jtag
return new(new Exception($"RecvDataPackage BodyData Length not Equal to 4: Total {retPackLen} bytes"));
if (Common.Number.BitsCheck(
Common.Number.BytesToUInt64(retPack.Value.Options.Data).Value, result, resultMask))
Common.Number.BytesToNumber(retPack.Value.Options.Data).Value, result, resultMask))
ret = true;
return ret;

View File

@ -1,535 +0,0 @@
using System.Net;
using DotNext;
namespace RemoteUpdate;
static class RemoteUpdateClientAddr
{
public const UInt32 Base = 0x20_00_00_00;
/// <summary>
/// ADDR: 0X00: 写Flash-读写地址——控制位 <br/>
/// [31:16]: wr_sector_num <br/>
/// [15: 0]: {flash_wr_en,-,-,-, start_wr_sector} <br/>
/// </summary>
public const UInt32 WriteCtrl = Base + 0x00;
/// <summary>
/// ADDR: 0X01: 写Flash-只写地址——FIFO入口 <br/>
/// [31:0]: 写比特流数据入口 <br/>
/// </summary>
public const UInt32 WriteFIFO = Base + 0x01;
/// <summary>
/// ADDR: 0X02: 写Flash-只读地址——标志位 <br/>
/// [31:24]: {-, -, -, -, -, -, -, wr_fifo_full} <br/>
/// [23:16]: {-, -, -, -, -, -, -, wr_fifo_empty} <br/>
/// [15: 8]: {-, -, -, -, -, -, -, flash_wr_done} <br/>
/// [ 7: 0]: {-, -, -, -, -, -, -, flash_clear_done} <br/>
/// </summary>
public const UInt32 WriteSign = Base + 0x02;
/// <summary>
/// ADDR: 0X03: 读Flash-读写地址——控制位1 <br/>
/// [31:16]: rd_sector_num <br/>
/// [15: 0]: {flash_rd_en,-,-,-, start_rd_sub_sector} <br/>
/// </summary>
public const UInt32 ReadCtrl1 = Base + 0x03;
/// <summary>
/// ADDR: 0X04: 读Flash-读写地址——控制位2 <br/>
/// [31:24]: { } <br/>
/// [23:16]: {-, -, -, -, -, -,{ bs_crc32_ok }} <br/>
/// [15: 8]: {-, -, -, -, -, -, -, crc_check_en} <br/>
/// [ 7: 0]: {-, -, -, -, -, -, -, bitstream_up2cpu_en} <br/>
/// </summary>
public const UInt32 ReadCtrl2 = Base + 0x04;
/// <summary>
/// ADDR: 0X05: 读Flash-只读地址——FIFO出口 <br/>
/// [31:0]: 读比特流数据出口 <br/>
/// </summary>
public const UInt32 ReadFIFO = Base + 0x05;
/// <summary>
/// ADDR: 0X06: 读Flash-只读地址——CRC校验值 <br/>
/// [31:0]: CRC校验值 bs_readback_crc <br/>
/// </summary>
public const UInt32 ReadCRC = Base + 0x06;
/// <summary>
/// ADDR: 0X07: 读Flash-只读地址——标志位 <br/>
/// [31:24]: {-, -, -, -, -, -, -, rd_fifo_afull} <br/>
/// [23:16]: {-, -, -, -, -, -, -, rd_fifo_empty} <br/>
/// [15: 8]: {-, -, -, -, -, -, -, flash_rd_done} <br/>
/// [ 7: 0]: {-, -, -, -, -, -, -, bs_readback_crc_valid} <br/>
/// </summary>
public const UInt32 ReadSign = Base + 0x07;
/// <summary>
/// ADDR: 0X08: 热启动开关-读写地址——控制位 <br/>
/// [31: 8]: hotreset_addr <br/>
/// [ 7: 0]: {-, -, -, -, -, -, -, hotreset_en} <br/>
/// </summary>
public const UInt32 HotResetCtrl = Base + 0x08;
/// <summary>
/// ADDR: 0X09: 只读地址 版本号 <br/>
/// [31: 0]: FPGA_VERSION[31:0] <br/>
/// </summary>
public const UInt32 Version = Base + 0x09;
}
static class FlashAddr
{
public static readonly UInt32[] Switch = { 0xFFFF, 0x0000, 0x2000, 0x4000 };
public static readonly UInt32[] Jump = { 0xFFFF, 0x1000, 0x3000, 0x5000 };
public static readonly UInt32[] Bitstream = { 0x006000, 0x406000, 0x806000, 0xC06000 };
}
/// <summary>
/// [TODO:description]
/// </summary>
public class RemoteUpdateClient
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
const int FLASH_SECTOR_LENGTH = 4 * 1024;
readonly int timeout = 2000;
readonly int timeoutForWait = 60 * 1000;
readonly int port;
readonly string address;
private IPEndPoint ep;
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="address">[TODO:parameter]</param>
/// <param name="port">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <param name="timeoutForWait">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public RemoteUpdateClient(string address, int port, int timeout = 2000, int timeoutForWait = 60 * 1000)
{
if (timeout < 0)
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
this.address = address;
this.port = port;
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
this.timeout = timeout;
this.timeoutForWait = timeoutForWait;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="flashAddr">[TODO:parameter]</param>
/// <param name="writeSectorNum">[TODO:parameter]</param>
/// <param name="bytesData">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> WriteFlash(UInt32 flashAddr, int writeSectorNum, byte[] bytesData)
{
// Assert
if (writeSectorNum <= 0 || writeSectorNum >= FLASH_SECTOR_LENGTH)
return new(new ArgumentException(
$"Write sector num should be 1 ~ 4096, but given {writeSectorNum}", nameof(writeSectorNum)));
if (bytesData.Length % (4 * 1024) != 0)
return new(new ArgumentException(
$"The length of data should be divided by 4096, bug given {bytesData.Length}", nameof(bytesData)));
{
var ret = await UDPClientPool.WriteAddr(
this.ep, RemoteUpdateClientAddr.WriteCtrl,
Convert.ToUInt32((writeSectorNum << 16) | (1 << 15) | Convert.ToInt32(flashAddr / 4096)), this.timeout);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Enable write flash failed"));
}
{
var ret = await UDPClientPool.ReadAddrWithWait(
this.ep, RemoteUpdateClientAddr.WriteSign,
0x00_00_00_01, 0x00_00_00_01, this.timeoutForWait);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception(
$"Flash clear failed after {this.timeoutForWait} milliseconds"));
}
{
var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdateClientAddr.WriteFIFO, bytesData, this.timeout);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Send data to flash failed"));
}
{
var ret = await UDPClientPool.ReadAddrWithWait(
this.ep, RemoteUpdateClientAddr.WriteSign,
0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> EnableBitstream(int bitstreamNum)
{
// Assert
if (bitstreamNum <= 0 || bitstreamNum > 3)
return new(new ArgumentException(
$"Bitsteam num should be 1 ~ 3 for EnableBitstream, but given {bitstreamNum}", nameof(bitstreamNum)));
var bytesData = new byte[FLASH_SECTOR_LENGTH];
Array.Fill<byte>(bytesData, 0xFF);
bytesData[FLASH_SECTOR_LENGTH - 1] = 0x01;
bytesData[FLASH_SECTOR_LENGTH - 2] = 0x33;
bytesData[FLASH_SECTOR_LENGTH - 3] = 0x2D;
bytesData[FLASH_SECTOR_LENGTH - 4] = 0x94;
var ret = await WriteFlash(FlashAddr.Switch[bitstreamNum], 1, bytesData);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> DisableBitstream(int bitstreamNum)
{
// Assert
if (bitstreamNum <= 0 || bitstreamNum > 3)
return new(new ArgumentException(
$"Bitsteam num should be 1 ~ 3 for DisableBitstream, but given {bitstreamNum}", nameof(bitstreamNum)));
var bytesData = new byte[FLASH_SECTOR_LENGTH];
Array.Fill<byte>(bytesData, 0xFF);
var ret = await WriteFlash(FlashAddr.Switch[bitstreamNum], 1, bytesData);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> WriteJumpCmd(int bitstreamNum)
{
// Assert
if (bitstreamNum <= 0 || bitstreamNum > 3)
return new(new ArgumentException(
$"Bitsteam num should be 1 ~ 3 for WriteJumpCmd, but given {bitstreamNum}", nameof(bitstreamNum)));
// Init data
var bytesData = new byte[FLASH_SECTOR_LENGTH];
for (int i = 3; i < FLASH_SECTOR_LENGTH; i += 4)
bytesData[i] = (byte)0xA0;
{
var bytesSrc = new byte[] { 0x01, 0x00, 0x00, 0xAB };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x04, 4);
}
{
var bytesSrc = new byte[] { 0x0B, 0x00, 0x00, 0x00 };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x08, 4);
}
{
var bytesSrc = new byte[] { 0x01, 0x00, 0xC0, 0xAB };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x34, 4);
}
{
var bytesSrc = new byte[] { 0x00, 0x00, 0x00, 0x00 };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x38, 4);
}
{
var bytesSrc = new byte[] { 0x01, 0x00, 0x00, 0xAC };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x3C, 4);
}
{
var bytesSrc = Common.Number.NumberToBytes(FlashAddr.Bitstream[bitstreamNum], 4).Value;
Buffer.BlockCopy(Common.Number.ReverseBytes(bytesSrc, 4).Value, 0, bytesData, 0x40, 4);
}
{
var bytesSrc = new byte[] { 0x01, 0x00, 0x80, 0xA8 };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x44, 4);
}
{
var bytesSrc = new byte[] { 0x0F, 0x00, 0x00, 0x00 };
Buffer.BlockCopy(bytesSrc, 0, bytesData, 0x48, 4);
}
var ret = await WriteFlash(FlashAddr.Jump[bitstreamNum], 1, bytesData);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> InitRemoteUpdate(int bitstreamNum)
{
// Assert
if (bitstreamNum < 0 || bitstreamNum > 3)
return new(new ArgumentException(
$"Bitsteam num should be 0 ~ 3 for InitRemoteUpdate, but given {bitstreamNum}", nameof(bitstreamNum)));
for (int i = 1; i <= 3; i++)
{
{
var ret = (i == bitstreamNum) ? await EnableBitstream(i) : await DisableBitstream(i);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception($"Write switch {i} failed"));
}
{
var ret = await WriteJumpCmd(i);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception($"Write jump {i} failed"));
}
}
return true;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <param name="bitstreamLen">[TODO:parameter]</param>
/// <param name="checkSum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> CheckBitstreamCRC(int bitstreamNum, int bitstreamLen, UInt32 checkSum)
{
{
var ret = await UDPClientPool.WriteAddr(this.ep, RemoteUpdateClientAddr.ReadCtrl2, 0x00_00_00_00, this.timeout);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Write read control 2 failed"));
}
{
var ret = await UDPClientPool.WriteAddr(
this.ep, RemoteUpdateClientAddr.ReadCtrl1,
Convert.ToUInt32((bitstreamLen << 16) | (1 << 15) | Convert.ToInt32(FlashAddr.Bitstream[bitstreamNum] / 4096)),
this.timeout);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Write read control 1 failed"));
}
{
var ret = await UDPClientPool.ReadAddrWithWait(
this.ep, RemoteUpdateClientAddr.ReadSign,
0x00_00_01_00, 0x00_00_01_00, this.timeoutForWait);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception(
$"Read bitstream failed after {this.timeoutForWait} milliseconds"));
}
{
var ret = await UDPClientPool.ReadAddr(this.ep, RemoteUpdateClientAddr.ReadCRC, this.timeout);
if (!ret.IsSuccessful) return new(ret.Error);
var bytes = ret.Value.Options.Data;
if (bytes is null)
return new(new Exception("CRC is null"));
var remoteCRC = Common.Number.BytesToUInt32(bytes);
if (!remoteCRC.IsSuccessful) return new(remoteCRC.Error);
logger.Debug($"Bitstream {bitstreamNum} Expected CRC: \t 0x{Convert.ToString(checkSum, 16)}");
logger.Debug($"Bitstream {bitstreamNum} Received CRC: \t 0x{Convert.ToString(remoteCRC.Value, 16)}");
return remoteCRC.Value == checkSum;
}
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
private async ValueTask<Result<bool>> HotReset(int bitstreamNum)
{
// Assert
if (bitstreamNum < 0 || bitstreamNum > 3)
return new(new ArgumentException(
$"Bitsteam num should be 0 ~ 3 for HotRest, but given {bitstreamNum}", nameof(bitstreamNum)));
var ret = await UDPClientPool.WriteAddr(
this.ep, RemoteUpdateClientAddr.HotResetCtrl,
((FlashAddr.Bitstream[bitstreamNum] << 8) | 1), this.timeout);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public async ValueTask<Result<bool>> HotResetBitstream(int bitstreamNum)
{
await MsgBus.UDPServer.ClearUDPData(this.address);
logger.Trace("Clear udp data finished");
{
var ret = await InitRemoteUpdate(bitstreamNum);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Init remote update failed"));
}
{
var ret = await HotReset(bitstreamNum);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="goldenBitream">[TODO:parameter]</param>
/// <param name="bitstream1">[TODO:parameter]</param>
/// <param name="bitstream2">[TODO:parameter]</param>
/// <param name="bitstream3">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public async ValueTask<Result<bool>> UploadBitstreams(
byte[]? goldenBitream,
byte[]? bitstream1,
byte[]? bitstream2,
byte[]? bitstream3)
{
await MsgBus.UDPServer.ClearUDPData(this.address);
logger.Trace("Clear udp data finished");
for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
{
byte[] bytesData;
if (bitstreamNum == 0 && goldenBitream is not null)
bytesData = goldenBitream;
else if (bitstreamNum == 1 && bitstream1 is not null)
bytesData = bitstream1;
else if (bitstreamNum == 2 && bitstream2 is not null)
bytesData = bitstream2;
else if (bitstreamNum == 3 && bitstream3 is not null)
bytesData = bitstream3;
else continue;
var bitstreamBlockNum = bytesData.Length / (4 * 1024);
{
var ret = await WriteFlash(FlashAddr.Bitstream[bitstreamNum], bitstreamBlockNum, bytesData);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Write bitstream failed"));
}
{
var crc = Honoo.IO.Hashing.Crc.Create(Honoo.IO.Hashing.CrcName.CRC32_MPEG_2);
var checkSum = crc.ComputeFinal(bytesData);
var ret = await CheckBitstreamCRC(bitstreamNum, bitstreamBlockNum, checkSum.ToUInt32());
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) logger.Warn($"Bitstream {bitstreamNum} CRC32 not correct!");
else logger.Info($"Bitstream {bitstreamNum} CRC32 calibration passed");
}
}
return true;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="bitstreamNum">[TODO:parameter]</param>
/// <param name="bytesData">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public async ValueTask<Result<bool>> UpdateBitstream(int bitstreamNum, byte[] bytesData)
{
if (bytesData.Length % (4 * 1024) != 0)
return new(new ArgumentException(
$"The length of data should be divided by 4096, bug given {bytesData.Length}", nameof(bytesData)));
var bitstreamBlockNum = bytesData.Length / (4 * 1024);
await MsgBus.UDPServer.ClearUDPData(this.address);
logger.Trace("Clear udp data finished");
{
var ret = await InitRemoteUpdate(bitstreamNum);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Init remote update failed"));
}
{
var ret = await WriteFlash(FlashAddr.Bitstream[bitstreamNum], bitstreamBlockNum, bytesData);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return new(new Exception("Write bitstream failed"));
}
{
var crc = Honoo.IO.Hashing.Crc.Create(Honoo.IO.Hashing.CrcName.CRC32_MPEG_2);
var checkSum = crc.ComputeFinal(bytesData);
var ret = await CheckBitstreamCRC(bitstreamNum, bitstreamBlockNum, checkSum.ToUInt32());
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) logger.Warn("CRC32 not correct!");
else logger.Info("CRC32 calibration passed");
}
{
var ret = await HotReset(bitstreamNum);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="enableBitstreamNum">[TODO:parameter]</param>
/// <param name="goldenBitream">[TODO:parameter]</param>
/// <param name="bitstream1">[TODO:parameter]</param>
/// <param name="bitstream2">[TODO:parameter]</param>
/// <param name="bitstream3">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public async ValueTask<Result<bool>> UpdateBitstreams(
int enableBitstreamNum,
byte[]? goldenBitream,
byte[]? bitstream1,
byte[]? bitstream2,
byte[]? bitstream3)
{
// Assert
if (goldenBitream is null && bitstream1 is null && bitstream2 is null && bitstream3 is null)
return new(new ArgumentException(
$"At least one bitstream should not be empty"));
if ((enableBitstreamNum == 0 && goldenBitream is null) ||
(enableBitstreamNum == 1 && bitstream1 is null) ||
(enableBitstreamNum == 2 && bitstream2 is null) ||
(enableBitstreamNum == 3 && bitstream3 is null))
return new(new ArgumentException($"Bitstream {enableBitstreamNum} shouldn't be empty"));
{
var ret = await UploadBitstreams(goldenBitream, bitstream1, bitstream2, bitstream3);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value) return false;
}
{
var ret = await HotResetBitstream(enableBitstreamNum);
if (!ret.IsSuccessful) return new(ret.Error);
return ret.Value;
}
}
}

View File

@ -1,8 +1,6 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
using DotNext;
using WebProtocol;
/// <summary>
/// UDP客户端发送池
@ -163,228 +161,4 @@ public class UDPClientPool
return isSuccessful;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="endPoint">[TODO:parameter]</param>
/// <param name="devAddr">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public static async ValueTask<Result<RecvDataPackage>> ReadAddr(
IPEndPoint endPoint, uint devAddr, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
opts.BurstType = BurstType.FixedBurst;
opts.BurstLength = 0;
opts.CommandID = 0;
opts.Address = devAddr;
// Read Jtag State Register
opts.IsWrite = false;
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send Address Package Failed!"));
// Wait for Read Ack
if (!MsgBus.IsRunning)
return new(new Exception("Message Bus not Working!"));
var retPack = await MsgBus.UDPServer.WaitForDataAsync(
endPoint.Address.ToString(), endPoint.Port, timeout);
if (!retPack.IsSuccessful) return new(retPack.Error);
else if (!retPack.Value.IsSuccessful)
return new(new Exception("Send address package failed"));
var retPackOpts = retPack.Value.Options;
if (retPackOpts.Data is null)
return new(new Exception($"Data is Null, package: {retPackOpts.ToString()}"));
return retPack;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="endPoint">[TODO:parameter]</param>
/// <param name="devAddr">[TODO:parameter]</param>
/// <param name="result">[TODO:parameter]</param>
/// <param name="resultMask">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public static async ValueTask<Result<bool>> ReadAddr(
IPEndPoint endPoint, uint devAddr, UInt32 result, UInt32 resultMask, int timeout = 1000)
{
var address = endPoint.Address.ToString();
var ret = await ReadAddr(endPoint, devAddr, timeout);
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value.IsSuccessful)
return new(new Exception($"Read device {address} address {devAddr} failed"));
var retData = ret.Value.Options.Data;
if (retData is null)
return new(new Exception($"Device {address} receive none"));
if (retData.Length != 4)
return new(new Exception(
$"Device {address} receive data is {retData.Length} bytes instead of 4 bytes"));
// Check result
try
{
var retCode = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value);
return Common.Number.BitsCheck(retCode, result, resultMask);
}
catch (Exception error)
{
return new(error);
}
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="endPoint">[TODO:parameter]</param>
/// <param name="devAddr">[TODO:parameter]</param>
/// <param name="result">[TODO:parameter]</param>
/// <param name="resultMask">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public static async ValueTask<Result<bool>> ReadAddrWithWait(
IPEndPoint endPoint, uint devAddr, UInt32 result, UInt32 resultMask, int timeout = 1000)
{
var address = endPoint.Address.ToString();
var startTime = DateTime.Now;
while (true)
{
var elapsed = DateTime.Now - startTime;
if (elapsed >= TimeSpan.FromMilliseconds(timeout)) break;
var timeleft = TimeSpan.FromMilliseconds(timeout) - elapsed;
try
{
var ret = await ReadAddr(endPoint, devAddr, Convert.ToInt32(timeleft.TotalMilliseconds));
if (!ret.IsSuccessful) return new(ret.Error);
if (!ret.Value.IsSuccessful)
return new(new Exception($"Read device {address} address {devAddr} failed"));
var retData = ret.Value.Options.Data;
if (retData is null)
return new(new Exception($"Device {address} receive none"));
if (retData.Length != 4)
return new(new Exception(
$"Device {address} receive data is {retData.Length} bytes instead of 4 bytes"));
// Check result
var retCode = Convert.ToUInt32(Common.Number.BytesToUInt64(retData).Value);
if (Common.Number.BitsCheck(retCode, result, resultMask)) return true;
}
catch (Exception error)
{
return new(error);
}
}
return false;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="endPoint">[TODO:parameter]</param>
/// <param name="devAddr">[TODO:parameter]</param>
/// <param name="data">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public static async ValueTask<Result<bool>> WriteAddr(
IPEndPoint endPoint, UInt32 devAddr, UInt32 data, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
opts.BurstType = BurstType.FixedBurst;
opts.BurstLength = 0;
opts.CommandID = 0;
opts.Address = devAddr;
// Write Jtag State Register
opts.IsWrite = true;
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send 1st address package failed!"));
// Send Data Package
ret = await UDPClientPool.SendDataPackAsync(endPoint,
new SendDataPackage(Common.Number.NumberToBytes(data, 4).Value));
if (!ret) return new(new Exception("Send data package failed!"));
// Check Msg Bus
if (!MsgBus.IsRunning)
return new(new Exception("Message bus not working!"));
// Wait for Write Ack
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(
endPoint.Address.ToString(), endPoint.Port, timeout);
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
return udpWriteAck.Value.IsSuccessful;
}
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="endPoint">[TODO:parameter]</param>
/// <param name="devAddr">[TODO:parameter]</param>
/// <param name="dataArray">[TODO:parameter]</param>
/// <param name="timeout">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public static async ValueTask<Result<bool>> WriteAddr(IPEndPoint endPoint, UInt32 devAddr, byte[] dataArray, int timeout = 1000)
{
var ret = false;
var opts = new SendAddrPackOptions();
opts.BurstType = BurstType.FixedBurst;
opts.CommandID = 0;
opts.Address = devAddr;
// Check Msg Bus
if (!MsgBus.IsRunning)
return new(new Exception("Message bus not working!"));
opts.IsWrite = true;
var hasRest = dataArray.Length % (256 * (32 / 8)) != 0;
var writeTimes = hasRest ?
dataArray.Length / (256 * (32 / 8)) + 1 :
dataArray.Length / (256 * (32 / 8));
for (var i = 0; i < writeTimes; i++)
{
// Sperate Data Array
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
opts.BurstLength = ((byte)(sendDataArray.Length / 4 - 1));
ret = await UDPClientPool.SendAddrPackAsync(endPoint, new SendAddrPackage(opts));
if (!ret) return new(new Exception("Send 1st address package failed!"));
// Send Data Package
ret = await UDPClientPool.SendDataPackAsync(endPoint, new SendDataPackage(sendDataArray));
if (!ret) return new(new Exception("Send data package failed!"));
// Wait for Write Ack
var udpWriteAck = await MsgBus.UDPServer.WaitForAckAsync(endPoint.Address.ToString(), endPoint.Port, timeout);
if (!udpWriteAck.IsSuccessful) return new(udpWriteAck.Error);
if (!udpWriteAck.Value.IsSuccessful)
return false;
}
return true;
}
}

View File

@ -248,7 +248,7 @@ namespace WebProtocol
));
}
var address = Common.Number.BytesToUInt64(bytes[4..]).Value;
var address = Common.Number.BytesToNumber(bytes[4..]).Value;
return new SendAddrPackage(bytes[1], bytes[2], Convert.ToUInt32(address));
}
}