接上文,当我们完成了单台设备的连接、读写编码后,怎样统一管理产线上多台设备的连接读写呢?
PlcConnectionManager-PLC连接管理
我们设想一下,希望这个类,在程序中运行中具有的功能
- 单例模式设计,方便在程序中调用,避免造成冲突和资源浪费
- 有加载/更新PLC配置的方法
- 自动重连服务的启动/停止(统一管理所有服务)
- 单台设备数据读写

单例实现
csharp
public class PlcConnectionManager : IDisposable
{
private static readonly Lazy<PlcConnectionManager> _instance =
new Lazy<PlcConnectionManager>(() => new PlcConnectionManager());
public static PlcConnectionManager Instance => _instance.Value;
// 存储所有PLC连接
private readonly ConcurrentDictionary<string, PlcConnection> _connections;
// 配置字典
private readonly ConcurrentDictionary<string, PlcConfig> _configs;
// 定时重连计时器
private Timer _reconnectTimer;
private bool _disposed = false;
/// <summary>
/// 私有化,不对外暴漏方法
/// </summary>
private PlcConnectionManager()
{
_connections = new ConcurrentDictionary<string, PlcConnection>();
_configs = new ConcurrentDictionary<string, PlcConfig>();
}
public void Dispose()
{
if (_disposed) return;
foreach (var connection in _connections.Values)
{
connection?.Dispose();
}
_connections.Clear();
_configs.Clear();
_disposed = true;
GC.SuppressFinalize(this);
}
~PlcConnectionManager()
{
Dispose();
}
}
添加更新配置
对配置信息进行管理,不进行PLC连接与否的管理
csharp
/// <summary>
/// 添加/更新PLC配置
/// </summary>
public bool AddOrUpdateConfig(PlcConfig config)
{
if (config == null || string.IsNullOrEmpty(config.PlcId))
return false;
_configs[config.PlcId] = config;
// 如果已经存在连接,关闭旧连接
if (_connections.TryRemove(config.PlcId, out var oldConnection))
{
oldConnection?.Dispose();
}
return true;
}
自动重连服务的启停
csharp
/// <summary>
/// 启动自动重连服务
/// </summary>
public void StartAutoReconnect(int checkIntervalSeconds = 10)
{
_reconnectTimer?.Dispose();
_reconnectTimer = new Timer(
_ => CheckAndReconnectConnections(),
null,
TimeSpan.Zero,
TimeSpan.FromSeconds(checkIntervalSeconds)
);
}
/// <summary>
/// 停止自动重连
/// </summary>
public void StopAutoReconnect()
{
_reconnectTimer?.Dispose();
_reconnectTimer = null;
}
/// <summary>
/// 检查并重连断开的连接
/// </summary>
private async void CheckAndReconnectConnections()
{
foreach (var kvp in _connections)
{
var plcId = kvp.Key;
var connection = kvp.Value;
if (connection == null || connection.IsConnected)
continue;
// 检查是否需要重连
if (_configs.TryGetValue(plcId, out var config) &&
config.AutoReconnectInterval > 0)
{
try
{
await connection.ReconnectAsync();
Console.WriteLine($"PLC '{plcId}' 重连成功");
}
catch (Exception ex)
{
Console.WriteLine($"PLC '{plcId}' 重连失败: {ex.Message}");
}
}
}
}
读写数据
在读写时,获取当前连接,进行数据读写
csharp
/// <summary>
/// 读取PLC数据
/// </summary>
public async Task<object> ReadAsync(string plcId, DataType dataType, int db,
int startByteAdr, VarType varType, int count = 1)
{
var connection = await GetConnectionAsync(plcId);
return await connection.ReadAsync(dataType, db, startByteAdr, varType, count);
}
/// <summary>
/// 写入PLC数据
/// </summary>
public async Task WriteAsync(string plcId, DataType dataType, int db,
int startByteAdr, object value)
{
var connection = await GetConnectionAsync(plcId);
await connection.WriteAsync(dataType, db, startByteAdr, value);
}
/// <summary>
/// 获取PLC连接(如果不存在则创建)
/// </summary>
public async Task<PlcConnection> GetConnectionAsync(string plcId)
{
if (string.IsNullOrEmpty(plcId))
throw new ArgumentNullException(nameof(plcId));
if (!_configs.TryGetValue(plcId, out var config))
throw new KeyNotFoundException($"PLC配置 '{plcId}' 不存在");
// 尝试获取现有连接
if (_connections.TryGetValue(plcId, out var connection))
{
if (connection.IsConnected)
return connection;
}
// 创建新连接
var newConnection = await CreateConnectionAsync(config);
_connections[plcId] = newConnection;
return newConnection;
}
/// <summary>
/// 创建PLC连接
/// </summary>
private async Task<PlcConnection> CreateConnectionAsync(PlcConfig config)
{
var connection = new PlcConnection(config);
await connection.ConnectAsync();
return connection;
}
完整代码
csharp
public class PlcConnectionManager : IDisposable
{
private static readonly Lazy<PlcConnectionManager> _instance =
new Lazy<PlcConnectionManager>(() => new PlcConnectionManager());
public static PlcConnectionManager Instance => _instance.Value;
// 存储所有PLC连接
private readonly ConcurrentDictionary<string, PlcConnection> _connections;
// 配置字典
private readonly ConcurrentDictionary<string, PlcConfig> _configs;
// 定时重连计时器
private Timer _reconnectTimer;
private bool _disposed = false;
private PlcConnectionManager()
{
_connections = new ConcurrentDictionary<string, PlcConnection>();
_configs = new ConcurrentDictionary<string, PlcConfig>();
}
/// <summary>
/// 添加/更新PLC配置
/// </summary>
public bool AddOrUpdateConfig(PlcConfig config)
{
if (config == null || string.IsNullOrEmpty(config.PlcId))
return false;
_configs[config.PlcId] = config;
// 如果已经存在连接,关闭旧连接
if (_connections.TryRemove(config.PlcId, out var oldConnection))
{
oldConnection?.Dispose();
}
return true;
}
/// <summary>
/// 获取PLC连接(如果不存在则创建)
/// 添加报错捕获
/// </summary>
public async Task<PlcConnection> GetConnectionAsync(string plcId)
{
try
{
if (string.IsNullOrEmpty(plcId))
throw new ArgumentNullException(nameof(plcId));
if (!_configs.TryGetValue(plcId, out var config))
throw new KeyNotFoundException($"PLC配置 '{plcId}' 不存在");
// 尝试获取现有连接
if (_connections.TryGetValue(plcId, out var connection))
{
if (connection.IsConnected)
return connection;
}
// 创建新连接
var newConnection = await CreateConnectionAsync(config);
_connections[plcId] = newConnection;
return newConnection;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
// 添加连接异常的日志
return null;
}
}
/// <summary>
/// 创建PLC连接
/// </summary>
private async Task<PlcConnection> CreateConnectionAsync(PlcConfig config)
{
var connection = new PlcConnection(config);
await connection.ConnectAsync();
return connection;
}
/// <summary>
/// 断开指定PLC连接
/// </summary>
public void Disconnect(string plcId)
{
if (_connections.TryRemove(plcId, out var connection))
{
connection?.Dispose();
}
}
/// <summary>
/// 获取所有PLC连接状态
/// </summary>
public Dictionary<string, bool> GetAllConnectionStatus()
{
return _connections.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.IsConnected ?? false
);
}
/// <summary>
/// 启动自动重连服务
/// </summary>
public void StartAutoReconnect(int checkIntervalSeconds = 10)
{
_reconnectTimer?.Dispose();
_reconnectTimer = new Timer(
_ => CheckAndReconnectConnections(),
null,
TimeSpan.Zero,
TimeSpan.FromSeconds(checkIntervalSeconds)
);
}
/// <summary>
/// 停止自动重连
/// </summary>
public void StopAutoReconnect()
{
_reconnectTimer?.Dispose();
_reconnectTimer = null;
}
/// <summary>
/// 检查并重连断开的连接
/// </summary>
private async void CheckAndReconnectConnections()
{
foreach (var kvp in _connections)
{
var plcId = kvp.Key;
var connection = kvp.Value;
if (connection == null || connection.IsConnected)
continue;
// 检查是否需要重连
if (_configs.TryGetValue(plcId, out var config) &&
config.AutoReconnectInterval > 0)
{
try
{
await connection.ReconnectAsync();
Console.WriteLine($"PLC '{plcId}' 重连成功");
}
catch (Exception ex)
{
Console.WriteLine($"PLC '{plcId}' 重连失败: {ex.Message}");
}
}
}
}
/// <summary>
/// 读取PLC数据
/// </summary>
public async Task<object> ReadAsync(string plcId, DataType dataType, int db,
int startByteAdr, VarType varType, int count = 1)
{
var connection = await GetConnectionAsync(plcId);
return await connection.ReadAsync(dataType, db, startByteAdr, varType, count);
}
/// <summary>
/// 写入PLC数据
/// </summary>
public async Task WriteAsync(string plcId, DataType dataType, int db,
int startByteAdr, object value)
{
var connection = await GetConnectionAsync(plcId);
await connection.WriteAsync(dataType, db, startByteAdr, value);
}
public void Dispose()
{
if (_disposed) return;
StopAutoReconnect();
foreach (var connection in _connections.Values)
{
connection?.Dispose();
}
_connections.Clear();
_configs.Clear();
_disposed = true;
GC.SuppressFinalize(this);
}
~PlcConnectionManager()
{
Dispose();
}
}
测试
PLC_Line2 可以连接虚拟设备,
PLC_Line1 不能正常连接,因为设备不存在,打印日志如下:

csharp
private async void button1_Click(object sender, EventArgs e)
{
// 1. 初始化PLC配置
var configs = new List<PlcConfig>
{
new PlcConfig
{
PlcId = "PLC_Line1",
IpAddress = "192.168.1.100",
CpuType = CpuType.S71500,
Rack = 0,
Slot = 1,
AutoReconnectInterval = 5
},
new PlcConfig
{
PlcId = "PLC_Line2",
IpAddress = "192.168.3.60",
CpuType = CpuType.S71500,
Rack = 0,
Slot = 1,
AutoReconnectInterval = 5
}
};
// 2. 添加配置到管理器
var manager = PlcConnectionManager.Instance;
foreach (var config in configs)
{
manager.AddOrUpdateConfig(config);
}
// 3. 启动自动重连服务
manager.StartAutoReconnect();
try
{
// 4. 读取数据示例
var plc1 = await manager.GetConnectionAsync("PLC_Line1");
var plc2 = await manager.GetConnectionAsync("PLC_Line2");
//读取DB10.DBW0(Word)
var wordValue = await manager.ReadAsync(
"PLC_Line2",
DataType.DataBlock,
1,
0,
VarType.Int,
1
);
Console.WriteLine($"PLC_Line2 DB1.DBW0: {wordValue}");
// 5. 获取所有连接状态
var status = manager.GetAllConnectionStatus();
foreach (var kvp in status)
{
Console.WriteLine($"PLC {kvp.Key}: {(kvp.Value ? "Connected" : "Disconnected")}");
}
}
catch (Exception ex)
{
Console.WriteLine($"通信错误: {ex.Message}");
}
finally
{
// 6. 清理资源
manager.Dispose();
}
}
存在问题:异常捕获,页面显示异常信息,记录日志等模块待完善