python使用s7协议与plc进行数据通讯(HslCommunication模拟)

1:安装S7通信库

bash 复制代码
pip install python-snap7

2:实现代码,可以读取数组和单独读取

bash 复制代码
# !/usr/bin/env python
# -*- coding:utf-8 -*-
import logging
import os
import struct
import time
from logging.handlers import TimedRotatingFileHandler

import snap7


def setup_logging(log_dir='logs', filename='sub.log', level=logging.INFO, console_level=None):
    import sys
    os.makedirs(log_dir, exist_ok=True)
    log_path = os.path.join(log_dir, filename)
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(lineno)d- %(threadName)s - %(message)s')

    # 文件按天切分
    file_handler = TimedRotatingFileHandler(log_path, when='midnight', interval=1, backupCount=30, encoding='utf-8')
    file_handler.setFormatter(formatter)
    file_handler.setLevel(level)

    root = logging.getLogger()
    root.setLevel(level)
    logging.getLogger('opcua').setLevel(logging.WARNING)
    logging.getLogger('paho').setLevel(logging.WARNING)
    logging.getLogger('urllib3').setLevel(logging.WARNING)

    # 避免重复添加相同的文件 handler(通过绝对路径判断)
    abs_log_path = os.path.abspath(log_path)
    if not any(isinstance(h, TimedRotatingFileHandler) and getattr(h, "baseFilename", None) == abs_log_path for h in
               root.handlers):
        root.addHandler(file_handler)

    # 控制台 handler 写到 stdout,确保在 PyCharm 控制台可见
    console_level = console_level if console_level is not None else level
    existing_console = None
    for h in root.handlers:
        if isinstance(h, logging.StreamHandler) and getattr(h, "stream", None) in (sys.stdout, sys.stderr):
            existing_console = h
            break

    if existing_console is None:
        console = logging.StreamHandler(sys.stdout)
        console.setFormatter(formatter)
        console.setLevel(console_level)
        root.addHandler(console)
    else:
        # 更新已存在的控制台 handler 的级别与格式
        existing_console.setFormatter(formatter)
        existing_console.setLevel(console_level)


# 读取DB块中的布尔数组
def read_bool_array_from_db(plc_client, db_number, offset, array_size):
    # 计算需要读取的字节数
    bytes_to_read = (array_size + 7) // 8
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    bool_array = []
    for i in range(array_size):
        byte_index = i // 8
        bit_index = i % 8
        bool_value = bool(data[byte_index] & (1 << bit_index))
        bool_array.append(bool_value)

    return bool_array


# 读取DB块中的BYTE数组(8位无符号整数)
def read_byte_array_from_db(plc_client, db_number, offset, array_size):
    # 每个BYTE占1个字节
    data = plc_client.db_read(db_number, offset, array_size)

    byte_array = []
    for i in range(array_size):
        byte_value = struct.unpack('>B', data[i:i + 1])[0]  # 使用'B'表示8位无符号整数
        byte_array.append(byte_value)

    return byte_array


# 读取DB块中的DINT数组(32位有符号整数)
def read_dint_array_from_db(plc_client, db_number, offset, array_size):
    # 每个DINT占4个字节
    bytes_to_read = array_size * 4
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    dint_array = []
    for i in range(array_size):
        dint_value = struct.unpack('>i', data[i * 4:(i + 1) * 4])[0]  # 使用'i'表示32位有符号整数
        dint_array.append(dint_value)

    return dint_array


# 读取DB块中的UDINT数组(32位无符号整数)
def read_udint_array_from_db(plc_client, db_number, offset, array_size):
    # 每个UDINT占4个字节
    bytes_to_read = array_size * 4
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    udint_array = []
    for i in range(array_size):
        udint_value = struct.unpack('>I', data[i * 4:(i + 1) * 4])[0]  # 使用'I'表示32位无符号整数
        udint_array.append(udint_value)

    return udint_array


# 读取DB块中的INT数组(16位有符号整数,short)
def read_int_array_from_db(plc_client, db_number, offset, array_size):
    # 每个INT占2个字节
    bytes_to_read = array_size * 2
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    int_array = []
    for i in range(array_size):
        int_value = struct.unpack('>h', data[i * 2:(i + 1) * 2])[0]  # 使用'h'表示16位有符号整数
        int_array.append(int_value)

    return int_array


# 读取DB块中的UINT数组(16位无符号整数,short)
def read_uint_array_from_db(plc_client, db_number, offset, array_size):
    # 每个UINT占2个字节
    bytes_to_read = array_size * 2
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    uint_array = []
    for i in range(array_size):
        uint_value = struct.unpack('>H', data[i * 2:(i + 1) * 2])[0]  # 使用'H'表示16位无符号整数
        uint_array.append(uint_value)

    return uint_array


# 读取DB块中的REAL数组(32位浮点数,float)
def read_real_array_from_db(plc_client, db_number, offset, array_size):
    # 每个REAL占4个字节
    bytes_to_read = array_size * 4
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    real_array = []
    for i in range(array_size):
        real_value = struct.unpack('>f', data[i * 4:(i + 1) * 4])[0]  # 使用'f'表示32位浮点数
        real_array.append(real_value)

    return real_array


# 读取DB块中的LONG数组(64位有符号整数)
def read_long_array_from_db(plc_client, db_number, offset, array_size):
    # 每个LONG占8个字节
    bytes_to_read = array_size * 8
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    long_array = []
    for i in range(array_size):
        long_value = struct.unpack('>q', data[i * 8:(i + 1) * 8])[0]  # 使用'q'表示64位有符号整数
        long_array.append(long_value)

    return long_array


# 读取DB块中的ULONG数组(64位无符号整数)
def read_ulong_array_from_db(plc_client, db_number, offset, array_size):
    # 每个ULONG占8个字节
    bytes_to_read = array_size * 8
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    ulong_array = []
    for i in range(array_size):
        ulong_value = struct.unpack('>Q', data[i * 8:(i + 1) * 8])[0]  # 使用'Q'表示64位无符号整数
        ulong_array.append(ulong_value)

    return ulong_array


# 读取DB块中的DOUBLE数组(64位双精度浮点数)
def read_double_array_from_db(plc_client, db_number, offset, array_size):
    # 每个DOUBLE占8个字节
    bytes_to_read = array_size * 8
    data = plc_client.db_read(db_number, offset, bytes_to_read)

    double_array = []
    for i in range(array_size):
        double_value = struct.unpack('>d', data[i * 8:(i + 1) * 8])[0]  # 使用'd'表示64位双精度浮点数
        double_array.append(double_value)

    return double_array


# 读取DB块中的字符串
def read_string_from_db(plc_client, db_number, offset, length):
    # 读取指定长度的字节数据
    data = plc_client.db_read(db_number, offset, length)
    return list(snap7.util.get_string(data, offset))


# 读取DB块中的CHAR数组(字符数组)
def read_char_array_from_db(plc_client, db_number, offset, array_size):
    # 每个CHAR占1个字节
    data = plc_client.db_read(db_number, offset, array_size)

    char_array = []
    for i in range(array_size):
        char_value = chr(data[i])  # 将字节转换为字符
        char_array.append(char_value)

    return char_array


if __name__ == "__main__":
    setup_logging(log_dir='logs', filename='sub.log')
    plc = snap7.client.Client()
    # S7连接配置
    plc.connect('127.0.0.1', 0, 1, 103)  # IP地址, rack, slot, port
    print("connected!")
    state_opcua = True
    print("S7连接成功!")
    # opcua_client()  # 阻塞线程

    # 读取DB1中的数据
    db_number = 1

    while True:
        a = time.time()
        # 读取布尔数组(从偏移量0开始,20个布尔值)
        bool_array = read_bool_array_from_db(plc, db_number, 0, 20)

        # 读取BYTE数组(从偏移量20开始,10个BYTE值)
        byte_array = read_byte_array_from_db(plc, db_number, 0, 10)

        # 读取有符号DINT整数数组(从偏移量20开始,10个DINT值)
        dint_array = read_dint_array_from_db(plc, db_number, 0, 10)

        # 读取UDINT数组(从偏移量20开始,10个UDINT值)
        udint_array = read_udint_array_from_db(plc, db_number, 0, 10)

        # 读取有符号整数数组(从偏移量20开始,10个INT值)
        int_array = read_int_array_from_db(plc, db_number, 0, 10)

        # 读取无符号整数数组(从偏移量20开始,10个UINT值)
        uint_array = read_uint_array_from_db(plc, db_number, 0, 10)

        # 读取实数数组(从偏移量10开始,5个REAL值)float
        real_array = read_real_array_from_db(plc, db_number, 0, 5)

        # 读取长整数数组(从偏移量20开始,10个LONG值)
        long_array = read_long_array_from_db(plc, db_number, 0, 10)

        # 读取无符号长整数数组(从偏移量20开始,10个ULONG值)
        ulong_array = read_ulong_array_from_db(plc, db_number, 0, 10)

        # 读取双精度浮点数数组(从偏移量20开始,5个DOUBLE值)
        double_array = read_double_array_from_db(plc, db_number, 0, 5)

        # 读取字符串(从偏移量20开始,10个字节)
        string_value = read_string_from_db(plc, db_number, 0, 10)

        # 读取字符数组(从偏移量20开始,10个CHAR值)
        char_array = read_char_array_from_db(plc, db_number, 0, 10)

        # 输出读取的数据

        print(f"布尔数组: {bool_array}")
        # print(f"BYTE数组: {byte_array}")
        # print(f"DINT数组: {dint_array}")
        # print(f"无符号DINT数组: {udint_array}")
        # print(f"整数数组short: {int_array}")
        # print(f"无符号整数数组short: {uint_array}")
        # print(f"实数数组float: {real_array}")
        # print(f"长整数数组long: {long_array}")
        # print(f"无符号长整数数组long: {ulong_array}")
        # print(f"双精度浮点数组double: {double_array}")

        # 读取字符串和字符数组有一些符号在里面,要注意,通用都不不适应这两个方法
        print(f"字符串: {string_value}")
        # print(f"字符数组: {char_array}")

        b = time.time()
        # print("首次读取耗时:", (b - a) * 1000, "ms")

        data = plc.read_area(snap7.types.Areas.DB, 1, 0, 20)  # 读取DB1,从偏移量0开始,读取20个字节
        plc.db_read(db_number, 0, 20)
        # value = snap7.util.get_int(data, 0)  # 从数据中获取整数值类型为short
        # value = snap7.util.get_bool(data, 0, 0)  # 从数据中获取布尔值
        # value = snap7.util.get_char(data, 0)  # 从数据中获取字符值
        value = snap7.util.get_string(data, 0)  # 从数据中获取字符串,使用这个方法最好
        # print("读取的值:", value)
        time.sleep(2)

3:PLC数据类型与Python数据类型对照表

4:使用HslCommunication模拟西门子读写数据

相关推荐
lly2024061 小时前
ASP 发送电子邮件详解
开发语言
likuolei1 小时前
XSL-FO 软件
java·开发语言·前端·数据库
6***37941 小时前
PHP在电商中的BigCommerce
开发语言·php
猫头虎1 小时前
如何解决 pip install 编译报错 fatal error: hdf5.h: No such file or directory(h5py)问题
人工智能·python·pycharm·开源·beautifulsoup·ai编程·pip
Dev7z1 小时前
基于Matlab的多制式条形码识别与图形界面(GUI)系统设计与实现
开发语言·matlab
合作小小程序员小小店1 小时前
桌面开发,在线%信息管理%系统,基于vs2022,c#,winform,sql server数据。
开发语言·数据库·sql·microsoft·c#
FL16238631291 小时前
ONNX RuntimeC++ 静态库下载安装和使用教程
开发语言·c++
星释1 小时前
Rust 练习册 95:React与响应式编程
开发语言·react.js·rust
Evand J1 小时前
【MATLAB例程】3D雷达-IMU融合定位系统(基于扩展卡尔曼滤波)|雷达观测距离、俯仰角、方向角,IMU包括6维(加速度与角速度)。附下载链接
开发语言·matlab·跟踪·雷达观测·三维定位·ekf滤波