Files
IOT/DeviceCommand/Base/S7Device.cs
2026-06-09 15:54:50 +08:00

169 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
// S7.Net 的 Plc.IsConnected 属性内部会通过 Socket 状态进行判断
public bool IsConnected => _plc?.IsConnected ?? false;
// 统一线程锁
protected readonly SemaphoreSlim _commLock = new(1, 1);
public S7Device()
{
// 初始化默认配置
_plc = new Plc(CpuType, IPAddress, Rack, Slot);
}
/// <summary>
/// 设备参数配置(符合你的命名风格)
/// </summary>
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
{
// 如果已经连接,检查当前的 IP 和 CPU 类型是否一致,一致则直接复用
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 对象并配置超时
_plc = new Plc(CpuType, IPAddress, Rack, Slot)
{
ReadTimeout = ReceiveTimeout,
WriteTimeout = SendTimeout
};
// 部分版本 S7.Net 的 OpenAsync 本身不接受 CancellationToken我们通过 WaitAsync 实现超时
await _plc.OpenAsync().WaitAsync(TimeSpan.FromMilliseconds(SendTimeout), ct);
return _plc.IsConnected;
}
catch
{
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);
return (T)result;
}
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();
}
}
}