using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Peripherals.DebuggerClient; namespace server.Controllers; /// /// FPGA调试器控制器,提供信号捕获、触发、数据读取等调试相关API /// [ApiController] [Route("api/[controller]")] [Authorize] public class DebuggerController : ControllerBase { private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); /// /// 表示单个信号通道的配置信息 /// public class ChannelConfig { /// /// 通道名称 /// required public string name; /// /// 通道显示颜色(如前端波形显示用) /// required public string color; /// /// 通道信号线宽度(位数) /// required public UInt32 wireWidth; /// /// 信号线在父端口中的起始索引(bit) /// required public UInt32 wireStartIndex; /// /// 父端口编号 /// required public UInt32 parentPort; /// /// 捕获模式(如上升沿、下降沿等) /// required public CaptureMode mode; } /// /// 调试器整体配置信息 /// public class DebuggerConfig { /// /// 时钟频率 /// required public UInt32 clkFreq; /// /// 总端口数量 /// required public UInt32 totalPortNum; /// /// 捕获深度(采样点数) /// required public UInt32 captureDepth; /// /// 触发器数量 /// required public UInt32 triggerNum; /// /// 所有信号通道的配置信息 /// required public ChannelConfig[] channelConfigs; } /// /// 单个通道的捕获数据 /// public class ChannelCaptureData { /// /// 通道名称 /// required public string name; /// /// 通道捕获到的数据(Base64编码的UInt32数组) /// required public string data; } /// /// 获取当前用户绑定的调试器实例 /// private DebuggerClient? GetDebugger() { try { var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return null; using var db = new Database.AppDataConnection(); var userRet = db.GetUserByName(userName); if (!userRet.IsSuccessful || !userRet.Value.HasValue) return null; var user = userRet.Value.Value; if (user.BoardID == Guid.Empty) return null; var boardRet = db.GetBoardByID(user.BoardID); if (!boardRet.IsSuccessful || !boardRet.Value.HasValue) return null; var board = boardRet.Value.Value; return new DebuggerClient(board.IpAddr, board.Port, 1); } catch (Exception ex) { logger.Error(ex, "获取调试器实例时发生异常"); return null; } } /// /// 设置指定信号线的捕获模式 /// /// 信号线编号(0~511) /// 捕获模式 [HttpPost("SetMode")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task SetMode(UInt32 wireNum, CaptureMode mode) { if (wireNum > 512) { return BadRequest($"最多只能建立512位信号线"); } try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); var result = await debugger.SetMode(wireNum, mode); if (!result.IsSuccessful) { logger.Error($"设置捕获模式失败: {result.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "设置捕获模式失败"); } return Ok(result.Value); } catch (Exception ex) { logger.Error(ex, "设置捕获模式时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } /// /// 为每个通道中的每根线设置捕获模式 /// /// 调试器配置信息,包含所有通道的捕获模式设置 [HttpPost("SetChannelsMode")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task SetChannelsMode([FromBody] DebuggerConfig config) { if (config == null || config.channelConfigs == null) return BadRequest("配置无效"); try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); foreach (var channel in config.channelConfigs) { // 检查每个通道的配置 if (channel.wireWidth > 32 || channel.wireStartIndex > 32 || channel.wireStartIndex + channel.wireWidth > 32) { return BadRequest($"通道 {channel.name} 配置错误"); } for (uint i = 0; i < channel.wireWidth; i++) { var result = await debugger.SetMode(channel.wireStartIndex * (channel.parentPort * 32) + i, channel.mode); if (!result.IsSuccessful) { logger.Error($"设置通道 {channel.name} 第 {i} 根线捕获模式失败: {result.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, $"设置通道 {channel.name} 第 {i} 根线捕获模式失败"); } } } return Ok(true); } catch (Exception ex) { logger.Error(ex, "为每个通道中的每根线设置捕获模式时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } /// /// 启动触发器,开始信号捕获 /// [HttpPost("StartTrigger")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task StartTrigger() { try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); var result = await debugger.StartTrigger(); if (!result.IsSuccessful) { logger.Error($"启动触发器失败: {result.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "启动触发器失败"); } return Ok(result.Value); } catch (Exception ex) { logger.Error(ex, "启动触发器时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } /// /// 读取触发器状态标志 /// [HttpGet("ReadFlag")] [EnableCors("Users")] [ProducesResponseType(typeof(byte), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task ReadFlag() { try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); var result = await debugger.ReadFlag(); if (!result.IsSuccessful) { logger.Error($"读取触发器状态标志失败: {result.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "读取触发器状态标志失败"); } return Ok(result.Value); } catch (Exception ex) { logger.Error(ex, "读取触发器状态标志时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } /// /// 清除触发器状态标志 /// [HttpPost("ClearFlag")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task ClearFlag() { try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); var result = await debugger.ClearFlag(); if (!result.IsSuccessful) { logger.Error($"清除触发器状态标志失败: {result.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "清除触发器状态标志失败"); } return Ok(result.Value); } catch (Exception ex) { logger.Error(ex, "清除触发器状态标志时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } /// /// 读取捕获数据(等待触发完成后返回各通道采样数据) /// /// 调试器配置信息,包含采样深度、端口数、通道配置等 /// 取消操作的令牌 [HttpPost("ReadData")] [EnableCors("Users")] [ProducesResponseType(typeof(ChannelCaptureData[]), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task ReadData([FromBody] DebuggerConfig config, CancellationToken cancellationToken) { // 检查每个通道的配置 foreach (var channel in config.channelConfigs) { if (channel.wireWidth > 32 || channel.wireStartIndex > 32 || channel.wireStartIndex + channel.wireWidth > 32) { return BadRequest($"通道 {channel.name} 配置错误"); } } try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); // 等待捕获标志位 while (true) { cancellationToken.ThrowIfCancellationRequested(); var flagResult = await debugger.ReadFlag(); if (!flagResult.IsSuccessful) { logger.Error($"读取捕获标志失败: {flagResult.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "读取捕获标志失败"); } if (flagResult.Value == 1) { var clearResult = await debugger.ClearFlag(); if (!clearResult.IsSuccessful) { logger.Error($"清除捕获标志失败: {clearResult.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "清除捕获标志失败"); } break; } await Task.Delay(500, cancellationToken); } var dataResult = await debugger.ReadData(config.totalPortNum); if (!dataResult.IsSuccessful) { logger.Error($"读取捕获数据失败: {dataResult.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "读取捕获数据失败"); } var freshResult = await debugger.Refresh(); if (!freshResult.IsSuccessful) { logger.Error($"刷新调试器状态失败: {freshResult.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "刷新调试器状态失败"); } var rawData = dataResult.Value; logger.Debug($"rawData: {BitConverter.ToString(rawData)}"); int depth = (int)config.captureDepth; int portDataLen = 4 * depth; int portNum = (int)config.totalPortNum; var channelDataList = new List(); foreach (var channel in config.channelConfigs) { int port = (int)channel.parentPort; int wireStart = (int)channel.wireStartIndex; int wireWidth = (int)channel.wireWidth; // 每个port的数据长度 int portOffset = port * portDataLen; var channelUintArr = new UInt32[depth]; for (int i = 0; i < depth; i++) { // 取出该port的第i个采样点的4字节 int sampleOffset = portOffset + i * 4; if (sampleOffset + 4 > rawData.Length) { logger.Error($"数据越界: port {port}, sample {i}"); return StatusCode(StatusCodes.Status500InternalServerError, "数据越界"); } var sampleBytes = rawData[sampleOffset..(sampleOffset + 4)]; UInt32 sample = Common.Number.BytesToUInt32(sampleBytes, true).Value; // 提取wireWidth位 UInt32 mask = (wireWidth == 32) ? 0xFFFFFFFF : ((1u << wireWidth) - 1u); channelUintArr[i] = (sample >> wireStart) & mask; } var channelBytes = new byte[4 * depth]; Buffer.BlockCopy(channelUintArr, 0, channelBytes, 0, channelBytes.Length); channelBytes = Common.Number.ReverseBytes(channelBytes, 4).Value; logger.Debug($"{channel.name} HexData: {BitConverter.ToString(channelBytes)}"); var base64 = Convert.ToBase64String(channelBytes); channelDataList.Add(new ChannelCaptureData { name = channel.name, data = base64 }); } return Ok(channelDataList.ToArray()); } catch (OperationCanceledException) { logger.Info("读取捕获数据请求被取消"); return StatusCode(StatusCodes.Status499ClientClosedRequest, "客户端已取消请求"); } catch (Exception ex) { logger.Error(ex, "读取捕获数据时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } /// /// 刷新调试器状态(重置采集状态等) /// [HttpPost("Refresh")] [EnableCors("Users")] [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] public async Task Refresh() { try { var debugger = GetDebugger(); if (debugger == null) return BadRequest("用户未绑定有效的实验板"); var result = await debugger.Refresh(); if (!result.IsSuccessful) { logger.Error($"刷新调试器状态失败: {result.Error}"); return StatusCode(StatusCodes.Status500InternalServerError, "刷新调试器状态失败"); } return Ok(result.Value); } catch (Exception ex) { logger.Error(ex, "刷新调试器状态时发生异常"); return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试"); } } }