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
{
///
/// ModBus TCP 驱动适配器。
/// 封装 HslCommunication.ModBusTcpNet,实现统一的 IDeviceDriver 接口。
/// 命令格式约定:
/// 写入: "WRITE:address:value" 如 "WRITE:40001:100" 或 "WRITE:00001:TRUE"
/// 读取: "READ:address" 如 "READ:00001" 或 "READ:40001"
///
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;
///
public string DeviceId { get; }
///
public bool IsConnected => _client != null;
///
public event EventHandler? ConnectionChanged;
///
/// 初始化 ModBus TCP 驱动适配器
///
/// 设备唯一标识符
/// 目标 IP 地址
/// 目标端口号
/// 从站地址(默认为 1)
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;
}
///
public Task ConnectAsync(CancellationToken ct)
{
ThrowIfDisposed();
Log.Info($"[ModBus] 设备 {DeviceId} 正在连接 {_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;
}
///
public Task DisconnectAsync()
{
ThrowIfDisposed();
Log.Info($"[ModBus] 设备 {DeviceId} 正在断开连接...");
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;
}
///
public Task SendCommandAsync(string command, CancellationToken ct)
{
ThrowIfDisposed();
if (!IsConnected) throw new InvalidOperationException("[ModBus] 设备未连接");
_latencyStopwatch.Restart();
try
{
Log.Debug($"[ModBus] 设备 {DeviceId} 发送命令: {command}");
// 解析命令格式: "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] 无法解析的值: {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;
}
}
///
public Task QueryAsync(string query, CancellationToken ct)
{
ThrowIfDisposed();
if (!IsConnected) throw new InvalidOperationException("[ModBus] 设备未连接");
_latencyStopwatch.Restart();
try
{
Log.Debug($"[ModBus] 设备 {DeviceId} 发送查询: {query}");
// 解析查询格式: "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: 线圈(BOOL)
// 10001-19999: 离散输入(BOOL)
// 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;
}
}
///
public DeviceDriverStatus GetStatus()
{
return new DeviceDriverStatus
{
IsConnected = IsConnected,
IsHealthy = IsConnected && string.IsNullOrEmpty(_lastError),
LastError = _lastError,
LastErrorTime = _lastErrorTime,
LatencyMs = _lastLatencyMs,
ReconnectCount = _reconnectCount
};
}
///
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);
}
}
///
/// 触发连接状态变化事件
///
private void OnConnectionChanged(bool isConnected, string reason)
{
ConnectionChanged?.Invoke(this, new DeviceConnectionEventArgs
{
DeviceId = DeviceId,
IsConnected = isConnected,
Reason = reason
});
if (!isConnected)
{
_reconnectCount++;
}
}
///
/// 检查是否已释放
///
private void ThrowIfDisposed()
{
if (_disposed) throw new ObjectDisposedException(nameof(ModBusDriverAdapter));
}
}
}