This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FPGA_WebLab/server/src/Peripherals/OscilloscopeClient.cs
2025-07-20 13:17:36 +08:00

349 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Net;
using Common;
using DotNext;
namespace Peripherals.OscilloscopeClient;
static class OscilloscopeAddr
{
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
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
readonly int timeout = 2000;
readonly int taskID = 0;
readonly int port;
readonly string address;
private IPEndPoint ep;
/// <summary>
/// 初始化示波器客户端
/// </summary>
/// <param name="address">示波器设备IP地址</param>
/// <param name="port">示波器设备端口</param>
/// <param name="timeout">超时时间(毫秒)</param>
public Oscilloscope(string address, int port, int timeout = 2000)
{
if (timeout < 0)
throw new ArgumentException("Timeout couldn't be negative", nameof(timeout));
this.address = address;
this.port = port;
this.ep = new IPEndPoint(IPAddress.Parse(address), port);
this.timeout = timeout;
}
/// <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 / 8)
{
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;
}
}