feat: 添加示波器前后端
This commit is contained in:
		
							
								
								
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -28,6 +28,7 @@ declare module 'vue' {
 | 
			
		||||
    MotherBoard: typeof import('./src/components/equipments/MotherBoard.vue')['default']
 | 
			
		||||
    MotherBoardCaps: typeof import('./src/components/equipments/MotherBoardCaps.vue')['default']
 | 
			
		||||
    Navbar: typeof import('./src/components/Navbar.vue')['default']
 | 
			
		||||
    OscilloscopeWaveformDisplay: typeof import('./src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue')['default']
 | 
			
		||||
    PG2L100H_FBG676: typeof import('./src/components/equipments/PG2L100H_FBG676.vue')['default']
 | 
			
		||||
    Pin: typeof import('./src/components/equipments/Pin.vue')['default']
 | 
			
		||||
    PopButton: typeof import('./src/components/PopButton.vue')['default']
 | 
			
		||||
@@ -48,7 +49,7 @@ declare module 'vue' {
 | 
			
		||||
    TriggerSettings: typeof import('./src/components/LogicAnalyzer/TriggerSettings.vue')['default']
 | 
			
		||||
    TutorialCarousel: typeof import('./src/components/TutorialCarousel.vue')['default']
 | 
			
		||||
    UploadCard: typeof import('./src/components/UploadCard.vue')['default']
 | 
			
		||||
    WaveformDisplay: typeof import('./src/components/Oscilloscope/WaveformDisplay.vue')['default']
 | 
			
		||||
    WaveformDisplay: typeof import('./src/components/WaveformDisplay/WaveformDisplay.vue')['default']
 | 
			
		||||
    Wire: typeof import('./src/components/equipments/Wire.vue')['default']
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										494
									
								
								server/src/Controllers/OscilloscopeController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								server/src/Controllers/OscilloscopeController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,494 @@
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Cors;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Peripherals.OscilloscopeClient;
 | 
			
		||||
 | 
			
		||||
namespace server.Controllers;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 示波器API控制器 - 普通用户权限
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiController]
 | 
			
		||||
[Route("api/[controller]")]
 | 
			
		||||
[Authorize(Roles = "User")]
 | 
			
		||||
public class OscilloscopeApiController : ControllerBase
 | 
			
		||||
{
 | 
			
		||||
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 示波器完整配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class OscilloscopeFullConfig
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 是否启动捕获
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool CaptureEnabled { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 触发电平(0-255)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public byte TriggerLevel { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 触发边沿(true为上升沿,false为下降沿)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool TriggerRisingEdge { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 水平偏移量(0-1023)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ushort HorizontalShift { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 抽样率(0-1023)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ushort DecimationRate { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 是否自动刷新RAM
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool AutoRefreshRAM { get; set; } = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 示波器状态和数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class OscilloscopeDataResponse
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// AD采样频率
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public uint ADFrequency { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// AD采样幅度
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public byte ADVpp { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// AD采样最大值
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public byte ADMax { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// AD采样最小值
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public byte ADMin { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 波形数据(Base64编码)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string WaveformData { get; set; } = string.Empty;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取示波器实例
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private Oscilloscope? GetOscilloscope()
 | 
			
		||||
    {
 | 
			
		||||
        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 Oscilloscope(board.IpAddr, board.Port);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "获取示波器实例时发生异常");
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 初始化示波器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="config">示波器配置</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("Initialize")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> Initialize([FromBody] OscilloscopeFullConfig config)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (config == null)
 | 
			
		||||
                return BadRequest("配置参数不能为空");
 | 
			
		||||
 | 
			
		||||
            if (config.HorizontalShift > 1023)
 | 
			
		||||
                return BadRequest("水平偏移量必须在0-1023之间");
 | 
			
		||||
 | 
			
		||||
            if (config.DecimationRate > 1023)
 | 
			
		||||
                return BadRequest("抽样率必须在0-1023之间");
 | 
			
		||||
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            // 首先关闭捕获
 | 
			
		||||
            var stopResult = await oscilloscope.SetCaptureEnable(false);
 | 
			
		||||
            if (!stopResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"关闭捕获失败: {stopResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "关闭捕获失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置触发电平
 | 
			
		||||
            var levelResult = await oscilloscope.SetTriggerLevel(config.TriggerLevel);
 | 
			
		||||
            if (!levelResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置触发电平失败: {levelResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置触发电平失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置触发边沿
 | 
			
		||||
            var edgeResult = await oscilloscope.SetTriggerEdge(config.TriggerRisingEdge);
 | 
			
		||||
            if (!edgeResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置触发边沿失败: {edgeResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置触发边沿失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置水平偏移量
 | 
			
		||||
            var shiftResult = await oscilloscope.SetHorizontalShift(config.HorizontalShift);
 | 
			
		||||
            if (!shiftResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置水平偏移量失败: {shiftResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置水平偏移量失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置抽样率
 | 
			
		||||
            var rateResult = await oscilloscope.SetDecimationRate(config.DecimationRate);
 | 
			
		||||
            if (!rateResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置抽样率失败: {rateResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置抽样率失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 刷新RAM
 | 
			
		||||
            if (config.AutoRefreshRAM)
 | 
			
		||||
            {
 | 
			
		||||
                var refreshResult = await oscilloscope.RefreshRAM();
 | 
			
		||||
                if (!refreshResult.IsSuccessful)
 | 
			
		||||
                {
 | 
			
		||||
                    logger.Error($"刷新RAM失败: {refreshResult.Error}");
 | 
			
		||||
                    return StatusCode(StatusCodes.Status500InternalServerError, "刷新RAM失败");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置捕获开关
 | 
			
		||||
            var captureResult = await oscilloscope.SetCaptureEnable(config.CaptureEnabled);
 | 
			
		||||
            if (!captureResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置捕获开关失败: {captureResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置捕获开关失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Ok(true);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "初始化示波器时发生异常");
 | 
			
		||||
            return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启动捕获
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("StartCapture")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> StartCapture()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            var result = await oscilloscope.SetCaptureEnable(true);
 | 
			
		||||
            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, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 停止捕获
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("StopCapture")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> StopCapture()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            var result = await oscilloscope.SetCaptureEnable(false);
 | 
			
		||||
            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, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取示波器数据和状态
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>示波器数据和状态信息</returns>
 | 
			
		||||
    [HttpGet("GetData")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(OscilloscopeDataResponse), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> GetData()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            // 并行获取所有数据
 | 
			
		||||
            var freqTask = oscilloscope.GetADFrequency();
 | 
			
		||||
            var vppTask = oscilloscope.GetADVpp();
 | 
			
		||||
            var maxTask = oscilloscope.GetADMax();
 | 
			
		||||
            var minTask = oscilloscope.GetADMin();
 | 
			
		||||
            var waveformTask = oscilloscope.GetWaveformData();
 | 
			
		||||
 | 
			
		||||
            await Task.WhenAll(freqTask.AsTask(), vppTask.AsTask(), maxTask.AsTask(), 
 | 
			
		||||
                             minTask.AsTask(), waveformTask.AsTask());
 | 
			
		||||
 | 
			
		||||
            var freqResult = await freqTask;
 | 
			
		||||
            var vppResult = await vppTask;
 | 
			
		||||
            var maxResult = await maxTask;
 | 
			
		||||
            var minResult = await minTask;
 | 
			
		||||
            var waveformResult = await waveformTask;
 | 
			
		||||
 | 
			
		||||
            if (!freqResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"获取AD采样频率失败: {freqResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "获取AD采样频率失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!vppResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"获取AD采样幅度失败: {vppResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "获取AD采样幅度失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!maxResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"获取AD采样最大值失败: {maxResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "获取AD采样最大值失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!minResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"获取AD采样最小值失败: {minResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "获取AD采样最小值失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!waveformResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"获取波形数据失败: {waveformResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "获取波形数据失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var response = new OscilloscopeDataResponse
 | 
			
		||||
            {
 | 
			
		||||
                ADFrequency = freqResult.Value,
 | 
			
		||||
                ADVpp = vppResult.Value,
 | 
			
		||||
                ADMax = maxResult.Value,
 | 
			
		||||
                ADMin = minResult.Value,
 | 
			
		||||
                WaveformData = Convert.ToBase64String(waveformResult.Value)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return Ok(response);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "获取示波器数据时发生异常");
 | 
			
		||||
            return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新触发参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="level">触发电平(0-255)</param>
 | 
			
		||||
    /// <param name="risingEdge">触发边沿(true为上升沿,false为下降沿)</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("UpdateTrigger")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> UpdateTrigger(byte level, bool risingEdge)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            // 设置触发电平
 | 
			
		||||
            var levelResult = await oscilloscope.SetTriggerLevel(level);
 | 
			
		||||
            if (!levelResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置触发电平失败: {levelResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置触发电平失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置触发边沿
 | 
			
		||||
            var edgeResult = await oscilloscope.SetTriggerEdge(risingEdge);
 | 
			
		||||
            if (!edgeResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置触发边沿失败: {edgeResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置触发边沿失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Ok(true);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "更新触发参数时发生异常");
 | 
			
		||||
            return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新采样参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="horizontalShift">水平偏移量(0-1023)</param>
 | 
			
		||||
    /// <param name="decimationRate">抽样率(0-1023)</param>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("UpdateSampling")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> UpdateSampling(ushort horizontalShift, ushort decimationRate)
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            if (horizontalShift > 1023)
 | 
			
		||||
                return BadRequest("水平偏移量必须在0-1023之间");
 | 
			
		||||
 | 
			
		||||
            if (decimationRate > 1023)
 | 
			
		||||
                return BadRequest("抽样率必须在0-1023之间");
 | 
			
		||||
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            // 设置水平偏移量
 | 
			
		||||
            var shiftResult = await oscilloscope.SetHorizontalShift(horizontalShift);
 | 
			
		||||
            if (!shiftResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置水平偏移量失败: {shiftResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置水平偏移量失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 设置抽样率
 | 
			
		||||
            var rateResult = await oscilloscope.SetDecimationRate(decimationRate);
 | 
			
		||||
            if (!rateResult.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"设置抽样率失败: {rateResult.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "设置抽样率失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Ok(true);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "更新采样参数时发生异常");
 | 
			
		||||
            return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 手动刷新RAM
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果</returns>
 | 
			
		||||
    [HttpPost("RefreshRAM")]
 | 
			
		||||
    [EnableCors("Users")]
 | 
			
		||||
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
 | 
			
		||||
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
 | 
			
		||||
    public async Task<IActionResult> RefreshRAM()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            var oscilloscope = GetOscilloscope();
 | 
			
		||||
            if (oscilloscope == null)
 | 
			
		||||
                return BadRequest("用户未绑定有效的实验板");
 | 
			
		||||
 | 
			
		||||
            var result = await oscilloscope.RefreshRAM();
 | 
			
		||||
            if (!result.IsSuccessful)
 | 
			
		||||
            {
 | 
			
		||||
                logger.Error($"刷新RAM失败: {result.Error}");
 | 
			
		||||
                return StatusCode(StatusCodes.Status500InternalServerError, "刷新RAM失败");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return Ok(result.Value);
 | 
			
		||||
        }
 | 
			
		||||
        catch (Exception ex)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error(ex, "刷新RAM时发生异常");
 | 
			
		||||
            return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +1,68 @@
 | 
			
		||||
using System.Net;
 | 
			
		||||
using Common;
 | 
			
		||||
using DotNext;
 | 
			
		||||
 | 
			
		||||
namespace Peripherals.OscilloscopeClient;
 | 
			
		||||
 | 
			
		||||
static class OscilloscopeAddr
 | 
			
		||||
{
 | 
			
		||||
    public const UInt32 Base = 0x0000_0000;
 | 
			
		||||
    const UInt32 BASE = 0x8000_0000;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0000:R/W[0] wave_run 启动捕获/关闭
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 START_CAPTURE = BASE + 0x0000_0000;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0001: R/W[7:0] trig_level 触发电平
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 TRIG_LEVEL = BASE + 0x0000_0001;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0002:R/W[0] trig_edge 触发边沿,0-下降沿,1-上升沿
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 TRIG_EDGE = BASE + 0x0000_0002;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0003: R/W[9:0] h shift 水平偏移量
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 H_SHIFT = BASE + 0x0000_0003;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0004: R/W[9:0] deci rate 抽样率,0—1023
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 DECI_RATE = BASE + 0x0000_0004;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0005:R/W[0] ram refresh RAM刷新
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 RAM_FRESH = BASE + 0x0000_0005;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000 0006:R[19: 0] ad_freq AD采样频率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 AD_FREQ = BASE + 0x0000_0006;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// Ox0000_0007: R[7:0] ad_vpp AD采样幅度
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 AD_VPP = BASE + 0x0000_0007;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0008: R[7:0] ad max AD采样最大值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 AD_MAX = BASE + 0x0000_0008;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_0009: R[7:0] ad_min AD采样最小值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 AD_MIN = BASE + 0x0000_0009;
 | 
			
		||||
 | 
			
		||||
    /// <summary> 
 | 
			
		||||
    /// 0x0000_1000-0x0000_13FF:R[7:0] wave_rd_data 共1024个字节
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const UInt32 RD_DATA_ADDR = BASE + 0x0000_1000;
 | 
			
		||||
    public const UInt32 RD_DATA_LENGTH = 0x0000_0400;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Oscilloscope
 | 
			
		||||
@@ -13,6 +70,7 @@ class Oscilloscope
 | 
			
		||||
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
 | 
			
		||||
 | 
			
		||||
    readonly int timeout = 2000;
 | 
			
		||||
    readonly int taskID = 1;
 | 
			
		||||
 | 
			
		||||
    readonly int port;
 | 
			
		||||
    readonly string address;
 | 
			
		||||
@@ -33,4 +91,258 @@ class Oscilloscope
 | 
			
		||||
        this.ep = new IPEndPoint(IPAddress.Parse(address), port);
 | 
			
		||||
        this.timeout = timeout;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 控制示波器的捕获开关
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="enable">是否启动捕获</param>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetCaptureEnable(bool enable)
 | 
			
		||||
    {
 | 
			
		||||
        UInt32 value = enable ? 1u : 0u;
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.START_CAPTURE, value, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to set capture enable: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (!ret.Value)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("WriteAddr to START_CAPTURE returned false");
 | 
			
		||||
            return new(new Exception("Failed to set capture enable"));
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置触发电平
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="level">触发电平值(0-255)</param>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetTriggerLevel(byte level)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.TRIG_LEVEL, level, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to set trigger level: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (!ret.Value)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("WriteAddr to TRIG_LEVEL returned false");
 | 
			
		||||
            return new(new Exception("Failed to set trigger level"));
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置触发边沿
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="risingEdge">true为上升沿,false为下降沿</param>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetTriggerEdge(bool risingEdge)
 | 
			
		||||
    {
 | 
			
		||||
        UInt32 value = risingEdge ? 1u : 0u;
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.TRIG_EDGE, value, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to set trigger edge: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (!ret.Value)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("WriteAddr to TRIG_EDGE returned false");
 | 
			
		||||
            return new(new Exception("Failed to set trigger edge"));
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置水平偏移量
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="shift">水平偏移量值(0-1023)</param>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetHorizontalShift(UInt16 shift)
 | 
			
		||||
    {
 | 
			
		||||
        if (shift > 1023)
 | 
			
		||||
            return new(new ArgumentException("Horizontal shift must be 0-1023", nameof(shift)));
 | 
			
		||||
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.H_SHIFT, shift, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to set horizontal shift: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (!ret.Value)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("WriteAddr to H_SHIFT returned false");
 | 
			
		||||
            return new(new Exception("Failed to set horizontal shift"));
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置抽样率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="rate">抽样率值(0-1023)</param>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> SetDecimationRate(UInt16 rate)
 | 
			
		||||
    {
 | 
			
		||||
        if (rate > 1023)
 | 
			
		||||
            return new(new ArgumentException("Decimation rate must be 0-1023", nameof(rate)));
 | 
			
		||||
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.DECI_RATE, rate, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to set decimation rate: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (!ret.Value)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("WriteAddr to DECI_RATE returned false");
 | 
			
		||||
            return new(new Exception("Failed to set decimation rate"));
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 刷新RAM
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果,成功返回true,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<bool>> RefreshRAM()
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.WriteAddr(this.ep, this.taskID, OscilloscopeAddr.RAM_FRESH, 1u, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to refresh RAM: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (!ret.Value)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("WriteAddr to RAM_FRESH returned false");
 | 
			
		||||
            return new(new Exception("Failed to refresh RAM"));
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取AD采样频率
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果,成功返回采样频率值,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<UInt32>> GetADFrequency()
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.ReadAddr(this.ep, this.taskID, OscilloscopeAddr.AD_FREQ, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to read AD frequency: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 4)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("ReadAddr returned invalid data for AD frequency");
 | 
			
		||||
            return new(new Exception("Failed to read AD frequency"));
 | 
			
		||||
        }
 | 
			
		||||
        UInt32 freq = Number.BytesToUInt32(ret.Value.Options.Data).Value;
 | 
			
		||||
        // 取低20位 [19:0]
 | 
			
		||||
        freq &= 0xFFFFF;
 | 
			
		||||
        return freq;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取AD采样幅度
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果,成功返回采样幅度值,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<byte>> GetADVpp()
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.ReadAddr(this.ep, this.taskID, OscilloscopeAddr.AD_VPP, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to read AD VPP: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 1)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("ReadAddr returned invalid data for AD VPP");
 | 
			
		||||
            return new(new Exception("Failed to read AD VPP"));
 | 
			
		||||
        }
 | 
			
		||||
        return ret.Value.Options.Data[3];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取AD采样最大值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果,成功返回采样最大值,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<byte>> GetADMax()
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.ReadAddr(this.ep, this.taskID, OscilloscopeAddr.AD_MAX, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to read AD max: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 1)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("ReadAddr returned invalid data for AD max");
 | 
			
		||||
            return new(new Exception("Failed to read AD max"));
 | 
			
		||||
        }
 | 
			
		||||
        return ret.Value.Options.Data[3];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取AD采样最小值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果,成功返回采样最小值,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<byte>> GetADMin()
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.ReadAddr(this.ep, this.taskID, OscilloscopeAddr.AD_MIN, this.timeout);
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to read AD min: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        if (ret.Value.Options.Data == null || ret.Value.Options.Data.Length < 1)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error("ReadAddr returned invalid data for AD min");
 | 
			
		||||
            return new(new Exception("Failed to read AD min"));
 | 
			
		||||
        }
 | 
			
		||||
        return ret.Value.Options.Data[3];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取波形采样数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>操作结果,成功返回采样数据数组,否则返回异常信息</returns>
 | 
			
		||||
    public async ValueTask<Result<byte[]>> GetWaveformData()
 | 
			
		||||
    {
 | 
			
		||||
        var ret = await UDPClientPool.ReadAddr4BytesAsync(
 | 
			
		||||
            this.ep,
 | 
			
		||||
            this.taskID,
 | 
			
		||||
            OscilloscopeAddr.RD_DATA_ADDR,
 | 
			
		||||
            (int)OscilloscopeAddr.RD_DATA_LENGTH / 32,
 | 
			
		||||
            this.timeout
 | 
			
		||||
        );
 | 
			
		||||
        if (!ret.IsSuccessful)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Failed to read waveform data: {ret.Error}");
 | 
			
		||||
            return new(ret.Error);
 | 
			
		||||
        }
 | 
			
		||||
        var data = ret.Value;
 | 
			
		||||
        if (data == null || data.Length != OscilloscopeAddr.RD_DATA_LENGTH)
 | 
			
		||||
        {
 | 
			
		||||
            logger.Error($"Waveform data length mismatch: {data?.Length}");
 | 
			
		||||
            return new(new Exception("Waveform data length mismatch"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 处理波形数据:从每4个字节中提取第4个字节(索引3)作为有效数据
 | 
			
		||||
        // 数据格式:低八位有效,即[4*i + 3]才是有效数据
 | 
			
		||||
        int sampleCount = data.Length / 4;
 | 
			
		||||
        byte[] waveformData = new byte[sampleCount];
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < sampleCount; i++)
 | 
			
		||||
        {
 | 
			
		||||
            waveformData[i] = data[4 * i + 3];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return waveformData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										631
									
								
								src/APIClient.ts
									
									
									
									
									
								
							
							
						
						
									
										631
									
								
								src/APIClient.ts
									
									
									
									
									
								
							@@ -3170,6 +3170,61 @@ export class NetConfigClient {
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置板卡MAC地址
 | 
			
		||||
     * @param boardMac (optional) 板卡MAC地址(格式:AA:BB:CC:DD:EE:FF)
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    setBoardMAC(boardMac: string | undefined): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/NetConfig/SetBoardMAC?";
 | 
			
		||||
        if (boardMac === null)
 | 
			
		||||
            throw new Error("The parameter 'boardMac' cannot be null.");
 | 
			
		||||
        else if (boardMac !== undefined)
 | 
			
		||||
            url_ += "boardMac=" + encodeURIComponent("" + boardMac) + "&";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processSetBoardMAC(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processSetBoardMAC(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置主机MAC地址
 | 
			
		||||
     * @param hostMac (optional) 主机MAC地址(格式:AA:BB:CC:DD:EE:FF)
 | 
			
		||||
@@ -3319,31 +3374,81 @@ export class NetConfigClient {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置板卡MAC地址
 | 
			
		||||
     * @param boardMac (optional) 板卡MAC地址(格式:AA:BB:CC:DD:EE:FF)
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     * 获取本机网络信息
 | 
			
		||||
     * @return 本机网络信息
 | 
			
		||||
     */
 | 
			
		||||
    setBoardMAC(boardMac: string | undefined): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/NetConfig/SetBoardMAC?";
 | 
			
		||||
        if (boardMac === null)
 | 
			
		||||
            throw new Error("The parameter 'boardMac' cannot be null.");
 | 
			
		||||
        else if (boardMac !== undefined)
 | 
			
		||||
            url_ += "boardMac=" + encodeURIComponent("" + boardMac) + "&";
 | 
			
		||||
    getLocalNetworkInfo(): Promise<any> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/NetConfig/GetLocalNetworkInfo";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            method: "GET",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processSetBoardMAC(_response);
 | 
			
		||||
            return this.processGetLocalNetworkInfo(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processSetBoardMAC(response: Response): Promise<boolean> {
 | 
			
		||||
    protected processGetLocalNetworkInfo(response: Response): Promise<any> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<any>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class OscilloscopeApiClient {
 | 
			
		||||
    private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
 | 
			
		||||
    private baseUrl: string;
 | 
			
		||||
    protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
 | 
			
		||||
 | 
			
		||||
    constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
 | 
			
		||||
        this.http = http ? http : window as any;
 | 
			
		||||
        this.baseUrl = baseUrl ?? "http://localhost:5000";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 初始化示波器
 | 
			
		||||
     * @param config 示波器配置
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    initialize(config: OscilloscopeFullConfig): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/Initialize";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        const content_ = JSON.stringify(config);
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            body: content_,
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Content-Type": "application/json",
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processInitialize(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processInitialize(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
@@ -3365,6 +3470,374 @@ export class NetConfigClient {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 启动捕获
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    startCapture(): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/StartCapture";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processStartCapture(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processStartCapture(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 停止捕获
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    stopCapture(): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/StopCapture";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processStopCapture(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processStopCapture(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取示波器数据和状态
 | 
			
		||||
     * @return 示波器数据和状态信息
 | 
			
		||||
     */
 | 
			
		||||
    getData(): Promise<OscilloscopeDataResponse> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/GetData";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "GET",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processGetData(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processGetData(response: Response): Promise<OscilloscopeDataResponse> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result200 = OscilloscopeDataResponse.fromJS(resultData200);
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<OscilloscopeDataResponse>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新触发参数
 | 
			
		||||
     * @param level (optional) 触发电平(0-255)
 | 
			
		||||
     * @param risingEdge (optional) 触发边沿(true为上升沿,false为下降沿)
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    updateTrigger(level: number | undefined, risingEdge: boolean | undefined): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/UpdateTrigger?";
 | 
			
		||||
        if (level === null)
 | 
			
		||||
            throw new Error("The parameter 'level' cannot be null.");
 | 
			
		||||
        else if (level !== undefined)
 | 
			
		||||
            url_ += "level=" + encodeURIComponent("" + level) + "&";
 | 
			
		||||
        if (risingEdge === null)
 | 
			
		||||
            throw new Error("The parameter 'risingEdge' cannot be null.");
 | 
			
		||||
        else if (risingEdge !== undefined)
 | 
			
		||||
            url_ += "risingEdge=" + encodeURIComponent("" + risingEdge) + "&";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processUpdateTrigger(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processUpdateTrigger(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新采样参数
 | 
			
		||||
     * @param horizontalShift (optional) 水平偏移量(0-1023)
 | 
			
		||||
     * @param decimationRate (optional) 抽样率(0-1023)
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    updateSampling(horizontalShift: number | undefined, decimationRate: number | undefined): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/UpdateSampling?";
 | 
			
		||||
        if (horizontalShift === null)
 | 
			
		||||
            throw new Error("The parameter 'horizontalShift' cannot be null.");
 | 
			
		||||
        else if (horizontalShift !== undefined)
 | 
			
		||||
            url_ += "horizontalShift=" + encodeURIComponent("" + horizontalShift) + "&";
 | 
			
		||||
        if (decimationRate === null)
 | 
			
		||||
            throw new Error("The parameter 'decimationRate' cannot be null.");
 | 
			
		||||
        else if (decimationRate !== undefined)
 | 
			
		||||
            url_ += "decimationRate=" + encodeURIComponent("" + decimationRate) + "&";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processUpdateSampling(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processUpdateSampling(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Promise.resolve<boolean>(null as any);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 手动刷新RAM
 | 
			
		||||
     * @return 操作结果
 | 
			
		||||
     */
 | 
			
		||||
    refreshRAM(): Promise<boolean> {
 | 
			
		||||
        let url_ = this.baseUrl + "/api/OscilloscopeApi/RefreshRAM";
 | 
			
		||||
        url_ = url_.replace(/[?&]$/, "");
 | 
			
		||||
 | 
			
		||||
        let options_: RequestInit = {
 | 
			
		||||
            method: "POST",
 | 
			
		||||
            headers: {
 | 
			
		||||
                "Accept": "application/json"
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return this.http.fetch(url_, options_).then((_response: Response) => {
 | 
			
		||||
            return this.processRefreshRAM(_response);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected processRefreshRAM(response: Response): Promise<boolean> {
 | 
			
		||||
        const status = response.status;
 | 
			
		||||
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
 | 
			
		||||
        if (status === 200) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result200: any = null;
 | 
			
		||||
            let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
                result200 = resultData200 !== undefined ? resultData200 : <any>null;
 | 
			
		||||
    
 | 
			
		||||
            return result200;
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 400) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result400: any = null;
 | 
			
		||||
            let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result400 = ProblemDetails.fromJS(resultData400);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result400);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 500) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status === 401) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            let result401: any = null;
 | 
			
		||||
            let resultData401 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
 | 
			
		||||
            result401 = ProblemDetails.fromJS(resultData401);
 | 
			
		||||
            return throwException("A server side error occurred.", status, _responseText, _headers, result401);
 | 
			
		||||
            });
 | 
			
		||||
        } else if (status !== 200 && status !== 204) {
 | 
			
		||||
            return response.text().then((_responseText) => {
 | 
			
		||||
            return throwException("An unexpected server error occurred.", status, _responseText, _headers);
 | 
			
		||||
@@ -4885,6 +5358,140 @@ export interface INetworkInterfaceDto {
 | 
			
		||||
    macAddress: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 示波器完整配置 */
 | 
			
		||||
export class OscilloscopeFullConfig implements IOscilloscopeFullConfig {
 | 
			
		||||
    /** 是否启动捕获 */
 | 
			
		||||
    captureEnabled!: boolean;
 | 
			
		||||
    /** 触发电平(0-255) */
 | 
			
		||||
    triggerLevel!: number;
 | 
			
		||||
    /** 触发边沿(true为上升沿,false为下降沿) */
 | 
			
		||||
    triggerRisingEdge!: boolean;
 | 
			
		||||
    /** 水平偏移量(0-1023) */
 | 
			
		||||
    horizontalShift!: number;
 | 
			
		||||
    /** 抽样率(0-1023) */
 | 
			
		||||
    decimationRate!: number;
 | 
			
		||||
    /** 是否自动刷新RAM */
 | 
			
		||||
    autoRefreshRAM!: boolean;
 | 
			
		||||
 | 
			
		||||
    constructor(data?: IOscilloscopeFullConfig) {
 | 
			
		||||
        if (data) {
 | 
			
		||||
            for (var property in data) {
 | 
			
		||||
                if (data.hasOwnProperty(property))
 | 
			
		||||
                    (<any>this)[property] = (<any>data)[property];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init(_data?: any) {
 | 
			
		||||
        if (_data) {
 | 
			
		||||
            this.captureEnabled = _data["captureEnabled"];
 | 
			
		||||
            this.triggerLevel = _data["triggerLevel"];
 | 
			
		||||
            this.triggerRisingEdge = _data["triggerRisingEdge"];
 | 
			
		||||
            this.horizontalShift = _data["horizontalShift"];
 | 
			
		||||
            this.decimationRate = _data["decimationRate"];
 | 
			
		||||
            this.autoRefreshRAM = _data["autoRefreshRAM"];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static fromJS(data: any): OscilloscopeFullConfig {
 | 
			
		||||
        data = typeof data === 'object' ? data : {};
 | 
			
		||||
        let result = new OscilloscopeFullConfig();
 | 
			
		||||
        result.init(data);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toJSON(data?: any) {
 | 
			
		||||
        data = typeof data === 'object' ? data : {};
 | 
			
		||||
        data["captureEnabled"] = this.captureEnabled;
 | 
			
		||||
        data["triggerLevel"] = this.triggerLevel;
 | 
			
		||||
        data["triggerRisingEdge"] = this.triggerRisingEdge;
 | 
			
		||||
        data["horizontalShift"] = this.horizontalShift;
 | 
			
		||||
        data["decimationRate"] = this.decimationRate;
 | 
			
		||||
        data["autoRefreshRAM"] = this.autoRefreshRAM;
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 示波器完整配置 */
 | 
			
		||||
export interface IOscilloscopeFullConfig {
 | 
			
		||||
    /** 是否启动捕获 */
 | 
			
		||||
    captureEnabled: boolean;
 | 
			
		||||
    /** 触发电平(0-255) */
 | 
			
		||||
    triggerLevel: number;
 | 
			
		||||
    /** 触发边沿(true为上升沿,false为下降沿) */
 | 
			
		||||
    triggerRisingEdge: boolean;
 | 
			
		||||
    /** 水平偏移量(0-1023) */
 | 
			
		||||
    horizontalShift: number;
 | 
			
		||||
    /** 抽样率(0-1023) */
 | 
			
		||||
    decimationRate: number;
 | 
			
		||||
    /** 是否自动刷新RAM */
 | 
			
		||||
    autoRefreshRAM: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 示波器状态和数据 */
 | 
			
		||||
export class OscilloscopeDataResponse implements IOscilloscopeDataResponse {
 | 
			
		||||
    /** AD采样频率 */
 | 
			
		||||
    adFrequency!: number;
 | 
			
		||||
    /** AD采样幅度 */
 | 
			
		||||
    adVpp!: number;
 | 
			
		||||
    /** AD采样最大值 */
 | 
			
		||||
    adMax!: number;
 | 
			
		||||
    /** AD采样最小值 */
 | 
			
		||||
    adMin!: number;
 | 
			
		||||
    /** 波形数据(Base64编码) */
 | 
			
		||||
    waveformData!: string;
 | 
			
		||||
 | 
			
		||||
    constructor(data?: IOscilloscopeDataResponse) {
 | 
			
		||||
        if (data) {
 | 
			
		||||
            for (var property in data) {
 | 
			
		||||
                if (data.hasOwnProperty(property))
 | 
			
		||||
                    (<any>this)[property] = (<any>data)[property];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init(_data?: any) {
 | 
			
		||||
        if (_data) {
 | 
			
		||||
            this.adFrequency = _data["adFrequency"];
 | 
			
		||||
            this.adVpp = _data["adVpp"];
 | 
			
		||||
            this.adMax = _data["adMax"];
 | 
			
		||||
            this.adMin = _data["adMin"];
 | 
			
		||||
            this.waveformData = _data["waveformData"];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static fromJS(data: any): OscilloscopeDataResponse {
 | 
			
		||||
        data = typeof data === 'object' ? data : {};
 | 
			
		||||
        let result = new OscilloscopeDataResponse();
 | 
			
		||||
        result.init(data);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toJSON(data?: any) {
 | 
			
		||||
        data = typeof data === 'object' ? data : {};
 | 
			
		||||
        data["adFrequency"] = this.adFrequency;
 | 
			
		||||
        data["adVpp"] = this.adVpp;
 | 
			
		||||
        data["adMax"] = this.adMax;
 | 
			
		||||
        data["adMin"] = this.adMin;
 | 
			
		||||
        data["waveformData"] = this.waveformData;
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 示波器状态和数据 */
 | 
			
		||||
export interface IOscilloscopeDataResponse {
 | 
			
		||||
    /** AD采样频率 */
 | 
			
		||||
    adFrequency: number;
 | 
			
		||||
    /** AD采样幅度 */
 | 
			
		||||
    adVpp: number;
 | 
			
		||||
    /** AD采样最大值 */
 | 
			
		||||
    adMax: number;
 | 
			
		||||
    /** AD采样最小值 */
 | 
			
		||||
    adMin: number;
 | 
			
		||||
    /** 波形数据(Base64编码) */
 | 
			
		||||
    waveformData: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** Package options which to send address to read or write */
 | 
			
		||||
export class SendAddrPackOptions implements ISendAddrPackOptions {
 | 
			
		||||
    /** 突发类型 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										264
									
								
								src/components/Oscilloscope/OscilloscopeManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								src/components/Oscilloscope/OscilloscopeManager.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,264 @@
 | 
			
		||||
import { createInjectionState } from "@vueuse/core";
 | 
			
		||||
import { shallowRef, reactive, ref, computed } from "vue";
 | 
			
		||||
import { Mutex } from "async-mutex";
 | 
			
		||||
import {
 | 
			
		||||
  OscilloscopeFullConfig,
 | 
			
		||||
  OscilloscopeDataResponse,
 | 
			
		||||
} from "@/APIClient";
 | 
			
		||||
import { AuthManager } from "@/utils/AuthManager";
 | 
			
		||||
import { useAlertStore } from "@/components/Alert";
 | 
			
		||||
import { useRequiredInjection } from "@/utils/Common";
 | 
			
		||||
 | 
			
		||||
export type OscilloscopeDataType = {
 | 
			
		||||
  x: number[];
 | 
			
		||||
  y: number[] | number[][];
 | 
			
		||||
  xUnit: "s" | "ms" | "us" | "ns";
 | 
			
		||||
  yUnit: "V" | "mV" | "uV";
 | 
			
		||||
  adFrequency: number;
 | 
			
		||||
  adVpp: number;
 | 
			
		||||
  adMax: number;
 | 
			
		||||
  adMin: number;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 默认配置
 | 
			
		||||
const DEFAULT_CONFIG: OscilloscopeFullConfig = new OscilloscopeFullConfig({
 | 
			
		||||
  captureEnabled: false,
 | 
			
		||||
  triggerLevel: 128,
 | 
			
		||||
  triggerRisingEdge: true,
 | 
			
		||||
  horizontalShift: 0,
 | 
			
		||||
  decimationRate: 0,
 | 
			
		||||
  autoRefreshRAM: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 采样频率常量(后端返回)
 | 
			
		||||
const [useProvideOscilloscope, useOscilloscopeState] = createInjectionState(() => {
 | 
			
		||||
  const oscData = shallowRef<OscilloscopeDataType>();
 | 
			
		||||
  const alert = useRequiredInjection(useAlertStore);
 | 
			
		||||
 | 
			
		||||
  // 互斥锁
 | 
			
		||||
  const operationMutex = new Mutex();
 | 
			
		||||
 | 
			
		||||
  // 状态
 | 
			
		||||
  const isApplying = ref(false);
 | 
			
		||||
  const isCapturing = ref(false);
 | 
			
		||||
 | 
			
		||||
  // 配置
 | 
			
		||||
  const config = reactive<OscilloscopeFullConfig>(new OscilloscopeFullConfig({ ...DEFAULT_CONFIG }));
 | 
			
		||||
 | 
			
		||||
  // 采样点数(由后端数据决定)
 | 
			
		||||
  const sampleCount = ref(0);
 | 
			
		||||
 | 
			
		||||
  // 采样周期(ns),由adFrequency计算
 | 
			
		||||
  const samplePeriodNs = computed(() =>
 | 
			
		||||
    oscData.value?.adFrequency ? 1_000_000_000 / oscData.value.adFrequency : 200
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // 应用配置
 | 
			
		||||
  const applyConfiguration = async () => {
 | 
			
		||||
    if (operationMutex.isLocked()) {
 | 
			
		||||
      alert.warn("有其他操作正在进行中,请稍后再试", 3000);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const release = await operationMutex.acquire();
 | 
			
		||||
    isApplying.value = true;
 | 
			
		||||
    try {
 | 
			
		||||
      const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
      const success = await client.initialize({ ...config });
 | 
			
		||||
      if (success) {
 | 
			
		||||
        alert.success("示波器配置已应用", 2000);
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error("应用失败");
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      alert.error("应用配置失败", 3000);
 | 
			
		||||
    } finally {
 | 
			
		||||
      isApplying.value = false;
 | 
			
		||||
      release();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 重置配置
 | 
			
		||||
  const resetConfiguration = () => {
 | 
			
		||||
    Object.assign(config, { ...DEFAULT_CONFIG });
 | 
			
		||||
    alert.info("配置已重置", 2000);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 获取数据
 | 
			
		||||
  const getOscilloscopeData = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
      const resp: OscilloscopeDataResponse = await client.getData();
 | 
			
		||||
 | 
			
		||||
      // 解析波形数据
 | 
			
		||||
      const binaryString = atob(resp.waveformData);
 | 
			
		||||
      const bytes = new Uint8Array(binaryString.length);
 | 
			
		||||
      for (let i = 0; i < binaryString.length; i++) {
 | 
			
		||||
        bytes[i] = binaryString.charCodeAt(i);
 | 
			
		||||
      }
 | 
			
		||||
      sampleCount.value = bytes.length;
 | 
			
		||||
 | 
			
		||||
      // 构建时间轴
 | 
			
		||||
      const x = Array.from(
 | 
			
		||||
        { length: bytes.length },
 | 
			
		||||
        (_, i) => (i * samplePeriodNs.value) / 1000 // us
 | 
			
		||||
      );
 | 
			
		||||
      const y = Array.from(bytes);
 | 
			
		||||
 | 
			
		||||
      oscData.value = {
 | 
			
		||||
        x,
 | 
			
		||||
        y,
 | 
			
		||||
        xUnit: "us",
 | 
			
		||||
        yUnit: "V",
 | 
			
		||||
        adFrequency: resp.adFrequency,
 | 
			
		||||
        adVpp: resp.adVpp,
 | 
			
		||||
        adMax: resp.adMax,
 | 
			
		||||
        adMin: resp.adMin,
 | 
			
		||||
      };
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      alert.error("获取示波器数据失败", 3000);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 启动捕获
 | 
			
		||||
  const startCapture = async () => {
 | 
			
		||||
    if (operationMutex.isLocked()) {
 | 
			
		||||
      alert.warn("有其他操作正在进行中,请稍后再试", 3000);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    isCapturing.value = true;
 | 
			
		||||
    const release = await operationMutex.acquire();
 | 
			
		||||
    try {
 | 
			
		||||
      const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
      const started = await client.startCapture();
 | 
			
		||||
      if (!started) throw new Error("无法启动捕获");
 | 
			
		||||
      alert.info("开始捕获...", 2000);
 | 
			
		||||
 | 
			
		||||
      // 简单轮询,直到捕获完成(可根据后端实际情况优化)
 | 
			
		||||
      await new Promise((resolve) => setTimeout(resolve, 1000));
 | 
			
		||||
      await getOscilloscopeData();
 | 
			
		||||
      alert.success("捕获完成", 2000);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      alert.error("捕获失败", 3000);
 | 
			
		||||
    } finally {
 | 
			
		||||
      isCapturing.value = false;
 | 
			
		||||
      release();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 停止捕获
 | 
			
		||||
  const stopCapture = async () => {
 | 
			
		||||
    if (!isCapturing.value) {
 | 
			
		||||
      alert.warn("当前没有正在进行的捕获操作", 2000);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    isCapturing.value = false;
 | 
			
		||||
    const release = await operationMutex.acquire();
 | 
			
		||||
    try {
 | 
			
		||||
      const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
      const stopped = await client.stopCapture();
 | 
			
		||||
      if (!stopped) throw new Error("无法停止捕获");
 | 
			
		||||
      alert.info("捕获已停止", 2000);
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      alert.error("停止捕获失败", 3000);
 | 
			
		||||
    } finally {
 | 
			
		||||
      release();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 更新触发参数
 | 
			
		||||
  const updateTrigger = async (level: number, risingEdge: boolean) => {
 | 
			
		||||
    const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
    try {
 | 
			
		||||
      const ok = await client.updateTrigger(level, risingEdge);
 | 
			
		||||
      if (ok) {
 | 
			
		||||
        config.triggerLevel = level;
 | 
			
		||||
        config.triggerRisingEdge = risingEdge;
 | 
			
		||||
        alert.success("触发参数已更新", 2000);
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error();
 | 
			
		||||
      }
 | 
			
		||||
    } catch {
 | 
			
		||||
      alert.error("更新触发参数失败", 2000);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 更新采样参数
 | 
			
		||||
  const updateSampling = async (horizontalShift: number, decimationRate: number) => {
 | 
			
		||||
    const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
    try {
 | 
			
		||||
      const ok = await client.updateSampling(horizontalShift, decimationRate);
 | 
			
		||||
      if (ok) {
 | 
			
		||||
        config.horizontalShift = horizontalShift;
 | 
			
		||||
        config.decimationRate = decimationRate;
 | 
			
		||||
        alert.success("采样参数已更新", 2000);
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error();
 | 
			
		||||
      }
 | 
			
		||||
    } catch {
 | 
			
		||||
      alert.error("更新采样参数失败", 2000);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 手动刷新RAM
 | 
			
		||||
  const refreshRAM = async () => {
 | 
			
		||||
    const client = AuthManager.createAuthenticatedOscilloscopeApiClient();
 | 
			
		||||
    try {
 | 
			
		||||
      const ok = await client.refreshRAM();
 | 
			
		||||
      if (ok) {
 | 
			
		||||
        alert.success("RAM已刷新", 2000);
 | 
			
		||||
      } else {
 | 
			
		||||
        throw new Error();
 | 
			
		||||
      }
 | 
			
		||||
    } catch {
 | 
			
		||||
      alert.error("刷新RAM失败", 2000);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // 生成测试数据
 | 
			
		||||
  const generateTestData = () => {
 | 
			
		||||
    const freq = 5_000_000;
 | 
			
		||||
    const duration = 0.001; // 1ms
 | 
			
		||||
    const points = Math.floor(freq * duration);
 | 
			
		||||
    const x = Array.from({ length: points }, (_, i) => (i * 1_000_000_000 / freq) / 1000);
 | 
			
		||||
    const y = Array.from({ length: points }, (_, i) =>
 | 
			
		||||
      Math.floor(Math.sin(i * 0.01) * 127 + 128)
 | 
			
		||||
    );
 | 
			
		||||
    oscData.value = {
 | 
			
		||||
      x,
 | 
			
		||||
      y,
 | 
			
		||||
      xUnit: "us",
 | 
			
		||||
      yUnit: "V",
 | 
			
		||||
      adFrequency: freq,
 | 
			
		||||
      adVpp: 2.0,
 | 
			
		||||
      adMax: 255,
 | 
			
		||||
      adMin: 0,
 | 
			
		||||
    };
 | 
			
		||||
    alert.success("测试数据生成成功", 2000);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const isOperationInProgress = computed(
 | 
			
		||||
    () => isApplying.value || isCapturing.value || operationMutex.isLocked()
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    oscData,
 | 
			
		||||
    config,
 | 
			
		||||
    isApplying,
 | 
			
		||||
    isCapturing,
 | 
			
		||||
    isOperationInProgress,
 | 
			
		||||
    sampleCount,
 | 
			
		||||
    samplePeriodNs,
 | 
			
		||||
 | 
			
		||||
    applyConfiguration,
 | 
			
		||||
    resetConfiguration,
 | 
			
		||||
    getOscilloscopeData,
 | 
			
		||||
    startCapture,
 | 
			
		||||
    stopCapture,
 | 
			
		||||
    updateTrigger,
 | 
			
		||||
    updateSampling,
 | 
			
		||||
    refreshRAM,
 | 
			
		||||
    generateTestData,
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export { useProvideOscilloscope, useOscilloscopeState, DEFAULT_CONFIG };
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="w-full h-100">
 | 
			
		||||
    <v-chart v-if="true" class="w-full h-full" :option="option" autoresize />
 | 
			
		||||
    <v-chart v-if="hasData" class="w-full h-full" :option="option" autoresize />
 | 
			
		||||
    <div
 | 
			
		||||
      v-else
 | 
			
		||||
      class="w-full h-full flex items-center justify-center text-gray-500"
 | 
			
		||||
@@ -11,16 +11,15 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed, withDefaults } from "vue";
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { forEach } from "lodash";
 | 
			
		||||
import VChart from "vue-echarts";
 | 
			
		||||
import { type WaveformDataType } from "./index";
 | 
			
		||||
import { useOscilloscopeState } from "./OscilloscopeManager";
 | 
			
		||||
 | 
			
		||||
// Echarts
 | 
			
		||||
import { use } from "echarts/core";
 | 
			
		||||
import { LineChart } from "echarts/charts";
 | 
			
		||||
import {
 | 
			
		||||
  TitleComponent,
 | 
			
		||||
  TooltipComponent,
 | 
			
		||||
  LegendComponent,
 | 
			
		||||
  ToolboxComponent,
 | 
			
		||||
@@ -37,6 +36,7 @@ import type {
 | 
			
		||||
  DataZoomComponentOption,
 | 
			
		||||
  GridComponentOption,
 | 
			
		||||
} from "echarts/components";
 | 
			
		||||
import { useRequiredInjection } from "@/utils/Common";
 | 
			
		||||
 | 
			
		||||
use([
 | 
			
		||||
  TooltipComponent,
 | 
			
		||||
@@ -57,44 +57,47 @@ type EChartsOption = ComposeOption<
 | 
			
		||||
  | LineSeriesOption
 | 
			
		||||
>;
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(
 | 
			
		||||
  defineProps<{
 | 
			
		||||
    data?: WaveformDataType;
 | 
			
		||||
  }>(),
 | 
			
		||||
  {
 | 
			
		||||
    data: () => ({
 | 
			
		||||
      x: [],
 | 
			
		||||
      y: [],
 | 
			
		||||
      xUnit: "s",
 | 
			
		||||
      yUnit: "V",
 | 
			
		||||
    }),
 | 
			
		||||
  },
 | 
			
		||||
);
 | 
			
		||||
// 使用 manager 获取 oscilloscope 数据
 | 
			
		||||
const { oscData } = useRequiredInjection(useOscilloscopeState);
 | 
			
		||||
 | 
			
		||||
const hasData = computed(() => {
 | 
			
		||||
  return (
 | 
			
		||||
    props.data &&
 | 
			
		||||
    props.data.x &&
 | 
			
		||||
    props.data.y &&
 | 
			
		||||
    props.data.x.length > 0 &&
 | 
			
		||||
    props.data.y.length > 0 &&
 | 
			
		||||
    props.data.y.some((channel) => channel.length > 0)
 | 
			
		||||
    oscData.value &&
 | 
			
		||||
    oscData.value.x &&
 | 
			
		||||
    oscData.value.y &&
 | 
			
		||||
    oscData.value.x.length > 0 &&
 | 
			
		||||
    (
 | 
			
		||||
      Array.isArray(oscData.value.y[0])
 | 
			
		||||
        ? oscData.value.y.some((channel: any) => channel.length > 0)
 | 
			
		||||
        : oscData.value.y.length > 0
 | 
			
		||||
    )
 | 
			
		||||
  );
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const option = computed((): EChartsOption => {
 | 
			
		||||
  if (!oscData.value || !oscData.value.x || !oscData.value.y) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const series: LineSeriesOption[] = [];
 | 
			
		||||
 | 
			
		||||
  forEach(props.data.y, (yData, index) => {
 | 
			
		||||
    // 将 x 和 y 数据组合成 [x, y] 格式
 | 
			
		||||
    const seriesData = props.data.x.map((xValue, i) => [xValue, yData[i] || 0]);
 | 
			
		||||
  // 兼容单通道和多通道,确保 yChannels 为 number[][]
 | 
			
		||||
  const yChannels: number[][] = Array.isArray(oscData.value.y[0])
 | 
			
		||||
    ? (oscData.value.y as number[][])
 | 
			
		||||
    : [oscData.value.y as number[]];
 | 
			
		||||
 | 
			
		||||
  forEach(yChannels, (yData, index) => {
 | 
			
		||||
    if (!oscData.value || !yData) return;
 | 
			
		||||
    const seriesData = oscData.value.x.map((xValue, i) => [
 | 
			
		||||
      xValue,
 | 
			
		||||
      yData && yData[i] !== undefined ? yData[i] : 0,
 | 
			
		||||
    ]);
 | 
			
		||||
    series.push({
 | 
			
		||||
      type: "line",
 | 
			
		||||
      name: `通道 ${index + 1}`,
 | 
			
		||||
      data: seriesData,
 | 
			
		||||
      smooth: false, // 示波器通常显示原始波形
 | 
			
		||||
      symbol: "none", // 不显示数据点标记
 | 
			
		||||
      smooth: false,
 | 
			
		||||
      symbol: "none",
 | 
			
		||||
      lineStyle: {
 | 
			
		||||
        width: 2,
 | 
			
		||||
      },
 | 
			
		||||
@@ -111,9 +114,10 @@ const option = computed((): EChartsOption => {
 | 
			
		||||
    tooltip: {
 | 
			
		||||
      trigger: "axis",
 | 
			
		||||
      formatter: (params: any) => {
 | 
			
		||||
        let result = `时间: ${params[0].data[0].toFixed(2)} ${props.data.xUnit}<br/>`;
 | 
			
		||||
        if (!oscData.value) return "";
 | 
			
		||||
        let result = `时间: ${params[0].data[0].toFixed(2)} ${oscData.value.xUnit}<br/>`;
 | 
			
		||||
        params.forEach((param: any) => {
 | 
			
		||||
          result += `${param.seriesName}: ${param.data[1].toFixed(3)} ${props.data.yUnit}<br/>`;
 | 
			
		||||
          result += `${param.seriesName}: ${param.data[1].toFixed(3)} ${oscData.value?.yUnit ?? ""}<br/>`;
 | 
			
		||||
        });
 | 
			
		||||
        return result;
 | 
			
		||||
      },
 | 
			
		||||
@@ -141,7 +145,7 @@ const option = computed((): EChartsOption => {
 | 
			
		||||
    ],
 | 
			
		||||
    xAxis: {
 | 
			
		||||
      type: "value",
 | 
			
		||||
      name: `时间 (${props.data.xUnit})`,
 | 
			
		||||
      name: oscData.value ? `时间 (${oscData.value.xUnit})` : "时间",
 | 
			
		||||
      nameLocation: "middle",
 | 
			
		||||
      nameGap: 30,
 | 
			
		||||
      axisLine: {
 | 
			
		||||
@@ -156,7 +160,7 @@ const option = computed((): EChartsOption => {
 | 
			
		||||
    },
 | 
			
		||||
    yAxis: {
 | 
			
		||||
      type: "value",
 | 
			
		||||
      name: `电压 (${props.data.yUnit})`,
 | 
			
		||||
      name: oscData.value ? `电压 (${oscData.value.yUnit})` : "电压",
 | 
			
		||||
      nameLocation: "middle",
 | 
			
		||||
      nameGap: 40,
 | 
			
		||||
      axisLine: {
 | 
			
		||||
@@ -1,42 +1,3 @@
 | 
			
		||||
import WaveformDisplay from "./WaveformDisplay.vue";
 | 
			
		||||
import OscilloscopeWaveformDisplay from "./OscilloscopeWaveformDisplay.vue";
 | 
			
		||||
 | 
			
		||||
type WaveformDataType = {
 | 
			
		||||
  x: number[];
 | 
			
		||||
  y: number[][];
 | 
			
		||||
  xUnit: "s" | "ms" | "us";
 | 
			
		||||
  yUnit: "V" | "mV" | "uV";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Test data generator
 | 
			
		||||
function generateTestData(): WaveformDataType {
 | 
			
		||||
  const sampleRate = 1000; // 1kHz
 | 
			
		||||
  const duration = 0.1; // 10ms
 | 
			
		||||
  const points = Math.floor(sampleRate * duration);
 | 
			
		||||
 | 
			
		||||
  const x = Array.from({ length: points }, (_, i) => (i / sampleRate) * 1000); // time in ms
 | 
			
		||||
 | 
			
		||||
  // Generate multiple channels with different waveforms
 | 
			
		||||
  const y = [
 | 
			
		||||
    // Channel 1: Sine wave 50Hz
 | 
			
		||||
    Array.from(
 | 
			
		||||
      { length: points },
 | 
			
		||||
      (_, i) => Math.sin((2 * Math.PI * 50 * i) / sampleRate) * 3.3,
 | 
			
		||||
    ),
 | 
			
		||||
    // Channel 2: Square wave 25Hz
 | 
			
		||||
    Array.from(
 | 
			
		||||
      { length: points },
 | 
			
		||||
      (_, i) => Math.sign(Math.sin((2 * Math.PI * 25 * i) / sampleRate)) * 5,
 | 
			
		||||
    ),
 | 
			
		||||
    // Channel 3: Sawtooth wave 33Hz
 | 
			
		||||
    Array.from(
 | 
			
		||||
      { length: points },
 | 
			
		||||
      (_, i) => (2 * (((33 * i) / sampleRate) % 1) - 1) * 2.5,
 | 
			
		||||
    ),
 | 
			
		||||
    // Channel 4: Noise + DC offset
 | 
			
		||||
    Array.from({ length: points }, () => Math.random() * 0.5 + 1.5),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return { x, y, xUnit: "ms", yUnit: "V" };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { WaveformDisplay, generateTestData , type WaveformDataType };
 | 
			
		||||
export { OscilloscopeWaveformDisplay };
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ import {
 | 
			
		||||
  UDPClient,
 | 
			
		||||
  LogicAnalyzerClient,
 | 
			
		||||
  NetConfigClient,
 | 
			
		||||
  OscilloscopeApiClient,
 | 
			
		||||
} from "@/APIClient";
 | 
			
		||||
 | 
			
		||||
// 支持的客户端类型联合类型
 | 
			
		||||
@@ -26,7 +27,8 @@ type SupportedClient =
 | 
			
		||||
  | TutorialClient
 | 
			
		||||
  | LogicAnalyzerClient
 | 
			
		||||
  | UDPClient
 | 
			
		||||
  | NetConfigClient;
 | 
			
		||||
  | NetConfigClient
 | 
			
		||||
  | OscilloscopeApiClient;
 | 
			
		||||
 | 
			
		||||
export class AuthManager {
 | 
			
		||||
  // 存储token到localStorage
 | 
			
		||||
@@ -158,10 +160,14 @@ export class AuthManager {
 | 
			
		||||
  public static createAuthenticatedLogicAnalyzerClient(): LogicAnalyzerClient {
 | 
			
		||||
    return AuthManager.createAuthenticatedClient(LogicAnalyzerClient);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
  public static createAuthenticatedNetConfigClient(): NetConfigClient {
 | 
			
		||||
    return AuthManager.createAuthenticatedClient(NetConfigClient);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  public static createAuthenticatedOscilloscopeApiClient(): OscilloscopeApiClient {
 | 
			
		||||
    return AuthManager.createAuthenticatedClient(OscilloscopeApiClient);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 登录函数
 | 
			
		||||
  public static async login(
 | 
			
		||||
 
 | 
			
		||||
@@ -104,9 +104,12 @@ import { onMounted, ref, watch } from "vue";
 | 
			
		||||
import Debugger from "./Debugger.vue";
 | 
			
		||||
import { useProvideLogicAnalyzer } from "@/components/LogicAnalyzer";
 | 
			
		||||
import { useProvideWaveformManager } from "@/components/WaveformDisplay/WaveformManager";
 | 
			
		||||
import { useProvideOscilloscope } from "@/components/Oscilloscope/OscilloscopeManager";
 | 
			
		||||
 | 
			
		||||
const analyzer = useProvideLogicAnalyzer();
 | 
			
		||||
const waveformManager = useProvideWaveformManager();
 | 
			
		||||
const oscilloscopeManager = useProvideOscilloscope();
 | 
			
		||||
 | 
			
		||||
waveformManager.logicData.value = waveformManager.generateTestData();
 | 
			
		||||
 | 
			
		||||
const checkID = useLocalStorage("checkID", 1);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
          <Activity class="w-5 h-5" />
 | 
			
		||||
          波形显示
 | 
			
		||||
        </h2>
 | 
			
		||||
        <WaveformDisplay :data="generateTestData()" />
 | 
			
		||||
        <OscilloscopeWaveformDisplay />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { Activity } from "lucide-vue-next";
 | 
			
		||||
import { WaveformDisplay, generateTestData } from "@/components/Oscilloscope";
 | 
			
		||||
import { OscilloscopeWaveformDisplay } from "@/components/Oscilloscope";
 | 
			
		||||
import { useEquipments } from "@/stores/equipments";
 | 
			
		||||
 | 
			
		||||
// 使用全局设备配置
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user