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

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

相关推荐
淡海水2 小时前
02-07-原理篇-GC暂停时间的本质
开发语言·unity·c#·gc·暂停时间
小羊没烦恼!4 小时前
OS X 上使用.NET开发应用程序
开发语言·前端·数据库·人工智能·c#·策略模式
Behavior5 小时前
Unity 7.0 Alpha 拆解:编辑器干掉 Mono 换 CoreCLR
c#·unity3d·游戏开发
淡海水6 小时前
02-06-原理篇-增量式GC与并发GC
算法·unity·c#·并发·gc·增量
Vertira6 小时前
C# 函数封装成 DLL,
开发语言·c#
浅尝辄止;8 小时前
设计模式在HIS/LIS医疗系统中的完整实践指南(C#版)
开发语言·设计模式·c#
kimloner18 小时前
开源一个 376KB 的 Windows 桌面小工具:便签 + 截图标注 + OCR + 待办提醒(C# / WinForms)
windows·开源·c#·ocr·winforms
阿松爱学习19 小时前
【Unity开发】Aspose.PDF的使用
unity·pdf·c#
半亩码田19 小时前
C#转Python第4.8篇:正则表达式:Python re 模块 vs C# System.Text.RegularExpressions
python·正则表达式·c#
FPGA小徐20 小时前
【一生一芯 / PA】异常响应机制:RISC-V 中 ecall → mtvec → mret 的完整代
开发语言·数据库·c#