feat: 添加示波器前后端
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user