feat: 完善用户界面,添加绑定与解除绑定的功能
This commit is contained in:
@@ -41,6 +41,11 @@ public class DataController : ControllerBase
|
||||
/// 用户关联的板卡ID
|
||||
/// </summary>
|
||||
public Guid BoardID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户绑定板子的过期时间
|
||||
/// </summary>
|
||||
public DateTime? BoardExpireTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -148,6 +153,7 @@ public class DataController : ControllerBase
|
||||
Name = user.Name,
|
||||
EMail = user.EMail,
|
||||
BoardID = user.BoardID,
|
||||
BoardExpireTime = user.BoardExpireTime,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -194,32 +200,32 @@ public class DataController : ControllerBase
|
||||
/// <summary>
|
||||
/// 获取一个空闲的实验板(普通用户权限)
|
||||
/// </summary>
|
||||
/// <param name="durationHours">绑定持续时间(小时),默认为1小时</param>
|
||||
[Authorize]
|
||||
[HttpGet("GetAvailableBoard")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public IActionResult GetAvailableBoard()
|
||||
public IActionResult GetAvailableBoard(int durationHours = 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = new Database.AppDataConnection();
|
||||
var boardOpt = db.GetAvailableBoard();
|
||||
if (!boardOpt.HasValue)
|
||||
return NotFound("没有可用的实验板");
|
||||
|
||||
// 绑定用户与实验板
|
||||
var userName = User.Identity?.Name;
|
||||
if (string.IsNullOrEmpty(userName))
|
||||
return Unauthorized("未找到用户名信息");
|
||||
|
||||
using var db = new Database.AppDataConnection();
|
||||
var userRet = db.GetUserByName(userName);
|
||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||
return BadRequest("用户不存在");
|
||||
|
||||
var user = userRet.Value.Value;
|
||||
db.BindUserToBoard(user.ID, boardOpt.Value.ID);
|
||||
var expireTime = DateTime.UtcNow.AddHours(durationHours);
|
||||
|
||||
var boardOpt = db.GetAvailableBoard(user.ID, expireTime);
|
||||
if (!boardOpt.HasValue)
|
||||
return NotFound("没有可用的实验板");
|
||||
|
||||
return Ok(boardOpt.Value);
|
||||
}
|
||||
@@ -230,6 +236,67 @@ public class DataController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解除当前用户绑定的实验板(普通用户权限)
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpPost("UnbindBoard")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public IActionResult UnbindBoard()
|
||||
{
|
||||
try
|
||||
{
|
||||
var userName = User.Identity?.Name;
|
||||
if (string.IsNullOrEmpty(userName))
|
||||
return Unauthorized("未找到用户名信息");
|
||||
|
||||
using var db = new Database.AppDataConnection();
|
||||
var userRet = db.GetUserByName(userName);
|
||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||
return BadRequest("用户不存在");
|
||||
|
||||
var user = userRet.Value.Value;
|
||||
var result = db.UnbindUserFromBoard(user.ID);
|
||||
return Ok(result > 0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "解除实验板绑定时发生异常");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "解除失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户根据实验板ID获取实验板信息(普通用户权限)
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[HttpGet("GetBoardByID")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(Database.Board), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public IActionResult GetBoardByID(Guid id)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = new Database.AppDataConnection();
|
||||
var ret = db.GetBoardByID(id);
|
||||
if (!ret.IsSuccessful)
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
||||
if (!ret.Value.HasValue)
|
||||
return NotFound("未找到对应的实验板");
|
||||
return Ok(ret.Value.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "获取实验板信息时发生异常");
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, "获取失败,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增板子(管理员权限)
|
||||
/// </summary>
|
||||
|
@@ -1,13 +1,15 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace server.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Jtag API
|
||||
/// JTAG 控制器 - 提供 JTAG 相关的 API 操作
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Authorize] // 添加用户认证要求
|
||||
public class JtagController : ControllerBase
|
||||
{
|
||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
@@ -15,134 +17,193 @@ public class JtagController : ControllerBase
|
||||
private const string BITSTREAM_PATH = "bitstream/Jtag";
|
||||
|
||||
/// <summary>
|
||||
/// 页面
|
||||
/// 控制器首页信息
|
||||
/// </summary>
|
||||
/// <returns>控制器描述信息</returns>
|
||||
[HttpGet]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
|
||||
public string Index()
|
||||
{
|
||||
logger.Info($"User {User.Identity?.Name} accessed Jtag controller index");
|
||||
return "This is Jtag Controller";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Jtag ID Code
|
||||
/// 获取 JTAG 设备的 ID Code
|
||||
/// </summary>
|
||||
/// <param name="address"> 设备地址 </param>
|
||||
/// <param name="port"> 设备端口 </param>
|
||||
/// <param name="address">JTAG 设备地址</param>
|
||||
/// <param name="port">JTAG 设备端口</param>
|
||||
/// <returns>设备的 ID Code</returns>
|
||||
[HttpGet("GetDeviceIDCode")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(uint), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async ValueTask<IResult> GetDeviceIDCode(string address, int port)
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.ReadIDCode();
|
||||
logger.Info($"User {User.Identity?.Name} requesting device ID code from {address}:{port}");
|
||||
|
||||
if (ret.IsSuccessful)
|
||||
try
|
||||
{
|
||||
logger.Info($"Get device {address} ID code: 0x{ret.Value:X4}");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.ReadIDCode();
|
||||
|
||||
if (ret.IsSuccessful)
|
||||
{
|
||||
logger.Info($"User {User.Identity?.Name} successfully got device {address} ID code: 0x{ret.Value:X8}");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"User {User.Identity?.Name} failed to get device {address} ID code: {ret.Error}");
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ret.Error);
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
logger.Error(ex, $"User {User.Identity?.Name} encountered exception while getting device {address} ID code");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取状态寄存器
|
||||
/// 读取 JTAG 设备的状态寄存器
|
||||
/// </summary>
|
||||
/// <param name="address"> 设备地址 </param>
|
||||
/// <param name="port"> 设备端口 </param>
|
||||
/// <param name="address">JTAG 设备地址</param>
|
||||
/// <param name="port">JTAG 设备端口</param>
|
||||
/// <returns>状态寄存器的原始值、二进制表示和解码值</returns>
|
||||
[HttpGet("ReadStatusReg")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async ValueTask<IResult> ReadStatusReg(string address, int port)
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.ReadStatusReg();
|
||||
logger.Info($"User {User.Identity?.Name} requesting status register from {address}:{port}");
|
||||
|
||||
if (ret.IsSuccessful)
|
||||
try
|
||||
{
|
||||
var binaryValue = Common.String.Reverse(Convert.ToString(ret.Value, 2).PadLeft(32, '0'));
|
||||
var decodeValue = new Peripherals.JtagClient.JtagStatusReg(ret.Value);
|
||||
logger.Info($"Read device {address} Status Register: \n\t 0b{binaryValue} \n\t {decodeValue}");
|
||||
return TypedResults.Ok(new
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.ReadStatusReg();
|
||||
|
||||
if (ret.IsSuccessful)
|
||||
{
|
||||
original = ret.Value,
|
||||
binaryValue,
|
||||
decodeValue,
|
||||
});
|
||||
var binaryValue = Common.String.Reverse(Convert.ToString(ret.Value, 2).PadLeft(32, '0'));
|
||||
var decodeValue = new Peripherals.JtagClient.JtagStatusReg(ret.Value);
|
||||
logger.Info($"User {User.Identity?.Name} successfully read device {address} Status Register: \n\t 0b{binaryValue} \n\t {decodeValue}");
|
||||
return TypedResults.Ok(new
|
||||
{
|
||||
original = ret.Value,
|
||||
binaryValue,
|
||||
decodeValue,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"User {User.Identity?.Name} failed to read device {address} status register: {ret.Error}");
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ret.Error);
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
logger.Error(ex, $"User {User.Identity?.Name} encountered exception while reading device {address} status register");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上传比特流文件
|
||||
/// 上传比特流文件到服务器
|
||||
/// </summary>
|
||||
/// <param name="address"> 设备地址 </param>
|
||||
/// <param name="address">目标设备地址</param>
|
||||
/// <param name="file">比特流文件</param>
|
||||
/// <returns>上传结果</returns>
|
||||
[HttpPost("UploadBitstream")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async ValueTask<IResult> UploadBitstream(string address, IFormFile file)
|
||||
{
|
||||
logger.Info($"User {User.Identity?.Name} uploading bitstream for device {address}");
|
||||
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
logger.Warn($"User {User.Identity?.Name} attempted to upload empty file for device {address}");
|
||||
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);
|
||||
|
||||
var filePath = Path.Combine(uploadsFolder, fileName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
logger.Info($"Device {address} Upload Bitstream Successfully");
|
||||
return TypedResults.Ok(true);
|
||||
try
|
||||
{
|
||||
// 生成安全的文件名(避免路径遍历攻击)
|
||||
var fileName = Path.GetRandomFileName();
|
||||
var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
|
||||
|
||||
// 如果存在文件,则删除原文件再上传
|
||||
if (Directory.Exists(uploadsFolder))
|
||||
{
|
||||
Directory.Delete(uploadsFolder, true);
|
||||
logger.Info($"User {User.Identity?.Name} removed existing bitstream folder for device {address}");
|
||||
}
|
||||
Directory.CreateDirectory(uploadsFolder);
|
||||
|
||||
var filePath = Path.Combine(uploadsFolder, fileName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
logger.Info($"User {User.Identity?.Name} successfully uploaded bitstream for device {address}, file size: {file.Length} bytes");
|
||||
return TypedResults.Ok(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, $"User {User.Identity?.Name} failed to upload bitstream for device {address}");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过Jtag下载比特流文件
|
||||
/// 通过 JTAG 下载比特流文件到 FPGA 设备
|
||||
/// </summary>
|
||||
/// <param name="address"> 设备地址 </param>
|
||||
/// <param name="port"> 设备端口 </param>
|
||||
/// <param name="address">JTAG 设备地址</param>
|
||||
/// <param name="port">JTAG 设备端口</param>
|
||||
/// <returns>下载结果</returns>
|
||||
[HttpPost("DownloadBitstream")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async ValueTask<IResult> DownloadBitstream(string address, int port)
|
||||
{
|
||||
logger.Info($"User {User.Identity?.Name} initiating bitstream download to device {address}:{port}");
|
||||
|
||||
// 检查文件
|
||||
var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
|
||||
if (!Directory.Exists(fileDir))
|
||||
{
|
||||
logger.Warn($"User {User.Identity?.Name} attempted to download non-existent bitstream for device {address}");
|
||||
return TypedResults.BadRequest("Empty bitstream, Please upload it first");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 读取文件
|
||||
var filePath = Directory.GetFiles(fileDir)[0];
|
||||
logger.Info($"User {User.Identity?.Name} reading bitstream file: {filePath}");
|
||||
|
||||
using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
|
||||
{
|
||||
if (fileStream is null || fileStream.Length <= 0)
|
||||
{
|
||||
logger.Warn($"User {User.Identity?.Name} found invalid bitstream file for device {address}");
|
||||
return TypedResults.BadRequest("Wrong bitstream, Please upload it again");
|
||||
}
|
||||
|
||||
logger.Info($"User {User.Identity?.Name} processing bitstream file of size: {fileStream.Length} bytes");
|
||||
|
||||
// 定义缓冲区大小: 32KB
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
@@ -158,7 +219,10 @@ public class JtagController : ControllerBase
|
||||
// 反转 32bits
|
||||
var retBuffer = Common.Number.ReverseBytes(buffer, 4);
|
||||
if (!retBuffer.IsSuccessful)
|
||||
{
|
||||
logger.Error($"User {User.Identity?.Name} failed to reverse bytes: {retBuffer.Error}");
|
||||
return TypedResults.InternalServerError(retBuffer.Error);
|
||||
}
|
||||
revBuffer = retBuffer.Value;
|
||||
|
||||
for (int i = 0; i < revBuffer.Length; i++)
|
||||
@@ -172,6 +236,7 @@ public class JtagController : ControllerBase
|
||||
|
||||
// 将所有数据转换为字节数组(注意:如果文件非常大,可能不适合完全加载到内存)
|
||||
var fileBytes = memoryStream.ToArray();
|
||||
logger.Info($"User {User.Identity?.Name} processed {totalBytesRead} bytes for device {address}");
|
||||
|
||||
// 下载比特流
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
@@ -179,100 +244,140 @@ public class JtagController : ControllerBase
|
||||
|
||||
if (ret.IsSuccessful)
|
||||
{
|
||||
logger.Info($"Device {address} dowload bitstream successfully");
|
||||
logger.Info($"User {User.Identity?.Name} successfully downloaded bitstream to device {address}");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error(ret.Error);
|
||||
logger.Error($"User {User.Identity?.Name} failed to download bitstream to device {address}: {ret.Error}");
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception error)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return TypedResults.InternalServerError(error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
logger.Error(ex, $"User {User.Identity?.Name} encountered exception while downloading bitstream to device {address}");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// 执行边界扫描,获取所有端口状态
|
||||
/// </summary>
|
||||
/// <param name="address">[TODO:parameter]</param>
|
||||
/// <param name="port">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
/// <param name="address">JTAG 设备地址</param>
|
||||
/// <param name="port">JTAG 设备端口</param>
|
||||
/// <returns>边界扫描结果</returns>
|
||||
[HttpPost("BoundaryScanAllPorts")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async ValueTask<IResult> BoundaryScanAllPorts(string address, int port)
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.BoundaryScan();
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
if (ret.Error is ArgumentException)
|
||||
return TypedResults.BadRequest(ret.Error);
|
||||
else return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
logger.Info($"User {User.Identity?.Name} initiating boundary scan for all ports on device {address}:{port}");
|
||||
|
||||
return TypedResults.Ok(ret.Value);
|
||||
try
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.BoundaryScan();
|
||||
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
logger.Error($"User {User.Identity?.Name} boundary scan failed for device {address}: {ret.Error}");
|
||||
if (ret.Error is ArgumentException)
|
||||
return TypedResults.BadRequest(ret.Error);
|
||||
else
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
|
||||
logger.Info($"User {User.Identity?.Name} successfully completed boundary scan for device {address}");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, $"User {User.Identity?.Name} encountered exception during boundary scan for device {address}");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// 执行逻辑端口边界扫描
|
||||
/// </summary>
|
||||
/// <param name="address">[TODO:parameter]</param>
|
||||
/// <param name="port">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
/// <param name="address">JTAG 设备地址</param>
|
||||
/// <param name="port">JTAG 设备端口</param>
|
||||
/// <returns>逻辑端口状态字典</returns>
|
||||
[HttpPost("BoundaryScanLogicalPorts")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(Dictionary<string, bool>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async ValueTask<IResult> BoundaryScanLogicalPorts(string address, int port)
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.BoundaryScanLogicalPorts();
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
if (ret.Error is ArgumentException)
|
||||
return TypedResults.BadRequest(ret.Error);
|
||||
else return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
logger.Info($"User {User.Identity?.Name} initiating logical ports boundary scan on device {address}:{port}");
|
||||
|
||||
return TypedResults.Ok(ret.Value);
|
||||
try
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.BoundaryScanLogicalPorts();
|
||||
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
logger.Error($"User {User.Identity?.Name} logical ports boundary scan failed for device {address}: {ret.Error}");
|
||||
if (ret.Error is ArgumentException)
|
||||
return TypedResults.BadRequest(ret.Error);
|
||||
else
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
|
||||
logger.Info($"User {User.Identity?.Name} successfully completed logical ports boundary scan for device {address}, found {ret.Value?.Count} ports");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, $"User {User.Identity?.Name} encountered exception during logical ports boundary scan for device {address}");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [TODO:description]
|
||||
/// 设置 JTAG 时钟速度
|
||||
/// </summary>
|
||||
/// <param name="address">[TODO:parameter]</param>
|
||||
/// <param name="port">[TODO:parameter]</param>
|
||||
/// <param name="speed">[TODO:parameter]</param>
|
||||
/// <returns>[TODO:return]</returns>
|
||||
/// <param name="address">JTAG 设备地址</param>
|
||||
/// <param name="port">JTAG 设备端口</param>
|
||||
/// <param name="speed">时钟速度 (Hz)</param>
|
||||
/// <returns>设置结果</returns>
|
||||
[HttpPost("SetSpeed")]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async ValueTask<IResult> SetSpeed(string address, int port, UInt32 speed)
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.SetSpeed(speed);
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
if (ret.Error is ArgumentException)
|
||||
return TypedResults.BadRequest(ret.Error);
|
||||
else return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
logger.Info($"User {User.Identity?.Name} setting JTAG speed to {speed} Hz for device {address}:{port}");
|
||||
|
||||
return TypedResults.Ok(ret.Value);
|
||||
try
|
||||
{
|
||||
var jtagCtrl = new Peripherals.JtagClient.Jtag(address, port);
|
||||
var ret = await jtagCtrl.SetSpeed(speed);
|
||||
|
||||
if (!ret.IsSuccessful)
|
||||
{
|
||||
logger.Error($"User {User.Identity?.Name} failed to set speed for device {address}: {ret.Error}");
|
||||
if (ret.Error is ArgumentException)
|
||||
return TypedResults.BadRequest(ret.Error);
|
||||
else
|
||||
return TypedResults.InternalServerError(ret.Error);
|
||||
}
|
||||
|
||||
logger.Info($"User {User.Identity?.Name} successfully set JTAG speed to {speed} Hz for device {address}");
|
||||
return TypedResults.Ok(ret.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, $"User {User.Identity?.Name} encountered exception while setting speed for device {address}");
|
||||
return TypedResults.InternalServerError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -46,6 +46,12 @@ public class User
|
||||
[Nullable]
|
||||
public Guid BoardID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户绑定板子的过期时间
|
||||
/// </summary>
|
||||
[Nullable]
|
||||
public DateTime? BoardExpireTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户权限枚举
|
||||
/// </summary>
|
||||
@@ -98,6 +104,18 @@ public class Board
|
||||
[NotNull]
|
||||
public required BoardStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 占用该板子的用户的唯一标识符
|
||||
/// </summary>
|
||||
[Nullable]
|
||||
public Guid OccupiedUserID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 占用该板子的用户的用户名
|
||||
/// </summary>
|
||||
[Nullable]
|
||||
public string? OccupiedUserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FPGA 板子的固件版本号
|
||||
/// </summary>
|
||||
@@ -210,7 +228,7 @@ public class AppDataConnection : DataConnection
|
||||
/// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
|
||||
public Result<Optional<User>> GetUserByName(string name)
|
||||
{
|
||||
var user = this.User.Where((user) => user.Name == name).ToArray();
|
||||
var user = this.UserTable.Where((user) => user.Name == name).ToArray();
|
||||
|
||||
if (user.Length > 1)
|
||||
{
|
||||
@@ -235,7 +253,7 @@ public class AppDataConnection : DataConnection
|
||||
/// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
|
||||
public Result<Optional<User>> GetUserByEMail(string email)
|
||||
{
|
||||
var user = this.User.Where((user) => user.EMail == email).ToArray();
|
||||
var user = this.UserTable.Where((user) => user.EMail == email).ToArray();
|
||||
|
||||
if (user.Length > 1)
|
||||
{
|
||||
@@ -270,12 +288,12 @@ public class AppDataConnection : DataConnection
|
||||
|
||||
var user = ret.Value.Value;
|
||||
|
||||
if (user.Password == password)
|
||||
if (user.Password == password)
|
||||
{
|
||||
logger.Info($"用户 {name} 密码验证成功");
|
||||
return new(user);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
logger.Warn($"用户 {name} 密码验证失败");
|
||||
return new(Optional<User>.None);
|
||||
@@ -287,15 +305,70 @@ public class AppDataConnection : DataConnection
|
||||
/// </summary>
|
||||
/// <param name="userId">用户的唯一标识符</param>
|
||||
/// <param name="boardId">实验板的唯一标识符</param>
|
||||
/// <param name="expireTime">绑定过期时间</param>
|
||||
/// <returns>更新的记录数</returns>
|
||||
public int BindUserToBoard(Guid userId, Guid boardId)
|
||||
public int BindUserToBoard(Guid userId, Guid boardId, DateTime expireTime)
|
||||
{
|
||||
var result = this.User
|
||||
// 获取用户信息
|
||||
var user = this.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||
if (user == null)
|
||||
{
|
||||
logger.Error($"未找到用户: {userId}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 更新用户的板子绑定信息
|
||||
var userResult = this.UserTable
|
||||
.Where(u => u.ID == userId)
|
||||
.Set(u => u.BoardID, boardId)
|
||||
.Set(u => u.BoardExpireTime, expireTime)
|
||||
.Update();
|
||||
logger.Info($"用户 {userId} 已绑定到实验板 {boardId}");
|
||||
return result;
|
||||
|
||||
// 更新板子的用户绑定信息
|
||||
var boardResult = this.BoardTable
|
||||
.Where(b => b.ID == boardId)
|
||||
.Set(b => b.Status, Board.BoardStatus.Busy)
|
||||
.Set(b => b.OccupiedUserID, userId)
|
||||
.Set(b => b.OccupiedUserName, user.Name)
|
||||
.Update();
|
||||
|
||||
logger.Info($"用户 {userId} ({user.Name}) 已绑定到实验板 {boardId},过期时间: {expireTime}");
|
||||
return userResult + boardResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解除用户与实验板的绑定
|
||||
/// </summary>
|
||||
/// <param name="userId">用户的唯一标识符</param>
|
||||
/// <returns>更新的记录数</returns>
|
||||
public int UnbindUserFromBoard(Guid userId)
|
||||
{
|
||||
// 获取用户当前绑定的板子ID
|
||||
var user = this.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||
Guid boardId = user?.BoardID ?? Guid.Empty;
|
||||
|
||||
// 清空用户的板子绑定信息
|
||||
var userResult = this.UserTable
|
||||
.Where(u => u.ID == userId)
|
||||
.Set(u => u.BoardID, Guid.Empty)
|
||||
.Set(u => u.BoardExpireTime, (DateTime?)null)
|
||||
.Update();
|
||||
|
||||
// 如果用户原本绑定了板子,则清空板子的用户绑定信息
|
||||
int boardResult = 0;
|
||||
if (boardId != Guid.Empty)
|
||||
{
|
||||
boardResult = this.BoardTable
|
||||
.Where(b => b.ID == boardId)
|
||||
.Set(b => b.Status, Board.BoardStatus.Available)
|
||||
.Set(b => b.OccupiedUserID, Guid.Empty)
|
||||
.Set(b => b.OccupiedUserName, (string?)null)
|
||||
.Update();
|
||||
logger.Info($"实验板 {boardId} 状态已设置为空闲,用户绑定信息已清空");
|
||||
}
|
||||
|
||||
logger.Info($"用户 {userId} 已解除实验板绑定");
|
||||
return userResult + boardResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -326,7 +399,26 @@ public class AppDataConnection : DataConnection
|
||||
/// <returns>删除的记录数</returns>
|
||||
public int DeleteBoardByName(string name)
|
||||
{
|
||||
var result = this.Board.Where(board => board.BoardName == name).Delete();
|
||||
// 先获取要删除的板子信息
|
||||
var board = this.BoardTable.Where(b => b.BoardName == name).FirstOrDefault();
|
||||
if (board == null)
|
||||
{
|
||||
logger.Warn($"未找到名称为 {name} 的实验板");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 如果板子被占用,先解除绑定
|
||||
if (board.OccupiedUserID != Guid.Empty)
|
||||
{
|
||||
this.UserTable
|
||||
.Where(u => u.ID == board.OccupiedUserID)
|
||||
.Set(u => u.BoardID, Guid.Empty)
|
||||
.Set(u => u.BoardExpireTime, (DateTime?)null)
|
||||
.Update();
|
||||
logger.Info($"已解除用户 {board.OccupiedUserID} 与实验板 {name} 的绑定");
|
||||
}
|
||||
|
||||
var result = this.BoardTable.Where(b => b.BoardName == name).Delete();
|
||||
logger.Info($"实验板已删除: {name},删除记录数: {result}");
|
||||
return result;
|
||||
}
|
||||
@@ -338,18 +430,62 @@ public class AppDataConnection : DataConnection
|
||||
/// <returns>删除的记录数</returns>
|
||||
public int DeleteBoardByID(Guid id)
|
||||
{
|
||||
var result = this.Board.Where(board => board.ID == id).Delete();
|
||||
// 先获取要删除的板子信息
|
||||
var board = this.BoardTable.Where(b => b.ID == id).FirstOrDefault();
|
||||
if (board == null)
|
||||
{
|
||||
logger.Warn($"未找到ID为 {id} 的实验板");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 如果板子被占用,先解除绑定
|
||||
if (board.OccupiedUserID != Guid.Empty)
|
||||
{
|
||||
this.UserTable
|
||||
.Where(u => u.ID == board.OccupiedUserID)
|
||||
.Set(u => u.BoardID, Guid.Empty)
|
||||
.Set(u => u.BoardExpireTime, (DateTime?)null)
|
||||
.Update();
|
||||
logger.Info($"已解除用户 {board.OccupiedUserID} 与实验板 {id} 的绑定");
|
||||
}
|
||||
|
||||
var result = this.BoardTable.Where(b => b.ID == id).Delete();
|
||||
logger.Info($"实验板已删除: {id},删除记录数: {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据实验板ID获取实验板信息
|
||||
/// </summary>
|
||||
/// <param name="id">实验板的唯一标识符</param>
|
||||
/// <returns>包含实验板信息的结果,如果未找到则返回空</returns>
|
||||
public Result<Optional<Board>> GetBoardByID(Guid id)
|
||||
{
|
||||
var boards = this.BoardTable.Where(board => board.ID == id).ToArray();
|
||||
|
||||
if (boards.Length > 1)
|
||||
{
|
||||
logger.Error($"数据库中存在多个相同ID的实验板: {id}");
|
||||
return new(new Exception($"数据库中存在多个相同ID的实验板: {id}"));
|
||||
}
|
||||
|
||||
if (boards.Length == 0)
|
||||
{
|
||||
logger.Info($"未找到ID对应的实验板: {id}");
|
||||
return new(Optional<Board>.None);
|
||||
}
|
||||
|
||||
logger.Debug($"成功获取实验板信息: {id}");
|
||||
return new(boards[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有实验板信息
|
||||
/// </summary>
|
||||
/// <returns>所有实验板的数组</returns>
|
||||
public Board[] GetAllBoard()
|
||||
{
|
||||
var boards = this.Board.ToArray();
|
||||
var boards = this.BoardTable.ToArray();
|
||||
logger.Debug($"获取所有实验板,共 {boards.Length} 块");
|
||||
return boards;
|
||||
}
|
||||
@@ -357,10 +493,12 @@ public class AppDataConnection : DataConnection
|
||||
/// <summary>
|
||||
/// 获取一块可用的实验板并将其状态设置为繁忙
|
||||
/// </summary>
|
||||
/// <param name="userId">要分配板子的用户ID</param>
|
||||
/// <param name="expireTime">绑定过期时间</param>
|
||||
/// <returns>可用的实验板,如果没有可用的板子则返回空</returns>
|
||||
public Optional<Board> GetAvailableBoard()
|
||||
public Optional<Board> GetAvailableBoard(Guid userId, DateTime expireTime)
|
||||
{
|
||||
var boards = this.Board.Where(
|
||||
var boards = this.BoardTable.Where(
|
||||
(board) => board.Status == Database.Board.BoardStatus.Available
|
||||
).ToArray();
|
||||
|
||||
@@ -372,12 +510,34 @@ public class AppDataConnection : DataConnection
|
||||
else
|
||||
{
|
||||
var board = boards[0];
|
||||
board.Status = Database.Board.BoardStatus.Busy;
|
||||
this.Board
|
||||
var user = this.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
logger.Error($"未找到用户: {userId}");
|
||||
return new(null);
|
||||
}
|
||||
|
||||
// 更新板子状态和用户绑定信息
|
||||
this.BoardTable
|
||||
.Where(target => target.ID == board.ID)
|
||||
.Set(target => target.Status, board.Status)
|
||||
.Set(target => target.Status, Board.BoardStatus.Busy)
|
||||
.Set(target => target.OccupiedUserID, userId)
|
||||
.Set(target => target.OccupiedUserName, user.Name)
|
||||
.Update();
|
||||
logger.Info($"实验板 {board.BoardName} ({board.ID}) 已分配,状态更新为繁忙");
|
||||
|
||||
// 更新用户的板子绑定信息
|
||||
this.UserTable
|
||||
.Where(u => u.ID == userId)
|
||||
.Set(u => u.BoardID, board.ID)
|
||||
.Set(u => u.BoardExpireTime, expireTime)
|
||||
.Update();
|
||||
|
||||
board.Status = Database.Board.BoardStatus.Busy;
|
||||
board.OccupiedUserID = userId;
|
||||
board.OccupiedUserName = user.Name;
|
||||
|
||||
logger.Info($"实验板 {board.BoardName} ({board.ID}) 已分配给用户 {user.Name} ({userId}),过期时间: {expireTime}");
|
||||
return new(board);
|
||||
}
|
||||
}
|
||||
@@ -385,10 +545,10 @@ public class AppDataConnection : DataConnection
|
||||
/// <summary>
|
||||
/// 用户表
|
||||
/// </summary>
|
||||
public ITable<User> User => this.GetTable<User>();
|
||||
public ITable<User> UserTable => this.GetTable<User>();
|
||||
|
||||
/// <summary>
|
||||
/// FPGA 板子表
|
||||
/// </summary>
|
||||
public ITable<Board> Board => this.GetTable<Board>();
|
||||
public ITable<Board> BoardTable => this.GetTable<Board>();
|
||||
}
|
||||
|
Reference in New Issue
Block a user