From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点
---
Aging.Infrastructure/Drivers/ScpiDriverAdapter.cs | 248 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 248 insertions(+), 0 deletions(-)
diff --git a/Aging.Infrastructure/Drivers/ScpiDriverAdapter.cs b/Aging.Infrastructure/Drivers/ScpiDriverAdapter.cs
new file mode 100644
index 0000000..c4cde19
--- /dev/null
+++ b/Aging.Infrastructure/Drivers/ScpiDriverAdapter.cs
@@ -0,0 +1,248 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Aging.Infrastructure.Contracts;
+using log4net;
+using PLCHelper;
+
+namespace Aging.Infrastructure.Drivers
+{
+ /// <summary>
+ /// SCPI 鍗忚椹卞姩閫傞厤鍣ㄣ��
+ /// 灏佽 PLCHelper.RestTcpClient锛屽疄鐜扮粺涓�鐨� IDeviceDriver 鎺ュ彛銆�
+ /// 鐢ㄤ簬鎺у埗鏀寔 SCPI 鍛戒护鐨勪华鍣紙濡傜數婧愩�佺數瀛愯礋杞界瓑锛夈��
+ /// </summary>
+ public sealed class ScpiDriverAdapter : IDeviceDriver
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ScpiDriverAdapter));
+
+ private readonly string _ip;
+ private readonly int _port;
+ private RestTcpClient? _client;
+ private bool _disposed;
+ private readonly Stopwatch _latencyStopwatch = new Stopwatch();
+ private double _lastLatencyMs;
+ private string _lastError = string.Empty;
+ private DateTime? _lastErrorTime;
+ private int _reconnectCount;
+
+ /// <inheritdoc/>
+ public string DeviceId { get; }
+
+ /// <inheritdoc/>
+ public bool IsConnected => _client != null;
+
+ /// <inheritdoc/>
+ public event EventHandler<DeviceConnectionEventArgs>? ConnectionChanged;
+
+ /// <summary>
+ /// 鍒濆鍖� SCPI 鍗忚椹卞姩閫傞厤鍣�
+ /// </summary>
+ /// <param name="deviceId">璁惧鍞竴鏍囪瘑绗�</param>
+ /// <param name="ip">鐩爣 IP 鍦板潃</param>
+ /// <param name="port">鐩爣绔彛鍙�</param>
+ public ScpiDriverAdapter(string deviceId, string ip, int port)
+ {
+ DeviceId = deviceId ?? throw new ArgumentNullException(nameof(deviceId));
+ _ip = ip ?? throw new ArgumentNullException(nameof(ip));
+ _port = port;
+ }
+
+ /// <inheritdoc/>
+ public Task ConnectAsync(CancellationToken ct)
+ {
+ ThrowIfDisposed();
+ Log.Info($"[SCPI] 璁惧 {DeviceId} 姝e湪杩炴帴 {_ip}:{_port}...");
+
+ try
+ {
+ _client = new RestTcpClient(_ip, _port);
+ _client.ReceiveTimeout = 5000;
+ _client.ConnectServer();
+
+ // 鍙戦�佸浣嶅懡浠ら獙璇佽繛鎺�
+ if (_client.Request("*RST", out string response, out string errMsg))
+ {
+ Log.Info($"[SCPI] 璁惧 {DeviceId} 澶嶄綅鍝嶅簲: {response}");
+ }
+ else
+ {
+ Log.Warn($"[SCPI] 璁惧 {DeviceId} 澶嶄綅澶辫触: {errMsg}");
+ }
+
+ _lastError = string.Empty;
+ Log.Info($"[SCPI] 璁惧 {DeviceId} 宸叉垚鍔熻繛鎺ュ埌 {_ip}:{_port}");
+ OnConnectionChanged(true, "杩炴帴鎴愬姛");
+ }
+ catch (Exception ex)
+ {
+ _lastError = ex.Message;
+ _lastErrorTime = DateTime.Now;
+ Log.Error($"[SCPI] 璁惧 {DeviceId} 杩炴帴寮傚父: {ex.Message}", ex);
+ _client?.Dispose();
+ _client = null;
+ throw;
+ }
+
+ return Task.CompletedTask;
+ }
+
+ /// <inheritdoc/>
+ public Task DisconnectAsync()
+ {
+ ThrowIfDisposed();
+ Log.Info($"[SCPI] 璁惧 {DeviceId} 姝e湪鏂紑杩炴帴...");
+
+ try
+ {
+ _client?.Dispose();
+ _client = null;
+
+ Log.Info($"[SCPI] 璁惧 {DeviceId} 宸叉柇寮�杩炴帴");
+ OnConnectionChanged(false, "涓诲姩鏂紑");
+ }
+ catch (Exception ex)
+ {
+ _lastError = ex.Message;
+ _lastErrorTime = DateTime.Now;
+ Log.Error($"[SCPI] 璁惧 {DeviceId} 鏂紑杩炴帴寮傚父: {ex.Message}", ex);
+ _client = null;
+ }
+
+ return Task.CompletedTask;
+ }
+
+ /// <inheritdoc/>
+ public Task<string> SendCommandAsync(string command, CancellationToken ct)
+ {
+ ThrowIfDisposed();
+ if (!IsConnected) throw new InvalidOperationException("[SCPI] 璁惧鏈繛鎺�");
+
+ _latencyStopwatch.Restart();
+ try
+ {
+ Log.Debug($"[SCPI] 璁惧 {DeviceId} 鍙戦�佸懡浠�: {command}");
+
+ // SCPI 鍛戒护浠� \r 缁撳熬锛圧estTcpClient 鍗忚鑷姩澶勭悊锛�
+ bool success = _client.Request(command, out string response, out string errMsg);
+
+ _latencyStopwatch.Stop();
+ _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+
+ if (!success)
+ {
+ _lastError = errMsg;
+ _lastErrorTime = DateTime.Now;
+ throw new InvalidOperationException($"[SCPI] 鍛戒护鎵ц澶辫触: {errMsg}");
+ }
+
+ Log.Debug($"[SCPI] 璁惧 {DeviceId} 鍛戒护鍝嶅簲: {response} (寤惰繜 {_lastLatencyMs:F2}ms)");
+ return Task.FromResult(response);
+ }
+ catch (Exception ex)
+ {
+ _latencyStopwatch.Stop();
+ _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+ _lastError = ex.Message;
+ _lastErrorTime = DateTime.Now;
+ Log.Error($"[SCPI] 璁惧 {DeviceId} 鍙戦�佸懡浠ゅけ璐�: {ex.Message}", ex);
+ throw;
+ }
+ }
+
+ /// <inheritdoc/>
+ public Task<string> QueryAsync(string query, CancellationToken ct)
+ {
+ ThrowIfDisposed();
+ if (!IsConnected) throw new InvalidOperationException("[SCPI] 璁惧鏈繛鎺�");
+
+ _latencyStopwatch.Restart();
+ try
+ {
+ Log.Debug($"[SCPI] 璁惧 {DeviceId} 鍙戦�佹煡璇�: {query}");
+
+ // 鍙戦�佹煡璇㈠懡浠ゅ苟绛夊緟鍥炶锛堝 "MEAS:CURR?"锛�
+ bool success = _client.Request(query, out string response, out string errMsg);
+
+ _latencyStopwatch.Stop();
+ _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+
+ if (!success)
+ {
+ _lastError = errMsg;
+ _lastErrorTime = DateTime.Now;
+ throw new TimeoutException($"[SCPI] 鏌ヨ瓒呮椂鎴栧け璐�: {errMsg}");
+ }
+
+ Log.Debug($"[SCPI] 璁惧 {DeviceId} 鏌ヨ鍝嶅簲: {response} (寤惰繜 {_lastLatencyMs:F2}ms)");
+ return Task.FromResult(response);
+ }
+ catch (Exception ex)
+ {
+ _latencyStopwatch.Stop();
+ _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+ _lastError = ex.Message;
+ _lastErrorTime = DateTime.Now;
+ Log.Error($"[SCPI] 璁惧 {DeviceId} 鏌ヨ澶辫触: {ex.Message}", ex);
+ throw;
+ }
+ }
+
+ /// <inheritdoc/>
+ public DeviceDriverStatus GetStatus()
+ {
+ return new DeviceDriverStatus
+ {
+ IsConnected = IsConnected,
+ IsHealthy = IsConnected && string.IsNullOrEmpty(_lastError),
+ LastError = _lastError,
+ LastErrorTime = _lastErrorTime,
+ LatencyMs = _lastLatencyMs,
+ ReconnectCount = _reconnectCount
+ };
+ }
+
+ /// <inheritdoc/>
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+
+ try
+ {
+ _client?.Dispose();
+ }
+ catch (Exception ex)
+ {
+ Log.Error($"[SCPI] 璁惧 {DeviceId} 閲婃斁璧勬簮寮傚父: {ex.Message}", ex);
+ }
+ }
+
+ /// <summary>
+ /// 瑙﹀彂杩炴帴鐘舵�佸彉鍖栦簨浠�
+ /// </summary>
+ private void OnConnectionChanged(bool isConnected, string reason)
+ {
+ ConnectionChanged?.Invoke(this, new DeviceConnectionEventArgs
+ {
+ DeviceId = DeviceId,
+ IsConnected = isConnected,
+ Reason = reason
+ });
+
+ if (!isConnected)
+ {
+ _reconnectCount++;
+ }
+ }
+
+ /// <summary>
+ /// 妫�鏌ユ槸鍚﹀凡閲婃斁
+ /// </summary>
+ private void ThrowIfDisposed()
+ {
+ if (_disposed) throw new ObjectDisposedException(nameof(ScpiDriverAdapter));
+ }
+ }
+}
--
Gitblit v1.9.1