diff --git a/components.d.ts b/components.d.ts
index c80ebed..6ba3abd 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -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']
}
}
diff --git a/server/src/Controllers/OscilloscopeController.cs b/server/src/Controllers/OscilloscopeController.cs
new file mode 100644
index 0000000..f7bce73
--- /dev/null
+++ b/server/src/Controllers/OscilloscopeController.cs
@@ -0,0 +1,494 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Cors;
+using Microsoft.AspNetCore.Mvc;
+using Peripherals.OscilloscopeClient;
+
+namespace server.Controllers;
+
+///
+/// 示波器API控制器 - 普通用户权限
+///
+[ApiController]
+[Route("api/[controller]")]
+[Authorize(Roles = "User")]
+public class OscilloscopeApiController : ControllerBase
+{
+ private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+ ///
+ /// 示波器完整配置
+ ///
+ public class OscilloscopeFullConfig
+ {
+ ///
+ /// 是否启动捕获
+ ///
+ public bool CaptureEnabled { get; set; }
+
+ ///
+ /// 触发电平(0-255)
+ ///
+ public byte TriggerLevel { get; set; }
+
+ ///
+ /// 触发边沿(true为上升沿,false为下降沿)
+ ///
+ public bool TriggerRisingEdge { get; set; }
+
+ ///
+ /// 水平偏移量(0-1023)
+ ///
+ public ushort HorizontalShift { get; set; }
+
+ ///
+ /// 抽样率(0-1023)
+ ///
+ public ushort DecimationRate { get; set; }
+
+ ///
+ /// 是否自动刷新RAM
+ ///
+ public bool AutoRefreshRAM { get; set; } = true;
+ }
+
+ ///
+ /// 示波器状态和数据
+ ///
+ public class OscilloscopeDataResponse
+ {
+ ///
+ /// AD采样频率
+ ///
+ public uint ADFrequency { get; set; }
+
+ ///
+ /// AD采样幅度
+ ///
+ public byte ADVpp { get; set; }
+
+ ///
+ /// AD采样最大值
+ ///
+ public byte ADMax { get; set; }
+
+ ///
+ /// AD采样最小值
+ ///
+ public byte ADMin { get; set; }
+
+ ///
+ /// 波形数据(Base64编码)
+ ///
+ public string WaveformData { get; set; } = string.Empty;
+ }
+
+ ///
+ /// 获取示波器实例
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// 初始化示波器
+ ///
+ /// 示波器配置
+ /// 操作结果
+ [HttpPost("Initialize")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+
+ ///
+ /// 启动捕获
+ ///
+ /// 操作结果
+ [HttpPost("StartCapture")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+
+ ///
+ /// 停止捕获
+ ///
+ /// 操作结果
+ [HttpPost("StopCapture")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+
+ ///
+ /// 获取示波器数据和状态
+ ///
+ /// 示波器数据和状态信息
+ [HttpGet("GetData")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(OscilloscopeDataResponse), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+
+ ///
+ /// 更新触发参数
+ ///
+ /// 触发电平(0-255)
+ /// 触发边沿(true为上升沿,false为下降沿)
+ /// 操作结果
+ [HttpPost("UpdateTrigger")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+
+ ///
+ /// 更新采样参数
+ ///
+ /// 水平偏移量(0-1023)
+ /// 抽样率(0-1023)
+ /// 操作结果
+ [HttpPost("UpdateSampling")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+
+ ///
+ /// 手动刷新RAM
+ ///
+ /// 操作结果
+ [HttpPost("RefreshRAM")]
+ [EnableCors("Users")]
+ [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+ [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+ public async Task 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, "操作失败,请稍后重试");
+ }
+ }
+}
\ No newline at end of file
diff --git a/server/src/Peripherals/OscilloscopeClient.cs b/server/src/Peripherals/OscilloscopeClient.cs
index 3eeff22..ad04055 100644
--- a/server/src/Peripherals/OscilloscopeClient.cs
+++ b/server/src/Peripherals/OscilloscopeClient.cs
@@ -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;
+
+ ///
+ /// 0x0000_0000:R/W[0] wave_run 启动捕获/关闭
+ ///
+ public const UInt32 START_CAPTURE = BASE + 0x0000_0000;
+
+ ///
+ /// 0x0000_0001: R/W[7:0] trig_level 触发电平
+ ///
+ public const UInt32 TRIG_LEVEL = BASE + 0x0000_0001;
+
+ ///
+ /// 0x0000_0002:R/W[0] trig_edge 触发边沿,0-下降沿,1-上升沿
+ ///
+ public const UInt32 TRIG_EDGE = BASE + 0x0000_0002;
+
+ ///
+ /// 0x0000_0003: R/W[9:0] h shift 水平偏移量
+ ///
+ public const UInt32 H_SHIFT = BASE + 0x0000_0003;
+
+ ///
+ /// 0x0000_0004: R/W[9:0] deci rate 抽样率,0—1023
+ ///
+ public const UInt32 DECI_RATE = BASE + 0x0000_0004;
+
+ ///
+ /// 0x0000_0005:R/W[0] ram refresh RAM刷新
+ ///
+ public const UInt32 RAM_FRESH = BASE + 0x0000_0005;
+
+ ///
+ /// 0x0000 0006:R[19: 0] ad_freq AD采样频率
+ ///
+ public const UInt32 AD_FREQ = BASE + 0x0000_0006;
+
+ ///
+ /// Ox0000_0007: R[7:0] ad_vpp AD采样幅度
+ ///
+ public const UInt32 AD_VPP = BASE + 0x0000_0007;
+
+ ///
+ /// 0x0000_0008: R[7:0] ad max AD采样最大值
+ ///
+ public const UInt32 AD_MAX = BASE + 0x0000_0008;
+
+ ///
+ /// 0x0000_0009: R[7:0] ad_min AD采样最小值
+ ///
+ public const UInt32 AD_MIN = BASE + 0x0000_0009;
+
+ ///
+ /// 0x0000_1000-0x0000_13FF:R[7:0] wave_rd_data 共1024个字节
+ ///
+ 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;
}
+
+ ///
+ /// 控制示波器的捕获开关
+ ///
+ /// 是否启动捕获
+ /// 操作结果,成功返回true,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 设置触发电平
+ ///
+ /// 触发电平值(0-255)
+ /// 操作结果,成功返回true,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 设置触发边沿
+ ///
+ /// true为上升沿,false为下降沿
+ /// 操作结果,成功返回true,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 设置水平偏移量
+ ///
+ /// 水平偏移量值(0-1023)
+ /// 操作结果,成功返回true,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 设置抽样率
+ ///
+ /// 抽样率值(0-1023)
+ /// 操作结果,成功返回true,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 刷新RAM
+ ///
+ /// 操作结果,成功返回true,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 获取AD采样频率
+ ///
+ /// 操作结果,成功返回采样频率值,否则返回异常信息
+ public async ValueTask> 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;
+ }
+
+ ///
+ /// 获取AD采样幅度
+ ///
+ /// 操作结果,成功返回采样幅度值,否则返回异常信息
+ public async ValueTask> 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];
+ }
+
+ ///
+ /// 获取AD采样最大值
+ ///
+ /// 操作结果,成功返回采样最大值,否则返回异常信息
+ public async ValueTask> 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];
+ }
+
+ ///
+ /// 获取AD采样最小值
+ ///
+ /// 操作结果,成功返回采样最小值,否则返回异常信息
+ public async ValueTask> 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];
+ }
+
+ ///
+ /// 获取波形采样数据
+ ///
+ /// 操作结果,成功返回采样数据数组,否则返回异常信息
+ public async ValueTask> 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;
+ }
}
diff --git a/src/APIClient.ts b/src/APIClient.ts
index 4f29235..9983da4 100644
--- a/src/APIClient.ts
+++ b/src/APIClient.ts
@@ -3170,6 +3170,61 @@ export class NetConfigClient {
return Promise.resolve(null as any);
}
+ /**
+ * 设置板卡MAC地址
+ * @param boardMac (optional) 板卡MAC地址(格式:AA:BB:CC:DD:EE:FF)
+ * @return 操作结果
+ */
+ setBoardMAC(boardMac: string | undefined): Promise {
+ 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 {
+ 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 : 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(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 {
- 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 {
+ 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 {
+ protected processGetLocalNetworkInfo(response: Response): Promise {
+ 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 : 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(null as any);
+ }
+}
+
+export class OscilloscopeApiClient {
+ private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };
+ private baseUrl: string;
+ protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
+
+ constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {
+ this.http = http ? http : window as any;
+ this.baseUrl = baseUrl ?? "http://localhost:5000";
+ }
+
+ /**
+ * 初始化示波器
+ * @param config 示波器配置
+ * @return 操作结果
+ */
+ initialize(config: OscilloscopeFullConfig): Promise {
+ 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 {
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(null as any);
+ }
+
+ /**
+ * 启动捕获
+ * @return 操作结果
+ */
+ startCapture(): Promise {
+ 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 {
+ 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 : 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(null as any);
+ }
+
+ /**
+ * 停止捕获
+ * @return 操作结果
+ */
+ stopCapture(): Promise {
+ 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 {
+ 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 : 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(null as any);
+ }
+
+ /**
+ * 获取示波器数据和状态
+ * @return 示波器数据和状态信息
+ */
+ getData(): Promise {
+ 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 {
+ 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(null as any);
+ }
+
+ /**
+ * 更新触发参数
+ * @param level (optional) 触发电平(0-255)
+ * @param risingEdge (optional) 触发边沿(true为上升沿,false为下降沿)
+ * @return 操作结果
+ */
+ updateTrigger(level: number | undefined, risingEdge: boolean | undefined): Promise {
+ 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 {
+ 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 : 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(null as any);
+ }
+
+ /**
+ * 更新采样参数
+ * @param horizontalShift (optional) 水平偏移量(0-1023)
+ * @param decimationRate (optional) 抽样率(0-1023)
+ * @return 操作结果
+ */
+ updateSampling(horizontalShift: number | undefined, decimationRate: number | undefined): Promise {
+ 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 {
+ 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 : 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(null as any);
+ }
+
+ /**
+ * 手动刷新RAM
+ * @return 操作结果
+ */
+ refreshRAM(): Promise {
+ 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 {
+ 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 : 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))
+ (this)[property] = (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))
+ (this)[property] = (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 {
/** 突发类型 */
diff --git a/src/components/Oscilloscope/OscilloscopeManager.ts b/src/components/Oscilloscope/OscilloscopeManager.ts
new file mode 100644
index 0000000..aa32fd4
--- /dev/null
+++ b/src/components/Oscilloscope/OscilloscopeManager.ts
@@ -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();
+ const alert = useRequiredInjection(useAlertStore);
+
+ // 互斥锁
+ const operationMutex = new Mutex();
+
+ // 状态
+ const isApplying = ref(false);
+ const isCapturing = ref(false);
+
+ // 配置
+ const config = reactive(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 };
\ No newline at end of file
diff --git a/src/components/Oscilloscope/WaveformDisplay.vue b/src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue
similarity index 65%
rename from src/components/Oscilloscope/WaveformDisplay.vue
rename to src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue
index 868df93..0960d64 100644
--- a/src/components/Oscilloscope/WaveformDisplay.vue
+++ b/src/components/Oscilloscope/OscilloscopeWaveformDisplay.vue
@@ -1,6 +1,6 @@