【python与PLC】python与PLC的通信介绍以及代码实战-Modbus TCP通信

项目概述

这是一个用于与PLC(可编程逻辑控制器)进行Modbus TCP通信的Python项目。项目提供了完整的Modbus TCP客户端功能,包括连接管理、数据读写等操作。

代码逐句解释

1. 导入模块

from pymodbus.client import ModbusTcpClient
import logging
from typing import Optional, Tuple, Union

解释:

  • pymodbus.client.ModbusTcpClient: 这是pymodbus库提供的Modbus TCP客户端类,用于建立与PLC的TCP连接
  • logging: Python标准库,用于记录日志信息
  • typing: Python类型提示模块,提供类型注解功能
    • Optional: 表示一个值可以是某种类型或None
    • Tuple: 表示元组类型
    • Union: 表示联合类型(Python 3.10+可用|替代)

2. 类定义和初始化

class ModbusCommunication:
    def __init__(self, plc_ip: str, plc_port: int = 502):

解释:

  • 定义了一个ModbusCommunication类来封装所有Modbus通信功能
  • __init__方法接收PLC的IP地址和端口号
  • plc_port: int = 502: 默认端口502是Modbus TCP的标准端口
        self.plc_ip = plc_ip
        self.plc_port = plc_port
        self.client = None
        self.connected = False

解释:

  • 存储PLC的IP地址和端口号
  • self.client: 将存储ModbusTcpClient实例
  • self.connected: 跟踪连接状态
        # 配置日志
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s'
        )
        self.logger = logging.getLogger(__name__)

解释:

  • 配置日志系统,设置日志级别为INFO
  • 日志格式包含时间戳、日志级别和消息内容
  • logging.getLogger(__name__): 获取当前模块的logger实例

3. 连接管理方法

connect() 方法
    def connect(self) -> bool:
        """
        连接到PLC
        :return: 连接是否成功
        """
        try:
            self.client = ModbusTcpClient(self.plc_ip, port=self.plc_port)
            self.connected = self.client.connect()

解释:

  • 创建ModbusTcpClient实例,传入PLC的IP地址和端口
  • self.client.connect(): 尝试建立TCP连接,返回布尔值表示连接状态
            if self.connected:
                self.logger.info(f"成功连接到PLC {self.plc_ip}:{self.plc_port}")
            else:
                self.logger.error("连接PLC失败")
            return self.connected
        except Exception as e:
            self.logger.error(f"连接PLC失败: {str(e)}")
            self.connected = False
            return False

解释:

  • 根据连接结果记录相应的日志信息
  • 使用异常处理捕获连接过程中的错误
  • 返回连接状态
disconnect() 方法
    def disconnect(self):
        """
        断开与PLC的连接
        """
        if self.client:
            try:
                self.client.close()
                self.connected = False
                self.logger.info("已断开与PLC的连接")
            except Exception as e:
                self.logger.error(f"断开连接时发生错误: {str(e)}")

解释:

  • 检查client是否存在,然后调用close()方法断开连接
  • 更新连接状态并记录日志

4. 数据读写方法

read_holding_registers() 方法
    def read_holding_registers(self, address: int, count: int = 1) -> Tuple[bool, Optional[list]]:
        """
        读取保持寄存器
        :param address: 起始地址
        :param count: 读取数量
        :return: (是否成功, 读取的数据)
        """
        if not self.connected:
            self.logger.error("未连接到PLC")
            return False, None

        try:
            result = self.client.read_holding_registers(address, count)
            if result.isError():
                self.logger.error(f"读取保持寄存器失败: {result}")
                return False, None
            return True, result.registers
        except Exception as e:
            self.logger.error(f"读取保持寄存器失败: {str(e)}")
            return False, None

解释:

  • 保持寄存器(Holding Registers): Modbus协议中的一种数据类型,用于存储16位数据
  • read_holding_registers(address, count): pymodbus的方法,读取指定地址开始的多个寄存器
  • result.isError(): 检查操作是否成功
  • result.registers: 获取读取到的寄存器数据列表
write_holding_register() 方法
    def write_holding_register(self, address: int, value: int) -> bool:
        """
        写入保持寄存器
        :param address: 地址
        :param value: 要写入的值
        :return: 是否成功
        """
        if not self.connected:
            self.logger.error("未连接到PLC")
            return False

        try:
            result = self.client.write_register(address, value)
            if result.isError():
                self.logger.error(f"写入保持寄存器失败: {result}")
                return False
            return True
        except Exception as e:
            self.logger.error(f"写入保持寄存器失败: {str(e)}")
            return False

解释:

  • write_register(address, value): 向指定地址写入单个寄存器值
  • 返回布尔值表示操作是否成功
read_coils() 方法
    def read_coils(self, address: int, count: int = 1) -> Tuple[bool, Optional[list]]:
        """
        读取线圈
        :param address: 起始地址
        :param count: 读取数量
        :return: (是否成功, 读取的数据)
        """
        if not self.connected:
            self.logger.error("未连接到PLC")
            return False, None

        try:
            result = self.client.read_coils(address, count)
            if result.isError():
                self.logger.error(f"读取线圈失败: {result}")
                return False, None
            return True, result.bits
        except Exception as e:
            self.logger.error(f"读取线圈失败: {str(e)}")
            return False, None

解释:

  • 线圈(Coils): Modbus协议中的另一种数据类型,用于存储布尔值(开/关状态)
  • read_coils(address, count): 读取指定地址开始的多个线圈状态
  • result.bits: 获取读取到的线圈状态列表(布尔值)
write_coil() 方法
    def write_coil(self, address: int, value: bool) -> bool:
        """
        写入线圈
        :param address: 地址
        :param value: 要写入的值
        :return: 是否成功
        """
        if not self.connected:
            self.logger.error("未连接到PLC")
            return False

        try:
            result = self.client.write_coil(address, value)
            if result.isError():
                self.logger.error(f"写入线圈失败: {result}")
                return False
            return True
        except Exception as e:
            self.logger.error(f"写入线圈失败: {str(e)}")
            return False

解释:

  • write_coil(address, value): 向指定地址写入单个线圈状态
  • value参数是布尔值,表示开(True)或关(False)

Modbus TCP 详细知识点

1. Modbus协议概述

Modbus是一种工业通信协议,广泛应用于工业自动化系统中。它支持多种传输方式:

  • Modbus RTU: 基于串行通信(RS-485)
  • Modbus ASCII: 基于串行通信的ASCII编码
  • Modbus TCP: 基于TCP/IP网络通信

2. pymodbus库详解

主要类和函数

ModbusTcpClient类

from pymodbus.client import ModbusTcpClient

构造函数参数:

  • host: PLC的IP地址
  • port: 端口号(默认502)
  • timeout: 连接超时时间
  • retries: 重试次数

主要方法:

  • connect(): 建立TCP连接
  • close(): 关闭连接
  • read_holding_registers(): 读取保持寄存器
  • write_register(): 写入单个寄存器
  • read_coils(): 读取线圈
  • write_coil(): 写入单个线圈

3. Modbus数据类型

保持寄存器(Holding Registers)
  • 功能码: 03(读)、06(写单个)、16(写多个)
  • 数据类型: 16位无符号整数
  • 地址范围: 40001-49999
  • 用途: 存储配置参数、传感器数据等
线圈(Coils)
  • 功能码: 01(读)、05(写单个)、15(写多个)
  • 数据类型: 布尔值(开/关)
  • 地址范围: 00001-09999
  • 用途: 控制开关、继电器等
输入寄存器(Input Registers)
  • 功能码: 04(读)
  • 数据类型: 16位无符号整数
  • 地址范围: 30001-39999
  • 用途: 只读传感器数据
离散输入(Discrete Inputs)
  • 功能码: 02(读)
  • 数据类型: 布尔值
  • 地址范围: 10001-19999
  • 用途: 只读开关状态

4. 错误处理

pymodbus提供了完善的错误处理机制:

result = client.read_holding_registers(0, 10)
if result.isError():
    print(f"错误: {result}")
else:
    print(f"数据: {result.registers}")

5. 连接管理最佳实践

  1. 连接检查: 每次操作前检查连接状态
  2. 异常处理: 使用try-catch捕获网络异常
  3. 日志记录: 记录所有操作和错误信息
  4. 资源清理: 及时关闭连接释放资源

6. 性能优化建议

  1. 连接复用: 避免频繁建立/断开连接
  2. 批量操作: 一次读取多个寄存器而不是逐个读取
  3. 超时设置: 合理设置连接和操作超时时间
  4. 重试机制: 实现自动重试机制处理临时网络问题

使用示例

# 创建通信实例
modbus = ModbusCommunication("192.168.1.100", 502)

# 连接到PLC
if modbus.connect():
    # 读取保持寄存器
    success, data = modbus.read_holding_registers(0, 10)
    if success:
        print(f"读取到的数据: {data}")
    
    # 写入线圈
    if modbus.write_coil(0, True):
        print("线圈写入成功")
    
    # 断开连接
    modbus.disconnect()

依赖安装

pip install pymodbus

注意事项

  1. 确保PLC的IP地址和端口号正确
  2. 检查网络连接和防火墙设置
  3. 确认PLC支持Modbus TCP协议
  4. 注意寄存器地址的起始偏移(有些PLC从0开始,有些从1开始)
  5. 数据类型转换时注意字节序(大端/小端)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值