基于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())
相关推荐
Greyson12 小时前
Redis如何解决哨兵通知延迟问题_优化客户端连接池动态刷新拓扑的订阅监听机制
jvm·数据库·python
bekote2 小时前
笔记|数据库
数据库·笔记
Swift社区2 小时前
鸿蒙游戏 UI 怎么设计才不乱?
游戏·ui·harmonyos
菜鸟‍2 小时前
【论文学习】Transformer中的数据流动
深度学习·学习·transformer
阿_旭2 小时前
基于YOLO26深度学习的【辣椒成熟度检测与计数系统】【python源码+Pyqt5界面+数据集+训练代码】
人工智能·python·深度学习·辣椒成熟度检测
戏舟的嵌入式开源笔记2 小时前
LVGL部件应用笔记(基于正点原子教程,持续更新)
笔记
九成宫2 小时前
IT项目管理期末复习——Chapter 7 项目成本管理
笔记·项目管理·软件工程
无风听海2 小时前
Python类型守卫深度解析
python
白羊by2 小时前
Softmax 激活函数详解:从数学原理到应用场景
网络·人工智能·深度学习·算法·损失函数