西门子S7通讯(三)

接上文,当我们完成了单台设备的连接、读写编码后,怎样统一管理产线上多台设备的连接读写呢?

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();
     }
 }

存在问题:异常捕获,页面显示异常信息,记录日志等模块待完善

相关推荐
cjp5606 小时前
018.C#管道服务,本机两软件间通讯交互
开发语言·c#
故事不长丨7 小时前
C#log4net详解:从入门到精通,配置、实战与框架对比
c#·.net·wpf·log4net·日志·winform·日志系统
不绝1918 小时前
C#核心——面向对象:封装
开发语言·javascript·c#
一然明月9 小时前
C#语言基础详解和面向对象编程核心概念与高级特性详解(万字详解带示例代码)
开发语言·c#
flysh059 小时前
.NET 基础 - StringBuilder 类
开发语言·c#·编程语言·c#10
cjp56010 小时前
002.为C#动态链接库添加wpf窗体
microsoft·c#·wpf
齐鲁大虾11 小时前
如何通过C#调取打印机打印文本和图片
开发语言·c#
TDengine (老段)11 小时前
TDengine C# 语言连接器入门指南
大数据·数据库·c#·时序数据库·tdengine·涛思数据
凯新生物11 小时前
Mannose-PEG-CY5.5,CY5.5-PEG-Mannose技术手册:分子量选型与溶解性说明
javascript·c#·bash·symfony