Files
IOT/DeviceCommand/Base/S7Device.cs

175 lines
5.7 KiB
C#

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<bool> 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<object> 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<T> ReadAsync<T>(string address, CancellationToken ct = default)
{
var result = await ReadAsync(address, ct);
System.Diagnostics.Debug.WriteLine($"[S7Device.ReadAsync<T>] 地址={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<T>] 类型转换失败: 目标类型={typeof(T).Name}, 实际类型={result.GetType().Name}, 值={result}, 异常={ex.Message}");
throw;
}
}
public async Task<byte[]> 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();
}
}
}