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/ModBusDriverAdapter.cs |  347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 347 insertions(+), 0 deletions(-)

diff --git a/Aging.Infrastructure/Drivers/ModBusDriverAdapter.cs b/Aging.Infrastructure/Drivers/ModBusDriverAdapter.cs
new file mode 100644
index 0000000..784e64b
--- /dev/null
+++ b/Aging.Infrastructure/Drivers/ModBusDriverAdapter.cs
@@ -0,0 +1,347 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Aging.Infrastructure.Contracts;
+using HslCommunication;
+using HslCommunication.ModBus;
+using log4net;
+
+namespace Aging.Infrastructure.Drivers
+{
+    /// <summary>
+    /// ModBus TCP 椹卞姩閫傞厤鍣ㄣ��
+    /// 灏佽 HslCommunication.ModBusTcpNet锛屽疄鐜扮粺涓�鐨� IDeviceDriver 鎺ュ彛銆�
+    /// 鍛戒护鏍煎紡绾﹀畾:
+    ///   鍐欏叆: "WRITE:address:value" 濡� "WRITE:40001:100" 鎴� "WRITE:00001:TRUE"
+    ///   璇诲彇: "READ:address" 濡� "READ:00001" 鎴� "READ:40001"
+    /// </summary>
+    public sealed class ModBusDriverAdapter : IDeviceDriver
+    {
+        private static readonly ILog Log = LogManager.GetLogger(typeof(ModBusDriverAdapter));
+
+        private readonly string _ip;
+        private readonly int _port;
+        private readonly byte _slaveId;
+        private ModbusTcpNet? _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>
+        /// 鍒濆鍖� ModBus TCP 椹卞姩閫傞厤鍣�
+        /// </summary>
+        /// <param name="deviceId">璁惧鍞竴鏍囪瘑绗�</param>
+        /// <param name="ip">鐩爣 IP 鍦板潃</param>
+        /// <param name="port">鐩爣绔彛鍙�</param>
+        /// <param name="slaveId">浠庣珯鍦板潃锛堥粯璁や负 1锛�</param>
+        public ModBusDriverAdapter(string deviceId, string ip, int port, byte slaveId = 1)
+        {
+            DeviceId = deviceId ?? throw new ArgumentNullException(nameof(deviceId));
+            _ip = ip ?? throw new ArgumentNullException(nameof(ip));
+            _port = port;
+            _slaveId = slaveId;
+        }
+
+        /// <inheritdoc/>
+        public Task ConnectAsync(CancellationToken ct)
+        {
+            ThrowIfDisposed();
+            Log.Info($"[ModBus] 璁惧 {DeviceId} 姝e湪杩炴帴 {_ip}:{_port} (浠庣珯: {_slaveId})...");
+
+            try
+            {
+                _client = new ModbusTcpNet(_ip, _port, _slaveId);
+                OperateResult result = _client.ConnectServer();
+
+                if (!result.IsSuccess)
+                {
+                    _lastError = result.Message;
+                    _lastErrorTime = DateTime.Now;
+                    throw new InvalidOperationException($"[ModBus] 璁惧 {DeviceId} 杩炴帴澶辫触: {result.Message}");
+                }
+
+                _lastError = string.Empty;
+                Log.Info($"[ModBus] 璁惧 {DeviceId} 宸叉垚鍔熻繛鎺ュ埌 {_ip}:{_port}");
+                OnConnectionChanged(true, "杩炴帴鎴愬姛");
+            }
+            catch (Exception ex)
+            {
+                _lastError = ex.Message;
+                _lastErrorTime = DateTime.Now;
+                Log.Error($"[ModBus] 璁惧 {DeviceId} 杩炴帴寮傚父: {ex.Message}", ex);
+                _client?.ConnectClose();
+                _client?.Dispose();
+                _client = null;
+                throw;
+            }
+
+            return Task.CompletedTask;
+        }
+
+        /// <inheritdoc/>
+        public Task DisconnectAsync()
+        {
+            ThrowIfDisposed();
+            Log.Info($"[ModBus] 璁惧 {DeviceId} 姝e湪鏂紑杩炴帴...");
+
+            try
+            {
+                if (_client != null)
+                {
+                    _client.ConnectClose();
+                    _client.Dispose();
+                    _client = null;
+                }
+
+                Log.Info($"[ModBus] 璁惧 {DeviceId} 宸叉柇寮�杩炴帴");
+                OnConnectionChanged(false, "涓诲姩鏂紑");
+            }
+            catch (Exception ex)
+            {
+                _lastError = ex.Message;
+                _lastErrorTime = DateTime.Now;
+                Log.Error($"[ModBus] 璁惧 {DeviceId} 鏂紑杩炴帴寮傚父: {ex.Message}", ex);
+                _client = null;
+            }
+
+            return Task.CompletedTask;
+        }
+
+        /// <inheritdoc/>
+        public Task<string> SendCommandAsync(string command, CancellationToken ct)
+        {
+            ThrowIfDisposed();
+            if (!IsConnected) throw new InvalidOperationException("[ModBus] 璁惧鏈繛鎺�");
+
+            _latencyStopwatch.Restart();
+            try
+            {
+                Log.Debug($"[ModBus] 璁惧 {DeviceId} 鍙戦�佸懡浠�: {command}");
+
+                // 瑙f瀽鍛戒护鏍煎紡: "WRITE:address:value"
+                string[] parts = command.Split(':');
+                if (parts.Length < 3)
+                {
+                    throw new ArgumentException($"[ModBus] 鍛戒护鏍煎紡閿欒锛屽簲涓� WRITE:address:value锛屽疄闄�: {command}");
+                }
+
+                string operation = parts[0].ToUpper();
+                string address = parts[1];
+                string value = parts[2];
+
+                if (operation != "WRITE")
+                {
+                    throw new ArgumentException($"[ModBus] 涓嶆敮鎸佺殑鎿嶄綔绫诲瀷: {operation}锛屽簲涓� WRITE");
+                }
+
+                // 鏍规嵁鍊煎垽鏂暟鎹被鍨嬪苟鍐欏叆
+                OperateResult result;
+                if (bool.TryParse(value, out bool boolVal))
+                {
+                    result = _client.Write(address, boolVal);
+                }
+                else if (short.TryParse(value, out short shortVal))
+                {
+                    result = _client.Write(address, shortVal);
+                }
+                else if (int.TryParse(value, out int intVal))
+                {
+                    result = _client.Write(address, intVal);
+                }
+                else if (float.TryParse(value, out float floatVal))
+                {
+                    result = _client.Write(address, floatVal);
+                }
+                else
+                {
+                    throw new ArgumentException($"[ModBus] 鏃犳硶瑙f瀽鐨勫��: {value}");
+                }
+
+                _latencyStopwatch.Stop();
+                _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+
+                if (!result.IsSuccess)
+                {
+                    _lastError = result.Message;
+                    _lastErrorTime = DateTime.Now;
+                    throw new InvalidOperationException($"[ModBus] 鍐欏叆澶辫触: {result.Message}");
+                }
+
+                string response = "OK";
+                Log.Debug($"[ModBus] 璁惧 {DeviceId} 鍐欏叆鎴愬姛 (寤惰繜 {_lastLatencyMs:F2}ms)");
+                return Task.FromResult(response);
+            }
+            catch (Exception ex)
+            {
+                _latencyStopwatch.Stop();
+                _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+                _lastError = ex.Message;
+                _lastErrorTime = DateTime.Now;
+                Log.Error($"[ModBus] 璁惧 {DeviceId} 鍙戦�佸懡浠ゅけ璐�: {ex.Message}", ex);
+                throw;
+            }
+        }
+
+        /// <inheritdoc/>
+        public Task<string> QueryAsync(string query, CancellationToken ct)
+        {
+            ThrowIfDisposed();
+            if (!IsConnected) throw new InvalidOperationException("[ModBus] 璁惧鏈繛鎺�");
+
+            _latencyStopwatch.Restart();
+            try
+            {
+                Log.Debug($"[ModBus] 璁惧 {DeviceId} 鍙戦�佹煡璇�: {query}");
+
+                // 瑙f瀽鏌ヨ鏍煎紡: "READ:address"
+                string[] parts = query.Split(':');
+                if (parts.Length < 2)
+                {
+                    throw new ArgumentException($"[ModBus] 鏌ヨ鏍煎紡閿欒锛屽簲涓� READ:address锛屽疄闄�: {query}");
+                }
+
+                string operation = parts[0].ToUpper();
+                string address = parts[1];
+
+                if (operation != "READ")
+                {
+                    throw new ArgumentException($"[ModBus] 涓嶆敮鎸佺殑鎿嶄綔绫诲瀷: {operation}锛屽簲涓� READ");
+                }
+
+                // 灏濊瘯璇诲彇涓嶅悓绫诲瀷锛堟牴鎹湴鍧�鑼冨洿鑷姩鍒ゆ柇锛�
+                // 00001-09999: 绾垮湀锛圔OOL锛�
+                // 10001-19999: 绂绘暎杈撳叆锛圔OOL锛�
+                // 30001-39999: 杈撳叆瀵勫瓨鍣紙INT16锛�
+                // 40001-49999: 淇濇寔瀵勫瓨鍣紙INT16锛�
+                string response;
+                int addrNum;
+                if (int.TryParse(address, out addrNum))
+                {
+                    if (addrNum >= 1 && addrNum <= 9999)
+                    {
+                        // 绾垮湀璇诲彇
+                        var result = _client.ReadBool(address);
+                        response = result.IsSuccess ? result.Content.ToString() : $"ERROR: {result.Message}";
+                    }
+                    else if (addrNum >= 10001 && addrNum <= 19999)
+                    {
+                        // 绂绘暎杈撳叆璇诲彇
+                        var result = _client.ReadBool(address);
+                        response = result.IsSuccess ? result.Content.ToString() : $"ERROR: {result.Message}";
+                    }
+                    else if (addrNum >= 30001 && addrNum <= 39999)
+                    {
+                        // 杈撳叆瀵勫瓨鍣ㄨ鍙�
+                        var result = _client.ReadInt16(address);
+                        response = result.IsSuccess ? result.Content.ToString() : $"ERROR: {result.Message}";
+                    }
+                    else if (addrNum >= 40001 && addrNum <= 49999)
+                    {
+                        // 淇濇寔瀵勫瓨鍣ㄨ鍙�
+                        var result = _client.ReadInt16(address);
+                        response = result.IsSuccess ? result.Content.ToString() : $"ERROR: {result.Message}";
+                    }
+                    else
+                    {
+                        // 榛樿璇诲彇 INT16
+                        var result = _client.ReadInt16(address);
+                        response = result.IsSuccess ? result.Content.ToString() : $"ERROR: {result.Message}";
+                    }
+                }
+                else
+                {
+                    // 闈炴暟瀛楀湴鍧�锛屽皾璇曡鍙� INT16
+                    var result = _client.ReadInt16(address);
+                    response = result.IsSuccess ? result.Content.ToString() : $"ERROR: {result.Message}";
+                }
+
+                _latencyStopwatch.Stop();
+                _lastLatencyMs = _latencyStopwatch.Elapsed.TotalMilliseconds;
+                Log.Debug($"[ModBus] 璁惧 {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($"[ModBus] 璁惧 {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
+            {
+                if (_client != null)
+                {
+                    _client.ConnectClose();
+                    _client.Dispose();
+                }
+            }
+            catch (Exception ex)
+            {
+                Log.Error($"[ModBus] 璁惧 {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(ModBusDriverAdapter));
+        }
+    }
+}

--
Gitblit v1.9.1