Files
ASTM-D7896-19TransientHot-W…/Services/LanScpiSocket.cs
2026-05-20 13:49:45 +08:00

178 lines
6.7 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;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ASTM_D7896_Tester.Services
{
/// <summary>
/// 基于 LAN (TCP/IP) 的 TH1963 数字多用表通信服务
/// 支持 SCPI 命令,实现高速批量采集
/// </summary>
public class Th1963LanService : IDisposable
{
private TcpClient _tcpClient;
private NetworkStream _stream;
private bool _disposed = false;
/// <summary>
/// 接收缓冲区大小(字节),建议至少 1MB 以支持大量数据
/// </summary>
public int ReceiveBufferSize { get; set; } = 2 * 1024 * 1024; // 2 MB
/// <summary>
/// 连接超时(毫秒)
/// </summary>
public int ConnectTimeoutMs { get; set; } = 3000;
/// <summary>
/// 读写超时(毫秒)
/// </summary>
public int ReadWriteTimeoutMs { get; set; } = 5000;
/// <summary>
/// 连接到 TH1963
/// </summary>
/// <param name="ipAddress">仪器 IP 地址,如 "192.168.1.241"</param>
/// <param name="port">端口号,默认 45454</param>
public async Task ConnectAsync(string ipAddress, int port = 45454)
{
if (_tcpClient != null && _tcpClient.Connected)
return;
_tcpClient = new TcpClient();
_tcpClient.ReceiveBufferSize = ReceiveBufferSize;
_tcpClient.SendBufferSize = ReceiveBufferSize;
var connectTask = _tcpClient.ConnectAsync(ipAddress, port);
if (await Task.WhenAny(connectTask, Task.Delay(ConnectTimeoutMs)) != connectTask)
throw new TimeoutException($"连接 TH1963 ({ipAddress}:{port}) 超时");
_stream = _tcpClient.GetStream();
_stream.ReadTimeout = ReadWriteTimeoutMs;
_stream.WriteTimeout = ReadWriteTimeoutMs;
}
/// <summary>
/// 发送 SCPI 命令并等待响应(适用于查询类命令)
/// </summary>
public async Task<string> QueryAsync(string command)
{
EnsureConnected();
byte[] cmdBytes = Encoding.ASCII.GetBytes(command + "\n");
await _stream.WriteAsync(cmdBytes, 0, cmdBytes.Length);
// 读取响应,直到换行符
var responseBuilder = new StringBuilder();
byte[] buffer = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
string chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead);
responseBuilder.Append(chunk);
if (chunk.Contains("\n")) break;
}
return responseBuilder.ToString().Trim();
}
/// <summary>
/// 发送命令但不等待响应(适用于设置类命令)
/// </summary>
public async Task SendCommandAsync(string command)
{
EnsureConnected();
byte[] cmdBytes = Encoding.ASCII.GetBytes(command + "\n");
await _stream.WriteAsync(cmdBytes, 0, cmdBytes.Length);
}
/// <summary>
/// 配置为高速直流电压测量(用于批量采集)
/// 设置0.02 PLC≈400μs积分时间自动量程关闭自动归零
/// </summary>
public async Task ConfigureForHighSpeedDcvAsync()
{
await SendCommandAsync("CONF:VOLT:DC AUTO");
await SendCommandAsync("VOLT:DC:NPLC 0.02"); // 最快速度
await SendCommandAsync("VOLT:DC:ZERO:AUTO OFF"); // 提高速度
await SendCommandAsync("TRIG:SOUR EXT"); // 外部触发与PLC同步
await SendCommandAsync("TRIG:DEL:AUTO OFF");
await SendCommandAsync("TRIG:DEL 0");
}
/// <summary>
/// 批量采集指定数量的电压值(使用外部触发,需等待硬件触发信号)
/// 调用前必须已执行 ConfigureForHighSpeedDcvAsync 和 SAMP:COUN
/// </summary>
/// <param name="sampleCount">采样点数(最大 5000</param>
/// <returns>电压数组(单位:伏特)</returns>
public async Task<double[]> AcquireBatchAsync(int sampleCount)
{
// 预置采样点数
await SendCommandAsync($"SAMP:COUN {sampleCount}");
// 进入等待触发状态
await SendCommandAsync("INIT");
// 等待采集完成估算sampleCount / 1000 秒 + 稳定时间)
int waitMs = (int)(sampleCount / 1000.0 * 1000) + 200;
await Task.Delay(waitMs);
// 取回数据
string response = await QueryAsync("FETCh?");
string[] parts = response.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
double[] values = new double[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (!double.TryParse(parts[i], System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out values[i]))
throw new Exception($"解析电压值失败: {parts[i]}");
}
if (values.Length != sampleCount)
throw new Exception($"期望 {sampleCount} 个点,实际收到 {values.Length}");
return values;
}
/// <summary>
/// 单次读取当前电压(非批量,用于实时监控)
/// </summary>
public async Task<double> ReadVoltageAsync()
{
string resp = await QueryAsync("MEAS:VOLT:DC?");
if (double.TryParse(resp, System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out double value))
return value;
throw new Exception($"电压读数无效: {resp}");
}
/// <summary>
/// 复位仪器
/// </summary>
public async Task ResetAsync() => await SendCommandAsync("*RST");
/// <summary>
/// 查询仪器标识
/// </summary>
public async Task<string> IdentifyAsync() => await QueryAsync("*IDN?");
private void EnsureConnected()
{
if (_tcpClient == null || !_tcpClient.Connected)
throw new InvalidOperationException("未连接到 TH1963请先调用 ConnectAsync");
}
// 实现 IDisposable
public void Dispose()
{
if (!_disposed)
{
_stream?.Close();
_tcpClient?.Close();
_stream?.Dispose();
_tcpClient?.Dispose();
_disposed = true;
}
}
}
}