这个对于plc上实现是非常容易得。它本来就是逻辑控制器,如果用代码实现它,该怎么做呢?这个实现起来看似简单,实则是有不少坑的(大神除外)。我一直想用类来封装,让它继承QObject,为啥非要继承QObject,而不集成python自己的object,因为在Qobject里有自己的定时器,这样就容易实现定时功能啦。开始我想把这个类都放在一个大定时器里或线程里,发现这个会不停地执行,如果要去处理,又有加入不少逻辑,看起来很不爽。解决方案是界面里放个定时器,它的功能就是监控状态,并对状态做出处理,如更新界面,对下位机做出处理。先设计两个类Input,InOut两个类,InPut类用来接受,比如启动,停止,设置时间间隔等,Inout类用来对plc进行输出控制,代码如下:
输入类:
class InPut:
__slots__=["start","stop","fault","interval"]
def __init__(self,start:bool=None,stop:bool=None,fault:bool=None,timeflag:bool = None,interval:int = 3000):
self.start: bool = start
self.stop: bool = stop
self.fault: bool = fault
self.interval: bool = interval
输入输出类:
class InOut:
def __init__(self,mainout,starout,tranout,byte:bytearray):
'''
:param mainout:
:param starout:
:param tranout:
:param byte:
'''
self.__mainout = mainout
self.__starout = starout
self.__tranout = tranout
self.__byte = byte
def main_out_state(self):return self.get_bool(self.__byte,self.__mainout//8,self.__mainout%8)
def star_out_state(self):return self.get_bool(self.__byte,self.__starout//8,self.__starout%8)
def tran_out_state(self):return self.get_bool(self.__byte,self.__tranout//8,self.__tranout%8)
def set_main_out(self,val):self.set_bool(self.__byte,self.__mainout//8,self.__mainout%8,val)
def set_star_out(self,val):self.set_bool(self.__byte,self.__starout//8,self.__starout%8,val)
def set_tran_out(self,val):self.set_bool(self.__byte,self.__tranout//8,self.__tranout%8,val)
@staticmethod
def get_bool(bytearray_: bytearray, byte_index: int, bool_index: int) -> bool:
index_value = 1 << bool_index
byte_value = bytearray_[byte_index]
current_value = byte_value & index_value
return current_value == index_value
@classmethod
def set_bool(cls,bytearray_: bytearray, byte_index: int, bool_index: int, value: bool):
if value not in {0, 1, True, False}:
raise TypeError(f"Value value:{value} is not a boolean expression.")
current_value = cls.get_bool(bytearray_, byte_index, bool_index)
index_value = 1 << bool_index
# check if bool already has correct value
if current_value == value:
return
if value:
# make sure index_v is IN current byte
bytearray_[byte_index] += index_value
else:
# make sure index_v is NOT in current byte
bytearray_[byte_index] -= index_value
下面是降压启动类;
class Motor(QObject):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.flag = False
def startStop(self,input:InPut,inout: InOut):
if input.start:
print("启动条件满足",not inout.main_out_state(),not inout.star_out_state(),not input.fault)
if all([not inout.main_out_state(),not inout.star_out_state(),not inout.tran_out_state(),not input.fault]):
inout.set_main_out(True)
inout.set_star_out(True)
inout.set_tran_out(False)
print("星启动")
if inout.main_out_state() and inout.star_out_state():
self.time_id = self.startTimer(input.interval)
self.flag = True
print("定时器启动")
if input.stop:
if self.flag:
self.killTimer(self.time_id)
inout.set_main_out(False)
inout.set_star_out(False)
inout.set_tran_out(False)
print("停止")
self.inout = inout
def checkState(self,input:InPut,inout: InOut):
if input.fault:
inout.set_main_out(False)
inout.set_star_out(False)
inout.set_tran_out(False)
print("故障停止")
def timerEvent(self, event):
self.inout.set_star_out(False)
self.inout.set_tran_out(True)
self.killTimer(self.time_id)
self.flag = False
print("三角启动")
下面是对该类进行测试:
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from snap7 import util
class Ui_Form(object):
def setupUi(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
Form.resize(848, 447)
self.btn_start = QPushButton(Form)
self.btn_start.setObjectName(u"btn_start")
self.btn_start.setGeometry(QRect(70, 50, 75, 23))
self.btn_stop = QPushButton(Form)
self.btn_stop.setObjectName(u"btn_stop")
self.btn_stop.setGeometry(QRect(70, 90, 75, 23))
self.formLayoutWidget = QWidget(Form)
self.formLayoutWidget.setObjectName(u"formLayoutWidget")
self.formLayoutWidget.setGeometry(QRect(420, 30, 211, 211))
self.formLayout = QFormLayout(self.formLayoutWidget)
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setContentsMargins(0, 0, 0, 0)
self.lab1 = QLabel(self.formLayoutWidget)
self.lab1.setObjectName(u"lab1")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.lab1)
self.lab2 = QLabel(self.formLayoutWidget)
self.lab2.setObjectName(u"lab2")
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.lab2)
self.lab3 = QLabel(self.formLayoutWidget)
self.lab3.setObjectName(u"lab3")
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.lab3)
self.lab4 = QLabel(self.formLayoutWidget)
self.lab4.setObjectName(u"lab4")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.lab4)
self.retranslateUi(Form)
self.btn_start.clicked["bool"].connect(Form.btn_start_click)
self.btn_stop.clicked.connect(Form.btn_stop_click)
QMetaObject.connectSlotsByName(Form)
# setupUi
def retranslateUi(self, Form):
Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
self.btn_start.setText(QCoreApplication.translate("Form", u"START", None))
self.btn_stop.setText(QCoreApplication.translate("Form", u"STOP", None))
self.lab1.setText(QCoreApplication.translate("Form", u"TextLabel", None))
self.lab2.setText(QCoreApplication.translate("Form", u"TextLabel", None))
self.lab3.setText(QCoreApplication.translate("Form", u"TextLabel", None))
self.lab4.setText(QCoreApplication.translate("Form", u"TextLabel", None))
# retranslateUi
class Ui(QWidget,Ui_Form):
def __init__(self):
super().__init__()
self.setupUi(self)
self.bytes = bytearray(1)
self.input = InPut()
self.inout = InOut(0,1,2,self.bytes)
self.motor = Motor()
self.timer = QTimer(self)
self.timer.timeout.connect(self.on_timer)
self.timer.start(200)
def on_timer(self):
self.motor.checkState(self.input,self.inout)
tex1="主接触器启动" if self.inout.main_out_state() else "主接触器停止"
self.lab1.setText(tex1)
tex2 = "星接触器启动" if self.inout.star_out_state() else "星接触器停止"
self.lab2.setText(tex2)
tex3 = "三角接触器启动" if self.inout.tran_out_state() else "三角接触器停止"
self.lab3.setText(tex3)
tex4 = "设备正常" if not self.input.fault else "设备故障"
self.lab4.setText(tex4)
print(self.bytes)
def btn_start_click(self):
self.input.start = True
self.input.stop = False
self.motor.startStop(self.input,self.inout)
def btn_stop_click(self):
self.input.start = False
self.input.stop = True
self.motor.startStop(self.input, self.inout)
def btn_main_click(self,state):
util.set_bool(self.bytes,0,0,state)
print("main",state)
def btn_star_click(self,state):
util.set_bool(self.bytes, 0, 1, state)
self.input.timeflag = state
print("star", state)
def btn_tran_click(self,state):
util.set_bool(self.bytes, 0, 2, state)
print("tran", state)
def le_enter_event(self):
...
def btn_fault_click(self,state):
self.input.fault=state
print("fault",state)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Ui()
win.show()
sys.exit(app.exec())
运行结果:
上面代的motor类再次优化一下,这样更简洁。代码复制后可以直接测试
from PySide6.QtCore import QObject
class InOut:
def __init__(self,mainout,starout,tranout,byte:bytearray):
'''
:param mainout:
:param starout:
:param tranout:
:param byte:
'''
self.__mainout = mainout
self.__starout = starout
self.__tranout = tranout
self.__byte = byte
def main_out_state(self):return self.get_bool(self.__byte,self.__mainout//8,self.__mainout%8)
def star_out_state(self):return self.get_bool(self.__byte,self.__starout//8,self.__starout%8)
def tran_out_state(self):return self.get_bool(self.__byte,self.__tranout//8,self.__tranout%8)
def set_main_out(self,val):self.set_bool(self.__byte,self.__mainout//8,self.__mainout%8,val)
def set_star_out(self,val):self.set_bool(self.__byte,self.__starout//8,self.__starout%8,val)
def set_tran_out(self,val):self.set_bool(self.__byte,self.__tranout//8,self.__tranout%8,val)
@staticmethod
def get_bool(bytearray_: bytearray, byte_index: int, bool_index: int) -> bool:
index_value = 1 << bool_index
byte_value = bytearray_[byte_index]
current_value = byte_value & index_value
return current_value == index_value
@classmethod
def set_bool(cls,bytearray_: bytearray, byte_index: int, bool_index: int, value: bool):
if value not in {0, 1, True, False}:
raise TypeError(f"Value value:{value} is not a boolean expression.")
current_value = cls.get_bool(bytearray_, byte_index, bool_index)
index_value = 1 << bool_index
# check if bool already has correct value
if current_value == value:
return
if value:
# make sure index_v is IN current byte
bytearray_[byte_index] += index_value
else:
# make sure index_v is NOT in current byte
bytearray_[byte_index] -= index_value
class InPut:
__slots__=["start","stop","fault","interval"]
def __init__(self,start:bool=None,stop:bool=None,fault:bool=None,timeflag:bool = None,interval:int = 3000):
self.start: bool = start
self.stop: bool = stop
self.fault: bool = fault
self.interval: bool = interval
class Motor(QObject):
def __init__(self,intput:InPut=None,inout:InOut=None,*args,**kwargs):
super().__init__(*args,**kwargs)
self.flag = False
self.input: InPut = intput
self.inout: InOut = inout
def startStop(self):
if self.input.start:
print("启动条件满足",not self.inout.main_out_state(),not self.inout.star_out_state(),not self.input.fault)
if all([not self.inout.main_out_state(),not self.inout.star_out_state(),not self.inout.tran_out_state(),not self.input.fault]):
self.inout.set_main_out(True)
self.inout.set_star_out(True)
self.inout.set_tran_out(False)
print("星启动")
if self.inout.main_out_state() and self.inout.star_out_state():
self.time_id = self.startTimer(self.input.interval)
self.flag = True
print("定时器启动")
if self.input.stop:
if self.flag:
self.killTimer(self.time_id)
self.inout.set_main_out(False)
self.inout.set_star_out(False)
self.inout.set_tran_out(False)
print("停止")
def checkState(self):
if self.input.fault:
self.inout.set_main_out(False)
self.inout.set_star_out(False)
self.inout.set_tran_out(False)
print("故障停止")
def timerEvent(self, event):
self.inout.set_star_out(False)
self.inout.set_tran_out(True)
self.killTimer(self.time_id)
self.flag = False
print("三角启动")
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from snap7 import util
class Ui_Form(object):
def setupUi(self, Form):
if not Form.objectName():
Form.setObjectName(u"Form")
Form.resize(848, 447)
self.btn_start = QPushButton(Form)
self.btn_start.setObjectName(u"btn_start")
self.btn_start.setGeometry(QRect(70, 50, 75, 23))
self.btn_stop = QPushButton(Form)
self.btn_stop.setObjectName(u"btn_stop")
self.btn_stop.setGeometry(QRect(70, 90, 75, 23))
self.formLayoutWidget = QWidget(Form)
self.formLayoutWidget.setObjectName(u"formLayoutWidget")
self.formLayoutWidget.setGeometry(QRect(420, 30, 211, 211))
self.formLayout = QFormLayout(self.formLayoutWidget)
self.formLayout.setObjectName(u"formLayout")
self.formLayout.setContentsMargins(0, 0, 0, 0)
self.lab1 = QLabel(self.formLayoutWidget)
self.lab1.setObjectName(u"lab1")
self.formLayout.setWidget(0, QFormLayout.FieldRole, self.lab1)
self.lab2 = QLabel(self.formLayoutWidget)
self.lab2.setObjectName(u"lab2")
self.formLayout.setWidget(1, QFormLayout.FieldRole, self.lab2)
self.lab3 = QLabel(self.formLayoutWidget)
self.lab3.setObjectName(u"lab3")
self.formLayout.setWidget(2, QFormLayout.FieldRole, self.lab3)
self.lab4 = QLabel(self.formLayoutWidget)
self.lab4.setObjectName(u"lab4")
self.formLayout.setWidget(3, QFormLayout.FieldRole, self.lab4)
self.retranslateUi(Form)
self.btn_start.clicked["bool"].connect(Form.btn_start_click)
self.btn_stop.clicked.connect(Form.btn_stop_click)
QMetaObject.connectSlotsByName(Form)
# setupUi
def retranslateUi(self, Form):
Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
self.btn_start.setText(QCoreApplication.translate("Form", u"START", None))
self.btn_stop.setText(QCoreApplication.translate("Form", u"STOP", None))
self.lab1.setText(QCoreApplication.translate("Form", u"TextLabel", None))
self.lab2.setText(QCoreApplication.translate("Form", u"TextLabel", None))
self.lab3.setText(QCoreApplication.translate("Form", u"TextLabel", None))
self.lab4.setText(QCoreApplication.translate("Form", u"TextLabel", None))
# retranslateUi
class Ui(QWidget,Ui_Form):
def __init__(self):
super().__init__()
self.setupUi(self)
self.btn_fault = QPushButton("故障测试",self)
self.btn_fault.setCheckable(True)
self.btn_fault.move(100,200)
self.btn_fault.clicked[bool].connect(self.btn_fault_click)
self.bytes = bytearray(1)
input = InPut()
inout = InOut(0,1,2,self.bytes)
self.motor = Motor(input,inout)
self.timer = QTimer(self)
self.timer.timeout.connect(self.on_timer)
self.timer.start(200)
def on_timer(self):
self.motor.checkState()
tex1="主接触器启动" if self.motor.inout.main_out_state() else "主接触器停止"
self.lab1.setText(tex1)
tex2 = "星接触器启动" if self.motor.inout.star_out_state() else "星接触器停止"
self.lab2.setText(tex2)
tex3 = "三角接触器启动" if self.motor.inout.tran_out_state() else "三角接触器停止"
self.lab3.setText(tex3)
tex4 = "设备正常" if not self.motor.input.fault else "设备故障"
self.lab4.setText(tex4)
print(self.bytes)
def btn_start_click(self):
self.motor.input.start = True
self.motor.input.stop = False
self.motor.startStop()
def btn_stop_click(self):
self.motor.input.start = False
self.motor.input.stop = True
self.motor.startStop()
def btn_fault_click(self,state):
self.motor.input.fault=state
print("fault",state)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
win = Ui()
win.show()
sys.exit(app.exec())