基于QtPy (PySide6) 的PLC-HMI工程项目(十)框架初成的阶段总结

一、阶段总结:

目前阶段的系统构成:

  • DataStore:变量存储区(变量池)

变量池位于上位机,是一个连续区域的存储区,最小存取单位是字节。

  • AreaVar:区域变量

区域变量:NamedTuple

python 复制代码
AreaVar(NamedTuple,
            area:I、Q、M或DB区,
            DBnum:DB编号,
            offset:地址偏移,
            byte_count:字节数,
            content:数据内容
        )

区域变量的作用是携带区域信息,用来成块 读写变量池中PLC连续数据。所有的通信读写均通过区域变量进行。

  • UiVari:UI变量
python 复制代码
class UiVari(QObject):
    valueChanged = Signal(object)
    def __init__(self, typ, byte_count, area, offset, DBnum=0, bit=0, value=None ):
        super().__init__()
        self.typ = typ,
        self.byte_count = byte_count,
        self.area = area,
        self.DBnum = DBnum,
        self.offset = offset,
        self.bit = bit
        self.value = value

UI变量通过信号槽机制与UI部件交互,UI变量中携带的地址和数据信息转换成区域变量(AreaVar),并通过该区域变量与变量池进行读写交互。

UI变量具备"变化才发射"的信号特性,只有它的值发生变化后,才执行与之连接的功能槽函数(比如刷新UI显示、发送下行数据等)。

  • 变量表:
    从excel文件直接生成变量表,变量表中的变量格式为UiVari(UI变量)。
  • 变量管理器(VariManager):
    使用变量管理器,以变量表为依据对数据池进行变量读写。
  • 上行报文:
    从PLC发送到上位机的报文,报文中包含了本帧数据的区域变量(AreaVar)的信息以及数据的具体内容,上位机收到报文后,按照区域变量所指的区域和内容对变量池写操作。上行报文由PLC主动周期发送。
  • 下行报文:
    从上位机发送到PLC的报文,报文中同样包含了本帧数据的区域变量(AreaVar)的信息以及数据的具体内容。上位机收到报文后,按照区域变量所指的区域和内容对PLC写操作。PLC没有需要写的数据时,发送固定的心跳代码。
  • 报文总结:
    上行:写上位机数据池,由PLC主动周期发送;
    下行:写PLC数据,上位机周期发送心跳和实时发送PLC写数据;
    上下行操作相互独立,不呼应、不答复。
    在目前的基础上,下一步就可以很方便改进成上下行呼应、答复的模式。
  • 阶段总结:
  1. 两个数据池:PLC的I、Q、M、DB存储区,上位机的DataStore;
  2. 两个数据变量:AreaVar,块读写数据池的时候使用,以字节为单位,忽略读写内容的变量类型;UiVari,按格式读写单个变量;
  3. 两个报文:上行报文写上位机的DataStore,下行报文写PLC的I、Q、M、DB存储区;
  4. 一个变量管理器,上位机中使用变量管理器读写变量;
  5. 上位机使用变量管理器的write_vari()函数写变量,对应的变量值和变量池中对应的存储区都会被写成新值,但不会改变PLC中的内容,需要通过一次立即发送send_now()将变量发送到PLC进行更新;使用变量管理器的read_vari()函数读变量,对应的变量值会被更新为变量池中对应的存储区内容。
  6. 使用上行报文写上位机的变量池,只会更新变量池的存储内容,需要执行read_vari()才能更新变量值。

二、一个最简的Qt demo

python 复制代码
import struct
import sys

from PySide6.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout, QLineEdit, QLabel

from VariManager import 变量表
from VariManager.manager import DataStore, VariManager, AreaVar


def invert_value(v):
    return not v

if __name__ == '__main__':
    app = QApplication(sys.argv)
    # 初始化存储
    vari_store = DataStore()  # 创建变量存储区
    vm = VariManager(vari_store)  # 创建变量管理器
    # 创建窗口
    form = QWidget()
    layout = QVBoxLayout()
    form.setLayout(layout)

    linedit = QLineEdit('0')
    linedit.textChanged.connect(lambda t:vm.write_vari(变量表.油泵2压力设定, float(t)))

    label = QLabel('压力设定显示')
    layout.addWidget(label)
    layout.addWidget(linedit)
    变量表.油泵2压力设定.valueChanged.connect(lambda t:label.setText(str(t)))

    btn1 = QPushButton('油泵启停(False)')
    layout.addWidget(btn1)
    form.show()

    btn1.clicked.connect(lambda :vm.write_vari(变量表.油泵1按钮起, invert_value(变量表.油泵1按钮起.value)))
    变量表.油泵1按钮起.valueChanged.connect(lambda :btn1.setText(f'油泵启停({str(变量表.油泵1按钮起.value)})'))

    btn2 = QPushButton('模拟字节串通信')
    layout.addWidget(btn2)

    def move_bytes():
        data_block = b''
        value_bytes = struct.pack('>f', 1.23888)
        data_block += value_bytes
        value_bytes = struct.pack('>f', 2.34567)
        data_block += value_bytes
        area_vari = AreaVar(
            area='DB',  # I、Q、M、
            DBnum=5,  # DB号(I/Q/M=0)
            offset=2,  # 字节偏移
            byte_count=8,  # 字节数
            content=data_block
        )
        vari_store.write_block(area_vari)
    btn2.clicked.connect(move_bytes)

    btn3 = QPushButton('读取变量')
    layout.addWidget(btn3)
    btn3.clicked.connect(lambda :vm.read_vari(变量表.油泵2压力设定))

    sys.exit(app.exec())
相关推荐
兵慌码乱5 小时前
基于Python+PyQt5+SQLite的药房管理系统实现:事务一致性与界面解耦全流程解析
python·sqlite·信号与槽·pyqt5·数据库设计·桌面应用开发·事务处理
金銀銅鐵7 小时前
[Python] 体验用欧几里得算法计算最大公约数的过程
python·数学
FreakStudio11 小时前
W55MH32L-EVB 上手测评:硬件 TCP/IP 加持的以太网单片机,MicroPython 零门槛开发
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
用户03321266636712 小时前
使用 Python 从零创建 Word 文档
python
Csvn16 小时前
Python 两大经典坑点 —— 可变默认参数 & 闭包延迟绑定
后端·python
曲幽17 小时前
别再用网页翻译看源码了!你的私人翻译神器LibreTranslate,部署避坑指南来了
python·docker·web·pot·translate·libretranslate·arogstranslate
用户5569188175319 小时前
#从脚本到独立程序:Python + Playwright 批量抓取的完整踩坑记录
python·自动化运维
LinXunFeng1 天前
Obsidian - 使用 Share Note 分享笔记并自部署
前端·笔记·github
兵慌码乱1 天前
基于 MediaPipe 与 PySide2 的手势交互音乐控制系统实现:轻量化视觉交互全流程解析
python·opencv·计算机视觉·人机交互·手势识别·mediapipe·pyside2
luckdewei1 天前
FastAPI 资产管理系统实战:复杂 ORM 关联、Alembic 迁移与 N+1 查询优化
python