这个对于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 = startself.stop: bool = stopself.fault: bool = faultself.interval: bool = interval
输入输出类:
class InOut:def __init__(self,mainout,starout,tranout,byte:bytearray):''':param mainout::param starout::param tranout::param byte:'''self.__mainout = mainoutself.__starout = staroutself.__tranout = tranoutself.__byte = bytedef 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)@staticmethoddef get_bool(bytearray_: bytearray, byte_index: int, bool_index: int) -> bool:index_value = 1 << bool_indexbyte_value = bytearray_[byte_index]current_value = byte_value & index_valuereturn current_value == index_value@classmethoddef 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 valueif current_value == value:returnif value:# make sure index_v is IN current bytebytearray_[byte_index] += index_valueelse:# make sure index_v is NOT in current bytebytearray_[byte_index] -= index_value
下面是降压启动类;
class Motor(QObject):def __init__(self,*args,**kwargs):super().__init__(*args,**kwargs)self.flag = Falsedef 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 = Trueprint("定时器启动")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 = inoutdef 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 = Falseprint("三角启动")
下面是对该类进行测试:
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from snap7 import utilclass 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)# setupUidef 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))# retranslateUiclass 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 = Trueself.input.stop = Falseself.motor.startStop(self.input,self.inout)def btn_stop_click(self):self.input.start = Falseself.input.stop = Trueself.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 = stateprint("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=stateprint("fault",state)if __name__ == '__main__':import sysapp = 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 = mainoutself.__starout = staroutself.__tranout = tranoutself.__byte = bytedef 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)@staticmethoddef get_bool(bytearray_: bytearray, byte_index: int, bool_index: int) -> bool:index_value = 1 << bool_indexbyte_value = bytearray_[byte_index]current_value = byte_value & index_valuereturn current_value == index_value@classmethoddef 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 valueif current_value == value:returnif value:# make sure index_v is IN current bytebytearray_[byte_index] += index_valueelse:# make sure index_v is NOT in current bytebytearray_[byte_index] -= index_valueclass 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 = startself.stop: bool = stopself.fault: bool = faultself.interval: bool = intervalclass Motor(QObject):def __init__(self,intput:InPut=None,inout:InOut=None,*args,**kwargs):super().__init__(*args,**kwargs)self.flag = Falseself.input: InPut = intputself.inout: InOut = inoutdef 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 = Trueprint("定时器启动")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 = Falseprint("三角启动")from PySide6.QtWidgets import *
from PySide6.QtCore import *
from snap7 import utilclass 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)# setupUidef 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))# retranslateUiclass 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 = Trueself.motor.input.stop = Falseself.motor.startStop()def btn_stop_click(self):self.motor.input.start = Falseself.motor.input.stop = Trueself.motor.startStop()def btn_fault_click(self,state):self.motor.input.fault=stateprint("fault",state)if __name__ == '__main__':import sysapp = QApplication(sys.argv)win = Ui()win.show()sys.exit(app.exec())