using S7.Net; using System; using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Base { public class S7Device : IS7Device { public string IPAddress { get; private set; } = "127.0.0.1"; public CpuType CpuType { get; private set; } = CpuType.S71200; public short Rack { get; private set; } = 0; public short Slot { get; private set; } = 1; public int SendTimeout { get; private set; } = 3000; public int ReceiveTimeout { get; private set; } = 3000; private Plc _plc; public Plc PlcContext => _plc; public bool IsConnected => _plc?.IsConnected ?? false; protected readonly SemaphoreSlim _commLock = new(1, 1); public S7Device() { _plc = new Plc(CpuType, IPAddress, Rack, Slot); } public void ConfigureDevice(string ipAddress, CpuType cpuType, short rack = 0, short slot = 1, int sendTimeout = 3000, int receiveTimeout = 3000) { IPAddress = ipAddress; CpuType = cpuType; Rack = rack; Slot = slot; SendTimeout = sendTimeout; ReceiveTimeout = receiveTimeout; } public virtual async Task ConnectAsync(CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { if (_plc != null && _plc.IsConnected) { if (_plc.IP == IPAddress && _plc.CPU == CpuType && _plc.Rack == Rack && _plc.Slot == Slot) return true; } if (_plc != null) { _plc.Close(); } _plc = new Plc(CpuType, IPAddress, Rack, Slot) { ReadTimeout = ReceiveTimeout, WriteTimeout = SendTimeout }; await _plc.OpenAsync(); return _plc.IsConnected; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"[S7Device] 连接异常: IP={IPAddress}, CPU={CpuType}, Error={ex.Message}"); System.Diagnostics.Debug.WriteLine($"[S7Device] 异常堆栈: {ex.StackTrace}"); return false; } finally { _commLock.Release(); } } public virtual void Close() { if (_plc != null && _plc.IsConnected) _plc.Close(); } public async Task WriteAsync(string address, object value, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { if (!IsConnected) throw new InvalidOperationException("PLC未连接。"); await _plc.WriteAsync(address, value) .WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct); } finally { _commLock.Release(); } } public async Task ReadAsync(string address, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { if (!IsConnected) throw new InvalidOperationException("PLC未连接。"); return await _plc.ReadAsync(address) .WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct); } finally { _commLock.Release(); } } public async Task ReadAsync(string address, CancellationToken ct = default) { var result = await ReadAsync(address, ct); System.Diagnostics.Debug.WriteLine($"[S7Device.ReadAsync] 地址={address}, 返回类型={result?.GetType().Name ?? "null"}, 值={result}"); try { if (result is IConvertible convertible) { return (T)Convert.ChangeType(convertible, typeof(T)); } return (T)result; } catch (InvalidCastException ex) { System.Diagnostics.Debug.WriteLine($"[S7Device.ReadAsync] 类型转换失败: 目标类型={typeof(T).Name}, 实际类型={result.GetType().Name}, 值={result}, 异常={ex.Message}"); throw; } } public async Task ReadBytesAsync(DataType dataType, int db, int startByteAdr, int count, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { if (!IsConnected) throw new InvalidOperationException("PLC未连接。"); return await _plc.ReadBytesAsync(dataType, db, startByteAdr, count) .WaitAsync(TimeSpan.FromMilliseconds(ReceiveTimeout), ct); } finally { _commLock.Release(); } } public async Task WriteBytesAsync(DataType dataType, int db, int startByteAdr, byte[] value, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { if (!IsConnected) throw new InvalidOperationException("PLC未连接。"); await _plc.WriteBytesAsync(dataType, db, startByteAdr, value) .WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct); } finally { _commLock.Release(); } } public void Dispose() { _plc?.Close(); _commLock?.Dispose(); } } }