Renamed version of revpipycontrol written with Qt framework
1
.gitignore
vendored
@@ -127,3 +127,4 @@ dmypy.json
|
|||||||
|
|
||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.pyre/
|
||||||
|
/make.conf
|
||||||
|
|||||||
17
.idea/$CACHE_FILE$
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectInspectionProfilesVisibleTreeState">
|
||||||
|
<entry key="Project Default">
|
||||||
|
<profile-state>
|
||||||
|
<expanded-state>
|
||||||
|
<State />
|
||||||
|
</expanded-state>
|
||||||
|
<selected-state>
|
||||||
|
<State>
|
||||||
|
<id>Angular</id>
|
||||||
|
</State>
|
||||||
|
</selected-state>
|
||||||
|
</profile-state>
|
||||||
|
</entry>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
31
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ourVersions">
|
||||||
|
<value>
|
||||||
|
<list size="4">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="3.6" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="3.7" />
|
||||||
|
<item index="2" class="java.lang.String" itemvalue="3.8" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="3.9" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyMandatoryEncodingInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="myAllPythons" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PyMissingTypeHintsInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredPackages">
|
||||||
|
<value>
|
||||||
|
<list size="2">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="tkinter" />
|
||||||
|
<item index="1" class="java.lang.String" itemvalue="PyQt5" />
|
||||||
|
</list>
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/revpicommander.iml" filepath="$PROJECT_DIR$/.idea/revpicommander.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/revpicommander.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/include" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/revpicommander" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/lib/revpimodio2" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
7
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/lib/revpimodio2" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
8
MANIFEST.in
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
include MANIFEST.in
|
||||||
|
include stdeb.cfg
|
||||||
|
recursive-include data *
|
||||||
|
recursive-include include *.py
|
||||||
|
recursive-include lib *.py
|
||||||
|
recursive-include revpicommander *.py *.qm
|
||||||
|
global-exclude *.pyc
|
||||||
|
include LICENSE.txt
|
||||||
3
data/revpicommander
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec "/usr/share/revpicommander/revpicommander.py" "$@"
|
||||||
11
data/revpicommander.desktop
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Name=RevPi PLC Commander
|
||||||
|
Comment=Controls the Python PLC program on your Revolution PI
|
||||||
|
Name[de]=RevPi PLC Steuerung
|
||||||
|
Comment[de]=Kontrolliert das Python PLC Programm auf dem Revolution PI
|
||||||
|
Exec=/usr/bin/revpicommander
|
||||||
|
Icon=revpicommander
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Application;
|
||||||
|
#StartupNotify=true
|
||||||
BIN
data/revpicommander.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
data/revpicommander.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
160
include/ui/aclmanager_ui.py
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'aclmanager.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_aclmanager(object):
|
||||||
|
def setupUi(self, diag_aclmanager):
|
||||||
|
diag_aclmanager.setObjectName("diag_aclmanager")
|
||||||
|
diag_aclmanager.resize(454, 572)
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(diag_aclmanager)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.gb_acls = QtWidgets.QGroupBox(diag_aclmanager)
|
||||||
|
self.gb_acls.setObjectName("gb_acls")
|
||||||
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.gb_acls)
|
||||||
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
|
self.tb_acls = QtWidgets.QTableWidget(self.gb_acls)
|
||||||
|
self.tb_acls.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
self.tb_acls.setTabKeyNavigation(False)
|
||||||
|
self.tb_acls.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
|
self.tb_acls.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
|
self.tb_acls.setWordWrap(False)
|
||||||
|
self.tb_acls.setCornerButtonEnabled(False)
|
||||||
|
self.tb_acls.setObjectName("tb_acls")
|
||||||
|
self.tb_acls.setColumnCount(2)
|
||||||
|
self.tb_acls.setRowCount(0)
|
||||||
|
item = QtWidgets.QTableWidgetItem()
|
||||||
|
self.tb_acls.setHorizontalHeaderItem(0, item)
|
||||||
|
item = QtWidgets.QTableWidgetItem()
|
||||||
|
self.tb_acls.setHorizontalHeaderItem(1, item)
|
||||||
|
self.tb_acls.horizontalHeader().setHighlightSections(False)
|
||||||
|
self.tb_acls.horizontalHeader().setStretchLastSection(True)
|
||||||
|
self.tb_acls.verticalHeader().setVisible(False)
|
||||||
|
self.verticalLayout_2.addWidget(self.tb_acls)
|
||||||
|
self.hl_acls = QtWidgets.QHBoxLayout()
|
||||||
|
self.hl_acls.setObjectName("hl_acls")
|
||||||
|
self.btn_edit = QtWidgets.QPushButton(self.gb_acls)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.btn_edit.sizePolicy().hasHeightForWidth())
|
||||||
|
self.btn_edit.setSizePolicy(sizePolicy)
|
||||||
|
self.btn_edit.setObjectName("btn_edit")
|
||||||
|
self.hl_acls.addWidget(self.btn_edit)
|
||||||
|
self.btn_remove = QtWidgets.QPushButton(self.gb_acls)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.btn_remove.sizePolicy().hasHeightForWidth())
|
||||||
|
self.btn_remove.setSizePolicy(sizePolicy)
|
||||||
|
self.btn_remove.setObjectName("btn_remove")
|
||||||
|
self.hl_acls.addWidget(self.btn_remove)
|
||||||
|
self.verticalLayout_2.addLayout(self.hl_acls)
|
||||||
|
self.verticalLayout.addWidget(self.gb_acls)
|
||||||
|
self.gb_edit = QtWidgets.QGroupBox(diag_aclmanager)
|
||||||
|
self.gb_edit.setObjectName("gb_edit")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.gb_edit)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.btn_clear = QtWidgets.QPushButton(self.gb_edit)
|
||||||
|
self.btn_clear.setObjectName("btn_clear")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_clear, 1, 0, 1, 1)
|
||||||
|
self.btn_add = QtWidgets.QPushButton(self.gb_edit)
|
||||||
|
self.btn_add.setObjectName("btn_add")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_add, 1, 1, 1, 1)
|
||||||
|
self.fl_edit = QtWidgets.QFormLayout()
|
||||||
|
self.fl_edit.setObjectName("fl_edit")
|
||||||
|
self.lbl_ip = QtWidgets.QLabel(self.gb_edit)
|
||||||
|
self.lbl_ip.setObjectName("lbl_ip")
|
||||||
|
self.fl_edit.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lbl_ip)
|
||||||
|
self.lbl_level = QtWidgets.QLabel(self.gb_edit)
|
||||||
|
self.lbl_level.setObjectName("lbl_level")
|
||||||
|
self.fl_edit.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.lbl_level)
|
||||||
|
self.cbb_level = QtWidgets.QComboBox(self.gb_edit)
|
||||||
|
self.cbb_level.setObjectName("cbb_level")
|
||||||
|
self.fl_edit.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.cbb_level)
|
||||||
|
self.hl_ip = QtWidgets.QHBoxLayout()
|
||||||
|
self.hl_ip.setObjectName("hl_ip")
|
||||||
|
self.txt_ip_a = QtWidgets.QLineEdit(self.gb_edit)
|
||||||
|
self.txt_ip_a.setMaxLength(3)
|
||||||
|
self.txt_ip_a.setObjectName("txt_ip_a")
|
||||||
|
self.hl_ip.addWidget(self.txt_ip_a)
|
||||||
|
self.lbl_ip_a = QtWidgets.QLabel(self.gb_edit)
|
||||||
|
self.lbl_ip_a.setObjectName("lbl_ip_a")
|
||||||
|
self.hl_ip.addWidget(self.lbl_ip_a)
|
||||||
|
self.txt_ip_b = QtWidgets.QLineEdit(self.gb_edit)
|
||||||
|
self.txt_ip_b.setMaxLength(3)
|
||||||
|
self.txt_ip_b.setObjectName("txt_ip_b")
|
||||||
|
self.hl_ip.addWidget(self.txt_ip_b)
|
||||||
|
self.lbl_ip_b = QtWidgets.QLabel(self.gb_edit)
|
||||||
|
self.lbl_ip_b.setObjectName("lbl_ip_b")
|
||||||
|
self.hl_ip.addWidget(self.lbl_ip_b)
|
||||||
|
self.txt_ip_c = QtWidgets.QLineEdit(self.gb_edit)
|
||||||
|
self.txt_ip_c.setMaxLength(3)
|
||||||
|
self.txt_ip_c.setObjectName("txt_ip_c")
|
||||||
|
self.hl_ip.addWidget(self.txt_ip_c)
|
||||||
|
self.lbl_ip_c = QtWidgets.QLabel(self.gb_edit)
|
||||||
|
self.lbl_ip_c.setObjectName("lbl_ip_c")
|
||||||
|
self.hl_ip.addWidget(self.lbl_ip_c)
|
||||||
|
self.txt_ip_d = QtWidgets.QLineEdit(self.gb_edit)
|
||||||
|
self.txt_ip_d.setMaxLength(3)
|
||||||
|
self.txt_ip_d.setObjectName("txt_ip_d")
|
||||||
|
self.hl_ip.addWidget(self.txt_ip_d)
|
||||||
|
self.fl_edit.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.hl_ip)
|
||||||
|
self.gridLayout_2.addLayout(self.fl_edit, 0, 0, 1, 2)
|
||||||
|
self.verticalLayout.addWidget(self.gb_edit)
|
||||||
|
self.lbl_level_info = QtWidgets.QLabel(diag_aclmanager)
|
||||||
|
self.lbl_level_info.setText("")
|
||||||
|
self.lbl_level_info.setObjectName("lbl_level_info")
|
||||||
|
self.verticalLayout.addWidget(self.lbl_level_info)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_aclmanager)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.verticalLayout.addWidget(self.btn_box)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_aclmanager)
|
||||||
|
self.btn_box.accepted.connect(diag_aclmanager.accept)
|
||||||
|
self.btn_box.rejected.connect(diag_aclmanager.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_aclmanager)
|
||||||
|
diag_aclmanager.setTabOrder(self.tb_acls, self.btn_edit)
|
||||||
|
diag_aclmanager.setTabOrder(self.btn_edit, self.btn_remove)
|
||||||
|
diag_aclmanager.setTabOrder(self.btn_remove, self.txt_ip_a)
|
||||||
|
diag_aclmanager.setTabOrder(self.txt_ip_a, self.txt_ip_b)
|
||||||
|
diag_aclmanager.setTabOrder(self.txt_ip_b, self.txt_ip_c)
|
||||||
|
diag_aclmanager.setTabOrder(self.txt_ip_c, self.txt_ip_d)
|
||||||
|
diag_aclmanager.setTabOrder(self.txt_ip_d, self.cbb_level)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_aclmanager):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_aclmanager.setWindowTitle(_translate("diag_aclmanager", "IP access control list"))
|
||||||
|
self.gb_acls.setTitle(_translate("diag_aclmanager", "Existing ACLs"))
|
||||||
|
item = self.tb_acls.horizontalHeaderItem(0)
|
||||||
|
item.setText(_translate("diag_aclmanager", "IP Address"))
|
||||||
|
item = self.tb_acls.horizontalHeaderItem(1)
|
||||||
|
item.setText(_translate("diag_aclmanager", "Access Level"))
|
||||||
|
self.btn_edit.setText(_translate("diag_aclmanager", "&Edit"))
|
||||||
|
self.btn_remove.setText(_translate("diag_aclmanager", "&Remove"))
|
||||||
|
self.gb_edit.setTitle(_translate("diag_aclmanager", "Add / Edit access entry"))
|
||||||
|
self.btn_clear.setText(_translate("diag_aclmanager", "Clear fields"))
|
||||||
|
self.btn_add.setText(_translate("diag_aclmanager", "&Save entry"))
|
||||||
|
self.lbl_ip.setText(_translate("diag_aclmanager", "IP address:"))
|
||||||
|
self.lbl_level.setText(_translate("diag_aclmanager", "Access level:"))
|
||||||
|
self.lbl_ip_a.setText(_translate("diag_aclmanager", "."))
|
||||||
|
self.lbl_ip_b.setText(_translate("diag_aclmanager", "."))
|
||||||
|
self.lbl_ip_c.setText(_translate("diag_aclmanager", "."))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_aclmanager = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_aclmanager()
|
||||||
|
ui.setupUi(diag_aclmanager)
|
||||||
|
diag_aclmanager.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
93
include/ui/avahisearch_ui.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'avahisearch.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_search(object):
|
||||||
|
def setupUi(self, diag_search):
|
||||||
|
diag_search.setObjectName("diag_search")
|
||||||
|
diag_search.resize(480, 360)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(diag_search)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.hl_header = QtWidgets.QHBoxLayout()
|
||||||
|
self.hl_header.setObjectName("hl_header")
|
||||||
|
self.lbl_search = QtWidgets.QLabel(diag_search)
|
||||||
|
self.lbl_search.setObjectName("lbl_search")
|
||||||
|
self.hl_header.addWidget(self.lbl_search)
|
||||||
|
self.btn_restart = QtWidgets.QPushButton(diag_search)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.btn_restart.sizePolicy().hasHeightForWidth())
|
||||||
|
self.btn_restart.setSizePolicy(sizePolicy)
|
||||||
|
self.btn_restart.setText("")
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/action/ico/reload.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_restart.setIcon(icon)
|
||||||
|
self.btn_restart.setIconSize(QtCore.QSize(24, 24))
|
||||||
|
self.btn_restart.setObjectName("btn_restart")
|
||||||
|
self.hl_header.addWidget(self.btn_restart)
|
||||||
|
self.gridLayout.addLayout(self.hl_header, 0, 0, 1, 2)
|
||||||
|
self.tb_revpi = QtWidgets.QTableWidget(diag_search)
|
||||||
|
self.tb_revpi.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
self.tb_revpi.setTabKeyNavigation(False)
|
||||||
|
self.tb_revpi.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
|
||||||
|
self.tb_revpi.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
|
self.tb_revpi.setWordWrap(False)
|
||||||
|
self.tb_revpi.setCornerButtonEnabled(False)
|
||||||
|
self.tb_revpi.setObjectName("tb_revpi")
|
||||||
|
self.tb_revpi.setColumnCount(2)
|
||||||
|
self.tb_revpi.setRowCount(0)
|
||||||
|
item = QtWidgets.QTableWidgetItem()
|
||||||
|
self.tb_revpi.setHorizontalHeaderItem(0, item)
|
||||||
|
item = QtWidgets.QTableWidgetItem()
|
||||||
|
self.tb_revpi.setHorizontalHeaderItem(1, item)
|
||||||
|
self.tb_revpi.horizontalHeader().setHighlightSections(False)
|
||||||
|
self.tb_revpi.horizontalHeader().setStretchLastSection(True)
|
||||||
|
self.tb_revpi.verticalHeader().setVisible(False)
|
||||||
|
self.gridLayout.addWidget(self.tb_revpi, 1, 0, 1, 2)
|
||||||
|
self.btn_connect = QtWidgets.QPushButton(diag_search)
|
||||||
|
self.btn_connect.setObjectName("btn_connect")
|
||||||
|
self.gridLayout.addWidget(self.btn_connect, 2, 0, 1, 1)
|
||||||
|
self.btn_save = QtWidgets.QPushButton(diag_search)
|
||||||
|
self.btn_save.setObjectName("btn_save")
|
||||||
|
self.gridLayout.addWidget(self.btn_save, 2, 1, 1, 1)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_search)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Close)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.gridLayout.addWidget(self.btn_box, 3, 0, 1, 2)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_search)
|
||||||
|
self.btn_box.rejected.connect(diag_search.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_search)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_search):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_search.setWindowTitle(_translate("diag_search", "Search Revolution Pi devices"))
|
||||||
|
self.lbl_search.setText(_translate("diag_search", "Searching for Revolution Pi devices in your network..."))
|
||||||
|
self.btn_restart.setToolTip(_translate("diag_search", "Restart search"))
|
||||||
|
self.tb_revpi.setSortingEnabled(True)
|
||||||
|
item = self.tb_revpi.horizontalHeaderItem(0)
|
||||||
|
item.setText(_translate("diag_search", "Zero conf Name"))
|
||||||
|
item = self.tb_revpi.horizontalHeaderItem(1)
|
||||||
|
item.setText(_translate("diag_search", "IP address"))
|
||||||
|
self.btn_connect.setText(_translate("diag_search", "&Connect to Revolution Pi"))
|
||||||
|
self.btn_save.setText(_translate("diag_search", "&Save connection"))
|
||||||
|
|
||||||
|
from . import ressources_rc
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_search = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_search()
|
||||||
|
ui.setupUi(diag_search)
|
||||||
|
diag_search.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
83
include/ui/debugcontrol_ui.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'debugcontrol.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_wid_debugcontrol(object):
|
||||||
|
def setupUi(self, wid_debugcontrol):
|
||||||
|
wid_debugcontrol.setObjectName("wid_debugcontrol")
|
||||||
|
wid_debugcontrol.resize(402, 214)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(wid_debugcontrol.sizePolicy().hasHeightForWidth())
|
||||||
|
wid_debugcontrol.setSizePolicy(sizePolicy)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(wid_debugcontrol)
|
||||||
|
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.gb_devices = QtWidgets.QGroupBox(wid_debugcontrol)
|
||||||
|
self.gb_devices.setObjectName("gb_devices")
|
||||||
|
self.vl_devices = QtWidgets.QVBoxLayout(self.gb_devices)
|
||||||
|
self.vl_devices.setObjectName("vl_devices")
|
||||||
|
self.gridLayout.addWidget(self.gb_devices, 0, 0, 1, 1)
|
||||||
|
self.cbx_stay_on_top = QtWidgets.QCheckBox(wid_debugcontrol)
|
||||||
|
self.cbx_stay_on_top.setObjectName("cbx_stay_on_top")
|
||||||
|
self.gridLayout.addWidget(self.cbx_stay_on_top, 1, 0, 1, 1)
|
||||||
|
self.gb_control = QtWidgets.QGroupBox(wid_debugcontrol)
|
||||||
|
self.gb_control.setObjectName("gb_control")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.gb_control)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.btn_read_io = QtWidgets.QPushButton(self.gb_control)
|
||||||
|
self.btn_read_io.setObjectName("btn_read_io")
|
||||||
|
self.verticalLayout.addWidget(self.btn_read_io)
|
||||||
|
self.btn_refresh_io = QtWidgets.QPushButton(self.gb_control)
|
||||||
|
self.btn_refresh_io.setObjectName("btn_refresh_io")
|
||||||
|
self.verticalLayout.addWidget(self.btn_refresh_io)
|
||||||
|
self.btn_write_o = QtWidgets.QPushButton(self.gb_control)
|
||||||
|
self.btn_write_o.setObjectName("btn_write_o")
|
||||||
|
self.verticalLayout.addWidget(self.btn_write_o)
|
||||||
|
self.cbx_refresh = QtWidgets.QCheckBox(self.gb_control)
|
||||||
|
self.cbx_refresh.setObjectName("cbx_refresh")
|
||||||
|
self.verticalLayout.addWidget(self.cbx_refresh)
|
||||||
|
self.cbx_write = QtWidgets.QCheckBox(self.gb_control)
|
||||||
|
self.cbx_write.setObjectName("cbx_write")
|
||||||
|
self.verticalLayout.addWidget(self.cbx_write)
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(20, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.verticalLayout.addItem(spacerItem)
|
||||||
|
self.gridLayout.addWidget(self.gb_control, 0, 1, 2, 1)
|
||||||
|
|
||||||
|
self.retranslateUi(wid_debugcontrol)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(wid_debugcontrol)
|
||||||
|
|
||||||
|
def retranslateUi(self, wid_debugcontrol):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
self.gb_devices.setTitle(_translate("wid_debugcontrol", "Revolution Pi devices"))
|
||||||
|
self.cbx_stay_on_top.setText(_translate("wid_debugcontrol", "Open to stay on top"))
|
||||||
|
self.gb_control.setTitle(_translate("wid_debugcontrol", "IO Control"))
|
||||||
|
self.btn_read_io.setToolTip(_translate("wid_debugcontrol", "Read all IO values and discard local changes (F4)"))
|
||||||
|
self.btn_read_io.setText(_translate("wid_debugcontrol", "Read &all IO values"))
|
||||||
|
self.btn_read_io.setShortcut(_translate("wid_debugcontrol", "F4"))
|
||||||
|
self.btn_refresh_io.setToolTip(_translate("wid_debugcontrol", "Refresh all IO values which are locally not changed (F5)"))
|
||||||
|
self.btn_refresh_io.setText(_translate("wid_debugcontrol", "&Refresh unchanged IOs"))
|
||||||
|
self.btn_refresh_io.setShortcut(_translate("wid_debugcontrol", "F5"))
|
||||||
|
self.btn_write_o.setToolTip(_translate("wid_debugcontrol", "Write locally changed output values to process image (F6)"))
|
||||||
|
self.btn_write_o.setText(_translate("wid_debugcontrol", "&Write changed outputs"))
|
||||||
|
self.btn_write_o.setShortcut(_translate("wid_debugcontrol", "F6"))
|
||||||
|
self.cbx_refresh.setText(_translate("wid_debugcontrol", "&Auto refresh values"))
|
||||||
|
self.cbx_write.setText(_translate("wid_debugcontrol", "and write outputs"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
wid_debugcontrol = QtWidgets.QWidget()
|
||||||
|
ui = Ui_wid_debugcontrol()
|
||||||
|
ui.setupUi(wid_debugcontrol)
|
||||||
|
wid_debugcontrol.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
75
include/ui/debugios_ui.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'debugios.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_win_debugios(object):
|
||||||
|
def setupUi(self, win_debugios):
|
||||||
|
win_debugios.setObjectName("win_debugios")
|
||||||
|
win_debugios.resize(434, 343)
|
||||||
|
self.centralwidget = QtWidgets.QWidget(win_debugios)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.gb_io = QtWidgets.QGroupBox(self.centralwidget)
|
||||||
|
self.gb_io.setObjectName("gb_io")
|
||||||
|
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.gb_io)
|
||||||
|
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.verticalLayout_2.setObjectName("verticalLayout_2")
|
||||||
|
self.splitter = QtWidgets.QSplitter(self.gb_io)
|
||||||
|
self.splitter.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.splitter.setChildrenCollapsible(False)
|
||||||
|
self.splitter.setObjectName("splitter")
|
||||||
|
self.sca_inp = QtWidgets.QScrollArea(self.splitter)
|
||||||
|
self.sca_inp.setLineWidth(0)
|
||||||
|
self.sca_inp.setWidgetResizable(True)
|
||||||
|
self.sca_inp.setObjectName("sca_inp")
|
||||||
|
self.saw_inp = QtWidgets.QWidget()
|
||||||
|
self.saw_inp.setGeometry(QtCore.QRect(0, 0, 201, 275))
|
||||||
|
self.saw_inp.setObjectName("saw_inp")
|
||||||
|
self.form_inp = QtWidgets.QFormLayout(self.saw_inp)
|
||||||
|
self.form_inp.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
|
self.form_inp.setContentsMargins(-1, 6, -1, 6)
|
||||||
|
self.form_inp.setObjectName("form_inp")
|
||||||
|
self.sca_inp.setWidget(self.saw_inp)
|
||||||
|
self.sca_out = QtWidgets.QScrollArea(self.splitter)
|
||||||
|
self.sca_out.setLineWidth(0)
|
||||||
|
self.sca_out.setWidgetResizable(True)
|
||||||
|
self.sca_out.setObjectName("sca_out")
|
||||||
|
self.saw_out = QtWidgets.QWidget()
|
||||||
|
self.saw_out.setGeometry(QtCore.QRect(0, 0, 201, 275))
|
||||||
|
self.saw_out.setObjectName("saw_out")
|
||||||
|
self.form_out = QtWidgets.QFormLayout(self.saw_out)
|
||||||
|
self.form_out.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
|
self.form_out.setContentsMargins(-1, 6, -1, 6)
|
||||||
|
self.form_out.setObjectName("form_out")
|
||||||
|
self.sca_out.setWidget(self.saw_out)
|
||||||
|
self.verticalLayout_2.addWidget(self.splitter)
|
||||||
|
self.verticalLayout.addWidget(self.gb_io)
|
||||||
|
win_debugios.setCentralWidget(self.centralwidget)
|
||||||
|
self.stat_bar = QtWidgets.QStatusBar(win_debugios)
|
||||||
|
self.stat_bar.setObjectName("stat_bar")
|
||||||
|
win_debugios.setStatusBar(self.stat_bar)
|
||||||
|
|
||||||
|
self.retranslateUi(win_debugios)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(win_debugios)
|
||||||
|
|
||||||
|
def retranslateUi(self, win_debugios):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
self.gb_io.setTitle(_translate("win_debugios", "{0}: Inputs | Outputs"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
win_debugios = QtWidgets.QMainWindow()
|
||||||
|
ui = Ui_win_debugios()
|
||||||
|
ui.setupUi(win_debugios)
|
||||||
|
win_debugios.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
155
include/ui/mqttmanager_ui.py
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'mqttmanager.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_mqtt(object):
|
||||||
|
def setupUi(self, diag_mqtt):
|
||||||
|
diag_mqtt.setObjectName("diag_mqtt")
|
||||||
|
diag_mqtt.resize(492, 704)
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(diag_mqtt)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.gb_basetopic = QtWidgets.QGroupBox(diag_mqtt)
|
||||||
|
self.gb_basetopic.setObjectName("gb_basetopic")
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(self.gb_basetopic)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.lbl_basetopic_description = QtWidgets.QLabel(self.gb_basetopic)
|
||||||
|
self.lbl_basetopic_description.setMaximumSize(QtCore.QSize(450, 16777215))
|
||||||
|
self.lbl_basetopic_description.setWordWrap(True)
|
||||||
|
self.lbl_basetopic_description.setObjectName("lbl_basetopic_description")
|
||||||
|
self.gridLayout.addWidget(self.lbl_basetopic_description, 1, 0, 1, 4)
|
||||||
|
self.lbl_basetopic = QtWidgets.QLabel(self.gb_basetopic)
|
||||||
|
self.lbl_basetopic.setObjectName("lbl_basetopic")
|
||||||
|
self.gridLayout.addWidget(self.lbl_basetopic, 0, 0, 1, 1)
|
||||||
|
self.txt_basetopic = QtWidgets.QLineEdit(self.gb_basetopic)
|
||||||
|
self.txt_basetopic.setObjectName("txt_basetopic")
|
||||||
|
self.gridLayout.addWidget(self.txt_basetopic, 0, 1, 1, 1)
|
||||||
|
self.verticalLayout.addWidget(self.gb_basetopic)
|
||||||
|
self.gb_send_on_event = QtWidgets.QGroupBox(diag_mqtt)
|
||||||
|
self.gb_send_on_event.setObjectName("gb_send_on_event")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.gb_send_on_event)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.lbl_sendinterval = QtWidgets.QLabel(self.gb_send_on_event)
|
||||||
|
self.lbl_sendinterval.setObjectName("lbl_sendinterval")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_sendinterval, 0, 0, 1, 1)
|
||||||
|
self.sbx_sendinterval = QtWidgets.QSpinBox(self.gb_send_on_event)
|
||||||
|
self.sbx_sendinterval.setMinimum(1)
|
||||||
|
self.sbx_sendinterval.setMaximum(21600)
|
||||||
|
self.sbx_sendinterval.setObjectName("sbx_sendinterval")
|
||||||
|
self.gridLayout_2.addWidget(self.sbx_sendinterval, 0, 1, 1, 1)
|
||||||
|
self.lbl_topic_io = QtWidgets.QLabel(self.gb_send_on_event)
|
||||||
|
self.lbl_topic_io.setObjectName("lbl_topic_io")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_topic_io, 1, 0, 1, 2)
|
||||||
|
self.cbx_send_on_event = QtWidgets.QCheckBox(self.gb_send_on_event)
|
||||||
|
self.cbx_send_on_event.setObjectName("cbx_send_on_event")
|
||||||
|
self.gridLayout_2.addWidget(self.cbx_send_on_event, 2, 0, 1, 2)
|
||||||
|
self.lbl_topic_event = QtWidgets.QLabel(self.gb_send_on_event)
|
||||||
|
self.lbl_topic_event.setObjectName("lbl_topic_event")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_topic_event, 3, 0, 1, 2)
|
||||||
|
self.verticalLayout.addWidget(self.gb_send_on_event)
|
||||||
|
self.gb_write_outputs = QtWidgets.QGroupBox(diag_mqtt)
|
||||||
|
self.gb_write_outputs.setObjectName("gb_write_outputs")
|
||||||
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.gb_write_outputs)
|
||||||
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
|
self.lbl_write_outputs = QtWidgets.QLabel(self.gb_write_outputs)
|
||||||
|
self.lbl_write_outputs.setMaximumSize(QtCore.QSize(450, 16777215))
|
||||||
|
self.lbl_write_outputs.setWordWrap(True)
|
||||||
|
self.lbl_write_outputs.setObjectName("lbl_write_outputs")
|
||||||
|
self.gridLayout_3.addWidget(self.lbl_write_outputs, 1, 0, 1, 1)
|
||||||
|
self.cbx_write_outputs = QtWidgets.QCheckBox(self.gb_write_outputs)
|
||||||
|
self.cbx_write_outputs.setObjectName("cbx_write_outputs")
|
||||||
|
self.gridLayout_3.addWidget(self.cbx_write_outputs, 0, 0, 1, 1)
|
||||||
|
self.verticalLayout.addWidget(self.gb_write_outputs)
|
||||||
|
self.gb_broker = QtWidgets.QGroupBox(diag_mqtt)
|
||||||
|
self.gb_broker.setObjectName("gb_broker")
|
||||||
|
self.formLayout = QtWidgets.QFormLayout(self.gb_broker)
|
||||||
|
self.formLayout.setObjectName("formLayout")
|
||||||
|
self.lbl_broker_address = QtWidgets.QLabel(self.gb_broker)
|
||||||
|
self.lbl_broker_address.setObjectName("lbl_broker_address")
|
||||||
|
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lbl_broker_address)
|
||||||
|
self.txt_broker_address = QtWidgets.QLineEdit(self.gb_broker)
|
||||||
|
self.txt_broker_address.setObjectName("txt_broker_address")
|
||||||
|
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.txt_broker_address)
|
||||||
|
self.lbl_port = QtWidgets.QLabel(self.gb_broker)
|
||||||
|
self.lbl_port.setObjectName("lbl_port")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.lbl_port)
|
||||||
|
self.lbl_username = QtWidgets.QLabel(self.gb_broker)
|
||||||
|
self.lbl_username.setObjectName("lbl_username")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_username)
|
||||||
|
self.lbl_password = QtWidgets.QLabel(self.gb_broker)
|
||||||
|
self.lbl_password.setObjectName("lbl_password")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_password)
|
||||||
|
self.lbl_client_id = QtWidgets.QLabel(self.gb_broker)
|
||||||
|
self.lbl_client_id.setObjectName("lbl_client_id")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lbl_client_id)
|
||||||
|
self.txt_username = QtWidgets.QLineEdit(self.gb_broker)
|
||||||
|
self.txt_username.setObjectName("txt_username")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.txt_username)
|
||||||
|
self.txt_password = QtWidgets.QLineEdit(self.gb_broker)
|
||||||
|
self.txt_password.setObjectName("txt_password")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txt_password)
|
||||||
|
self.txt_client_id = QtWidgets.QLineEdit(self.gb_broker)
|
||||||
|
self.txt_client_id.setObjectName("txt_client_id")
|
||||||
|
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.txt_client_id)
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.sbx_port = QtWidgets.QSpinBox(self.gb_broker)
|
||||||
|
self.sbx_port.setMinimum(1)
|
||||||
|
self.sbx_port.setMaximum(65535)
|
||||||
|
self.sbx_port.setObjectName("sbx_port")
|
||||||
|
self.horizontalLayout.addWidget(self.sbx_port)
|
||||||
|
self.cbx_tls_set = QtWidgets.QCheckBox(self.gb_broker)
|
||||||
|
self.cbx_tls_set.setObjectName("cbx_tls_set")
|
||||||
|
self.horizontalLayout.addWidget(self.cbx_tls_set)
|
||||||
|
self.formLayout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
|
||||||
|
self.verticalLayout.addWidget(self.gb_broker)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_mqtt)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.verticalLayout.addWidget(self.btn_box)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_mqtt)
|
||||||
|
self.btn_box.accepted.connect(diag_mqtt.accept)
|
||||||
|
self.btn_box.rejected.connect(diag_mqtt.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_mqtt)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_mqtt):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_mqtt.setWindowTitle(_translate("diag_mqtt", "MQTT settings"))
|
||||||
|
self.gb_basetopic.setTitle(_translate("diag_mqtt", "Base topic"))
|
||||||
|
self.lbl_basetopic_description.setText(_translate("diag_mqtt", "<html><head/><body><p>The base topic is the first part of any mqtt topic, the Revolution Pi will publish. You can use any character includig \'/\' to structure the messages on your broker.</p><p>For example: revpi0000/data</p></body></html>"))
|
||||||
|
self.lbl_basetopic.setText(_translate("diag_mqtt", "Base topic:"))
|
||||||
|
self.gb_send_on_event.setTitle(_translate("diag_mqtt", "Publish settings"))
|
||||||
|
self.lbl_sendinterval.setText(_translate("diag_mqtt", "Publish all exported values every n seconds:"))
|
||||||
|
self.lbl_topic_io.setText(_translate("diag_mqtt", "Topic: [basetopic]/io/[ioname]"))
|
||||||
|
self.cbx_send_on_event.setText(_translate("diag_mqtt", "Send exported values immediately on value change"))
|
||||||
|
self.lbl_topic_event.setText(_translate("diag_mqtt", "Topic: [basetopic]/event/[ioname]"))
|
||||||
|
self.gb_write_outputs.setTitle(_translate("diag_mqtt", "Set outputs"))
|
||||||
|
self.lbl_write_outputs.setText(_translate("diag_mqtt", "The Revolution Pi will subscribe a topic on which your mqtt client can publish messages with the new io value as payload.\n"
|
||||||
|
"\n"
|
||||||
|
"Publish values with topic: [basetopic]/set/[outputname]"))
|
||||||
|
self.cbx_write_outputs.setText(_translate("diag_mqtt", "Allow MQTT to to set outputs on Revolution Pi"))
|
||||||
|
self.gb_broker.setTitle(_translate("diag_mqtt", "Brocker settings"))
|
||||||
|
self.lbl_broker_address.setText(_translate("diag_mqtt", "Broker address:"))
|
||||||
|
self.lbl_port.setText(_translate("diag_mqtt", "Broker port:"))
|
||||||
|
self.lbl_username.setText(_translate("diag_mqtt", "User name:"))
|
||||||
|
self.lbl_password.setText(_translate("diag_mqtt", "Password:"))
|
||||||
|
self.lbl_client_id.setText(_translate("diag_mqtt", "Client ID:"))
|
||||||
|
self.cbx_tls_set.setText(_translate("diag_mqtt", "Use TLS"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_mqtt = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_mqtt()
|
||||||
|
ui.setupUi(diag_mqtt)
|
||||||
|
diag_mqtt.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
2761
include/ui/ressources_rc.py
Normal file
167
include/ui/revpicommander_ui.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpicommander.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_win_revpicommander(object):
|
||||||
|
def setupUi(self, win_revpicommander):
|
||||||
|
win_revpicommander.setObjectName("win_revpicommander")
|
||||||
|
win_revpicommander.resize(318, 273)
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/main/ico/revpipycontrol.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
win_revpicommander.setWindowIcon(icon)
|
||||||
|
self.centralwidget = QtWidgets.QWidget(win_revpicommander)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.gl = QtWidgets.QGridLayout(self.centralwidget)
|
||||||
|
self.gl.setObjectName("gl")
|
||||||
|
self.txt_connection = QtWidgets.QLineEdit(self.centralwidget)
|
||||||
|
self.txt_connection.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
self.txt_connection.setReadOnly(True)
|
||||||
|
self.txt_connection.setObjectName("txt_connection")
|
||||||
|
self.gl.addWidget(self.txt_connection, 0, 0, 1, 1)
|
||||||
|
self.btn_plc_start = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.btn_plc_start.setObjectName("btn_plc_start")
|
||||||
|
self.gl.addWidget(self.btn_plc_start, 1, 0, 1, 1)
|
||||||
|
self.btn_plc_stop = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.btn_plc_stop.setObjectName("btn_plc_stop")
|
||||||
|
self.gl.addWidget(self.btn_plc_stop, 2, 0, 1, 1)
|
||||||
|
self.btn_plc_restart = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.btn_plc_restart.setObjectName("btn_plc_restart")
|
||||||
|
self.gl.addWidget(self.btn_plc_restart, 3, 0, 1, 1)
|
||||||
|
self.btn_plc_logs = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.btn_plc_logs.setObjectName("btn_plc_logs")
|
||||||
|
self.gl.addWidget(self.btn_plc_logs, 4, 0, 1, 1)
|
||||||
|
self.txt_status = QtWidgets.QLineEdit(self.centralwidget)
|
||||||
|
self.txt_status.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
self.txt_status.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.txt_status.setReadOnly(True)
|
||||||
|
self.txt_status.setObjectName("txt_status")
|
||||||
|
self.gl.addWidget(self.txt_status, 5, 0, 1, 1)
|
||||||
|
self.btn_plc_debug = QtWidgets.QPushButton(self.centralwidget)
|
||||||
|
self.btn_plc_debug.setMinimumSize(QtCore.QSize(300, 0))
|
||||||
|
self.btn_plc_debug.setCheckable(True)
|
||||||
|
self.btn_plc_debug.setObjectName("btn_plc_debug")
|
||||||
|
self.gl.addWidget(self.btn_plc_debug, 6, 0, 1, 1)
|
||||||
|
win_revpicommander.setCentralWidget(self.centralwidget)
|
||||||
|
self.menubar = QtWidgets.QMenuBar(win_revpicommander)
|
||||||
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 318, 22))
|
||||||
|
self.menubar.setObjectName("menubar")
|
||||||
|
self.men_file = QtWidgets.QMenu(self.menubar)
|
||||||
|
self.men_file.setObjectName("men_file")
|
||||||
|
self.men_help = QtWidgets.QMenu(self.menubar)
|
||||||
|
self.men_help.setObjectName("men_help")
|
||||||
|
self.men_plc = QtWidgets.QMenu(self.menubar)
|
||||||
|
self.men_plc.setObjectName("men_plc")
|
||||||
|
self.men_connections = QtWidgets.QMenu(self.menubar)
|
||||||
|
self.men_connections.setObjectName("men_connections")
|
||||||
|
win_revpicommander.setMenuBar(self.menubar)
|
||||||
|
self.statusbar = QtWidgets.QStatusBar(win_revpicommander)
|
||||||
|
self.statusbar.setSizeGripEnabled(False)
|
||||||
|
self.statusbar.setObjectName("statusbar")
|
||||||
|
win_revpicommander.setStatusBar(self.statusbar)
|
||||||
|
self.act_connections = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_connections.setObjectName("act_connections")
|
||||||
|
self.act_search = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_search.setObjectName("act_search")
|
||||||
|
self.act_quit = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_quit.setObjectName("act_quit")
|
||||||
|
self.act_webpage = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_webpage.setObjectName("act_webpage")
|
||||||
|
self.act_info = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_info.setObjectName("act_info")
|
||||||
|
self.act_logs = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_logs.setObjectName("act_logs")
|
||||||
|
self.act_options = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_options.setObjectName("act_options")
|
||||||
|
self.act_program = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_program.setObjectName("act_program")
|
||||||
|
self.act_developer = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_developer.setCheckable(True)
|
||||||
|
self.act_developer.setObjectName("act_developer")
|
||||||
|
self.act_pictory = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_pictory.setObjectName("act_pictory")
|
||||||
|
self.act_disconnect = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_disconnect.setObjectName("act_disconnect")
|
||||||
|
self.act_reset = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_reset.setObjectName("act_reset")
|
||||||
|
self.act_simulator = QtWidgets.QAction(win_revpicommander)
|
||||||
|
self.act_simulator.setObjectName("act_simulator")
|
||||||
|
self.men_file.addAction(self.act_connections)
|
||||||
|
self.men_file.addAction(self.act_search)
|
||||||
|
self.men_file.addSeparator()
|
||||||
|
self.men_file.addAction(self.act_simulator)
|
||||||
|
self.men_file.addSeparator()
|
||||||
|
self.men_file.addAction(self.act_quit)
|
||||||
|
self.men_help.addAction(self.act_webpage)
|
||||||
|
self.men_help.addSeparator()
|
||||||
|
self.men_help.addAction(self.act_info)
|
||||||
|
self.men_plc.addAction(self.act_logs)
|
||||||
|
self.men_plc.addAction(self.act_options)
|
||||||
|
self.men_plc.addAction(self.act_program)
|
||||||
|
self.men_plc.addAction(self.act_developer)
|
||||||
|
self.men_plc.addSeparator()
|
||||||
|
self.men_plc.addAction(self.act_pictory)
|
||||||
|
self.men_plc.addAction(self.act_reset)
|
||||||
|
self.men_plc.addSeparator()
|
||||||
|
self.men_plc.addAction(self.act_disconnect)
|
||||||
|
self.menubar.addAction(self.men_file.menuAction())
|
||||||
|
self.menubar.addAction(self.men_plc.menuAction())
|
||||||
|
self.menubar.addAction(self.men_connections.menuAction())
|
||||||
|
self.menubar.addAction(self.men_help.menuAction())
|
||||||
|
|
||||||
|
self.retranslateUi(win_revpicommander)
|
||||||
|
self.act_quit.triggered.connect(win_revpicommander.close)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(win_revpicommander)
|
||||||
|
win_revpicommander.setTabOrder(self.btn_plc_start, self.btn_plc_stop)
|
||||||
|
win_revpicommander.setTabOrder(self.btn_plc_stop, self.btn_plc_restart)
|
||||||
|
win_revpicommander.setTabOrder(self.btn_plc_restart, self.btn_plc_logs)
|
||||||
|
win_revpicommander.setTabOrder(self.btn_plc_logs, self.btn_plc_debug)
|
||||||
|
|
||||||
|
def retranslateUi(self, win_revpicommander):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
win_revpicommander.setWindowTitle(_translate("win_revpicommander", "RevPi Python PLC Commander"))
|
||||||
|
self.btn_plc_start.setText(_translate("win_revpicommander", "PLC &start"))
|
||||||
|
self.btn_plc_stop.setText(_translate("win_revpicommander", "PLC s&top"))
|
||||||
|
self.btn_plc_restart.setText(_translate("win_revpicommander", "PLC restart"))
|
||||||
|
self.btn_plc_logs.setText(_translate("win_revpicommander", "PLC &logs"))
|
||||||
|
self.btn_plc_debug.setText(_translate("win_revpicommander", "PLC watch &mode"))
|
||||||
|
self.men_file.setTitle(_translate("win_revpicommander", "&File"))
|
||||||
|
self.men_help.setTitle(_translate("win_revpicommander", "&Help"))
|
||||||
|
self.men_plc.setTitle(_translate("win_revpicommander", "&PLC"))
|
||||||
|
self.men_connections.setTitle(_translate("win_revpicommander", "&Connections"))
|
||||||
|
self.act_connections.setText(_translate("win_revpicommander", "&Connections..."))
|
||||||
|
self.act_search.setText(_translate("win_revpicommander", "&Search Revolution Pi..."))
|
||||||
|
self.act_search.setShortcut(_translate("win_revpicommander", "Ctrl+F"))
|
||||||
|
self.act_quit.setText(_translate("win_revpicommander", "&Quit"))
|
||||||
|
self.act_webpage.setText(_translate("win_revpicommander", "Visit &webpage..."))
|
||||||
|
self.act_info.setText(_translate("win_revpicommander", "&Info..."))
|
||||||
|
self.act_logs.setText(_translate("win_revpicommander", "PLC &logs..."))
|
||||||
|
self.act_logs.setShortcut(_translate("win_revpicommander", "F3"))
|
||||||
|
self.act_options.setText(_translate("win_revpicommander", "PLC &options..."))
|
||||||
|
self.act_options.setShortcut(_translate("win_revpicommander", "Ctrl+O"))
|
||||||
|
self.act_program.setText(_translate("win_revpicommander", "PLC progra&m..."))
|
||||||
|
self.act_program.setShortcut(_translate("win_revpicommander", "Ctrl+P"))
|
||||||
|
self.act_developer.setText(_translate("win_revpicommander", "PLC de&veloper..."))
|
||||||
|
self.act_developer.setShortcut(_translate("win_revpicommander", "F9"))
|
||||||
|
self.act_pictory.setText(_translate("win_revpicommander", "piCtory configuraiton..."))
|
||||||
|
self.act_disconnect.setText(_translate("win_revpicommander", "&Disconnect"))
|
||||||
|
self.act_disconnect.setShortcut(_translate("win_revpicommander", "Ctrl+X"))
|
||||||
|
self.act_reset.setText(_translate("win_revpicommander", "Reset driver..."))
|
||||||
|
self.act_simulator.setText(_translate("win_revpicommander", "Start local si&mulator..."))
|
||||||
|
|
||||||
|
from . import ressources_rc
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
win_revpicommander = QtWidgets.QMainWindow()
|
||||||
|
ui = Ui_win_revpicommander()
|
||||||
|
ui.setupUi(win_revpicommander)
|
||||||
|
win_revpicommander.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
88
include/ui/revpidevelop_ui.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpidevelop.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_wid_develop(object):
|
||||||
|
def setupUi(self, wid_develop):
|
||||||
|
wid_develop.setObjectName("wid_develop")
|
||||||
|
wid_develop.resize(374, 444)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(wid_develop.sizePolicy().hasHeightForWidth())
|
||||||
|
wid_develop.setSizePolicy(sizePolicy)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(wid_develop)
|
||||||
|
self.gridLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.btn_all = QtWidgets.QPushButton(wid_develop)
|
||||||
|
self.btn_all.setObjectName("btn_all")
|
||||||
|
self.gridLayout.addWidget(self.btn_all, 2, 0, 1, 1)
|
||||||
|
self.btn_upload = QtWidgets.QPushButton(wid_develop)
|
||||||
|
self.btn_upload.setObjectName("btn_upload")
|
||||||
|
self.gridLayout.addWidget(self.btn_upload, 2, 1, 1, 1)
|
||||||
|
self.tree_files = QtWidgets.QTreeWidget(wid_develop)
|
||||||
|
self.tree_files.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
self.tree_files.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
||||||
|
self.tree_files.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||||
|
self.tree_files.setIconSize(QtCore.QSize(24, 24))
|
||||||
|
self.tree_files.setObjectName("tree_files")
|
||||||
|
self.tree_files.headerItem().setText(0, "1")
|
||||||
|
self.tree_files.header().setVisible(False)
|
||||||
|
self.gridLayout.addWidget(self.tree_files, 1, 0, 1, 2)
|
||||||
|
self.gb_select = QtWidgets.QGroupBox(wid_develop)
|
||||||
|
self.gb_select.setObjectName("gb_select")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.gb_select)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.lbl_select = QtWidgets.QLabel(self.gb_select)
|
||||||
|
self.lbl_select.setObjectName("lbl_select")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_select, 0, 0, 1, 1)
|
||||||
|
self.btn_select = QtWidgets.QPushButton(self.gb_select)
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/action/ico/folder-open.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_select.setIcon(icon)
|
||||||
|
self.btn_select.setIconSize(QtCore.QSize(24, 24))
|
||||||
|
self.btn_select.setObjectName("btn_select")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_select, 0, 1, 1, 1)
|
||||||
|
self.lbl_path = QtWidgets.QLabel(self.gb_select)
|
||||||
|
self.lbl_path.setObjectName("lbl_path")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_path, 1, 0, 1, 2)
|
||||||
|
self.btn_refresh = QtWidgets.QPushButton(self.gb_select)
|
||||||
|
icon1 = QtGui.QIcon()
|
||||||
|
icon1.addPixmap(QtGui.QPixmap(":/action/ico/refresh.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_refresh.setIcon(icon1)
|
||||||
|
self.btn_refresh.setIconSize(QtCore.QSize(24, 24))
|
||||||
|
self.btn_refresh.setObjectName("btn_refresh")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_refresh, 0, 2, 1, 1)
|
||||||
|
self.gridLayout_2.setColumnStretch(0, 1)
|
||||||
|
self.gridLayout.addWidget(self.gb_select, 0, 0, 1, 2)
|
||||||
|
|
||||||
|
self.retranslateUi(wid_develop)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(wid_develop)
|
||||||
|
|
||||||
|
def retranslateUi(self, wid_develop):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
self.btn_all.setText(_translate("wid_develop", "Stop / Upload / Start"))
|
||||||
|
self.btn_upload.setText(_translate("wid_develop", "Just upload"))
|
||||||
|
self.gb_select.setTitle(_translate("wid_develop", "File watcher for PLC development"))
|
||||||
|
self.lbl_select.setText(_translate("wid_develop", "Path to development root:"))
|
||||||
|
self.btn_select.setToolTip(_translate("wid_develop", "Open developer root directory"))
|
||||||
|
self.lbl_path.setText(_translate("wid_develop", "/"))
|
||||||
|
self.btn_refresh.setToolTip(_translate("wid_develop", "Reload file list"))
|
||||||
|
|
||||||
|
from . import ressources_rc
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
wid_develop = QtWidgets.QWidget()
|
||||||
|
ui = Ui_wid_develop()
|
||||||
|
ui.setupUi(wid_develop)
|
||||||
|
wid_develop.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
106
include/ui/revpiinfo_ui.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpiinfo.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_revpiinfo(object):
|
||||||
|
def setupUi(self, diag_revpiinfo):
|
||||||
|
diag_revpiinfo.setObjectName("diag_revpiinfo")
|
||||||
|
diag_revpiinfo.resize(627, 398)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(diag_revpiinfo)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.lbl_head = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(20)
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.lbl_head.setFont(font)
|
||||||
|
self.lbl_head.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.lbl_head.setObjectName("lbl_head")
|
||||||
|
self.gridLayout.addWidget(self.lbl_head, 0, 0, 1, 3)
|
||||||
|
self.lbl_lbl_version_pyload = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.lbl_lbl_version_pyload.setFont(font)
|
||||||
|
self.lbl_lbl_version_pyload.setObjectName("lbl_lbl_version_pyload")
|
||||||
|
self.gridLayout.addWidget(self.lbl_lbl_version_pyload, 3, 0, 1, 1)
|
||||||
|
self.lst_files = QtWidgets.QListWidget(diag_revpiinfo)
|
||||||
|
self.lst_files.setObjectName("lst_files")
|
||||||
|
self.gridLayout.addWidget(self.lst_files, 3, 2, 4, 1)
|
||||||
|
self.lbl_version_pyload = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.lbl_version_pyload.setFont(font)
|
||||||
|
self.lbl_version_pyload.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.lbl_version_pyload.setObjectName("lbl_version_pyload")
|
||||||
|
self.gridLayout.addWidget(self.lbl_version_pyload, 3, 1, 1, 1)
|
||||||
|
self.lbl_link = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
self.lbl_link.setOpenExternalLinks(True)
|
||||||
|
self.lbl_link.setObjectName("lbl_link")
|
||||||
|
self.gridLayout.addWidget(self.lbl_link, 6, 0, 1, 1)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_revpiinfo)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.gridLayout.addWidget(self.btn_box, 7, 0, 1, 3)
|
||||||
|
self.horizontalLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||||
|
self.lbl_lbl_version_control = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(14)
|
||||||
|
self.lbl_lbl_version_control.setFont(font)
|
||||||
|
self.lbl_lbl_version_control.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
|
self.lbl_lbl_version_control.setObjectName("lbl_lbl_version_control")
|
||||||
|
self.horizontalLayout.addWidget(self.lbl_lbl_version_control)
|
||||||
|
self.lbl_version_control = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(14)
|
||||||
|
self.lbl_version_control.setFont(font)
|
||||||
|
self.lbl_version_control.setObjectName("lbl_version_control")
|
||||||
|
self.horizontalLayout.addWidget(self.lbl_version_control)
|
||||||
|
self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 3)
|
||||||
|
self.lbl_info = QtWidgets.QLabel(diag_revpiinfo)
|
||||||
|
self.lbl_info.setMinimumSize(QtCore.QSize(300, 0))
|
||||||
|
self.lbl_info.setWordWrap(True)
|
||||||
|
self.lbl_info.setObjectName("lbl_info")
|
||||||
|
self.gridLayout.addWidget(self.lbl_info, 4, 0, 1, 2)
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.gridLayout.addItem(spacerItem, 5, 0, 1, 1)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_revpiinfo)
|
||||||
|
self.btn_box.accepted.connect(diag_revpiinfo.accept)
|
||||||
|
self.btn_box.rejected.connect(diag_revpiinfo.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_revpiinfo)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_revpiinfo):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_revpiinfo.setWindowTitle(_translate("diag_revpiinfo", "Dialog"))
|
||||||
|
self.lbl_head.setText(_translate("diag_revpiinfo", "RevPi Python PLC - Control"))
|
||||||
|
self.lbl_lbl_version_pyload.setText(_translate("diag_revpiinfo", "RevPiPyLoad version on RevPi:"))
|
||||||
|
self.lbl_version_pyload.setText(_translate("diag_revpiinfo", "0.0.0"))
|
||||||
|
self.lbl_link.setText(_translate("diag_revpiinfo", "<html><head/><body><p><a href=\"https://revpimodio.org/\"><span style=\" text-decoration: underline; color:#0000ff;\">https://revpimodio.org/</span></a></p></body></html>"))
|
||||||
|
self.lbl_lbl_version_control.setText(_translate("diag_revpiinfo", "Version:"))
|
||||||
|
self.lbl_version_control.setText(_translate("diag_revpiinfo", "0.0.0"))
|
||||||
|
self.lbl_info.setText(_translate("diag_revpiinfo", "RevPiModIO, RevPiPyLoad and RevPiPyControl are community driven projects. They are all free and open source software.\n"
|
||||||
|
"All of them comes with ABSOLUTELY NO WARRANTY, to the extent permitted by\n"
|
||||||
|
"applicable law.\n"
|
||||||
|
"\n"
|
||||||
|
"(c) Sven Sager, License: GPLv3"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_revpiinfo = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_revpiinfo()
|
||||||
|
ui.setupUi(diag_revpiinfo)
|
||||||
|
diag_revpiinfo.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
96
include/ui/revpilogfile_ui.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpilogfile.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_win_revpilogfile(object):
|
||||||
|
def setupUi(self, win_revpilogfile):
|
||||||
|
win_revpilogfile.setObjectName("win_revpilogfile")
|
||||||
|
win_revpilogfile.resize(796, 347)
|
||||||
|
self.centralwidget = QtWidgets.QWidget(win_revpilogfile)
|
||||||
|
self.centralwidget.setObjectName("centralwidget")
|
||||||
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.centralwidget)
|
||||||
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
|
self.cbx_stay_on_top = QtWidgets.QCheckBox(self.centralwidget)
|
||||||
|
self.cbx_stay_on_top.setObjectName("cbx_stay_on_top")
|
||||||
|
self.gridLayout_3.addWidget(self.cbx_stay_on_top, 1, 0, 1, 1)
|
||||||
|
self.cbx_wrap = QtWidgets.QCheckBox(self.centralwidget)
|
||||||
|
self.cbx_wrap.setObjectName("cbx_wrap")
|
||||||
|
self.gridLayout_3.addWidget(self.cbx_wrap, 1, 1, 1, 1)
|
||||||
|
self.splitter = QtWidgets.QSplitter(self.centralwidget)
|
||||||
|
self.splitter.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.splitter.setChildrenCollapsible(False)
|
||||||
|
self.splitter.setObjectName("splitter")
|
||||||
|
self.gridLayoutWidget = QtWidgets.QWidget(self.splitter)
|
||||||
|
self.gridLayoutWidget.setObjectName("gridLayoutWidget")
|
||||||
|
self.grid_daemon = QtWidgets.QGridLayout(self.gridLayoutWidget)
|
||||||
|
self.grid_daemon.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.grid_daemon.setObjectName("grid_daemon")
|
||||||
|
self.lbl_daemon = QtWidgets.QLabel(self.gridLayoutWidget)
|
||||||
|
self.lbl_daemon.setObjectName("lbl_daemon")
|
||||||
|
self.grid_daemon.addWidget(self.lbl_daemon, 0, 0, 1, 1)
|
||||||
|
self.btn_daemon = QtWidgets.QPushButton(self.gridLayoutWidget)
|
||||||
|
self.btn_daemon.setObjectName("btn_daemon")
|
||||||
|
self.grid_daemon.addWidget(self.btn_daemon, 0, 1, 1, 1)
|
||||||
|
self.txt_daemon = QtWidgets.QPlainTextEdit(self.gridLayoutWidget)
|
||||||
|
self.txt_daemon.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||||
|
self.txt_daemon.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||||
|
self.txt_daemon.setTabChangesFocus(True)
|
||||||
|
self.txt_daemon.setUndoRedoEnabled(False)
|
||||||
|
self.txt_daemon.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)
|
||||||
|
self.txt_daemon.setReadOnly(True)
|
||||||
|
self.txt_daemon.setObjectName("txt_daemon")
|
||||||
|
self.grid_daemon.addWidget(self.txt_daemon, 1, 0, 1, 2)
|
||||||
|
self.grid_daemon.setColumnStretch(0, 1)
|
||||||
|
self.gridLayoutWidget_2 = QtWidgets.QWidget(self.splitter)
|
||||||
|
self.gridLayoutWidget_2.setObjectName("gridLayoutWidget_2")
|
||||||
|
self.grid_app = QtWidgets.QGridLayout(self.gridLayoutWidget_2)
|
||||||
|
self.grid_app.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.grid_app.setObjectName("grid_app")
|
||||||
|
self.btn_app = QtWidgets.QPushButton(self.gridLayoutWidget_2)
|
||||||
|
self.btn_app.setObjectName("btn_app")
|
||||||
|
self.grid_app.addWidget(self.btn_app, 0, 1, 1, 1)
|
||||||
|
self.lbl_app = QtWidgets.QLabel(self.gridLayoutWidget_2)
|
||||||
|
self.lbl_app.setObjectName("lbl_app")
|
||||||
|
self.grid_app.addWidget(self.lbl_app, 0, 0, 1, 1)
|
||||||
|
self.txt_app = QtWidgets.QPlainTextEdit(self.gridLayoutWidget_2)
|
||||||
|
self.txt_app.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||||
|
self.txt_app.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||||
|
self.txt_app.setTabChangesFocus(True)
|
||||||
|
self.txt_app.setUndoRedoEnabled(False)
|
||||||
|
self.txt_app.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)
|
||||||
|
self.txt_app.setReadOnly(True)
|
||||||
|
self.txt_app.setObjectName("txt_app")
|
||||||
|
self.grid_app.addWidget(self.txt_app, 1, 0, 1, 2)
|
||||||
|
self.grid_app.setColumnStretch(0, 1)
|
||||||
|
self.gridLayout_3.addWidget(self.splitter, 0, 0, 1, 2)
|
||||||
|
win_revpilogfile.setCentralWidget(self.centralwidget)
|
||||||
|
|
||||||
|
self.retranslateUi(win_revpilogfile)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(win_revpilogfile)
|
||||||
|
|
||||||
|
def retranslateUi(self, win_revpilogfile):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
win_revpilogfile.setWindowTitle(_translate("win_revpilogfile", "RevPi Python PLC Logfiles"))
|
||||||
|
self.cbx_stay_on_top.setText(_translate("win_revpilogfile", "Stay on top of all windows"))
|
||||||
|
self.cbx_wrap.setText(_translate("win_revpilogfile", "Linewrap"))
|
||||||
|
self.lbl_daemon.setText(_translate("win_revpilogfile", "RevPiPyLoad - Logfile"))
|
||||||
|
self.btn_daemon.setText(_translate("win_revpilogfile", "Clear view"))
|
||||||
|
self.btn_app.setText(_translate("win_revpilogfile", "Clear view"))
|
||||||
|
self.lbl_app.setText(_translate("win_revpilogfile", "Python PLC program - Logfile"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
win_revpilogfile = QtWidgets.QMainWindow()
|
||||||
|
ui = Ui_win_revpilogfile()
|
||||||
|
ui.setupUi(win_revpilogfile)
|
||||||
|
win_revpilogfile.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
144
include/ui/revpioption_ui.py
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpioption.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_options(object):
|
||||||
|
def setupUi(self, diag_options):
|
||||||
|
diag_options.setObjectName("diag_options")
|
||||||
|
diag_options.resize(452, 503)
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(diag_options)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.gb_plc = QtWidgets.QGroupBox(diag_options)
|
||||||
|
self.gb_plc.setObjectName("gb_plc")
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(self.gb_plc)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.lbl_replace_io = QtWidgets.QLabel(self.gb_plc)
|
||||||
|
self.lbl_replace_io.setObjectName("lbl_replace_io")
|
||||||
|
self.gridLayout.addWidget(self.lbl_replace_io, 6, 0, 1, 1)
|
||||||
|
self.sbx_autoreloaddelay = QtWidgets.QSpinBox(self.gb_plc)
|
||||||
|
self.sbx_autoreloaddelay.setMinimum(5)
|
||||||
|
self.sbx_autoreloaddelay.setMaximum(120)
|
||||||
|
self.sbx_autoreloaddelay.setObjectName("sbx_autoreloaddelay")
|
||||||
|
self.gridLayout.addWidget(self.sbx_autoreloaddelay, 2, 2, 1, 1)
|
||||||
|
self.cbx_zeroonexit = QtWidgets.QCheckBox(self.gb_plc)
|
||||||
|
self.cbx_zeroonexit.setObjectName("cbx_zeroonexit")
|
||||||
|
self.gridLayout.addWidget(self.cbx_zeroonexit, 4, 1, 1, 2)
|
||||||
|
self.cbx_zeroonerror = QtWidgets.QCheckBox(self.gb_plc)
|
||||||
|
self.cbx_zeroonerror.setObjectName("cbx_zeroonerror")
|
||||||
|
self.gridLayout.addWidget(self.cbx_zeroonerror, 5, 1, 1, 2)
|
||||||
|
self.cbx_autostart = QtWidgets.QCheckBox(self.gb_plc)
|
||||||
|
self.cbx_autostart.setObjectName("cbx_autostart")
|
||||||
|
self.gridLayout.addWidget(self.cbx_autostart, 0, 0, 1, 3)
|
||||||
|
self.cbx_autoreload = QtWidgets.QCheckBox(self.gb_plc)
|
||||||
|
self.cbx_autoreload.setObjectName("cbx_autoreload")
|
||||||
|
self.gridLayout.addWidget(self.cbx_autoreload, 1, 0, 1, 3)
|
||||||
|
self.lbl_plc_zero = QtWidgets.QLabel(self.gb_plc)
|
||||||
|
self.lbl_plc_zero.setObjectName("lbl_plc_zero")
|
||||||
|
self.gridLayout.addWidget(self.lbl_plc_zero, 3, 0, 1, 3)
|
||||||
|
self.txt_replace_io = QtWidgets.QLineEdit(self.gb_plc)
|
||||||
|
self.txt_replace_io.setObjectName("txt_replace_io")
|
||||||
|
self.gridLayout.addWidget(self.txt_replace_io, 7, 1, 1, 2)
|
||||||
|
self.cbb_replace_io = QtWidgets.QComboBox(self.gb_plc)
|
||||||
|
self.cbb_replace_io.setObjectName("cbb_replace_io")
|
||||||
|
self.cbb_replace_io.addItem("")
|
||||||
|
self.cbb_replace_io.addItem("")
|
||||||
|
self.cbb_replace_io.addItem("")
|
||||||
|
self.cbb_replace_io.addItem("")
|
||||||
|
self.gridLayout.addWidget(self.cbb_replace_io, 6, 1, 1, 2)
|
||||||
|
self.lbl_plc_delay = QtWidgets.QLabel(self.gb_plc)
|
||||||
|
self.lbl_plc_delay.setObjectName("lbl_plc_delay")
|
||||||
|
self.gridLayout.addWidget(self.lbl_plc_delay, 2, 0, 1, 2)
|
||||||
|
self.verticalLayout.addWidget(self.gb_plc)
|
||||||
|
self.gb_server = QtWidgets.QGroupBox(diag_options)
|
||||||
|
self.gb_server.setObjectName("gb_server")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.gb_server)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.btn_aclplcslave = QtWidgets.QPushButton(self.gb_server)
|
||||||
|
self.btn_aclplcslave.setObjectName("btn_aclplcslave")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_aclplcslave, 0, 1, 1, 1)
|
||||||
|
self.cbx_mqtt = QtWidgets.QCheckBox(self.gb_server)
|
||||||
|
self.cbx_mqtt.setObjectName("cbx_mqtt")
|
||||||
|
self.gridLayout_2.addWidget(self.cbx_mqtt, 2, 0, 1, 1)
|
||||||
|
self.cbx_plcslave = QtWidgets.QCheckBox(self.gb_server)
|
||||||
|
self.cbx_plcslave.setObjectName("cbx_plcslave")
|
||||||
|
self.gridLayout_2.addWidget(self.cbx_plcslave, 0, 0, 1, 1)
|
||||||
|
self.lbl_slave_status = QtWidgets.QLabel(self.gb_server)
|
||||||
|
self.lbl_slave_status.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.lbl_slave_status.setObjectName("lbl_slave_status")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_slave_status, 1, 1, 1, 1)
|
||||||
|
self.lbl_lbl_slave_status = QtWidgets.QLabel(self.gb_server)
|
||||||
|
self.lbl_lbl_slave_status.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
|
self.lbl_lbl_slave_status.setObjectName("lbl_lbl_slave_status")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_lbl_slave_status, 1, 0, 1, 1)
|
||||||
|
self.lbl_mqtt_status = QtWidgets.QLabel(self.gb_server)
|
||||||
|
self.lbl_mqtt_status.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.lbl_mqtt_status.setObjectName("lbl_mqtt_status")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_mqtt_status, 3, 1, 1, 1)
|
||||||
|
self.lbl_lbl_mqtt_status = QtWidgets.QLabel(self.gb_server)
|
||||||
|
self.lbl_lbl_mqtt_status.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
|
||||||
|
self.lbl_lbl_mqtt_status.setObjectName("lbl_lbl_mqtt_status")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_lbl_mqtt_status, 3, 0, 1, 1)
|
||||||
|
self.btn_mqtt = QtWidgets.QPushButton(self.gb_server)
|
||||||
|
self.btn_mqtt.setObjectName("btn_mqtt")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_mqtt, 2, 1, 1, 1)
|
||||||
|
self.btn_aclxmlrpc = QtWidgets.QPushButton(self.gb_server)
|
||||||
|
self.btn_aclxmlrpc.setObjectName("btn_aclxmlrpc")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_aclxmlrpc, 4, 1, 1, 1)
|
||||||
|
self.cbx_xmlrpc = QtWidgets.QCheckBox(self.gb_server)
|
||||||
|
self.cbx_xmlrpc.setObjectName("cbx_xmlrpc")
|
||||||
|
self.gridLayout_2.addWidget(self.cbx_xmlrpc, 4, 0, 1, 1)
|
||||||
|
self.verticalLayout.addWidget(self.gb_server)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_options)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.verticalLayout.addWidget(self.btn_box)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_options)
|
||||||
|
self.btn_box.accepted.connect(diag_options.accept)
|
||||||
|
self.btn_box.rejected.connect(diag_options.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_options)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_options):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_options.setWindowTitle(_translate("diag_options", "RevPi Python PLC Options"))
|
||||||
|
self.gb_plc.setTitle(_translate("diag_options", "Start / Stop behavior of PLC program"))
|
||||||
|
self.lbl_replace_io.setText(_translate("diag_options", "Replace IO file:"))
|
||||||
|
self.cbx_zeroonexit.setText(_translate("diag_options", "... sucessfully without error"))
|
||||||
|
self.cbx_zeroonerror.setText(_translate("diag_options", "... after exception and errors"))
|
||||||
|
self.cbx_autostart.setText(_translate("diag_options", "Start PLC program automatically"))
|
||||||
|
self.cbx_autoreload.setText(_translate("diag_options", "Restart PLC program after exit or crash"))
|
||||||
|
self.lbl_plc_zero.setText(_translate("diag_options", "Set process image to NULL if program terminates..."))
|
||||||
|
self.cbb_replace_io.setItemText(0, _translate("diag_options", "Do not use replace io file"))
|
||||||
|
self.cbb_replace_io.setItemText(1, _translate("diag_options", "Use static file from RevPiPyLoad"))
|
||||||
|
self.cbb_replace_io.setItemText(2, _translate("diag_options", "Use dynamic file from work directory"))
|
||||||
|
self.cbb_replace_io.setItemText(3, _translate("diag_options", "Give own path and filename"))
|
||||||
|
self.lbl_plc_delay.setText(_translate("diag_options", "Restart delay in seconds:"))
|
||||||
|
self.gb_server.setTitle(_translate("diag_options", "RevPiPyLoad server services"))
|
||||||
|
self.btn_aclplcslave.setText(_translate("diag_options", "Edit ACL"))
|
||||||
|
self.cbx_mqtt.setText(_translate("diag_options", "MQTT process image publisher"))
|
||||||
|
self.cbx_plcslave.setText(_translate("diag_options", "Start RevPi piControl server"))
|
||||||
|
self.lbl_slave_status.setText(_translate("diag_options", "status"))
|
||||||
|
self.lbl_lbl_slave_status.setText(_translate("diag_options", "piControl server is:"))
|
||||||
|
self.lbl_mqtt_status.setText(_translate("diag_options", "status"))
|
||||||
|
self.lbl_lbl_mqtt_status.setText(_translate("diag_options", "MQTT publish service is:"))
|
||||||
|
self.btn_mqtt.setText(_translate("diag_options", "Settings"))
|
||||||
|
self.btn_aclxmlrpc.setText(_translate("diag_options", "Edit ACL"))
|
||||||
|
self.cbx_xmlrpc.setText(_translate("diag_options", "Activate XML-RPC for RevPiPyControl"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_options = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_options()
|
||||||
|
ui.setupUi(diag_options)
|
||||||
|
diag_options.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
126
include/ui/revpiplclist_ui.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpiplclist.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_connections(object):
|
||||||
|
def setupUi(self, diag_connections):
|
||||||
|
diag_connections.setObjectName("diag_connections")
|
||||||
|
diag_connections.resize(520, 508)
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(diag_connections)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.tre_connections = QtWidgets.QTreeWidget(diag_connections)
|
||||||
|
self.tre_connections.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
|
||||||
|
self.tre_connections.setObjectName("tre_connections")
|
||||||
|
self.gridLayout.addWidget(self.tre_connections, 0, 0, 1, 1)
|
||||||
|
self.vl_edit = QtWidgets.QVBoxLayout()
|
||||||
|
self.vl_edit.setObjectName("vl_edit")
|
||||||
|
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
self.vl_edit.addItem(spacerItem)
|
||||||
|
self.btn_up = QtWidgets.QPushButton(diag_connections)
|
||||||
|
self.btn_up.setText("")
|
||||||
|
icon = QtGui.QIcon()
|
||||||
|
icon.addPixmap(QtGui.QPixmap(":/action/ico/arrow-up.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_up.setIcon(icon)
|
||||||
|
self.btn_up.setObjectName("btn_up")
|
||||||
|
self.vl_edit.addWidget(self.btn_up)
|
||||||
|
self.btn_down = QtWidgets.QPushButton(diag_connections)
|
||||||
|
self.btn_down.setText("")
|
||||||
|
icon1 = QtGui.QIcon()
|
||||||
|
icon1.addPixmap(QtGui.QPixmap(":/action/ico/arrow-down.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_down.setIcon(icon1)
|
||||||
|
self.btn_down.setObjectName("btn_down")
|
||||||
|
self.vl_edit.addWidget(self.btn_down)
|
||||||
|
self.btn_delete = QtWidgets.QPushButton(diag_connections)
|
||||||
|
self.btn_delete.setText("")
|
||||||
|
icon2 = QtGui.QIcon()
|
||||||
|
icon2.addPixmap(QtGui.QPixmap(":/action/ico/edit-delete.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_delete.setIcon(icon2)
|
||||||
|
self.btn_delete.setObjectName("btn_delete")
|
||||||
|
self.vl_edit.addWidget(self.btn_delete)
|
||||||
|
self.btn_add = QtWidgets.QPushButton(diag_connections)
|
||||||
|
self.btn_add.setText("")
|
||||||
|
icon3 = QtGui.QIcon()
|
||||||
|
icon3.addPixmap(QtGui.QPixmap(":/action/ico/edit-add.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
|
self.btn_add.setIcon(icon3)
|
||||||
|
self.btn_add.setObjectName("btn_add")
|
||||||
|
self.vl_edit.addWidget(self.btn_add)
|
||||||
|
self.gridLayout.addLayout(self.vl_edit, 0, 1, 1, 1)
|
||||||
|
self.gb_properties = QtWidgets.QGroupBox(diag_connections)
|
||||||
|
self.gb_properties.setObjectName("gb_properties")
|
||||||
|
self.formLayout = QtWidgets.QFormLayout(self.gb_properties)
|
||||||
|
self.formLayout.setObjectName("formLayout")
|
||||||
|
self.lbl_name = QtWidgets.QLabel(self.gb_properties)
|
||||||
|
self.lbl_name.setObjectName("lbl_name")
|
||||||
|
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.lbl_name)
|
||||||
|
self.lbl_folder = QtWidgets.QLabel(self.gb_properties)
|
||||||
|
self.lbl_folder.setObjectName("lbl_folder")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_folder)
|
||||||
|
self.lbl_address = QtWidgets.QLabel(self.gb_properties)
|
||||||
|
self.lbl_address.setObjectName("lbl_address")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.lbl_address)
|
||||||
|
self.lbl_port = QtWidgets.QLabel(self.gb_properties)
|
||||||
|
self.lbl_port.setObjectName("lbl_port")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_port)
|
||||||
|
self.txt_name = QtWidgets.QLineEdit(self.gb_properties)
|
||||||
|
self.txt_name.setObjectName("txt_name")
|
||||||
|
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.txt_name)
|
||||||
|
self.txt_address = QtWidgets.QLineEdit(self.gb_properties)
|
||||||
|
self.txt_address.setObjectName("txt_address")
|
||||||
|
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txt_address)
|
||||||
|
self.sbx_port = QtWidgets.QSpinBox(self.gb_properties)
|
||||||
|
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||||
|
sizePolicy.setHorizontalStretch(0)
|
||||||
|
sizePolicy.setVerticalStretch(0)
|
||||||
|
sizePolicy.setHeightForWidth(self.sbx_port.sizePolicy().hasHeightForWidth())
|
||||||
|
self.sbx_port.setSizePolicy(sizePolicy)
|
||||||
|
self.sbx_port.setMinimum(1)
|
||||||
|
self.sbx_port.setMaximum(65535)
|
||||||
|
self.sbx_port.setProperty("value", 55123)
|
||||||
|
self.sbx_port.setObjectName("sbx_port")
|
||||||
|
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.sbx_port)
|
||||||
|
self.cbb_folder = QtWidgets.QComboBox(self.gb_properties)
|
||||||
|
self.cbb_folder.setEditable(True)
|
||||||
|
self.cbb_folder.setObjectName("cbb_folder")
|
||||||
|
self.cbb_folder.addItem("")
|
||||||
|
self.cbb_folder.setItemText(0, "")
|
||||||
|
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.cbb_folder)
|
||||||
|
self.gridLayout.addWidget(self.gb_properties, 1, 0, 1, 2)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_connections)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Discard|QtWidgets.QDialogButtonBox.Save)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.gridLayout.addWidget(self.btn_box, 2, 0, 1, 2)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_connections)
|
||||||
|
self.btn_box.accepted.connect(diag_connections.accept)
|
||||||
|
self.btn_box.rejected.connect(diag_connections.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_connections)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_connections):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_connections.setWindowTitle(_translate("diag_connections", "Revolution Pi connections"))
|
||||||
|
self.tre_connections.headerItem().setText(0, _translate("diag_connections", "Connection name"))
|
||||||
|
self.tre_connections.headerItem().setText(1, _translate("diag_connections", "Address"))
|
||||||
|
self.gb_properties.setTitle(_translate("diag_connections", "Connection properties"))
|
||||||
|
self.lbl_name.setText(_translate("diag_connections", "Display name:"))
|
||||||
|
self.lbl_folder.setText(_translate("diag_connections", "Sub folder:"))
|
||||||
|
self.lbl_address.setText(_translate("diag_connections", "Address (DNS/IP):"))
|
||||||
|
self.lbl_port.setText(_translate("diag_connections", "Port (Default {0}):"))
|
||||||
|
|
||||||
|
from . import ressources_rc
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_connections = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_connections()
|
||||||
|
ui.setupUi(diag_connections)
|
||||||
|
diag_connections.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
152
include/ui/revpiprogram_ui.py
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'revpiprogram.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.10.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
class Ui_diag_program(object):
|
||||||
|
def setupUi(self, diag_program):
|
||||||
|
diag_program.setObjectName("diag_program")
|
||||||
|
diag_program.resize(488, 500)
|
||||||
|
self.verticalLayout = QtWidgets.QVBoxLayout(diag_program)
|
||||||
|
self.verticalLayout.setObjectName("verticalLayout")
|
||||||
|
self.gb_plc = QtWidgets.QGroupBox(diag_program)
|
||||||
|
self.gb_plc.setObjectName("gb_plc")
|
||||||
|
self.gridLayout = QtWidgets.QGridLayout(self.gb_plc)
|
||||||
|
self.gridLayout.setObjectName("gridLayout")
|
||||||
|
self.rbn_pythonversion_2 = QtWidgets.QRadioButton(self.gb_plc)
|
||||||
|
self.rbn_pythonversion_2.setObjectName("rbn_pythonversion_2")
|
||||||
|
self.gridLayout.addWidget(self.rbn_pythonversion_2, 3, 1, 1, 1)
|
||||||
|
self.txt_plcarguments = QtWidgets.QLineEdit(self.gb_plc)
|
||||||
|
self.txt_plcarguments.setObjectName("txt_plcarguments")
|
||||||
|
self.gridLayout.addWidget(self.txt_plcarguments, 2, 1, 1, 2)
|
||||||
|
self.rbn_pythonversion_3 = QtWidgets.QRadioButton(self.gb_plc)
|
||||||
|
self.rbn_pythonversion_3.setObjectName("rbn_pythonversion_3")
|
||||||
|
self.gridLayout.addWidget(self.rbn_pythonversion_3, 3, 2, 1, 1)
|
||||||
|
self.lbl_plcprogram = QtWidgets.QLabel(self.gb_plc)
|
||||||
|
self.lbl_plcprogram.setObjectName("lbl_plcprogram")
|
||||||
|
self.gridLayout.addWidget(self.lbl_plcprogram, 0, 0, 1, 3)
|
||||||
|
self.cbx_plcworkdir_set_uid = QtWidgets.QCheckBox(self.gb_plc)
|
||||||
|
self.cbx_plcworkdir_set_uid.setObjectName("cbx_plcworkdir_set_uid")
|
||||||
|
self.gridLayout.addWidget(self.cbx_plcworkdir_set_uid, 4, 0, 1, 3)
|
||||||
|
self.cbb_plcprogram = QtWidgets.QComboBox(self.gb_plc)
|
||||||
|
self.cbb_plcprogram.setObjectName("cbb_plcprogram")
|
||||||
|
self.gridLayout.addWidget(self.cbb_plcprogram, 1, 0, 1, 3)
|
||||||
|
self.lbl_pythonversion = QtWidgets.QLabel(self.gb_plc)
|
||||||
|
self.lbl_pythonversion.setObjectName("lbl_pythonversion")
|
||||||
|
self.gridLayout.addWidget(self.lbl_pythonversion, 3, 0, 1, 1)
|
||||||
|
self.lbl_plcarguments = QtWidgets.QLabel(self.gb_plc)
|
||||||
|
self.lbl_plcarguments.setObjectName("lbl_plcarguments")
|
||||||
|
self.gridLayout.addWidget(self.lbl_plcarguments, 2, 0, 1, 1)
|
||||||
|
self.verticalLayout.addWidget(self.gb_plc)
|
||||||
|
self.cb_transfair = QtWidgets.QGroupBox(diag_program)
|
||||||
|
self.cb_transfair.setObjectName("cb_transfair")
|
||||||
|
self.gridLayout_2 = QtWidgets.QGridLayout(self.cb_transfair)
|
||||||
|
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||||
|
self.cbb_format = QtWidgets.QComboBox(self.cb_transfair)
|
||||||
|
self.cbb_format.setObjectName("cbb_format")
|
||||||
|
self.cbb_format.addItem("")
|
||||||
|
self.cbb_format.addItem("")
|
||||||
|
self.cbb_format.addItem("")
|
||||||
|
self.cbb_format.addItem("")
|
||||||
|
self.gridLayout_2.addWidget(self.cbb_format, 0, 1, 1, 1)
|
||||||
|
self.btn_program_upload = QtWidgets.QPushButton(self.cb_transfair)
|
||||||
|
self.btn_program_upload.setObjectName("btn_program_upload")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_program_upload, 3, 1, 1, 1)
|
||||||
|
self.btn_program_download = QtWidgets.QPushButton(self.cb_transfair)
|
||||||
|
self.btn_program_download.setObjectName("btn_program_download")
|
||||||
|
self.gridLayout_2.addWidget(self.btn_program_download, 3, 0, 1, 1)
|
||||||
|
self.lbl_format = QtWidgets.QLabel(self.cb_transfair)
|
||||||
|
self.lbl_format.setObjectName("lbl_format")
|
||||||
|
self.gridLayout_2.addWidget(self.lbl_format, 0, 0, 1, 1)
|
||||||
|
self.cbx_pictory = QtWidgets.QCheckBox(self.cb_transfair)
|
||||||
|
self.cbx_pictory.setObjectName("cbx_pictory")
|
||||||
|
self.gridLayout_2.addWidget(self.cbx_pictory, 1, 0, 1, 2)
|
||||||
|
self.cbx_clear = QtWidgets.QCheckBox(self.cb_transfair)
|
||||||
|
self.cbx_clear.setObjectName("cbx_clear")
|
||||||
|
self.gridLayout_2.addWidget(self.cbx_clear, 2, 0, 1, 2)
|
||||||
|
self.verticalLayout.addWidget(self.cb_transfair)
|
||||||
|
self.gb_control = QtWidgets.QGroupBox(diag_program)
|
||||||
|
self.gb_control.setObjectName("gb_control")
|
||||||
|
self.gridLayout_3 = QtWidgets.QGridLayout(self.gb_control)
|
||||||
|
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||||
|
self.btn_procimg_download = QtWidgets.QPushButton(self.gb_control)
|
||||||
|
self.btn_procimg_download.setObjectName("btn_procimg_download")
|
||||||
|
self.gridLayout_3.addWidget(self.btn_procimg_download, 1, 1, 1, 1)
|
||||||
|
self.btn_pictory_download = QtWidgets.QPushButton(self.gb_control)
|
||||||
|
self.btn_pictory_download.setObjectName("btn_pictory_download")
|
||||||
|
self.gridLayout_3.addWidget(self.btn_pictory_download, 0, 1, 1, 1)
|
||||||
|
self.btn_pictory_upload = QtWidgets.QPushButton(self.gb_control)
|
||||||
|
self.btn_pictory_upload.setObjectName("btn_pictory_upload")
|
||||||
|
self.gridLayout_3.addWidget(self.btn_pictory_upload, 0, 2, 1, 1)
|
||||||
|
self.lbl_pictory = QtWidgets.QLabel(self.gb_control)
|
||||||
|
self.lbl_pictory.setObjectName("lbl_pictory")
|
||||||
|
self.gridLayout_3.addWidget(self.lbl_pictory, 0, 0, 1, 1)
|
||||||
|
self.lbl_procimg = QtWidgets.QLabel(self.gb_control)
|
||||||
|
self.lbl_procimg.setObjectName("lbl_procimg")
|
||||||
|
self.gridLayout_3.addWidget(self.lbl_procimg, 1, 0, 1, 1)
|
||||||
|
self.verticalLayout.addWidget(self.gb_control)
|
||||||
|
self.btn_box = QtWidgets.QDialogButtonBox(diag_program)
|
||||||
|
self.btn_box.setOrientation(QtCore.Qt.Horizontal)
|
||||||
|
self.btn_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||||
|
self.btn_box.setObjectName("btn_box")
|
||||||
|
self.verticalLayout.addWidget(self.btn_box)
|
||||||
|
|
||||||
|
self.retranslateUi(diag_program)
|
||||||
|
self.btn_box.accepted.connect(diag_program.accept)
|
||||||
|
self.btn_box.rejected.connect(diag_program.reject)
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(diag_program)
|
||||||
|
diag_program.setTabOrder(self.cbb_plcprogram, self.txt_plcarguments)
|
||||||
|
diag_program.setTabOrder(self.txt_plcarguments, self.rbn_pythonversion_2)
|
||||||
|
diag_program.setTabOrder(self.rbn_pythonversion_2, self.rbn_pythonversion_3)
|
||||||
|
diag_program.setTabOrder(self.rbn_pythonversion_3, self.cbx_plcworkdir_set_uid)
|
||||||
|
diag_program.setTabOrder(self.cbx_plcworkdir_set_uid, self.cbb_format)
|
||||||
|
diag_program.setTabOrder(self.cbb_format, self.cbx_pictory)
|
||||||
|
diag_program.setTabOrder(self.cbx_pictory, self.cbx_clear)
|
||||||
|
diag_program.setTabOrder(self.cbx_clear, self.btn_program_download)
|
||||||
|
diag_program.setTabOrder(self.btn_program_download, self.btn_program_upload)
|
||||||
|
diag_program.setTabOrder(self.btn_program_upload, self.btn_pictory_download)
|
||||||
|
diag_program.setTabOrder(self.btn_pictory_download, self.btn_pictory_upload)
|
||||||
|
diag_program.setTabOrder(self.btn_pictory_upload, self.btn_procimg_download)
|
||||||
|
|
||||||
|
def retranslateUi(self, diag_program):
|
||||||
|
_translate = QtCore.QCoreApplication.translate
|
||||||
|
diag_program.setWindowTitle(_translate("diag_program", "PLC program"))
|
||||||
|
self.gb_plc.setTitle(_translate("diag_program", "PLC program"))
|
||||||
|
self.rbn_pythonversion_2.setText(_translate("diag_program", "Python 2"))
|
||||||
|
self.rbn_pythonversion_3.setText(_translate("diag_program", "Python 3"))
|
||||||
|
self.lbl_plcprogram.setText(_translate("diag_program", "Python PLC start program:"))
|
||||||
|
self.cbx_plcworkdir_set_uid.setText(_translate("diag_program", "Set write permissions for plc program to workdirectory"))
|
||||||
|
self.lbl_pythonversion.setText(_translate("diag_program", "Python version:"))
|
||||||
|
self.lbl_plcarguments.setText(_translate("diag_program", "Program arguments:"))
|
||||||
|
self.cb_transfair.setTitle(_translate("diag_program", "Transfair PLC program"))
|
||||||
|
self.cbb_format.setItemText(0, _translate("diag_program", "Files"))
|
||||||
|
self.cbb_format.setItemText(1, _translate("diag_program", "Folder"))
|
||||||
|
self.cbb_format.setItemText(2, _translate("diag_program", "ZIP archive"))
|
||||||
|
self.cbb_format.setItemText(3, _translate("diag_program", "TGZ archive"))
|
||||||
|
self.btn_program_upload.setText(_translate("diag_program", "Upload"))
|
||||||
|
self.btn_program_download.setText(_translate("diag_program", "Download"))
|
||||||
|
self.lbl_format.setText(_translate("diag_program", "Transfair format:"))
|
||||||
|
self.cbx_pictory.setText(_translate("diag_program", "Including piCtory configuration"))
|
||||||
|
self.cbx_clear.setText(_translate("diag_program", "Remove all files on Revolution Pi before upload"))
|
||||||
|
self.gb_control.setTitle(_translate("diag_program", "Control files"))
|
||||||
|
self.btn_procimg_download.setText(_translate("diag_program", "Download"))
|
||||||
|
self.btn_pictory_download.setText(_translate("diag_program", "Download"))
|
||||||
|
self.btn_pictory_upload.setText(_translate("diag_program", "Upload"))
|
||||||
|
self.lbl_pictory.setText(_translate("diag_program", "piCtory configuraiton"))
|
||||||
|
self.lbl_procimg.setText(_translate("diag_program", "Process image from piControl0"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
diag_program = QtWidgets.QDialog()
|
||||||
|
ui = Ui_diag_program()
|
||||||
|
ui.setupUi(diag_program)
|
||||||
|
diag_program.show()
|
||||||
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
258
include/ui_dev/aclmanager.ui
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_aclmanager</class>
|
||||||
|
<widget class="QDialog" name="diag_aclmanager">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>454</width>
|
||||||
|
<height>572</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>IP access control list</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_acls">
|
||||||
|
<property name="title">
|
||||||
|
<string>Existing ACLs</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QTableWidget" name="tb_acls">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="tabKeyNavigation">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="cornerButtonEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="horizontalHeaderHighlightSections">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderStretchLastSection">
|
||||||
|
<bool>true</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>IP Address</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Access Level</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="hl_acls">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_edit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Edit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_remove">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_edit">
|
||||||
|
<property name="title">
|
||||||
|
<string>Add / Edit access entry</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_clear">
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear fields</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_add">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Save entry</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<layout class="QFormLayout" name="fl_edit">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_ip">
|
||||||
|
<property name="text">
|
||||||
|
<string>IP address:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_level">
|
||||||
|
<property name="text">
|
||||||
|
<string>Access level:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="cbb_level"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="hl_ip">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="txt_ip_a">
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_ip_a">
|
||||||
|
<property name="text">
|
||||||
|
<string>.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="txt_ip_b">
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_ip_b">
|
||||||
|
<property name="text">
|
||||||
|
<string>.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="txt_ip_c">
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_ip_c">
|
||||||
|
<property name="text">
|
||||||
|
<string>.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="txt_ip_d">
|
||||||
|
<property name="maxLength">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_level_info">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>tb_acls</tabstop>
|
||||||
|
<tabstop>btn_edit</tabstop>
|
||||||
|
<tabstop>btn_remove</tabstop>
|
||||||
|
<tabstop>txt_ip_a</tabstop>
|
||||||
|
<tabstop>txt_ip_b</tabstop>
|
||||||
|
<tabstop>txt_ip_c</tabstop>
|
||||||
|
<tabstop>txt_ip_d</tabstop>
|
||||||
|
<tabstop>cbb_level</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>diag_aclmanager</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>247</x>
|
||||||
|
<y>538</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_aclmanager</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>315</x>
|
||||||
|
<y>538</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
145
include/ui_dev/avahisearch.ui
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_search</class>
|
||||||
|
<widget class="QDialog" name="diag_search">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>480</width>
|
||||||
|
<height>360</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Search Revolution Pi devices</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<layout class="QHBoxLayout" name="hl_header">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_search">
|
||||||
|
<property name="text">
|
||||||
|
<string>Searching for Revolution Pi devices in your network...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_restart">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Restart search</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/reload.ico</normaloff>:/action/ico/reload.ico</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QTableWidget" name="tb_revpi">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="tabKeyNavigation">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::SingleSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="cornerButtonEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="horizontalHeaderHighlightSections">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderStretchLastSection">
|
||||||
|
<bool>true</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Zero conf Name</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>IP address</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_connect">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Connect to Revolution Pi</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_save">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Save connection</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ressources.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_search</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>239</x>
|
||||||
|
<y>338</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>239</x>
|
||||||
|
<y>179</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
126
include/ui_dev/debugcontrol.ui
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>wid_debugcontrol</class>
|
||||||
|
<widget class="QWidget" name="wid_debugcontrol">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>402</width>
|
||||||
|
<height>214</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="gb_devices">
|
||||||
|
<property name="title">
|
||||||
|
<string>Revolution Pi devices</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="vl_devices"/>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="cbx_stay_on_top">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open to stay on top</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" rowspan="2">
|
||||||
|
<widget class="QGroupBox" name="gb_control">
|
||||||
|
<property name="title">
|
||||||
|
<string>IO Control</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_read_io">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Read all IO values and discard local changes (F4)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Read &all IO values</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F4</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_refresh_io">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Refresh all IO values which are locally not changed (F5)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Refresh unchanged IOs</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F5</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_write_o">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Write locally changed output values to process image (F6)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Write changed outputs</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F6</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="cbx_refresh">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Auto refresh values</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="cbx_write">
|
||||||
|
<property name="text">
|
||||||
|
<string>and write outputs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>1</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
110
include/ui_dev/debugios.ui
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>win_debugios</class>
|
||||||
|
<widget class="QMainWindow" name="win_debugios">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>434</width>
|
||||||
|
<height>343</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_io">
|
||||||
|
<property name="title">
|
||||||
|
<string>{0}: Inputs | Outputs</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="childrenCollapsible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QScrollArea" name="sca_inp">
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="saw_inp">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>201</width>
|
||||||
|
<height>275</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="form_inp">
|
||||||
|
<property name="labelAlignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QScrollArea" name="sca_out">
|
||||||
|
<property name="lineWidth">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="saw_out">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>201</width>
|
||||||
|
<height>275</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="form_out">
|
||||||
|
<property name="labelAlignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="stat_bar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
BIN
include/ui_dev/ico/arrow-down.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/arrow-up.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/cpu.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/edit-add.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/edit-delete.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/file-else.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/file-python.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/folder-open.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/folder.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/refresh.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/reload.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
include/ui_dev/ico/revpipycontrol.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
257
include/ui_dev/mqttmanager.ui
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_mqtt</class>
|
||||||
|
<widget class="QDialog" name="diag_mqtt">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>492</width>
|
||||||
|
<height>704</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MQTT settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_basetopic">
|
||||||
|
<property name="title">
|
||||||
|
<string>Base topic</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="0" colspan="4">
|
||||||
|
<widget class="QLabel" name="lbl_basetopic_description">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>450</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>The base topic is the first part of any mqtt topic, the Revolution Pi will publish. You can use any character includig '/' to structure the messages on your broker.</p><p>For example: revpi0000/data</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_basetopic">
|
||||||
|
<property name="text">
|
||||||
|
<string>Base topic:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_basetopic"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_send_on_event">
|
||||||
|
<property name="title">
|
||||||
|
<string>Publish settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_sendinterval">
|
||||||
|
<property name="text">
|
||||||
|
<string>Publish all exported values every n seconds:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QSpinBox" name="sbx_sendinterval">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>21600</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="lbl_topic_io">
|
||||||
|
<property name="text">
|
||||||
|
<string>Topic: [basetopic]/io/[ioname]</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="cbx_send_on_event">
|
||||||
|
<property name="text">
|
||||||
|
<string>Send exported values immediately on value change</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="lbl_topic_event">
|
||||||
|
<property name="text">
|
||||||
|
<string>Topic: [basetopic]/event/[ioname]</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_write_outputs">
|
||||||
|
<property name="title">
|
||||||
|
<string>Set outputs</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_write_outputs">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>450</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>The Revolution Pi will subscribe a topic on which your mqtt client can publish messages with the new io value as payload.
|
||||||
|
|
||||||
|
Publish values with topic: [basetopic]/set/[outputname]</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="cbx_write_outputs">
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow MQTT to to set outputs on Revolution Pi</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_broker">
|
||||||
|
<property name="title">
|
||||||
|
<string>Brocker settings</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_broker_address">
|
||||||
|
<property name="text">
|
||||||
|
<string>Broker address:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_broker_address"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_port">
|
||||||
|
<property name="text">
|
||||||
|
<string>Broker port:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_username">
|
||||||
|
<property name="text">
|
||||||
|
<string>User name:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_password">
|
||||||
|
<property name="text">
|
||||||
|
<string>Password:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_client_id">
|
||||||
|
<property name="text">
|
||||||
|
<string>Client ID:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_username"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_password"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_client_id"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="sbx_port">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="cbx_tls_set">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use TLS</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>diag_mqtt</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_mqtt</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
20
include/ui_dev/ressources.qrc
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="main">
|
||||||
|
<file>ico/cpu.ico</file>
|
||||||
|
<file>ico/folder.ico</file>
|
||||||
|
<file>ico/revpipycontrol.ico</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="file">
|
||||||
|
<file>ico/file-else.ico</file>
|
||||||
|
<file>ico/file-python.ico</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="action">
|
||||||
|
<file>ico/folder-open.ico</file>
|
||||||
|
<file>ico/refresh.ico</file>
|
||||||
|
<file>ico/reload.ico</file>
|
||||||
|
<file>ico/arrow-down.ico</file>
|
||||||
|
<file>ico/arrow-up.ico</file>
|
||||||
|
<file>ico/edit-add.ico</file>
|
||||||
|
<file>ico/edit-delete.ico</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
264
include/ui_dev/revpicommander.ui
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<author>Sven Sager</author>
|
||||||
|
<class>win_revpicommander</class>
|
||||||
|
<widget class="QMainWindow" name="win_revpicommander">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>318</width>
|
||||||
|
<height>273</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>RevPi Python PLC Commander</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/main/ico/revpipycontrol.ico</normaloff>:/main/ico/revpipycontrol.ico</iconset>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QGridLayout" name="gl">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLineEdit" name="txt_connection">
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::NoFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_plc_start">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC &start</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_plc_stop">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC s&top</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_plc_restart">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC restart</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_plc_logs">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC &logs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLineEdit" name="txt_status">
|
||||||
|
<property name="focusPolicy">
|
||||||
|
<enum>Qt::NoFocus</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_plc_debug">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC watch &mode</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>318</width>
|
||||||
|
<height>22</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<widget class="QMenu" name="men_file">
|
||||||
|
<property name="title">
|
||||||
|
<string>&File</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="act_connections"/>
|
||||||
|
<addaction name="act_search"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="act_simulator"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="act_quit"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="men_help">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Help</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="act_webpage"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="act_info"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="men_plc">
|
||||||
|
<property name="title">
|
||||||
|
<string>&PLC</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="act_logs"/>
|
||||||
|
<addaction name="act_options"/>
|
||||||
|
<addaction name="act_program"/>
|
||||||
|
<addaction name="act_developer"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="act_pictory"/>
|
||||||
|
<addaction name="act_reset"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="act_disconnect"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenu" name="men_connections">
|
||||||
|
<property name="title">
|
||||||
|
<string>&Connections</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<addaction name="men_file"/>
|
||||||
|
<addaction name="men_plc"/>
|
||||||
|
<addaction name="men_connections"/>
|
||||||
|
<addaction name="men_help"/>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar">
|
||||||
|
<property name="sizeGripEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<action name="act_connections">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Connections...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_search">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Search Revolution Pi...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+F</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_quit">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Quit</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_webpage">
|
||||||
|
<property name="text">
|
||||||
|
<string>Visit &webpage...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_info">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Info...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_logs">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC &logs...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F3</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_options">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC &options...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+O</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_program">
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC progra&m...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+P</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_developer">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>PLC de&veloper...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F9</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_pictory">
|
||||||
|
<property name="text">
|
||||||
|
<string>piCtory configuraiton...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_disconnect">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Disconnect</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+X</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_reset">
|
||||||
|
<property name="text">
|
||||||
|
<string>Reset driver...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="act_simulator">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start local si&mulator...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>btn_plc_start</tabstop>
|
||||||
|
<tabstop>btn_plc_stop</tabstop>
|
||||||
|
<tabstop>btn_plc_restart</tabstop>
|
||||||
|
<tabstop>btn_plc_logs</tabstop>
|
||||||
|
<tabstop>btn_plc_debug</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources>
|
||||||
|
<include location="ressources.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>act_quit</sender>
|
||||||
|
<signal>triggered()</signal>
|
||||||
|
<receiver>win_revpicommander</receiver>
|
||||||
|
<slot>close()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>-1</x>
|
||||||
|
<y>-1</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>159</x>
|
||||||
|
<y>136</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
136
include/ui_dev/revpidevelop.ui
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>wid_develop</class>
|
||||||
|
<widget class="QWidget" name="wid_develop">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>374</width>
|
||||||
|
<height>444</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_all">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stop / Upload / Start</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_upload">
|
||||||
|
<property name="text">
|
||||||
|
<string>Just upload</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QTreeWidget" name="tree_files">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::MultiSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QGroupBox" name="gb_select">
|
||||||
|
<property name="title">
|
||||||
|
<string>File watcher for PLC development</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_select">
|
||||||
|
<property name="text">
|
||||||
|
<string>Path to development root:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_select">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Open developer root directory</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/folder-open.ico</normaloff>:/action/ico/folder-open.ico</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="lbl_path">
|
||||||
|
<property name="text">
|
||||||
|
<string>/</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="btn_refresh">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Reload file list</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/refresh.ico</normaloff>:/action/ico/refresh.ico</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="iconSize">
|
||||||
|
<size>
|
||||||
|
<width>24</width>
|
||||||
|
<height>24</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ressources.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
187
include/ui_dev/revpiinfo.ui
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_revpiinfo</class>
|
||||||
|
<widget class="QDialog" name="diag_revpiinfo">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>627</width>
|
||||||
|
<height>398</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="lbl_head">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>20</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>RevPi Python PLC - Control</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_lbl_version_pyload">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>RevPiPyLoad version on RevPi:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2" rowspan="4">
|
||||||
|
<widget class="QListWidget" name="lst_files"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="lbl_version_pyload">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0.0</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_link">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><a href="https://revpimodio.org/"><span style=" text-decoration: underline; color:#0000ff;">https://revpimodio.org/</span></a></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0" colspan="3">
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="3">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_lbl_version_control">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>14</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Version:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lbl_version_control">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>14</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0.0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="lbl_info">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>RevPiModIO, RevPiPyLoad and RevPiPyControl are community driven projects. They are all free and open source software.
|
||||||
|
All of them comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
|
||||||
|
applicable law.
|
||||||
|
|
||||||
|
(c) Sven Sager, License: GPLv3</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>diag_revpiinfo</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_revpiinfo</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
127
include/ui_dev/revpilogfile.ui
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>win_revpilogfile</class>
|
||||||
|
<widget class="QMainWindow" name="win_revpilogfile">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>796</width>
|
||||||
|
<height>347</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>RevPi Python PLC Logfiles</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="cbx_stay_on_top">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stay on top of all windows</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="cbx_wrap">
|
||||||
|
<property name="text">
|
||||||
|
<string>Linewrap</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="childrenCollapsible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="gridLayoutWidget">
|
||||||
|
<layout class="QGridLayout" name="grid_daemon" columnstretch="1,0">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_daemon">
|
||||||
|
<property name="text">
|
||||||
|
<string>RevPiPyLoad - Logfile</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_daemon">
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear view</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QPlainTextEdit" name="txt_daemon">
|
||||||
|
<property name="verticalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tabChangesFocus">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="undoRedoEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="lineWrapMode">
|
||||||
|
<enum>QPlainTextEdit::NoWrap</enum>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="gridLayoutWidget_2">
|
||||||
|
<layout class="QGridLayout" name="grid_app" columnstretch="1,0">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_app">
|
||||||
|
<property name="text">
|
||||||
|
<string>Clear view</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_app">
|
||||||
|
<property name="text">
|
||||||
|
<string>Python PLC program - Logfile</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QPlainTextEdit" name="txt_app">
|
||||||
|
<property name="verticalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tabChangesFocus">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="undoRedoEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="lineWrapMode">
|
||||||
|
<enum>QPlainTextEdit::NoWrap</enum>
|
||||||
|
</property>
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
250
include/ui_dev/revpioption.ui
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_options</class>
|
||||||
|
<widget class="QDialog" name="diag_options">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>452</width>
|
||||||
|
<height>503</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>RevPi Python PLC Options</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_plc">
|
||||||
|
<property name="title">
|
||||||
|
<string>Start / Stop behavior of PLC program</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_replace_io">
|
||||||
|
<property name="text">
|
||||||
|
<string>Replace IO file:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<widget class="QSpinBox" name="sbx_autoreloaddelay">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>120</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="cbx_zeroonexit">
|
||||||
|
<property name="text">
|
||||||
|
<string>... sucessfully without error</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="cbx_zeroonerror">
|
||||||
|
<property name="text">
|
||||||
|
<string>... after exception and errors</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="cbx_autostart">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start PLC program automatically</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="cbx_autoreload">
|
||||||
|
<property name="text">
|
||||||
|
<string>Restart PLC program after exit or crash</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="lbl_plc_zero">
|
||||||
|
<property name="text">
|
||||||
|
<string>Set process image to NULL if program terminates...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="txt_replace_io"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1" colspan="2">
|
||||||
|
<widget class="QComboBox" name="cbb_replace_io">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Do not use replace io file</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Use static file from RevPiPyLoad</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Use dynamic file from work directory</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Give own path and filename</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="lbl_plc_delay">
|
||||||
|
<property name="text">
|
||||||
|
<string>Restart delay in seconds:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_server">
|
||||||
|
<property name="title">
|
||||||
|
<string>RevPiPyLoad server services</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_aclplcslave">
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit ACL</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="cbx_mqtt">
|
||||||
|
<property name="text">
|
||||||
|
<string>MQTT process image publisher</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="cbx_plcslave">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start RevPi piControl server</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="lbl_slave_status">
|
||||||
|
<property name="text">
|
||||||
|
<string>status</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_lbl_slave_status">
|
||||||
|
<property name="text">
|
||||||
|
<string>piControl server is:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="lbl_mqtt_status">
|
||||||
|
<property name="text">
|
||||||
|
<string>status</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_lbl_mqtt_status">
|
||||||
|
<property name="text">
|
||||||
|
<string>MQTT publish service is:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_mqtt">
|
||||||
|
<property name="text">
|
||||||
|
<string>Settings</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_aclxmlrpc">
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit ACL</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QCheckBox" name="cbx_xmlrpc">
|
||||||
|
<property name="text">
|
||||||
|
<string>Activate XML-RPC for RevPiPyControl</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>diag_options</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_options</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
218
include/ui_dev/revpiplclist.ui
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_connections</class>
|
||||||
|
<widget class="QDialog" name="diag_connections">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>520</width>
|
||||||
|
<height>508</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Revolution Pi connections</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QTreeWidget" name="tre_connections">
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Connection name</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Address</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QVBoxLayout" name="vl_edit">
|
||||||
|
<item>
|
||||||
|
<spacer name="vs_edit">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_up">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/arrow-up.ico</normaloff>:/action/ico/arrow-up.ico</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_down">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/arrow-down.ico</normaloff>:/action/ico/arrow-down.ico</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_delete">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/edit-delete.ico</normaloff>:/action/ico/edit-delete.ico</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_add">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="ressources.qrc">
|
||||||
|
<normaloff>:/action/ico/edit-add.ico</normaloff>:/action/ico/edit-add.ico</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QGroupBox" name="gb_properties">
|
||||||
|
<property name="title">
|
||||||
|
<string>Connection properties</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_name">
|
||||||
|
<property name="text">
|
||||||
|
<string>Display name:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_folder">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sub folder:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_address">
|
||||||
|
<property name="text">
|
||||||
|
<string>Address (DNS/IP):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_port">
|
||||||
|
<property name="text">
|
||||||
|
<string>Port (Default {0}):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_name"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="txt_address"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="sbx_port">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>55123</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QComboBox" name="cbb_folder">
|
||||||
|
<property name="editable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Discard|QDialogButtonBox::Save</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="ressources.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>diag_connections</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_connections</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
248
include/ui_dev/revpiprogram.ui
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>diag_program</class>
|
||||||
|
<widget class="QDialog" name="diag_program">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>488</width>
|
||||||
|
<height>500</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>PLC program</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_plc">
|
||||||
|
<property name="title">
|
||||||
|
<string>PLC program</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QRadioButton" name="rbn_pythonversion_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Python 2</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1" colspan="2">
|
||||||
|
<widget class="QLineEdit" name="txt_plcarguments"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="QRadioButton" name="rbn_pythonversion_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Python 3</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="lbl_plcprogram">
|
||||||
|
<property name="text">
|
||||||
|
<string>Python PLC start program:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="3">
|
||||||
|
<widget class="QCheckBox" name="cbx_plcworkdir_set_uid">
|
||||||
|
<property name="text">
|
||||||
|
<string>Set write permissions for plc program to workdirectory</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="3">
|
||||||
|
<widget class="QComboBox" name="cbb_plcprogram"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_pythonversion">
|
||||||
|
<property name="text">
|
||||||
|
<string>Python version:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_plcarguments">
|
||||||
|
<property name="text">
|
||||||
|
<string>Program arguments:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="cb_transfair">
|
||||||
|
<property name="title">
|
||||||
|
<string>Transfair PLC program</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="cbb_format">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Files</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Folder</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>ZIP archive</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>TGZ archive</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_program_upload">
|
||||||
|
<property name="text">
|
||||||
|
<string>Upload</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QPushButton" name="btn_program_download">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_format">
|
||||||
|
<property name="text">
|
||||||
|
<string>Transfair format:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="cbx_pictory">
|
||||||
|
<property name="text">
|
||||||
|
<string>Including piCtory configuration</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="cbx_clear">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove all files on Revolution Pi before upload</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gb_control">
|
||||||
|
<property name="title">
|
||||||
|
<string>Control files</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_procimg_download">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_pictory_download">
|
||||||
|
<property name="text">
|
||||||
|
<string>Download</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="btn_pictory_upload">
|
||||||
|
<property name="text">
|
||||||
|
<string>Upload</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_pictory">
|
||||||
|
<property name="text">
|
||||||
|
<string>piCtory configuraiton</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_procimg">
|
||||||
|
<property name="text">
|
||||||
|
<string>Process image from piControl0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="btn_box">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>cbb_plcprogram</tabstop>
|
||||||
|
<tabstop>txt_plcarguments</tabstop>
|
||||||
|
<tabstop>rbn_pythonversion_2</tabstop>
|
||||||
|
<tabstop>rbn_pythonversion_3</tabstop>
|
||||||
|
<tabstop>cbx_plcworkdir_set_uid</tabstop>
|
||||||
|
<tabstop>cbb_format</tabstop>
|
||||||
|
<tabstop>cbx_pictory</tabstop>
|
||||||
|
<tabstop>cbx_clear</tabstop>
|
||||||
|
<tabstop>btn_program_download</tabstop>
|
||||||
|
<tabstop>btn_program_upload</tabstop>
|
||||||
|
<tabstop>btn_pictory_download</tabstop>
|
||||||
|
<tabstop>btn_pictory_upload</tabstop>
|
||||||
|
<tabstop>btn_procimg_download</tabstop>
|
||||||
|
</tabstops>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>diag_program</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>257</x>
|
||||||
|
<y>490</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btn_box</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>diag_program</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>325</x>
|
||||||
|
<y>490</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
5
make.bat
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
pyinstaller --clean -D --windowed ^
|
||||||
|
--add-data="data\revpipycontrol.ico;." ^
|
||||||
|
--icon=data\\revpipycontrol.ico ^
|
||||||
|
revpipycontrol\revpipycontrol.py
|
||||||
430
revpicommander/aclmanager.py
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
u"""Manager for ACL lists."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from re import compile
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
from helper import WidgetData
|
||||||
|
from ui.aclmanager_ui import Ui_diag_aclmanager
|
||||||
|
|
||||||
|
|
||||||
|
class AclManager(QtWidgets.QDialog, Ui_diag_aclmanager):
|
||||||
|
"""ACL manager."""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(AclManager, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setFixedSize(self.size())
|
||||||
|
|
||||||
|
self.__re_ipacl = compile(r"([\d*]{1,3}\.){3}[\d*]{1,3},-1")
|
||||||
|
self.__dict_acltext = {}
|
||||||
|
self.__cbb_level_loaded_index = 0
|
||||||
|
self.__mrk_message_shown = 0
|
||||||
|
self.__oldacl = ""
|
||||||
|
self.__read_only = False
|
||||||
|
|
||||||
|
# Prepare GUI
|
||||||
|
self.tb_acls.setColumnWidth(0, 300)
|
||||||
|
self.tb_acls.setColumnWidth(1, 50)
|
||||||
|
self.btn_edit.setEnabled(False)
|
||||||
|
self.btn_remove.setEnabled(False)
|
||||||
|
self.btn_add.setEnabled(False)
|
||||||
|
|
||||||
|
# Move to next focus when enter a "."
|
||||||
|
self.mrk_txt_ip_a_keyPressEvent = self.txt_ip_a.keyPressEvent
|
||||||
|
self.mrk_txt_ip_b_keyPressEvent = self.txt_ip_b.keyPressEvent
|
||||||
|
self.mrk_txt_ip_c_keyPressEvent = self.txt_ip_c.keyPressEvent
|
||||||
|
self.mrk_txt_ip_d_keyPressEvent = self.txt_ip_d.keyPressEvent
|
||||||
|
self.txt_ip_a.keyPressEvent = self.txt_ip_a_keyPressEvent
|
||||||
|
self.txt_ip_b.keyPressEvent = self.txt_ip_b_keyPressEvent
|
||||||
|
self.txt_ip_c.keyPressEvent = self.txt_ip_c_keyPressEvent
|
||||||
|
self.txt_ip_d.keyPressEvent = self.txt_ip_d_keyPressEvent
|
||||||
|
|
||||||
|
def __check_load_error(self):
|
||||||
|
"""
|
||||||
|
Check load errors and shows a message one time.
|
||||||
|
:return: True, if message was shown
|
||||||
|
"""
|
||||||
|
if bool(self.__mrk_message_shown & 1):
|
||||||
|
return False
|
||||||
|
|
||||||
|
for row in range(self.tb_acls.rowCount()):
|
||||||
|
if self.tb_acls.item(row, 0).data(WidgetData.has_error):
|
||||||
|
self.__mrk_message_shown += 1
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"There are errors in the ACL list!\nCheck the ALC levels of the "
|
||||||
|
"red lines in the table. The ACL levels or ip addresses are "
|
||||||
|
"invalid. If you save this dialog again, we will remove the "
|
||||||
|
"wrong entries automatically."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _changes_done(self):
|
||||||
|
"""
|
||||||
|
Check for unsaved changes in dialog.
|
||||||
|
|
||||||
|
:return: True, if unsaved changes was found
|
||||||
|
"""
|
||||||
|
return self.__table_to_acl() != self.__oldacl
|
||||||
|
|
||||||
|
def accept(self) -> None:
|
||||||
|
"""Save settings."""
|
||||||
|
if self.btn_add.isEnabled():
|
||||||
|
# Entry is ready to save, did the user forgot to click the button?
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Unsaved entry"), self.tr(
|
||||||
|
"You worked on a new ACL entry. Do you want to save "
|
||||||
|
"that entry, too?"
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
if ask:
|
||||||
|
self.on_btn_add_pressed()
|
||||||
|
|
||||||
|
if self.__check_load_error():
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__oldacl = self.__table_to_acl()
|
||||||
|
super(AclManager, self).accept()
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
if self._changes_done():
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Do you really want to quit? \nUnsaved changes will be lost"
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if ask:
|
||||||
|
self.reject()
|
||||||
|
else:
|
||||||
|
a0.ignore()
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
return super(AclManager, self).exec()
|
||||||
|
|
||||||
|
def reject(self) -> None:
|
||||||
|
"""Restore old settings."""
|
||||||
|
self.setup_acl_manager(self.__oldacl, self.__dict_acltext)
|
||||||
|
super(AclManager, self).reject()
|
||||||
|
|
||||||
|
def setup_acl_manager(self, acl_string: str, acl_texts: dict):
|
||||||
|
if type(acl_string) != str:
|
||||||
|
raise TypeError("acl_string must be <class 'str'>")
|
||||||
|
if type(acl_texts) != dict:
|
||||||
|
raise TypeError("acl_texts must be <class 'dict'>")
|
||||||
|
|
||||||
|
self.__dict_acltext = acl_texts.copy()
|
||||||
|
|
||||||
|
# Clean up widgets
|
||||||
|
while self.tb_acls.rowCount() > 0:
|
||||||
|
self.tb_acls.removeRow(0)
|
||||||
|
self.cbb_level.clear()
|
||||||
|
self.cbb_level.addItem(self.tr("Select..."), -1)
|
||||||
|
self.lbl_level_info.clear()
|
||||||
|
|
||||||
|
self.__re_ipacl = compile(
|
||||||
|
r"([\d*]{1,3}\.){3}[\d*]{1,3},[" +
|
||||||
|
str(min(self.__dict_acltext.keys(), default=0)) + r"-" +
|
||||||
|
str(max(self.__dict_acltext.keys(), default=0)) + r"]"
|
||||||
|
)
|
||||||
|
|
||||||
|
for ip_level in acl_string.split(" "):
|
||||||
|
self.__table_add_acl(ip_level)
|
||||||
|
|
||||||
|
lst_level_text = []
|
||||||
|
for level in sorted(self.__dict_acltext.keys()):
|
||||||
|
level_text = self.tr("Level") + " {0}: {1}".format(level, self.__dict_acltext[level])
|
||||||
|
lst_level_text.append(level_text)
|
||||||
|
self.cbb_level.addItem(level_text, level)
|
||||||
|
|
||||||
|
self.lbl_level_info.setText("\n".join(lst_level_text))
|
||||||
|
self.__oldacl = self.__table_to_acl()
|
||||||
|
|
||||||
|
def get_acl(self):
|
||||||
|
"""Get current ACL string."""
|
||||||
|
return self.__oldacl
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: ACL list
|
||||||
|
|
||||||
|
def __load_selected_entry(self):
|
||||||
|
row = self.tb_acls.currentRow()
|
||||||
|
if row < 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
item = self.tb_acls.item(row, 0)
|
||||||
|
ip_blocks = item.text().split(".")
|
||||||
|
self.txt_ip_a.setText(ip_blocks[0])
|
||||||
|
self.txt_ip_b.setText(ip_blocks[1])
|
||||||
|
self.txt_ip_c.setText(ip_blocks[2])
|
||||||
|
self.txt_ip_d.setText(ip_blocks[3])
|
||||||
|
|
||||||
|
for index in range(self.cbb_level.count()):
|
||||||
|
if self.cbb_level.itemData(index, QtCore.Qt.UserRole) == item.data(WidgetData.acl_level):
|
||||||
|
self.__cbb_level_loaded_index = index
|
||||||
|
self.cbb_level.setCurrentIndex(index)
|
||||||
|
break
|
||||||
|
|
||||||
|
self._check_all_filled()
|
||||||
|
|
||||||
|
def __table_add_acl(self, ip_level: str):
|
||||||
|
"""Add ACL entry to table."""
|
||||||
|
# Empty acl_string create empty ip_level
|
||||||
|
if not ip_level:
|
||||||
|
return
|
||||||
|
|
||||||
|
ip, level = ip_level.split(",")
|
||||||
|
if self.__re_ipacl.match(ip_level):
|
||||||
|
brush = QtGui.QBrush()
|
||||||
|
has_error = False
|
||||||
|
tool_tip = ""
|
||||||
|
else:
|
||||||
|
brush = QtGui.QBrush(QtGui.QColor("red"))
|
||||||
|
has_error = True
|
||||||
|
tool_tip = self.tr("This entry has an invalid ACL level or wrong IP format!")
|
||||||
|
|
||||||
|
for row in range(self.tb_acls.rowCount()):
|
||||||
|
item_0 = self.tb_acls.item(row, 0)
|
||||||
|
if item_0.text() == ip:
|
||||||
|
item_1 = self.tb_acls.item(row, 1)
|
||||||
|
|
||||||
|
# Update existing entry
|
||||||
|
item_0.setData(WidgetData.acl_level, int(level))
|
||||||
|
item_0.setData(WidgetData.has_error, has_error)
|
||||||
|
item_0.setBackground(brush)
|
||||||
|
item_0.setToolTip(tool_tip)
|
||||||
|
item_1.setText(level)
|
||||||
|
item_1.setBackground(brush)
|
||||||
|
item_1.setToolTip(tool_tip)
|
||||||
|
return
|
||||||
|
|
||||||
|
row_count = self.tb_acls.rowCount()
|
||||||
|
self.tb_acls.insertRow(row_count)
|
||||||
|
|
||||||
|
item = QtWidgets.QTableWidgetItem(ip)
|
||||||
|
item.setData(WidgetData.acl_level, int(level))
|
||||||
|
item.setData(WidgetData.has_error, has_error)
|
||||||
|
item.setBackground(brush)
|
||||||
|
item.setToolTip(tool_tip)
|
||||||
|
self.tb_acls.setItem(row_count, 0, item)
|
||||||
|
|
||||||
|
item = QtWidgets.QTableWidgetItem(level)
|
||||||
|
item.setBackground(brush)
|
||||||
|
item.setToolTip(tool_tip)
|
||||||
|
self.tb_acls.setItem(row_count, 1, item)
|
||||||
|
|
||||||
|
def __table_to_acl(self, force=False, row_indexes=None):
|
||||||
|
"""
|
||||||
|
Create acl string with valid entries only.
|
||||||
|
|
||||||
|
:param force: If True, return all entries
|
||||||
|
:param row_indexes: Only from indexes ist <class 'list'>
|
||||||
|
:return: ACL string
|
||||||
|
"""
|
||||||
|
if row_indexes is None:
|
||||||
|
row_indexes = range(self.tb_acls.rowCount())
|
||||||
|
|
||||||
|
buff_acl = ""
|
||||||
|
for i in row_indexes:
|
||||||
|
item = self.tb_acls.item(i, 0)
|
||||||
|
ip_level = "{0},{1} ".format(item.text(), item.data(WidgetData.acl_level))
|
||||||
|
if not (force or self.__re_ipacl.match(ip_level)):
|
||||||
|
continue
|
||||||
|
buff_acl += ip_level
|
||||||
|
return buff_acl.strip()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtWidgets.QTableWidgetItem)
|
||||||
|
def on_tb_acls_itemDoubleClicked(self, item: QtWidgets.QTableWidgetItem):
|
||||||
|
if not self.__read_only:
|
||||||
|
self.__load_selected_entry()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_tb_acls_itemSelectionChanged(self):
|
||||||
|
selected_rows = int(len(self.tb_acls.selectedItems()) / self.tb_acls.columnCount())
|
||||||
|
self.btn_edit.setEnabled(not self.__read_only and selected_rows == 1)
|
||||||
|
self.btn_remove.setEnabled(not self.__read_only and selected_rows > 0)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_edit_pressed(self):
|
||||||
|
self.__load_selected_entry()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_remove_pressed(self):
|
||||||
|
lst_selected_row_indexes = [mi.row() for mi in self.tb_acls.selectionModel().selectedRows(0)]
|
||||||
|
if len(lst_selected_row_indexes) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
str_remove = ""
|
||||||
|
for ip_level in self.__table_to_acl(True, lst_selected_row_indexes).split(" "):
|
||||||
|
if not ip_level:
|
||||||
|
# Empty string will return empty field
|
||||||
|
continue
|
||||||
|
ip, level = ip_level.split(",")
|
||||||
|
str_remove += "\nIP: {0:>15}\tLevel: {1}".format(ip, level)
|
||||||
|
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Do you really want to delete the following items?\n{0}"
|
||||||
|
).format(str_remove)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if ask:
|
||||||
|
# Turn order to start deleting from the button to preserve right indexes
|
||||||
|
lst_selected_row_indexes.sort(reverse=True)
|
||||||
|
for row in lst_selected_row_indexes:
|
||||||
|
self.tb_acls.removeRow(row)
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Group box IP
|
||||||
|
|
||||||
|
def _check_all_filled(self):
|
||||||
|
all_filled = \
|
||||||
|
len(self.txt_ip_a.text()) > 0 and \
|
||||||
|
len(self.txt_ip_b.text()) > 0 and \
|
||||||
|
len(self.txt_ip_c.text()) > 0 and \
|
||||||
|
len(self.txt_ip_d.text()) > 0 and \
|
||||||
|
self.cbb_level.currentIndex() != 0 and (
|
||||||
|
self.txt_ip_a.isModified() or self.txt_ip_b.isModified() or
|
||||||
|
self.txt_ip_c.isModified() or self.txt_ip_d.isModified() or
|
||||||
|
self.cbb_level.currentIndex() != self.__cbb_level_loaded_index
|
||||||
|
)
|
||||||
|
|
||||||
|
self.btn_add.setEnabled(not self.__read_only and all_filled)
|
||||||
|
|
||||||
|
def _move_ip_cursor(self, key: int, sender_widget: QtWidgets.QLineEdit,
|
||||||
|
last_widget: QtWidgets.QLineEdit, next_widget: QtWidgets.QLineEdit):
|
||||||
|
"""
|
||||||
|
Move cursor between ip enter widgets.
|
||||||
|
|
||||||
|
:param key: Pressed key to check
|
||||||
|
:param sender_widget: Sender widget of this key
|
||||||
|
:param last_widget: Set focus to this widget on backspace key
|
||||||
|
:param next_widget: Set focus to this widget on period key
|
||||||
|
:return: True, if the key should not be processed further
|
||||||
|
"""
|
||||||
|
if last_widget and key == QtCore.Qt.Key_Backspace and len(sender_widget.text()) == 0:
|
||||||
|
last_widget.setFocus()
|
||||||
|
elif next_widget and key == QtCore.Qt.Key_Period:
|
||||||
|
next_widget.setFocus()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def txt_ip_a_keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
|
||||||
|
if not self._move_ip_cursor(a0.key(), self.txt_ip_a, self.txt_ip_a, self.txt_ip_b):
|
||||||
|
self.mrk_txt_ip_a_keyPressEvent(a0)
|
||||||
|
|
||||||
|
def txt_ip_b_keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
|
||||||
|
if not self._move_ip_cursor(a0.key(), self.txt_ip_b, self.txt_ip_a, self.txt_ip_c):
|
||||||
|
self.mrk_txt_ip_b_keyPressEvent(a0)
|
||||||
|
|
||||||
|
def txt_ip_c_keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
|
||||||
|
if not self._move_ip_cursor(a0.key(), self.txt_ip_c, self.txt_ip_b, self.txt_ip_d):
|
||||||
|
self.mrk_txt_ip_c_keyPressEvent(a0)
|
||||||
|
|
||||||
|
def txt_ip_d_keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
|
||||||
|
if not self._move_ip_cursor(a0.key(), self.txt_ip_d, self.txt_ip_c, self.txt_ip_d):
|
||||||
|
self.mrk_txt_ip_d_keyPressEvent(a0)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_txt_ip_a_textChanged(self, text: str):
|
||||||
|
self._check_all_filled()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_txt_ip_b_textChanged(self, text: str):
|
||||||
|
self._check_all_filled()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_txt_ip_c_textChanged(self, text: str):
|
||||||
|
self._check_all_filled()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_txt_ip_d_textChanged(self, text: str):
|
||||||
|
self._check_all_filled()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbb_level_currentIndexChanged(self, index: int):
|
||||||
|
self._check_all_filled()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_add_pressed(self):
|
||||||
|
"""Add a new entry to acl table."""
|
||||||
|
ip_level = "{0}.{1}.{2}.{3},{4}".format(
|
||||||
|
self.txt_ip_a.text(),
|
||||||
|
self.txt_ip_b.text(),
|
||||||
|
self.txt_ip_c.text(),
|
||||||
|
self.txt_ip_d.text(),
|
||||||
|
self.cbb_level.currentData(QtCore.Qt.UserRole)
|
||||||
|
)
|
||||||
|
if self.__re_ipacl.match(ip_level):
|
||||||
|
self.__table_add_acl(ip_level)
|
||||||
|
self.on_btn_clear_pressed()
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not save new ACL entry! Check format of ip address "
|
||||||
|
"and acl level is in value list."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_clear_pressed(self):
|
||||||
|
"""Clear entry widgets."""
|
||||||
|
self.txt_ip_a.clear()
|
||||||
|
self.txt_ip_b.clear()
|
||||||
|
self.txt_ip_c.clear()
|
||||||
|
self.txt_ip_d.clear()
|
||||||
|
self.cbb_level.setCurrentIndex(0)
|
||||||
|
self.__cbb_level_loaded_index = 0
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
@property
|
||||||
|
def read_only(self):
|
||||||
|
"""Getter for read_only value."""
|
||||||
|
return self.__read_only
|
||||||
|
|
||||||
|
@read_only.setter
|
||||||
|
def read_only(self, value):
|
||||||
|
"""Setter for read_only window."""
|
||||||
|
self.__read_only = value
|
||||||
|
self.txt_ip_a.setEnabled(not value)
|
||||||
|
self.txt_ip_b.setEnabled(not value)
|
||||||
|
self.txt_ip_c.setEnabled(not value)
|
||||||
|
self.txt_ip_d.setEnabled(not value)
|
||||||
|
self.cbb_level.setEnabled(not value)
|
||||||
|
self.btn_clear.setEnabled(not value)
|
||||||
|
if value:
|
||||||
|
self.btn_box.setStandardButtons(
|
||||||
|
QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.btn_box.setStandardButtons(
|
||||||
|
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Debugging
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QtWidgets.QApplication([])
|
||||||
|
win = AclManager(None)
|
||||||
|
win.setup_acl_manager(
|
||||||
|
"127.0.1.*,0 127.0.0.1,1 127.0.0.2,2", {
|
||||||
|
0: "Just have a look",
|
||||||
|
1: "Do more things",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
win.read_only = False
|
||||||
|
rc = win.exec()
|
||||||
|
print(
|
||||||
|
"return code:", rc,
|
||||||
|
"acl:", win.get_acl()
|
||||||
|
)
|
||||||
232
revpicommander/avahisearch.py
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Revolution Pi search with zeroconf."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2020 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from os import name as osname
|
||||||
|
from re import compile
|
||||||
|
from socket import gethostbyname
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
from zeroconf import ServiceBrowser, Zeroconf
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from helper import WidgetData
|
||||||
|
from ui.avahisearch_ui import Ui_diag_search
|
||||||
|
|
||||||
|
|
||||||
|
class AvahiSearchThread(QtCore.QThread):
|
||||||
|
"""Search thread for Revolution Pi with installed RevPiPyLoad."""
|
||||||
|
added = QtCore.pyqtSignal(str, str, int, str, str)
|
||||||
|
removed = QtCore.pyqtSignal(str, str)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(AvahiSearchThread, self).__init__(parent)
|
||||||
|
self._cycle_wait_ms = 1000
|
||||||
|
|
||||||
|
self.__dict_arp = {}
|
||||||
|
self.re_posix = compile(
|
||||||
|
r"(?P<ip>(\d{1,3}\.){3}\d{1,3}).*"
|
||||||
|
r"(?P<mac>([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2})"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_arp(self):
|
||||||
|
if osname == "posix":
|
||||||
|
with open("/proc/net/arp") as fh:
|
||||||
|
for line in fh.readlines():
|
||||||
|
ip_mac = self.re_posix.search(line)
|
||||||
|
if ip_mac:
|
||||||
|
self.__dict_arp[ip_mac.group("ip")] = ip_mac.group("mac")
|
||||||
|
|
||||||
|
def get_mac(self, ip: str):
|
||||||
|
return self.__dict_arp.get(ip, None)
|
||||||
|
|
||||||
|
def remove_service(self, zeroconf: Zeroconf, conf_type: str, name: str):
|
||||||
|
"""Revolution Pi disappeared."""
|
||||||
|
pi.logger.debug("AvahiSearchThread.remove_service")
|
||||||
|
self.removed.emit(name, conf_type)
|
||||||
|
|
||||||
|
def add_service(self, zeroconf: Zeroconf, conf_type: str, name: str):
|
||||||
|
"""New Revolution Pi found."""
|
||||||
|
pi.logger.debug("AvahiSearchThread.add_service")
|
||||||
|
info = zeroconf.get_service_info(conf_type, name)
|
||||||
|
if not info:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
ip = gethostbyname(info.server)
|
||||||
|
except Exception:
|
||||||
|
ip = self.tr("N/A")
|
||||||
|
self.added.emit(name, info.server, info.port, conf_type, ip)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
pi.logger.debug("Started zero conf discovery.")
|
||||||
|
zeroconf = Zeroconf()
|
||||||
|
revpi_browser = ServiceBrowser(zeroconf, "_revpipyload._tcp.local.", self)
|
||||||
|
while not self.isInterruptionRequested():
|
||||||
|
# Just hanging around :)
|
||||||
|
self.msleep(self._cycle_wait_ms)
|
||||||
|
zeroconf.close()
|
||||||
|
pi.logger.debug("Stopped zero conf discovery.")
|
||||||
|
|
||||||
|
|
||||||
|
class AvahiSearch(QtWidgets.QDialog, Ui_diag_search):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(AvahiSearch, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setFixedSize(self.size())
|
||||||
|
|
||||||
|
self.connect_index = -1
|
||||||
|
self.th_zero_conf = AvahiSearchThread(self)
|
||||||
|
|
||||||
|
self.tb_revpi.setColumnWidth(0, 250)
|
||||||
|
self.btn_connect.setEnabled(False)
|
||||||
|
self.btn_save.setEnabled(False)
|
||||||
|
|
||||||
|
def _restart_search(self):
|
||||||
|
"""Clean up an restart search thread."""
|
||||||
|
while self.tb_revpi.rowCount() > 0:
|
||||||
|
self.tb_revpi.removeRow(0)
|
||||||
|
self.th_zero_conf.requestInterruption()
|
||||||
|
|
||||||
|
self.th_zero_conf = AvahiSearchThread(self)
|
||||||
|
self.th_zero_conf.added.connect(self.on_avahi_added)
|
||||||
|
self.th_zero_conf.removed.connect(self.on_avahi_removed)
|
||||||
|
self.th_zero_conf.start()
|
||||||
|
|
||||||
|
def _save_connection(self, row: int, no_warn=False):
|
||||||
|
"""
|
||||||
|
Save the connection from given row to settings.
|
||||||
|
|
||||||
|
:param row: Row with connection data
|
||||||
|
:param no_warn: If True, no message boxes will appear
|
||||||
|
:return: Array index of connection (found or saved)
|
||||||
|
"""
|
||||||
|
item = self.tb_revpi.item(row, 0)
|
||||||
|
if not item:
|
||||||
|
return
|
||||||
|
|
||||||
|
folder_name = self.tr("Auto discovered")
|
||||||
|
selected_name = item.text()
|
||||||
|
selected_address = item.data(WidgetData.address)
|
||||||
|
selected_port = item.data(WidgetData.port)
|
||||||
|
i = 0
|
||||||
|
for i in range(helper.settings.beginReadArray("connections")):
|
||||||
|
helper.settings.setArrayIndex(i)
|
||||||
|
|
||||||
|
name = helper.settings.value("name", type=str)
|
||||||
|
address = helper.settings.value("address", type=str)
|
||||||
|
port = helper.settings.value("port", type=int)
|
||||||
|
if address.lower() == selected_address.lower() and port == selected_port:
|
||||||
|
if not no_warn:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Already in list..."), self.tr(
|
||||||
|
"The selected Revolution Pi is already saved in your "
|
||||||
|
"connection list as '{0}.".format(name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
helper.settings.endArray()
|
||||||
|
return i
|
||||||
|
|
||||||
|
helper.settings.endArray()
|
||||||
|
helper.settings.beginWriteArray("connections")
|
||||||
|
|
||||||
|
helper.settings.setArrayIndex(i + 1)
|
||||||
|
helper.settings.setValue("address", selected_address)
|
||||||
|
helper.settings.setValue("folder", folder_name)
|
||||||
|
helper.settings.setValue("name", selected_name)
|
||||||
|
helper.settings.setValue("port", selected_port)
|
||||||
|
|
||||||
|
helper.settings.endArray()
|
||||||
|
|
||||||
|
if not no_warn:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"The connection with the name '{0}' was successfully saved "
|
||||||
|
"to folder '{1}' in your connections.".format(selected_name, folder_name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return i + 1
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
self._restart_search()
|
||||||
|
rc = super(AvahiSearch, self).exec()
|
||||||
|
self.th_zero_conf.requestInterruption()
|
||||||
|
return rc
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str, str, int, str, str)
|
||||||
|
def on_avahi_added(self, name: str, server: str, port: int, conf_type: str, ip: str):
|
||||||
|
"""New Revolution Pi found."""
|
||||||
|
index = -1
|
||||||
|
for i in range(self.tb_revpi.rowCount()):
|
||||||
|
if self.tb_revpi.item(i, 0).data(WidgetData.object_name) == name:
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
# New Row
|
||||||
|
item_name = QtWidgets.QTableWidgetItem()
|
||||||
|
item_ip = QtWidgets.QTableWidgetItem()
|
||||||
|
|
||||||
|
index = self.tb_revpi.rowCount()
|
||||||
|
self.tb_revpi.insertRow(index)
|
||||||
|
self.tb_revpi.setItem(index, 0, item_name)
|
||||||
|
self.tb_revpi.setItem(index, 1, item_ip)
|
||||||
|
else:
|
||||||
|
# Update row
|
||||||
|
item_name = self.tb_revpi.item(index, 0)
|
||||||
|
item_ip = self.tb_revpi.item(index, 1)
|
||||||
|
|
||||||
|
item_name.setIcon(QtGui.QIcon(":/main/ico/cpu.ico"))
|
||||||
|
item_name.setText(server[:-1])
|
||||||
|
item_name.setData(WidgetData.object_name, name)
|
||||||
|
item_name.setData(WidgetData.address, server[:-1])
|
||||||
|
item_name.setData(WidgetData.port, port)
|
||||||
|
item_ip.setText(ip)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str, str)
|
||||||
|
def on_avahi_removed(self, name: str, conf_type: str):
|
||||||
|
"""Revolution Pi disappeared."""
|
||||||
|
for i in range(self.tb_revpi.rowCount()):
|
||||||
|
if self.tb_revpi.item(i, 0).data(WidgetData.object_name) == name:
|
||||||
|
self.tb_revpi.removeRow(i)
|
||||||
|
break
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int, int)
|
||||||
|
def on_tb_revpi_cellDoubleClicked(self, row: int, column: int):
|
||||||
|
"""Connect to double clicked Revolution Pi."""
|
||||||
|
pi.logger.debug("AvahiSearch.on_tb_revpi_cellDoubleClicked")
|
||||||
|
self.connect_index = self._save_connection(row, no_warn=True)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int, int, int, int)
|
||||||
|
def on_tb_revpi_currentCellChanged(self, row: int, column: int, last_row: int, last_column: int):
|
||||||
|
"""Manage state of buttons."""
|
||||||
|
self.btn_connect.setEnabled(row >= 0)
|
||||||
|
self.btn_save.setEnabled(row >= 0)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_connect_pressed(self):
|
||||||
|
"""Connect to selected Revolution Pi."""
|
||||||
|
pi.logger.debug("AvahiSearch.on_btn_connect_pressed")
|
||||||
|
if self.tb_revpi.currentRow() == -1:
|
||||||
|
return
|
||||||
|
self.connect_index = self._save_connection(self.tb_revpi.currentRow(), no_warn=True)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_save_pressed(self):
|
||||||
|
"""Save selected Revolution Pi."""
|
||||||
|
pi.logger.debug("AvahiSearch.on_btn_save_pressed")
|
||||||
|
if self.tb_revpi.currentRow() == -1:
|
||||||
|
return
|
||||||
|
self.connect_index = self._save_connection(self.tb_revpi.currentRow())
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_restart_pressed(self):
|
||||||
|
"""Clean up an restart search thread."""
|
||||||
|
self._restart_search()
|
||||||
436
revpicommander/debugcontrol.py
Normal file
@@ -0,0 +1,436 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Debug control widget to append to main window."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2020 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
from xmlrpc.client import Binary, Fault, MultiCall, MultiCallIterator
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from debugios import DebugIos
|
||||||
|
from ui.debugcontrol_ui import Ui_wid_debugcontrol
|
||||||
|
|
||||||
|
|
||||||
|
class PsValues(QtCore.QThread):
|
||||||
|
"""
|
||||||
|
Get process image from Revolution Pi.
|
||||||
|
|
||||||
|
If this thread detects a driver reset, it will finish the work.
|
||||||
|
"""
|
||||||
|
|
||||||
|
driver_reset_detected = QtCore.pyqtSignal()
|
||||||
|
process_image_received = QtCore.pyqtSignal(Binary)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(PsValues, self).__init__()
|
||||||
|
self._cycle_time = 200
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Read IO values of Revolution Pi."""
|
||||||
|
pi.logger.debug("PsValues.run enter")
|
||||||
|
|
||||||
|
while not self.isInterruptionRequested():
|
||||||
|
try:
|
||||||
|
self.process_image_received.emit(
|
||||||
|
helper.cm.call_remote_function("ps_values", raise_exception=True)
|
||||||
|
)
|
||||||
|
except Fault:
|
||||||
|
pi.logger.warning("Detected piCtory reset.")
|
||||||
|
self.requestInterruption()
|
||||||
|
self.driver_reset_detected.emit()
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.error(e)
|
||||||
|
self.process_image_received.emit(Binary())
|
||||||
|
|
||||||
|
self.msleep(self._cycle_time)
|
||||||
|
|
||||||
|
pi.logger.debug("PsValues.run exit")
|
||||||
|
|
||||||
|
|
||||||
|
class DebugControl(QtWidgets.QWidget, Ui_wid_debugcontrol):
|
||||||
|
"""Debug controller for main window."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(DebugControl, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.dict_devices = {}
|
||||||
|
"""Key=position, value=device name."""
|
||||||
|
self.dict_ios = {"inp": {}, "out": {}}
|
||||||
|
"""IO types "inp" "out" which include key=device position, value=list with ios."""
|
||||||
|
self.dict_windows = {}
|
||||||
|
"""Debug IO windows with key=device position, value=DebugIos."""
|
||||||
|
|
||||||
|
self.driver_reset_detected = False
|
||||||
|
self.err_workvalues = 0
|
||||||
|
self.max_errors = 10
|
||||||
|
self.th_worker = PsValues()
|
||||||
|
|
||||||
|
self.vl_devices.addItem(
|
||||||
|
QtWidgets.QSpacerItem(20, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||||
|
)
|
||||||
|
self.cbx_write.setEnabled(False)
|
||||||
|
self.cbx_stay_on_top.setChecked(helper.settings.value("stay_on_top", False, bool))
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
pi.logger.debug("DebugControl.__del__")
|
||||||
|
|
||||||
|
def _set_gui_control_states(self):
|
||||||
|
"""Set states depending on acl level."""
|
||||||
|
pi.logger.debug("DebugControl._set_gui_control_states")
|
||||||
|
# xml_mode view >= 1
|
||||||
|
# xml_mode write >= 3
|
||||||
|
self.btn_read_io.setEnabled(not self.cbx_write.isChecked())
|
||||||
|
self.btn_refresh_io.setEnabled(not self.cbx_refresh.isChecked())
|
||||||
|
self.btn_write_o.setEnabled(
|
||||||
|
not self.cbx_write.isChecked() and (helper.cm.xml_mode >= 3 or helper.cm.simulating)
|
||||||
|
)
|
||||||
|
self.cbx_write.setEnabled(
|
||||||
|
self.cbx_refresh.isChecked() and (helper.cm.xml_mode >= 3 or helper.cm.simulating)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _destroy_io_view(self, device_position=-1):
|
||||||
|
"""
|
||||||
|
Destroy IO view including the button and dict entry.
|
||||||
|
|
||||||
|
:param device_position: Only device position or -1 for all
|
||||||
|
"""
|
||||||
|
pi.logger.debug("DebugControl._destroy_io_view")
|
||||||
|
for position in sorted(self.dict_devices) if device_position == -1 else [device_position]:
|
||||||
|
if position in self.dict_windows:
|
||||||
|
# Remove singe window and button
|
||||||
|
win = self.dict_windows[position] # type: DebugIos
|
||||||
|
win.close()
|
||||||
|
win.deleteLater()
|
||||||
|
win.disconnect()
|
||||||
|
del self.dict_windows[position]
|
||||||
|
|
||||||
|
btn = self.gb_devices.findChild((QtWidgets.QPushButton,), str(position)) # type: QtWidgets.QPushButton
|
||||||
|
if btn:
|
||||||
|
self.vl_devices.removeWidget(btn)
|
||||||
|
btn.deleteLater()
|
||||||
|
btn.disconnect()
|
||||||
|
|
||||||
|
def _driver_reset_detected(self):
|
||||||
|
"""Things to do after driver reset."""
|
||||||
|
self.driver_reset_detected = True
|
||||||
|
self.cbx_write.setChecked(False)
|
||||||
|
self.cbx_refresh.setChecked(False)
|
||||||
|
for win in self.dict_windows.values(): # type: DebugIos
|
||||||
|
win.stat_bar.showMessage(
|
||||||
|
self.tr("Driver reset for piControl detected."),
|
||||||
|
10000
|
||||||
|
)
|
||||||
|
self.reload_devices()
|
||||||
|
|
||||||
|
def _work_values(self, refresh=False, write_out=False, process_image=None):
|
||||||
|
"""
|
||||||
|
Read input and output values.
|
||||||
|
|
||||||
|
:param refresh: Refresh unchanged ios from process image
|
||||||
|
:param write_out: Write changed outputs to process image
|
||||||
|
:param process_image: Use this <class 'Binary'> for work and do not fetch
|
||||||
|
"""
|
||||||
|
if process_image is not None:
|
||||||
|
ba_values = process_image
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
ba_values = helper.cm.call_remote_function("ps_values", raise_exception=True)
|
||||||
|
except Fault:
|
||||||
|
pi.logger.warning("Detected piCtory reset.")
|
||||||
|
self._driver_reset_detected()
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.error(e)
|
||||||
|
ba_values = Binary()
|
||||||
|
|
||||||
|
# From now on use bytes instead of Binary
|
||||||
|
ba_values = bytearray(ba_values.data)
|
||||||
|
|
||||||
|
if not ba_values:
|
||||||
|
if self.cbx_refresh.isChecked():
|
||||||
|
self.err_workvalues += 1
|
||||||
|
else:
|
||||||
|
# Raise error on button press
|
||||||
|
self.err_workvalues = self.max_errors
|
||||||
|
|
||||||
|
if self.err_workvalues >= self.max_errors:
|
||||||
|
for win in self.dict_windows.values(): # type: DebugIos
|
||||||
|
win.stat_bar.setStyleSheet("background-color: red;")
|
||||||
|
win.stat_bar.showMessage(self.tr(
|
||||||
|
"Error while getting values from Revolution Pi."
|
||||||
|
), 5000)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.err_workvalues > 0:
|
||||||
|
self.err_workvalues = 0
|
||||||
|
for win in self.dict_windows.values(): # type: DebugIos
|
||||||
|
win.stat_bar.setStyleSheet("")
|
||||||
|
|
||||||
|
# Use multicall to set all changed values
|
||||||
|
if write_out and helper.cm.connected:
|
||||||
|
cli = helper.cm.get_cli()
|
||||||
|
xmlmc = MultiCall(cli)
|
||||||
|
else:
|
||||||
|
xmlmc = []
|
||||||
|
|
||||||
|
for io_type in self.dict_ios:
|
||||||
|
for position in self.dict_ios[io_type]:
|
||||||
|
if position not in self.dict_windows:
|
||||||
|
continue
|
||||||
|
|
||||||
|
win = self.dict_windows[position]
|
||||||
|
for io in self.dict_ios[io_type][position]: # type: list
|
||||||
|
# ['name', bitlength, byte_address, 'bmk', bitaddress, 'byteorder', signed]
|
||||||
|
value_procimg = ba_values[io[2]:io[2] + io[1]]
|
||||||
|
if io[4] >= 0:
|
||||||
|
# Bit-IO
|
||||||
|
value_procimg = bool(
|
||||||
|
int.from_bytes(value_procimg, byteorder=io[5], signed=io[6]) & 1 << io[4]
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.driver_reset_detected:
|
||||||
|
# Change last value to save the actual states of outputs
|
||||||
|
win.set_last_value(io[0], value_procimg)
|
||||||
|
|
||||||
|
if (refresh or write_out) and io_type == "out":
|
||||||
|
widget_value, last_value = win.get_value(io[0])
|
||||||
|
if widget_value != last_value:
|
||||||
|
# User changed value
|
||||||
|
|
||||||
|
if not write_out:
|
||||||
|
# Do not write output after change to save this state
|
||||||
|
continue
|
||||||
|
|
||||||
|
value_procimg = widget_value
|
||||||
|
if type(xmlmc) == MultiCall:
|
||||||
|
xmlmc.ps_setvalue(position, io[0], widget_value)
|
||||||
|
else:
|
||||||
|
# Simulate multicall an collect result to list
|
||||||
|
xmlmc.append(
|
||||||
|
helper.cm.call_remote_function("ps_setvalue", position, io[0], widget_value)
|
||||||
|
)
|
||||||
|
|
||||||
|
win.set_value(io[0], value_procimg)
|
||||||
|
|
||||||
|
if self.cbx_refresh.isChecked():
|
||||||
|
win.stat_bar.showMessage(self.tr("Auto update values..."), 1000)
|
||||||
|
else:
|
||||||
|
win.stat_bar.showMessage(self.tr("Values updated..."), 2000)
|
||||||
|
|
||||||
|
if self.driver_reset_detected:
|
||||||
|
# Show values, which we can recover to empty process image
|
||||||
|
win.reset_change_value_colors()
|
||||||
|
|
||||||
|
self.driver_reset_detected = False
|
||||||
|
|
||||||
|
# Set values by multi call
|
||||||
|
if write_out:
|
||||||
|
if isinstance(xmlmc, list):
|
||||||
|
self._validate_multicall(xmlmc)
|
||||||
|
else:
|
||||||
|
self._validate_multicall(xmlmc())
|
||||||
|
|
||||||
|
def _validate_multicall(self, return_list):
|
||||||
|
"""
|
||||||
|
Check xml rpc multi call return values.
|
||||||
|
|
||||||
|
:param return_list: Return values of multi call
|
||||||
|
"""
|
||||||
|
if isinstance(return_list, MultiCallIterator):
|
||||||
|
return_list = return_list.results
|
||||||
|
if len(return_list) == 0:
|
||||||
|
return
|
||||||
|
elif not isinstance(return_list, list):
|
||||||
|
return
|
||||||
|
pi.logger.debug("DebugControl._validate_multicall")
|
||||||
|
|
||||||
|
str_errmsg = ""
|
||||||
|
for lst_result in return_list: # type: list
|
||||||
|
# [[device, io, status, msg]] - Yes, double list list :D
|
||||||
|
if type(lst_result[0]) == list:
|
||||||
|
lst_result = lst_result.pop()
|
||||||
|
if not lst_result[2]:
|
||||||
|
# Create error message
|
||||||
|
device_name = self.dict_devices[lst_result[0]]
|
||||||
|
str_errmsg += self.tr(
|
||||||
|
"Error set value of device '{0}' Output '{1}': {2}\n"
|
||||||
|
).format(device_name, lst_result[1], lst_result[3])
|
||||||
|
else:
|
||||||
|
self.dict_windows[lst_result[0]].reset_change_value_colors(lst_result[1])
|
||||||
|
|
||||||
|
if str_errmsg != "":
|
||||||
|
pi.logger.error(str_errmsg)
|
||||||
|
if not self.cbx_refresh.isChecked():
|
||||||
|
QtWidgets.QMessageBox.critical(self, self.tr("Error"), str_errmsg)
|
||||||
|
|
||||||
|
def deleteLater(self):
|
||||||
|
"""Clean up all sub windows."""
|
||||||
|
pi.logger.debug("DebugControl.deleteLater")
|
||||||
|
|
||||||
|
self.cbx_write.setChecked(False)
|
||||||
|
self.cbx_refresh.setChecked(False)
|
||||||
|
self._destroy_io_view()
|
||||||
|
|
||||||
|
super(DebugControl, self).deleteLater()
|
||||||
|
|
||||||
|
def reload_devices(self):
|
||||||
|
"""Rebuild GUI depending on devices and ios of Revolution Pi."""
|
||||||
|
pi.logger.debug("DebugControl.reload_devices")
|
||||||
|
|
||||||
|
if not helper.cm.call_remote_function("psstart", default_value=False):
|
||||||
|
# RevPiPyLoad does not support psstart (too old)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ps_devices format: [[0, 'picore01'], [32, 'di01'], ...
|
||||||
|
dict_devices = {v[0]: v[1] for v in helper.cm.call_remote_function("ps_devices", default_value=[])}
|
||||||
|
if len(dict_devices) == 0:
|
||||||
|
# There is not piCtory configuration on the Revolution Pi
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Remove not existing or renamed devices
|
||||||
|
for position in self.dict_devices:
|
||||||
|
if position not in dict_devices or self.dict_devices[position] != dict_devices[position]:
|
||||||
|
self._destroy_io_view(position)
|
||||||
|
|
||||||
|
self.dict_devices = dict_devices
|
||||||
|
|
||||||
|
# Format: {position: [['name', bitlength, byte_address, 'bmk', bitaddress, 'byteorder', signed], ...
|
||||||
|
inps_data = helper.cm.call_remote_function("ps_inps", default_value=Binary()).data
|
||||||
|
outs_data = helper.cm.call_remote_function("ps_outs", default_value=Binary()).data
|
||||||
|
if inps_data == b'' or outs_data == b'':
|
||||||
|
return False
|
||||||
|
|
||||||
|
dict_inps = pickle.loads(inps_data)
|
||||||
|
dict_outs = pickle.loads(outs_data)
|
||||||
|
|
||||||
|
# Take spacer at last position and reinsert it after buttons
|
||||||
|
spacer = self.vl_devices.takeAt(self.vl_devices.count() - 1)
|
||||||
|
|
||||||
|
for position in sorted(self.dict_devices):
|
||||||
|
if position in self.dict_windows:
|
||||||
|
# DebugIos already exists
|
||||||
|
if self.dict_windows[position].update_ios(dict_inps[position], dict_outs[position]):
|
||||||
|
# All IOs match the old ones
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# Destroy old window to build a new one
|
||||||
|
self._destroy_io_view(position)
|
||||||
|
|
||||||
|
win = DebugIos(
|
||||||
|
position, self.dict_devices[position],
|
||||||
|
dict_inps[position], dict_outs[position]
|
||||||
|
)
|
||||||
|
win.device_closed.connect(self.on_device_closed)
|
||||||
|
win.do_read.connect(self.btn_refresh_io.pressed)
|
||||||
|
win.do_write.connect(self.btn_write_o.pressed)
|
||||||
|
self.dict_windows[position] = win
|
||||||
|
|
||||||
|
btn = QtWidgets.QPushButton(self.gb_devices)
|
||||||
|
btn.setCheckable(True)
|
||||||
|
btn.setObjectName(str(position))
|
||||||
|
btn.setText("{0} | {1}".format(position, self.dict_devices[position]))
|
||||||
|
btn.clicked.connect(self.on_btn_device_clicked)
|
||||||
|
self.vl_devices.addWidget(btn)
|
||||||
|
|
||||||
|
self.vl_devices.addItem(spacer)
|
||||||
|
|
||||||
|
self.dict_ios["inp"] = dict_inps
|
||||||
|
self.dict_ios["out"] = dict_outs
|
||||||
|
|
||||||
|
self._work_values(refresh=True)
|
||||||
|
self._set_gui_control_states()
|
||||||
|
|
||||||
|
self.cbx_refresh.setChecked(helper.settings.value("auto_refresh", False, bool))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(bool)
|
||||||
|
def on_btn_device_clicked(self, checked: bool):
|
||||||
|
"""Open or close IO window."""
|
||||||
|
pi.logger.debug("DebugControl.on_btn_device_clicked")
|
||||||
|
|
||||||
|
position = int(self.sender().objectName())
|
||||||
|
if position in self.dict_windows:
|
||||||
|
win = self.dict_windows[position] # type: QtWidgets.QMainWindow
|
||||||
|
win.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint, self.cbx_stay_on_top.isChecked())
|
||||||
|
win.setVisible(checked)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_device_closed(self, position: int):
|
||||||
|
"""Change the check state of button, if window was closed."""
|
||||||
|
pi.logger.debug("DebugControl.on_device_closed")
|
||||||
|
btn = self.gb_devices.findChild(QtWidgets.QPushButton, str(position)) # type: QtWidgets.QPushButton
|
||||||
|
btn.setChecked(False)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_read_io_pressed(self):
|
||||||
|
"""Read all IO values and replace changed ones."""
|
||||||
|
pi.logger.debug("DebugControl.on_btn_read_io_pressed")
|
||||||
|
for win in self.dict_windows.values(): # type: DebugIos
|
||||||
|
win.reset_label_colors()
|
||||||
|
self._work_values()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_refresh_io_pressed(self):
|
||||||
|
"""Read all IO values but do not touch changed ones."""
|
||||||
|
pi.logger.debug("DebugControl.on_btn_refresh_io_pressed")
|
||||||
|
if not self.cbx_refresh.isChecked():
|
||||||
|
self._work_values(refresh=True)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_write_o_pressed(self):
|
||||||
|
"""Write outputs."""
|
||||||
|
pi.logger.debug("DebugControl.on_btn_write_o_pressed")
|
||||||
|
if not self.cbx_write.isChecked() and (helper.cm.xml_mode >= 3 or helper.cm.simulating):
|
||||||
|
for win in self.dict_windows.values(): # type: DebugIos
|
||||||
|
win.reset_label_colors()
|
||||||
|
self._work_values(write_out=True)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbx_refresh_stateChanged(self, state: int):
|
||||||
|
"""Start or stop the auto refresh thread."""
|
||||||
|
pi.logger.debug("DebugControl.cbx_refresh_stateChanged")
|
||||||
|
|
||||||
|
# Start / stop worker thread
|
||||||
|
if state == QtCore.Qt.Checked and (helper.cm.connected or helper.cm.simulating):
|
||||||
|
self.th_worker = PsValues()
|
||||||
|
self.th_worker.driver_reset_detected.connect(self._driver_reset_detected)
|
||||||
|
self.th_worker.process_image_received.connect(lambda process_image: self._work_values(
|
||||||
|
refresh=True,
|
||||||
|
write_out=self.cbx_write.isChecked(),
|
||||||
|
process_image=process_image
|
||||||
|
))
|
||||||
|
self.th_worker.start()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.th_worker.requestInterruption()
|
||||||
|
self.th_worker.wait()
|
||||||
|
self.th_worker.deleteLater()
|
||||||
|
self.cbx_write.setChecked(False)
|
||||||
|
|
||||||
|
self._set_gui_control_states()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(bool)
|
||||||
|
def on_cbx_refresh_clicked(self, state: bool):
|
||||||
|
"""Save the state on user action."""
|
||||||
|
helper.settings.setValue("auto_refresh", state)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(bool)
|
||||||
|
def on_cbx_stay_on_top_clicked(self, state: bool):
|
||||||
|
"""Save the state on user action."""
|
||||||
|
helper.settings.setValue("stay_on_top", state)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbx_write_stateChanged(self, state: int):
|
||||||
|
pi.logger.debug("DebugControl.cbx_write_stateChanged")
|
||||||
|
checked = state == QtCore.Qt.Checked
|
||||||
|
for win in self.dict_windows.values(): # type: DebugIos
|
||||||
|
win.write_values = checked
|
||||||
|
|
||||||
|
self._set_gui_control_states()
|
||||||
310
revpicommander/debugios.py
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""One device of the Revolution Pi."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2020 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from ui.debugios_ui import Ui_win_debugios
|
||||||
|
|
||||||
|
|
||||||
|
class DebugIos(QtWidgets.QMainWindow, Ui_win_debugios):
|
||||||
|
"""IO window of one device."""
|
||||||
|
|
||||||
|
device_closed = QtCore.pyqtSignal(int)
|
||||||
|
"""This window was closed."""
|
||||||
|
do_read = QtCore.pyqtSignal()
|
||||||
|
do_write = QtCore.pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, position: int, name: str, inputs: list, outputs: list, parent=None):
|
||||||
|
super(DebugIos, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.restoreGeometry(helper.cm.debug_geos.get(position, b''))
|
||||||
|
self.setWindowTitle("{0} - {1}".format(position, name))
|
||||||
|
self.gb_io.setTitle(self.gb_io.title().format(name))
|
||||||
|
|
||||||
|
self.position = position
|
||||||
|
self.name = name
|
||||||
|
self.inputs = inputs.copy()
|
||||||
|
self.outputs = outputs.copy()
|
||||||
|
self.search_class = (QtWidgets.QLineEdit, QtWidgets.QDoubleSpinBox, QtWidgets.QCheckBox)
|
||||||
|
self.write_values = False
|
||||||
|
|
||||||
|
min_input = min(inputs, key=lambda k: k[2])
|
||||||
|
max_output = max(outputs, key=lambda k: k[2])
|
||||||
|
self.length = max_output[2] + max_output[1] - min_input[2]
|
||||||
|
|
||||||
|
self.style_sheet = ""
|
||||||
|
self._create_io(self.inputs, self.saw_inp, True)
|
||||||
|
self._create_io(self.outputs, self.saw_out, False)
|
||||||
|
self.style_sheet = "background-color: red;"
|
||||||
|
|
||||||
|
shc_read = QtWidgets.QShortcut(QtGui.QKeySequence("F5"), self)
|
||||||
|
shc_read.activated.connect(self.do_read)
|
||||||
|
shc_write = QtWidgets.QShortcut(QtGui.QKeySequence("F6"), self)
|
||||||
|
shc_write.activated.connect(self.do_write)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
pi.logger.debug("DebugIos.__del__")
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent):
|
||||||
|
pi.logger.debug("DebugIos.closeEvent")
|
||||||
|
helper.cm.debug_geos[self.position] = self.saveGeometry()
|
||||||
|
self.device_closed.emit(self.position)
|
||||||
|
|
||||||
|
def _calc_min_max(self, byte_length: int, signed: bool):
|
||||||
|
"""Calculate min an max value which fits to bytes."""
|
||||||
|
max_int_value = 256 ** byte_length
|
||||||
|
return max_int_value / 2 * -1 if signed else 0.0, \
|
||||||
|
max_int_value / 2 - 1 if signed else max_int_value - 1
|
||||||
|
|
||||||
|
def _create_io(self, lst_ios: list, container: QtWidgets.QWidget, read_only: bool):
|
||||||
|
lst_names = list(lst[0] for lst in lst_ios)
|
||||||
|
layout = container.layout() # type: QtWidgets.QFormLayout
|
||||||
|
|
||||||
|
for val in container.findChildren(self.search_class, options=QtCore.Qt.FindDirectChildrenOnly):
|
||||||
|
name = val.objectName()
|
||||||
|
if name not in lst_names:
|
||||||
|
# Remove old io from layout
|
||||||
|
layout.removeRow(layout.getWidgetPosition(val)[0])
|
||||||
|
|
||||||
|
counter = -1
|
||||||
|
for io in lst_ios:
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
name = io[0]
|
||||||
|
byte_length = io[1]
|
||||||
|
bit_address = io[4]
|
||||||
|
byteorder = io[5]
|
||||||
|
signed = io[6]
|
||||||
|
|
||||||
|
if container.findChild(self.search_class, name) is not None:
|
||||||
|
# Check properties of this IO
|
||||||
|
continue
|
||||||
|
|
||||||
|
lbl = QtWidgets.QLabel(name, container)
|
||||||
|
lbl.setObjectName("lbl_".format(name))
|
||||||
|
lbl.setStyleSheet(self.style_sheet)
|
||||||
|
|
||||||
|
val = self._create_widget(name, byte_length, bit_address, byteorder, signed, read_only)
|
||||||
|
val.setParent(container)
|
||||||
|
layout.insertRow(counter, val, lbl)
|
||||||
|
|
||||||
|
self.splitter.setSizes([1, 1])
|
||||||
|
|
||||||
|
def _create_widget(self, name: str, byte_length: int, bit_address: int, byteorder: str, signed: bool, read_only: bool):
|
||||||
|
"""Create widget in functions address space to use lambda functions."""
|
||||||
|
if bit_address >= 0:
|
||||||
|
val = QtWidgets.QCheckBox()
|
||||||
|
val.setEnabled(not read_only)
|
||||||
|
|
||||||
|
# Set alias to use the same function name on all widget types
|
||||||
|
val.setValue = val.setChecked
|
||||||
|
if not read_only:
|
||||||
|
val.stateChanged.connect(self._change_cbx_value)
|
||||||
|
val.value = val.isChecked
|
||||||
|
|
||||||
|
elif byte_length > 4:
|
||||||
|
# Bytes or string
|
||||||
|
val = QtWidgets.QLineEdit()
|
||||||
|
val.setReadOnly(read_only)
|
||||||
|
val.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
|
|
||||||
|
# Set alias to use the same function name on all widget types
|
||||||
|
val.setValue = val.setText
|
||||||
|
if not read_only:
|
||||||
|
val.textChanged.connect(self._change_txt_value)
|
||||||
|
val.value = val.text
|
||||||
|
|
||||||
|
else:
|
||||||
|
struct_type = "B" if byte_length == 1 else "H" if byte_length == 2 else "I"
|
||||||
|
|
||||||
|
val = QtWidgets.QDoubleSpinBox()
|
||||||
|
val.setReadOnly(read_only)
|
||||||
|
val.setProperty("big_endian", byteorder == "big")
|
||||||
|
val.setProperty("byte_length", byte_length)
|
||||||
|
val.setProperty("signed", signed)
|
||||||
|
val.setProperty("struct_type", struct_type)
|
||||||
|
val.setProperty("frm", "{0}{1}".format(
|
||||||
|
">" if val.property("big_endian") else "<",
|
||||||
|
struct_type.lower() if signed else struct_type
|
||||||
|
))
|
||||||
|
|
||||||
|
val.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
|
val.customContextMenuRequested.connect(self.on_context_menu)
|
||||||
|
|
||||||
|
val.setDecimals(0)
|
||||||
|
min_value, max_value = self._calc_min_max(byte_length, signed)
|
||||||
|
val.setMinimum(min_value)
|
||||||
|
val.setMaximum(max_value)
|
||||||
|
if not read_only:
|
||||||
|
val.valueChanged.connect(self._change_sbx_dvalue)
|
||||||
|
|
||||||
|
val.setObjectName(name)
|
||||||
|
return val
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def _change_cbx_value(self, value: int):
|
||||||
|
"""Change value of a check box."""
|
||||||
|
if self.sender().property("last_value") == (value == 2):
|
||||||
|
self.sender().setStyleSheet("")
|
||||||
|
else:
|
||||||
|
self.sender().setStyleSheet("background-color: yellow;")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(float)
|
||||||
|
def _change_sbx_dvalue(self, value: float):
|
||||||
|
"""Change value of a spin box."""
|
||||||
|
if self.sender().property("last_value") == int(value):
|
||||||
|
self.sender().setStyleSheet("")
|
||||||
|
else:
|
||||||
|
self.sender().setStyleSheet("background-color: yellow;")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def _change_txt_value(self, value: str):
|
||||||
|
"""Change value of a text box."""
|
||||||
|
if self.sender().property("last_value") == value:
|
||||||
|
self.sender().setStyleSheet("")
|
||||||
|
else:
|
||||||
|
self.sender().setStyleSheet("background-color: yellow;")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtCore.QPoint)
|
||||||
|
def on_context_menu(self, point: QtCore.QPoint):
|
||||||
|
"""Generate menu for data format changes."""
|
||||||
|
pi.logger.debug("DebugIos.on_context_menu")
|
||||||
|
|
||||||
|
sender = self.sender()
|
||||||
|
men = QtWidgets.QMenu(sender)
|
||||||
|
|
||||||
|
act_signed = QtWidgets.QAction(self.tr("signed"), men)
|
||||||
|
act_signed.setCheckable(True)
|
||||||
|
act_signed.setChecked(sender.property("signed") or False)
|
||||||
|
men.addAction(act_signed)
|
||||||
|
|
||||||
|
act_byteorder = QtWidgets.QAction(self.tr("big_endian"), men)
|
||||||
|
act_byteorder.setCheckable(True)
|
||||||
|
act_byteorder.setChecked(sender.property("big_endian") or False)
|
||||||
|
men.addAction(act_byteorder)
|
||||||
|
|
||||||
|
rc = men.exec(sender.mapToGlobal(point))
|
||||||
|
if not rc:
|
||||||
|
men.deleteLater()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get actual value to reformat it
|
||||||
|
actual_value, last_value = self.get_value(sender.objectName())
|
||||||
|
if rc == act_signed:
|
||||||
|
sender.setProperty("signed", act_signed.isChecked())
|
||||||
|
if type(sender) == QtWidgets.QDoubleSpinBox:
|
||||||
|
# Recalculate min / max for spinbox
|
||||||
|
min_value, max_value = self._calc_min_max(
|
||||||
|
sender.property("byte_length"), sender.property("signed")
|
||||||
|
)
|
||||||
|
sender.setMinimum(min_value)
|
||||||
|
sender.setMaximum(max_value)
|
||||||
|
elif rc == act_byteorder:
|
||||||
|
sender.setProperty("big_endian", act_byteorder.isChecked())
|
||||||
|
|
||||||
|
sender.setProperty("frm", "{0}{1}".format(
|
||||||
|
">" if act_byteorder.isChecked() else "<",
|
||||||
|
sender.property("struct_type").lower() if act_signed.isChecked() else sender.property("struct_type").upper()
|
||||||
|
))
|
||||||
|
|
||||||
|
self.set_value(sender.objectName(), actual_value)
|
||||||
|
men.deleteLater()
|
||||||
|
|
||||||
|
def reset_label_colors(self):
|
||||||
|
"""Clean up color from labels."""
|
||||||
|
for wid in self.findChildren(QtWidgets.QLabel): # type: QtWidgets.QLabel
|
||||||
|
wid.setStyleSheet("")
|
||||||
|
|
||||||
|
def reset_change_value_colors(self, io_name=None):
|
||||||
|
"""
|
||||||
|
Clean up color from changed outputs.
|
||||||
|
|
||||||
|
:param io_name: Clean up only this IO
|
||||||
|
"""
|
||||||
|
pi.logger.debug("DebugIos.reset_change_value_colors")
|
||||||
|
if io_name is None:
|
||||||
|
lst_wid = self.saw_out.findChildren(
|
||||||
|
self.search_class, options=QtCore.Qt.FindDirectChildrenOnly)
|
||||||
|
else:
|
||||||
|
lst_wid = self.saw_out.findChildren(
|
||||||
|
self.search_class, name=io_name, options=QtCore.Qt.FindDirectChildrenOnly)
|
||||||
|
|
||||||
|
for wid in lst_wid:
|
||||||
|
value, last_value = self.get_value(wid.objectName())
|
||||||
|
if value == last_value:
|
||||||
|
wid.setStyleSheet("")
|
||||||
|
else:
|
||||||
|
wid.setStyleSheet("background-color: yellow;")
|
||||||
|
|
||||||
|
def update_ios(self, inputs: list, outputs: list):
|
||||||
|
"""Update IOs after driver reset of piCtory."""
|
||||||
|
|
||||||
|
# Check device length, this has to match to reuse this device
|
||||||
|
min_input = min(inputs, key=lambda k: k[2])
|
||||||
|
max_output = max(outputs, key=lambda k: k[2])
|
||||||
|
new_length = max_output[2] + max_output[1] - min_input[2]
|
||||||
|
if self.length != new_length:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Remove IOs, which was remove or renamed
|
||||||
|
self._create_io(inputs, self.saw_inp, True)
|
||||||
|
self._create_io(outputs, self.saw_out, False)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_value(self, io_name: str):
|
||||||
|
"""
|
||||||
|
Standard get function for a value of different widgets and last value.
|
||||||
|
|
||||||
|
:param io_name: Name of IO
|
||||||
|
:return: (actual value, last value) <class 'tuple'>
|
||||||
|
"""
|
||||||
|
child = self.findChild(self.search_class, io_name)
|
||||||
|
actual_value = child.value()
|
||||||
|
last_value = child.value() if child.property("last_value") is None else child.property("last_value")
|
||||||
|
if child.property("frm"):
|
||||||
|
return struct.pack(child.property("frm"), int(actual_value)), \
|
||||||
|
struct.pack(child.property("frm"), int(last_value))
|
||||||
|
elif type(actual_value) == str:
|
||||||
|
return actual_value.encode(), last_value.encode()
|
||||||
|
else:
|
||||||
|
return actual_value, last_value
|
||||||
|
|
||||||
|
def set_last_value(self, io_name: str, value):
|
||||||
|
"""
|
||||||
|
Set last value for widget to sync after driver reset.
|
||||||
|
|
||||||
|
:param io_name: Name of IO
|
||||||
|
:param value: Process value as <class 'bool'> or <class 'bytes'>
|
||||||
|
"""
|
||||||
|
child = self.findChild(self.search_class, io_name)
|
||||||
|
if child.property("frm"):
|
||||||
|
value = struct.unpack(child.property("frm"), value)[0]
|
||||||
|
elif type(value) == bytearray:
|
||||||
|
value = value.decode()
|
||||||
|
|
||||||
|
child.setProperty("last_value", value)
|
||||||
|
|
||||||
|
def set_value(self, io_name: str, value):
|
||||||
|
"""
|
||||||
|
Standard set function for a value of different widgets.
|
||||||
|
|
||||||
|
:param io_name: Name of IO
|
||||||
|
:param value: New value as bytes or bool for widget
|
||||||
|
"""
|
||||||
|
child = self.findChild(self.search_class, io_name)
|
||||||
|
if child.property("frm"):
|
||||||
|
value = struct.unpack(child.property("frm"), value)[0]
|
||||||
|
elif type(value) == bytearray:
|
||||||
|
value = value.decode()
|
||||||
|
|
||||||
|
child.setProperty("last_value", value)
|
||||||
|
child.setValue(value)
|
||||||
462
revpicommander/helper.py
Normal file
@@ -0,0 +1,462 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Helper functions for this application."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2020 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
import socket
|
||||||
|
from enum import IntEnum
|
||||||
|
from http.client import CannotSendRequest
|
||||||
|
from os import environ, remove
|
||||||
|
from queue import Queue
|
||||||
|
from threading import Lock
|
||||||
|
from xmlrpc.client import Binary, ServerProxy
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
|
import proginit as pi
|
||||||
|
|
||||||
|
|
||||||
|
class WidgetData(IntEnum):
|
||||||
|
address = 260
|
||||||
|
replace_ios_config = 261
|
||||||
|
acl_level = 262
|
||||||
|
has_error = 263
|
||||||
|
port = 264
|
||||||
|
object_name = 265
|
||||||
|
last_dir_upload = 301
|
||||||
|
last_file_upload = 302
|
||||||
|
last_dir_pictory = 303
|
||||||
|
last_dir_picontrol = 304
|
||||||
|
last_dir_selected = 305
|
||||||
|
last_pictory_file = 306
|
||||||
|
last_tar_file = 307
|
||||||
|
last_zip_file = 308
|
||||||
|
file_name = 309
|
||||||
|
watch_files = 310
|
||||||
|
watch_path = 311
|
||||||
|
debug_geos = 312
|
||||||
|
|
||||||
|
|
||||||
|
class ConnectionManager(QtCore.QThread):
|
||||||
|
"""Check connection and status for PLC program on Revolution Pi."""
|
||||||
|
|
||||||
|
connection_established = QtCore.pyqtSignal()
|
||||||
|
"""New connection established successfully with <class 'ServerProxy'>."""
|
||||||
|
connection_disconnected = QtCore.pyqtSignal()
|
||||||
|
"""Connection disconnected."""
|
||||||
|
connection_disconnecting = QtCore.pyqtSignal()
|
||||||
|
"""Signal emitted before closing connection."""
|
||||||
|
connection_error_observed = QtCore.pyqtSignal(str)
|
||||||
|
"""This will be triggered, if a connection error was detected."""
|
||||||
|
status_changed = QtCore.pyqtSignal(str, str)
|
||||||
|
"""Status message and color suggestion."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None, cycle_time_ms=1000):
|
||||||
|
super(ConnectionManager, self).__init__(parent)
|
||||||
|
|
||||||
|
self._cli = None
|
||||||
|
self._cli_connect = Queue()
|
||||||
|
self._cycle_time = cycle_time_ms
|
||||||
|
self._lck_cli = Lock()
|
||||||
|
self._ps_started = False
|
||||||
|
self._revpi = None
|
||||||
|
self._revpi_output = None
|
||||||
|
|
||||||
|
self.address = ""
|
||||||
|
self.name = ""
|
||||||
|
self.port = 55123
|
||||||
|
|
||||||
|
# Sync this with revpiplclist to preserve settings
|
||||||
|
self.program_last_dir_upload = ""
|
||||||
|
self.program_last_file_upload = ""
|
||||||
|
self.program_last_dir_pictory = ""
|
||||||
|
self.program_last_dir_picontrol = ""
|
||||||
|
self.program_last_dir_selected = ""
|
||||||
|
self.program_last_pictory_file = ""
|
||||||
|
self.program_last_tar_file = ""
|
||||||
|
self.program_last_zip_file = ""
|
||||||
|
self.develop_watch_files = []
|
||||||
|
self.develop_watch_path = ""
|
||||||
|
self.debug_geos = {}
|
||||||
|
|
||||||
|
self.pyload_version = (0, 0, 0)
|
||||||
|
"""Version number of RevPiPyLoad 0.0.0 with <class 'int'> values."""
|
||||||
|
self.xml_funcs = []
|
||||||
|
"""Name list of all supported functions of RevPiPyLoad."""
|
||||||
|
self.xml_mode = -1
|
||||||
|
"""ACL level for this connection (-1 on connection denied)."""
|
||||||
|
self._xml_mode_refresh = False
|
||||||
|
|
||||||
|
def __call_simulator(self, function_name: str, *args, default_value=None, **kwargs):
|
||||||
|
pi.logger.debug("ConnectionManager.__call_simulator({0})".format(function_name))
|
||||||
|
if function_name == "ps_values":
|
||||||
|
if self._revpi.readprocimg():
|
||||||
|
bytebuff = bytearray()
|
||||||
|
for dev in self._revpi.device:
|
||||||
|
bytebuff += bytes(dev)
|
||||||
|
return Binary(bytes(bytebuff))
|
||||||
|
|
||||||
|
elif function_name == "ps_setvalue":
|
||||||
|
# args: 0=device, 1=io, 2=value
|
||||||
|
device = args[0]
|
||||||
|
io = args[1]
|
||||||
|
if type(args[2]) == Binary:
|
||||||
|
value = args[2].data
|
||||||
|
else:
|
||||||
|
value = args[2]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Write new value to IO
|
||||||
|
self._revpi.io[io].set_value(value)
|
||||||
|
except Exception as e:
|
||||||
|
return [device, io, False, str(e)]
|
||||||
|
|
||||||
|
return [device, io, True, ""]
|
||||||
|
|
||||||
|
elif function_name == "psstart":
|
||||||
|
self._revpi.autorefresh_all()
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif function_name == "psstop":
|
||||||
|
self._revpi.exit(full=False)
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif function_name == "ps_devices":
|
||||||
|
return [(dev.position, dev.name) for dev in self._revpi.device]
|
||||||
|
|
||||||
|
elif function_name == "ps_inps":
|
||||||
|
return self.__simulator_ios("inp")
|
||||||
|
|
||||||
|
elif function_name == "ps_outs":
|
||||||
|
return self.__simulator_ios("out")
|
||||||
|
|
||||||
|
else:
|
||||||
|
return default_value
|
||||||
|
|
||||||
|
def __simulator_ios(self, iotype: str):
|
||||||
|
dict_ios = {}
|
||||||
|
for dev in self._revpi.device:
|
||||||
|
dict_ios[dev.position] = []
|
||||||
|
|
||||||
|
if iotype == "inp":
|
||||||
|
lst_io = dev.get_inputs()
|
||||||
|
elif iotype == "out":
|
||||||
|
lst_io = dev.get_outputs()
|
||||||
|
else:
|
||||||
|
lst_io = []
|
||||||
|
|
||||||
|
for io in lst_io:
|
||||||
|
dict_ios[dev.position].append([
|
||||||
|
io.name,
|
||||||
|
1 if io._bitlength == 1 else int(io._bitlength / 8),
|
||||||
|
io._slc_address.start + dev.offset,
|
||||||
|
io.bmk,
|
||||||
|
io._bitaddress,
|
||||||
|
io._byteorder,
|
||||||
|
io._signed,
|
||||||
|
])
|
||||||
|
return Binary(pickle.dumps(dict_ios))
|
||||||
|
|
||||||
|
def _clear_settings(self):
|
||||||
|
"""Clear connection settings."""
|
||||||
|
self.address = ""
|
||||||
|
self.name = ""
|
||||||
|
self.port = 55123
|
||||||
|
self.pyload_version = (0, 0, 0)
|
||||||
|
self.xml_funcs.clear()
|
||||||
|
self.xml_mode = -1
|
||||||
|
|
||||||
|
self.program_last_dir_upload = ""
|
||||||
|
self.program_last_file_upload = ""
|
||||||
|
self.program_last_dir_pictory = ""
|
||||||
|
self.program_last_dir_picontrol = ""
|
||||||
|
self.program_last_dir_selected = ""
|
||||||
|
self.program_last_pictory_file = ""
|
||||||
|
self.program_last_tar_file = ""
|
||||||
|
self.program_last_zip_file = ""
|
||||||
|
self.develop_watch_files = []
|
||||||
|
self.develop_watch_path = ""
|
||||||
|
self.debug_geos = {}
|
||||||
|
|
||||||
|
def _save_settings(self):
|
||||||
|
"""Save settings to named Revolution Pi."""
|
||||||
|
for i in range(settings.beginReadArray("connections")):
|
||||||
|
settings.setArrayIndex(i)
|
||||||
|
if settings.value("address") != self.address:
|
||||||
|
# Search used connection, because connection manager could reorganize array
|
||||||
|
continue
|
||||||
|
|
||||||
|
settings.setValue("last_dir_upload", self.program_last_dir_upload)
|
||||||
|
settings.setValue("last_file_upload", self.program_last_file_upload)
|
||||||
|
settings.setValue("last_dir_pictory", self.program_last_dir_pictory)
|
||||||
|
settings.setValue("last_dir_picontrol", self.program_last_dir_picontrol)
|
||||||
|
settings.setValue("last_dir_selected", self.program_last_dir_selected)
|
||||||
|
settings.setValue("last_pictory_file", self.program_last_pictory_file)
|
||||||
|
settings.setValue("last_tar_file", self.program_last_tar_file)
|
||||||
|
settings.setValue("last_zip_file", self.program_last_zip_file)
|
||||||
|
settings.setValue("watch_files", self.develop_watch_files)
|
||||||
|
settings.setValue("watch_path", self.develop_watch_path)
|
||||||
|
settings.setValue("debug_geos", self.debug_geos)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
settings.endArray()
|
||||||
|
|
||||||
|
def pyload_connect(self, settings_index: int):
|
||||||
|
"""
|
||||||
|
Create a new connection from settings object.
|
||||||
|
|
||||||
|
:param settings_index: Index of settings array 'connections'
|
||||||
|
:return: True, if the connection was successfully established
|
||||||
|
"""
|
||||||
|
|
||||||
|
# First disconnect to send signal and clean up values
|
||||||
|
self.pyload_disconnect()
|
||||||
|
|
||||||
|
settings.beginReadArray("connections")
|
||||||
|
settings.setArrayIndex(settings_index)
|
||||||
|
|
||||||
|
address = settings.value("address")
|
||||||
|
name = settings.value("name")
|
||||||
|
port = settings.value("port", defaultValue=55123)
|
||||||
|
|
||||||
|
self.program_last_dir_upload = settings.value("last_dir_upload", ".", str)
|
||||||
|
self.program_last_file_upload = settings.value("last_file_upload", ".", str)
|
||||||
|
self.program_last_dir_pictory = settings.value("last_dir_pictory", ".", str)
|
||||||
|
self.program_last_dir_picontrol = settings.value("last_dir_picontrol", ".", str)
|
||||||
|
self.program_last_dir_selected = settings.value("last_dir_selected", ".", str)
|
||||||
|
self.program_last_pictory_file = settings.value("last_pictory_file", "{0}.rsc".format(name), str)
|
||||||
|
self.program_last_tar_file = settings.value("last_tar_file", "{0}.tgz".format(name))
|
||||||
|
self.program_last_zip_file = settings.value("last_zip_file", "{0}.zip".format(name))
|
||||||
|
self.develop_watch_files = settings.value("watch_files", [], list)
|
||||||
|
self.develop_watch_path = settings.value("watch_path", "", str)
|
||||||
|
self.debug_geos = settings.value("debug_geos", {}, dict)
|
||||||
|
|
||||||
|
settings.endArray()
|
||||||
|
|
||||||
|
socket.setdefaulttimeout(2)
|
||||||
|
sp = ServerProxy("http://{0}:{1}".format(address, port))
|
||||||
|
|
||||||
|
# Load values and test connection to Revolution Pi
|
||||||
|
try:
|
||||||
|
pyload_version = list(map(int, sp.version().split(".")))
|
||||||
|
xml_funcs = sp.system.listMethods()
|
||||||
|
xml_mode = sp.xmlmodus()
|
||||||
|
except Exception as e:
|
||||||
|
self.connection_error_observed.emit(str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.address = address
|
||||||
|
self.name = name
|
||||||
|
self.port = port
|
||||||
|
self.pyload_version = pyload_version
|
||||||
|
self.xml_funcs = xml_funcs
|
||||||
|
self.xml_mode = xml_mode
|
||||||
|
|
||||||
|
with self._lck_cli:
|
||||||
|
socket.setdefaulttimeout(5)
|
||||||
|
self._cli = sp
|
||||||
|
self._cli_connect.put_nowait((address, port))
|
||||||
|
|
||||||
|
self.connection_established.emit()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def pyload_disconnect(self):
|
||||||
|
"""Disconnect from Revolution Pi."""
|
||||||
|
if self._revpi is not None:
|
||||||
|
self.connection_disconnecting.emit()
|
||||||
|
|
||||||
|
self._revpi.cleanup()
|
||||||
|
self._revpi_output.cleanup()
|
||||||
|
remove(self._revpi.procimg)
|
||||||
|
self._revpi = None
|
||||||
|
self._revpi_output = None
|
||||||
|
|
||||||
|
pi.logger.debug("Simulator destroyed.")
|
||||||
|
self.connection_disconnected.emit()
|
||||||
|
|
||||||
|
elif self._cli is not None:
|
||||||
|
|
||||||
|
# Tell all widget, that we want do disconnect, to save the settings
|
||||||
|
self.connection_disconnecting.emit()
|
||||||
|
self._save_settings()
|
||||||
|
|
||||||
|
with self._lck_cli:
|
||||||
|
if self._ps_started:
|
||||||
|
try:
|
||||||
|
self._cli.psstop()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self._clear_settings()
|
||||||
|
self._cli = None
|
||||||
|
|
||||||
|
self.connection_disconnected.emit()
|
||||||
|
|
||||||
|
def pyload_simulate(self, configrsc: str, procimg: str):
|
||||||
|
"""Start the simulator for piControl on local computer."""
|
||||||
|
pi.logger.debug("ConnectionManager.start_simulate")
|
||||||
|
|
||||||
|
with open(procimg, "wb") as fh:
|
||||||
|
fh.write(b'\x00' * 4096)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import revpimodio2
|
||||||
|
|
||||||
|
# Prepare process image with default values
|
||||||
|
self._revpi_output = revpimodio2.RevPiModIO(configrsc=configrsc, procimg=procimg)
|
||||||
|
self._revpi_output.setdefaultvalues()
|
||||||
|
self._revpi_output.writeprocimg()
|
||||||
|
|
||||||
|
# This is our simulator to work with
|
||||||
|
self._revpi = revpimodio2.RevPiModIO(simulator=True, configrsc=configrsc, procimg=procimg)
|
||||||
|
self._revpi.setdefaultvalues()
|
||||||
|
self._revpi.writeprocimg()
|
||||||
|
|
||||||
|
self.xml_funcs = ["psstart", "psstop", "ps_devices", "ps_inps", "ps_outs", "ps_values", "ps_setvalue"]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.exception(e)
|
||||||
|
self._revpi_output = None
|
||||||
|
self._revpi = None
|
||||||
|
remove(procimg)
|
||||||
|
|
||||||
|
return self._revpi is not None
|
||||||
|
|
||||||
|
def refresh_xml_mode(self):
|
||||||
|
"""Refresh XML ACL level after some change could be done."""
|
||||||
|
self._xml_mode_refresh = True
|
||||||
|
|
||||||
|
def reset_simulator(self):
|
||||||
|
"""Reset all io to piCtory defaults."""
|
||||||
|
pi.logger.debug("ConnectionManager.reset_simulator")
|
||||||
|
self._revpi_output.writeprocimg()
|
||||||
|
self._revpi.setdefaultvalues()
|
||||||
|
self._revpi.writeprocimg()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
"""Thread worker to check status of RevPiPyLoad."""
|
||||||
|
self.setPriority(QtCore.QThread.NormalPriority)
|
||||||
|
|
||||||
|
sp = None
|
||||||
|
while not self.isInterruptionRequested():
|
||||||
|
|
||||||
|
if self._revpi is not None:
|
||||||
|
sp = None
|
||||||
|
self.status_changed.emit("SIMULATING", "yellow")
|
||||||
|
elif self._cli is None:
|
||||||
|
sp = None
|
||||||
|
self.status_changed.emit("NOT CONNECTED", "lightblue")
|
||||||
|
elif not self._cli_connect.empty():
|
||||||
|
# Get new connection information to create object in this thread
|
||||||
|
item = self._cli_connect.get()
|
||||||
|
sp = ServerProxy("http://{0}:{1}".format(*item))
|
||||||
|
|
||||||
|
if sp:
|
||||||
|
try:
|
||||||
|
plc_exit_code = sp.plcexitcode()
|
||||||
|
if self._xml_mode_refresh:
|
||||||
|
self.xml_mode = sp.xmlmodus()
|
||||||
|
self._xml_mode_refresh = False
|
||||||
|
except CannotSendRequest as e:
|
||||||
|
pi.logger.warning(e)
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.warning(e)
|
||||||
|
self.status_changed.emit("SERVER ERROR", "red")
|
||||||
|
self.connection_error_observed.emit("{0} | {1}".format(e, type(e)))
|
||||||
|
else:
|
||||||
|
if plc_exit_code == -1:
|
||||||
|
self.status_changed.emit("RUNNING", "green")
|
||||||
|
elif plc_exit_code == -2:
|
||||||
|
self.status_changed.emit("FILE NOT FOUND", "red")
|
||||||
|
elif plc_exit_code == -3:
|
||||||
|
self.status_changed.emit("NOT RUNNING (NO STATUS)", "yellow")
|
||||||
|
elif plc_exit_code == -9:
|
||||||
|
self.status_changed.emit("PROGRAM KILLED", "red")
|
||||||
|
elif plc_exit_code == -15:
|
||||||
|
self.status_changed.emit("PROGRAM TERMED", "red")
|
||||||
|
elif plc_exit_code == 0:
|
||||||
|
self.status_changed.emit("NOT RUNNING", "yellow")
|
||||||
|
else:
|
||||||
|
self.status_changed.emit("FINISHED WITH CODE {0}".format(plc_exit_code), "yellow")
|
||||||
|
|
||||||
|
self.msleep(self._cycle_time)
|
||||||
|
|
||||||
|
def call_remote_function(self, function_name: str, *args, default_value=None, raise_exception=False, **kwargs):
|
||||||
|
"""
|
||||||
|
Save call of a remote function with given name and parameters on Revolution Pi.
|
||||||
|
|
||||||
|
:param function_name: Function to call on RevPiPyLoad
|
||||||
|
:param args: Functions arguments
|
||||||
|
:param default_value: Default value will be returned on error
|
||||||
|
:param raise_exception: Will raise the exception returned from server
|
||||||
|
:param kwargs: Functions key word arguments
|
||||||
|
:return: Return value of remote function or default_value
|
||||||
|
"""
|
||||||
|
if self._cli is None and self._revpi is None:
|
||||||
|
pi.logger.error("Not connected while calling {0}".format(function_name))
|
||||||
|
if raise_exception:
|
||||||
|
raise ConnectionError("Connection manager not connected")
|
||||||
|
return default_value
|
||||||
|
|
||||||
|
reload_funcs = False
|
||||||
|
if function_name == "psstart":
|
||||||
|
self._ps_started = True
|
||||||
|
reload_funcs = True
|
||||||
|
elif function_name == "psstop":
|
||||||
|
self._ps_started = False
|
||||||
|
reload_funcs = True
|
||||||
|
|
||||||
|
# On connection problems do not freeze
|
||||||
|
if self._lck_cli.acquire(timeout=1.0):
|
||||||
|
if self._revpi is not None:
|
||||||
|
# Redirect call to simulator
|
||||||
|
return_value = self.__call_simulator(function_name, *args, default_value=default_value, **kwargs)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return_value = getattr(self._cli, function_name)(*args, **kwargs)
|
||||||
|
if reload_funcs:
|
||||||
|
self.xml_funcs = self._cli.system.listMethods()
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.error(e)
|
||||||
|
if raise_exception:
|
||||||
|
self._lck_cli.release()
|
||||||
|
raise
|
||||||
|
return_value = default_value
|
||||||
|
|
||||||
|
self._lck_cli.release()
|
||||||
|
return return_value
|
||||||
|
|
||||||
|
elif raise_exception:
|
||||||
|
raise ConnectionError("Can not get lock of connection")
|
||||||
|
|
||||||
|
return default_value
|
||||||
|
|
||||||
|
def get_cli(self):
|
||||||
|
"""Connection proxy of actual connection."""
|
||||||
|
if self.address and self.port:
|
||||||
|
return ServerProxy("http://{0}:{1}".format(self.address, self.port))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connected(self):
|
||||||
|
"""True if we have an active connection."""
|
||||||
|
return self._cli is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def simulating(self):
|
||||||
|
"""True, if simulating mode is running."""
|
||||||
|
return self._revpi is not None
|
||||||
|
|
||||||
|
|
||||||
|
cm = ConnectionManager()
|
||||||
|
"""Clobal connection manager instance."""
|
||||||
|
|
||||||
|
settings = QtCore.QSettings("revpipyplc", "revpipyload")
|
||||||
|
"""Global application settings."""
|
||||||
|
|
||||||
|
homedir = environ.get("HOME", "") or environ.get("APPDATA", "")
|
||||||
|
"""Home dir of user."""
|
||||||
BIN
revpicommander/locale/revpicommander_de.qm
Normal file
1569
revpicommander/locale/revpicommander_de.ts
Normal file
130
revpicommander/mqttmanager.py
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Options for MQTT system."""
|
||||||
|
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from PyQt5 import QtGui, QtWidgets
|
||||||
|
|
||||||
|
import proginit as pi
|
||||||
|
from ui.mqttmanager_ui import Ui_diag_mqtt
|
||||||
|
|
||||||
|
|
||||||
|
class MqttManager(QtWidgets.QDialog, Ui_diag_mqtt):
|
||||||
|
"""MQTT settings for option window."""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(MqttManager, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setFixedSize(self.size())
|
||||||
|
|
||||||
|
self.dc = {}
|
||||||
|
|
||||||
|
def _changesdone(self):
|
||||||
|
"""
|
||||||
|
Check for unsaved changes in dialog.
|
||||||
|
|
||||||
|
:return: True, if unsaved changes was found
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
not self.dc or
|
||||||
|
self.dc["mqttbasetopic"] != self.txt_basetopic.text() or
|
||||||
|
self.dc["mqttsendinterval"] != self.sbx_sendinterval.value() or
|
||||||
|
self.dc["mqttsend_on_event"] != int(self.cbx_send_on_event.isChecked()) or
|
||||||
|
self.dc["mqttwrite_outputs"] != int(self.cbx_write_outputs.isChecked()) or
|
||||||
|
self.dc["mqttbroker_address"] != self.txt_broker_address.text() or
|
||||||
|
self.dc["mqtttls_set"] != int(self.cbx_tls_set.isChecked()) or
|
||||||
|
self.dc["mqttport"] != self.sbx_port.value() or
|
||||||
|
self.dc["mqttusername"] != self.txt_username.text() or
|
||||||
|
self.dc["mqttpassword"] != self.txt_password.text() or
|
||||||
|
self.dc["mqttclient_id"] != self.txt_client_id.text()
|
||||||
|
)
|
||||||
|
|
||||||
|
def _load_settings(self):
|
||||||
|
"""Load values to GUI widgets."""
|
||||||
|
try:
|
||||||
|
self.txt_basetopic.setText(self.dc["mqttbasetopic"])
|
||||||
|
self.sbx_sendinterval.setValue(self.dc["mqttsendinterval"])
|
||||||
|
self.dc["mqttsend_on_event"] = int(self.cbx_send_on_event.isChecked())
|
||||||
|
self.dc["mqttwrite_outputs"] = int(self.cbx_write_outputs.isChecked())
|
||||||
|
self.txt_broker_address.setText(self.dc["mqttbroker_address"])
|
||||||
|
self.cbx_tls_set.setChecked(bool(self.dc["mqtttls_set"]))
|
||||||
|
self.sbx_port.setValue(self.dc["mqttport"])
|
||||||
|
self.txt_username.setText(self.dc["mqttusername"])
|
||||||
|
self.txt_password.setText(self.dc["mqttpassword"])
|
||||||
|
self.txt_client_id.setText(self.dc["mqttclient_id"])
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.exception(e)
|
||||||
|
self.dc = {}
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def accept(self) -> None:
|
||||||
|
"""Save values to master dict."""
|
||||||
|
self.dc["mqttbasetopic"] = self.txt_basetopic.text()
|
||||||
|
self.dc["mqttsendinterval"] = self.sbx_sendinterval.value()
|
||||||
|
self.dc["mqttsend_on_event"] = int(self.cbx_send_on_event.isChecked())
|
||||||
|
self.dc["mqttwrite_outputs"] = int(self.cbx_write_outputs.isChecked())
|
||||||
|
self.dc["mqttbroker_address"] = self.txt_broker_address.text()
|
||||||
|
self.dc["mqtttls_set"] = int(self.cbx_tls_set.isChecked())
|
||||||
|
self.dc["mqttport"] = self.sbx_port.value()
|
||||||
|
self.dc["mqttusername"] = self.txt_username.text()
|
||||||
|
self.dc["mqttpassword"] = self.txt_password.text()
|
||||||
|
self.dc["mqttclient_id"] = self.txt_client_id.text()
|
||||||
|
super(MqttManager, self).accept()
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
if self._changesdone():
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Do you really want to quit? \nUnsaved changes will be lost!"
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
if ask:
|
||||||
|
self.reject()
|
||||||
|
else:
|
||||||
|
a0.ignore()
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
"""Load settings from dc and show dialog."""
|
||||||
|
if not self._load_settings():
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not load the MQTT settings dialog. Missing values!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return QtWidgets.QDialog.Rejected
|
||||||
|
return super(MqttManager, self).exec()
|
||||||
|
|
||||||
|
def reject(self) -> None:
|
||||||
|
"""Reject settings."""
|
||||||
|
self._load_settings()
|
||||||
|
super(MqttManager, self).reject()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def read_only(self):
|
||||||
|
"""Getter for read_only value."""
|
||||||
|
return not self.txt_basetopic.isEnabled()
|
||||||
|
|
||||||
|
@read_only.setter
|
||||||
|
def read_only(self, value: bool):
|
||||||
|
"""Setter for read_only window."""
|
||||||
|
self.txt_basetopic.setEnabled(not value)
|
||||||
|
self.sbx_sendinterval.setEnabled(not value)
|
||||||
|
self.cbx_send_on_event.setEnabled(not value)
|
||||||
|
self.cbx_write_outputs.setEnabled(not value)
|
||||||
|
self.txt_broker_address.setEnabled(not value)
|
||||||
|
self.sbx_port.setEnabled(not value)
|
||||||
|
self.cbx_tls_set.setEnabled(not value)
|
||||||
|
self.txt_username.setEnabled(not value)
|
||||||
|
self.txt_password.setEnabled(not value)
|
||||||
|
self.txt_client_id.setEnabled(not value)
|
||||||
|
if value:
|
||||||
|
self.btn_box.setStandardButtons(
|
||||||
|
QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.btn_box.setStandardButtons(
|
||||||
|
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
122
revpicommander/proginit.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Global program initialization."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2019 Sven Sager"
|
||||||
|
__license__ = "LGPLv3"
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from os import R_OK, W_OK, access
|
||||||
|
from os.path import abspath, dirname, join
|
||||||
|
|
||||||
|
# Program name
|
||||||
|
programname = "revpicommander"
|
||||||
|
|
||||||
|
# Set to True, if you want to save config file
|
||||||
|
conf_rw = False
|
||||||
|
|
||||||
|
conf = ConfigParser()
|
||||||
|
logger = logging.getLogger()
|
||||||
|
pidfile = "/var/run/{0}.pid".format(programname)
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup():
|
||||||
|
"""Clean up program."""
|
||||||
|
# Shutdown logging system
|
||||||
|
logging.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
def reconfigure_logger():
|
||||||
|
"""Configure logging module of program."""
|
||||||
|
# Clear all log handler
|
||||||
|
for lhandler in logger.handlers.copy():
|
||||||
|
lhandler.close()
|
||||||
|
logger.removeHandler(lhandler)
|
||||||
|
|
||||||
|
# Create new log handler
|
||||||
|
logformat = logging.Formatter(
|
||||||
|
"{asctime} [{levelname:8}] {message}",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S", style="{"
|
||||||
|
)
|
||||||
|
lhandler = logging.StreamHandler(sys.stdout)
|
||||||
|
lhandler.setFormatter(logformat)
|
||||||
|
logger.addHandler(lhandler)
|
||||||
|
|
||||||
|
if "logfile" in pargs and pargs.logfile is not None:
|
||||||
|
# Write logs to a logfile
|
||||||
|
lhandler = logging.FileHandler(filename=pargs.logfile)
|
||||||
|
lhandler.setFormatter(logformat)
|
||||||
|
logger.addHandler(lhandler)
|
||||||
|
|
||||||
|
# Loglevel auswerten
|
||||||
|
if pargs.verbose == 1:
|
||||||
|
loglevel = logging.INFO
|
||||||
|
elif pargs.verbose > 1:
|
||||||
|
loglevel = logging.DEBUG
|
||||||
|
else:
|
||||||
|
loglevel = logging.WARNING
|
||||||
|
logger.setLevel(loglevel)
|
||||||
|
|
||||||
|
|
||||||
|
def reload_conf():
|
||||||
|
"""Reload config file."""
|
||||||
|
if "conffile" in pargs:
|
||||||
|
|
||||||
|
# Check config file
|
||||||
|
if not access(pargs.conffile, R_OK):
|
||||||
|
raise RuntimeError(
|
||||||
|
"can not access config file '{0}'".format(pargs.conffile)
|
||||||
|
)
|
||||||
|
if conf_rw and not access(pargs.conffile, W_OK):
|
||||||
|
raise RuntimeError(
|
||||||
|
"can not write to config file '{0}'".format(pargs.conffile)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create global config
|
||||||
|
global conf
|
||||||
|
logger.info("loading config file: {0}".format(pargs.conffile))
|
||||||
|
conf.read(pargs.conffile)
|
||||||
|
|
||||||
|
|
||||||
|
# Generate command arguments of the program
|
||||||
|
parser = ArgumentParser(
|
||||||
|
prog=programname,
|
||||||
|
description="Program description"
|
||||||
|
)
|
||||||
|
# parser.add_argument(
|
||||||
|
# "-c", "--conffile", dest="conffile",
|
||||||
|
# default="{0}.conf".format(programname),
|
||||||
|
# help="Application configuration file"
|
||||||
|
# )
|
||||||
|
parser.add_argument(
|
||||||
|
"-f", "--logfile", dest="logfile",
|
||||||
|
help="Save log entries to this file"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"-v", "--verbose", action="count", dest="verbose", default=0,
|
||||||
|
help="Switch on verbose logging"
|
||||||
|
)
|
||||||
|
pargs = parser.parse_args()
|
||||||
|
|
||||||
|
# Check important objects and set to default if they do not exists
|
||||||
|
if "verbose" not in pargs:
|
||||||
|
pargs.verbose = 0
|
||||||
|
|
||||||
|
# Get absolute paths
|
||||||
|
pwd = abspath(".")
|
||||||
|
|
||||||
|
# Configure logger
|
||||||
|
if "logfile" in pargs and pargs.logfile is not None \
|
||||||
|
and dirname(pargs.logfile) == "":
|
||||||
|
pargs.logfile = join(pwd, pargs.logfile)
|
||||||
|
reconfigure_logger()
|
||||||
|
|
||||||
|
# Initialize configparser of globalconfig
|
||||||
|
if "conffile" in pargs and dirname(pargs.conffile) == "":
|
||||||
|
pargs.conffile = join(pwd, pargs.conffile)
|
||||||
|
|
||||||
|
# Load configuration - Comment out, if you do that in your own program
|
||||||
|
reload_conf()
|
||||||
523
revpicommander/revpicommander.py
Executable file
@@ -0,0 +1,523 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""RevPiCommander main program."""
|
||||||
|
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
__version__ = "0.9.0"
|
||||||
|
|
||||||
|
import webbrowser
|
||||||
|
from os.path import basename, dirname, join
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
import revpilogfile
|
||||||
|
from avahisearch import AvahiSearch
|
||||||
|
from debugcontrol import DebugControl
|
||||||
|
from revpidevelop import RevPiDevelop
|
||||||
|
from revpiinfo import RevPiInfo
|
||||||
|
from revpioption import RevPiOption
|
||||||
|
from revpiplclist import RevPiPlcList
|
||||||
|
from revpiprogram import RevPiProgram
|
||||||
|
from ui.revpicommander_ui import Ui_win_revpicommander
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||||
|
"""Main application of RevPiCommander."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
"""Init main program."""
|
||||||
|
super(RevPiCommander, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.wid_debugcontrol = None # type: DebugControl
|
||||||
|
"""Holds the widget of DebugControl."""
|
||||||
|
self.wid_develop = None # type: RevPiDevelop
|
||||||
|
"""Holds the widget of RevPiDevelop."""
|
||||||
|
self.simulating = False
|
||||||
|
"""True, if simulation is running."""
|
||||||
|
self.dict_men_connections_subfolder = {}
|
||||||
|
"""Submenus for folder entries."""
|
||||||
|
|
||||||
|
# fixme: Prepare gui
|
||||||
|
#self.__base_size = self.size()
|
||||||
|
#self.setFixedSize(self.__base_size)
|
||||||
|
|
||||||
|
self._set_gui_control_states()
|
||||||
|
self._load_men_connections()
|
||||||
|
|
||||||
|
# Load sub windows
|
||||||
|
self.diag_connections = RevPiPlcList(self)
|
||||||
|
self.diag_search = AvahiSearch(self)
|
||||||
|
self.diag_info = RevPiInfo(self, __version__)
|
||||||
|
self.diag_options = RevPiOption(self)
|
||||||
|
self.diag_program = RevPiProgram(self)
|
||||||
|
self.win_log = revpilogfile.RevPiLogfile(self)
|
||||||
|
|
||||||
|
self.btn_plc_logs.pressed.connect(self.on_act_logs_triggered)
|
||||||
|
|
||||||
|
helper.cm.connection_disconnected.connect(self.on_cm_connection_disconnected)
|
||||||
|
helper.cm.connection_disconnecting.connect(self.on_cm_connection_disconnecting)
|
||||||
|
helper.cm.connection_established.connect(self.on_cm_connection_established)
|
||||||
|
helper.cm.connection_error_observed.connect(self.on_cm_connection_error_observed)
|
||||||
|
helper.cm.status_changed.connect(self.on_cm_status_changed)
|
||||||
|
|
||||||
|
self.restoreGeometry(helper.settings.value("geo", b''))
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
pi.logger.debug("RevPiCommander.closeEvent")
|
||||||
|
helper.cm.pyload_disconnect()
|
||||||
|
helper.settings.setValue("geo", self.saveGeometry())
|
||||||
|
|
||||||
|
def _set_gui_control_states(self):
|
||||||
|
"""Setup states of actions and buttons."""
|
||||||
|
connected = helper.cm.connected
|
||||||
|
self.men_plc.setEnabled(connected)
|
||||||
|
self.act_logs.setEnabled(connected)
|
||||||
|
self.act_options.setEnabled(connected and helper.cm.xml_mode >= 2)
|
||||||
|
self.act_program.setEnabled(connected and helper.cm.xml_mode >= 2)
|
||||||
|
self.act_developer.setEnabled(connected and helper.cm.xml_mode >= 3)
|
||||||
|
self.act_pictory.setEnabled(connected)
|
||||||
|
self.act_reset.setEnabled(connected and helper.cm.xml_mode >= 3)
|
||||||
|
self.act_disconnect.setEnabled(connected)
|
||||||
|
self.btn_plc_start.setEnabled(connected)
|
||||||
|
self.btn_plc_stop.setEnabled(connected)
|
||||||
|
self.btn_plc_restart.setEnabled(connected)
|
||||||
|
self.btn_plc_logs.setEnabled(connected)
|
||||||
|
self.btn_plc_debug.setEnabled(connected and helper.cm.xml_mode >= 1)
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Connection management
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_cm_connection_error_observed(self, message: str):
|
||||||
|
"""
|
||||||
|
Connection error occurred in connection manager.
|
||||||
|
|
||||||
|
:param message: Error message
|
||||||
|
"""
|
||||||
|
self.statusbar.showMessage(message, 15000)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_cm_connection_disconnected(self):
|
||||||
|
"""Connection of connection manager was disconnected."""
|
||||||
|
pi.logger.debug("RevPiCommander.on_cm_connection_disconnected")
|
||||||
|
|
||||||
|
self._set_gui_control_states()
|
||||||
|
self.txt_connection.clear()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_cm_connection_disconnecting(self):
|
||||||
|
"""Connection of connection manager will now disconnect."""
|
||||||
|
pi.logger.debug("RevPiCommander.on_cm_connection_disconnecting")
|
||||||
|
|
||||||
|
# This will remove the widgets in the button functions
|
||||||
|
self.btn_plc_debug.setChecked(False)
|
||||||
|
self.act_developer.setChecked(False)
|
||||||
|
|
||||||
|
self.diag_info.reject()
|
||||||
|
self.diag_options.reject()
|
||||||
|
self.diag_program.reject()
|
||||||
|
|
||||||
|
self.centralwidget.adjustSize()
|
||||||
|
self.adjustSize()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_cm_connection_established(self):
|
||||||
|
"""Connection manager established a new connection and loaded values."""
|
||||||
|
pi.logger.debug("RevPiCommander.on_cm_connection_established")
|
||||||
|
|
||||||
|
self._set_gui_control_states()
|
||||||
|
self.txt_connection.setText("{0} - {1}:{2}".format(
|
||||||
|
helper.cm.name,
|
||||||
|
helper.cm.address,
|
||||||
|
helper.cm.port
|
||||||
|
))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str, str)
|
||||||
|
def on_cm_status_changed(self, text: str, color: str):
|
||||||
|
"""PLC program status from Revolution Pi."""
|
||||||
|
self.txt_status.setText(text)
|
||||||
|
self.txt_status.setStyleSheet("background: {0}".format(color))
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Menu entries
|
||||||
|
|
||||||
|
def _load_men_connections(self):
|
||||||
|
"""Build up connections menu."""
|
||||||
|
|
||||||
|
self.men_connections.clear()
|
||||||
|
self.dict_men_connections_subfolder.clear()
|
||||||
|
|
||||||
|
for i in range(helper.settings.beginReadArray("connections")):
|
||||||
|
helper.settings.setArrayIndex(i)
|
||||||
|
|
||||||
|
act = QtWidgets.QAction(self)
|
||||||
|
act.setText(helper.settings.value("name"))
|
||||||
|
act.setData(i)
|
||||||
|
act.setToolTip("{0}:{1}".format(
|
||||||
|
helper.settings.value("address"),
|
||||||
|
helper.settings.value("port"),
|
||||||
|
))
|
||||||
|
|
||||||
|
if helper.settings.value("folder"):
|
||||||
|
if helper.settings.value("folder") not in self.dict_men_connections_subfolder:
|
||||||
|
men_sub = QtWidgets.QMenu(self)
|
||||||
|
men_sub.setTitle(helper.settings.value("folder"))
|
||||||
|
self.dict_men_connections_subfolder[helper.settings.value("folder")] = men_sub
|
||||||
|
self.men_connections.addMenu(men_sub)
|
||||||
|
self.dict_men_connections_subfolder[helper.settings.value("folder")].addAction(act)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.men_connections.addAction(act)
|
||||||
|
|
||||||
|
helper.settings.endArray()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_connections_triggered(self):
|
||||||
|
"""Edit saved connections to Revolution Pi devices."""
|
||||||
|
if self.diag_connections.exec() == QtWidgets.QDialog.Accepted:
|
||||||
|
self._load_men_connections()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_search_triggered(self):
|
||||||
|
"""Search for Revolution Pi with zero conf."""
|
||||||
|
if self.diag_search.exec() == QtWidgets.QDialog.Accepted:
|
||||||
|
if self.diag_search.connect_index >= 0:
|
||||||
|
helper.cm.pyload_connect(self.diag_search.connect_index)
|
||||||
|
|
||||||
|
self._load_men_connections()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_simulator_triggered(self):
|
||||||
|
"""Start the simulator function."""
|
||||||
|
helper.cm.pyload_disconnect()
|
||||||
|
|
||||||
|
diag_open = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Select downloaded piCtory file..."),
|
||||||
|
helper.settings.value("simulator_pictory", ".", str),
|
||||||
|
self.tr("piCtory file (*.rsc);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||||
|
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||||
|
diag_open.setDefaultSuffix("rsc")
|
||||||
|
|
||||||
|
if diag_open.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_open.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
configrsc_file = diag_open.selectedFiles()[0]
|
||||||
|
dir_name = dirname(configrsc_file)
|
||||||
|
procimg_file = join(dir_name, "{0}.img".format(
|
||||||
|
basename(configrsc_file).rsplit(".", maxsplit=1)[0]
|
||||||
|
))
|
||||||
|
helper.settings.setValue("simulator_pictory", configrsc_file)
|
||||||
|
|
||||||
|
if helper.cm.pyload_simulate(configrsc_file, procimg_file):
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Simulator started..."), self.tr(
|
||||||
|
"The simulator is running!\n\nYou can work with this simulator if your call "
|
||||||
|
"RevPiModIO with this additional parameters:\nprocimg={procimg}\nconfigrsc={config}\n\n"
|
||||||
|
"You can copy that from header textbox."
|
||||||
|
).format(procimg=procimg_file, config=configrsc_file)
|
||||||
|
)
|
||||||
|
self.txt_connection.setText(
|
||||||
|
"configrsc=\"{0}\", procimg=\"{1}\"".format(configrsc_file, procimg_file)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Stop button will disable simulating
|
||||||
|
self.btn_plc_stop.setEnabled(True)
|
||||||
|
self.btn_plc_restart.setEnabled(True)
|
||||||
|
self.btn_plc_debug.setEnabled(True)
|
||||||
|
else:
|
||||||
|
pi.logger.error("Can not start simulator")
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Can not start..."), self.tr(
|
||||||
|
"Can not start the simulator! Maybe the piCtory file is corrupt "
|
||||||
|
"or you can not write to the location '{0}'.".format(dir_name)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_logs_triggered(self):
|
||||||
|
"""Show log window."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
if "load_plclog" not in helper.cm.xml_funcs:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"This version of Logviewer ist not supported in version {0} "
|
||||||
|
"of RevPiPyLoad on your RevPi! You need at least version "
|
||||||
|
"0.4.1."
|
||||||
|
).format(helper.cm.call_remote_function("version", default_value="-"))
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.win_log.isHidden():
|
||||||
|
self.win_log.show()
|
||||||
|
else:
|
||||||
|
self.win_log.activateWindow()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_options_triggered(self):
|
||||||
|
"""Show option dialog."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
if helper.cm.xml_mode < 2:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"XML-RPC access mode in the RevPiPyLoad "
|
||||||
|
"configuration is too small to access this dialog!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check version of RevPiPyLoad, must be greater than 0.5!
|
||||||
|
if helper.cm.pyload_version[0] == 0 and helper.cm.pyload_version[1] < 6:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The Version of RevPiPyLoad on your Revolution Pi ({0}) is to old. "
|
||||||
|
"This Version of RevPiCommander require at least version 0.6.0 "
|
||||||
|
"of RevPiPyLoad. Please update your Revolution Pi!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
rc = self.diag_options.exec()
|
||||||
|
if rc == QtWidgets.QDialog.Accepted:
|
||||||
|
helper.cm.refresh_xml_mode()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_program_triggered(self):
|
||||||
|
"""Show program dialog."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
if helper.cm.xml_mode < 2:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"XML-RPC access mode in the RevPiPyLoad "
|
||||||
|
"configuration is too small to access this dialog!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.diag_program.exec()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(bool)
|
||||||
|
def on_act_developer_toggled(self, state: bool):
|
||||||
|
"""Extent developer mode to main window."""
|
||||||
|
if not (state or self.wid_develop is None):
|
||||||
|
# Remove widget
|
||||||
|
self.gl.removeWidget(self.wid_develop)
|
||||||
|
self.wid_develop.deleteLater()
|
||||||
|
self.wid_develop = None
|
||||||
|
self.gl.setColumnStretch(1, 0)
|
||||||
|
|
||||||
|
elif state and helper.cm.connected:
|
||||||
|
if helper.cm.xml_mode < 2:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"XML-RPC access mode in the RevPiPyLoad "
|
||||||
|
"configuration is too small to access this dialog!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.act_developer.setChecked(False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.wid_develop = RevPiDevelop(self.centralwidget)
|
||||||
|
self.gl.addWidget(self.wid_develop, 0, 1, 8, 1)
|
||||||
|
self.gl.setColumnStretch(1, 1)
|
||||||
|
|
||||||
|
elif state:
|
||||||
|
self.act_developer.setChecked(False)
|
||||||
|
|
||||||
|
self.centralwidget.adjustSize()
|
||||||
|
self.adjustSize()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_pictory_triggered(self):
|
||||||
|
"""Open piCtory in default browser of operating system."""
|
||||||
|
if helper.cm.address:
|
||||||
|
webbrowser.open("http://{0}/".format(helper.cm.address))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_reset_triggered(self):
|
||||||
|
"""Reset piControl driver on revolution pi."""
|
||||||
|
if not (helper.cm.connected and helper.cm.xml_mode >= 3):
|
||||||
|
return
|
||||||
|
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Are you sure to reset piControl?\n"
|
||||||
|
"The pictory configuration will be reloaded. During that time "
|
||||||
|
"the process image will be interrupted and could rise errors "
|
||||||
|
"on running control programs!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if ask != QtWidgets.QMessageBox.Yes:
|
||||||
|
return
|
||||||
|
|
||||||
|
ec = helper.cm.call_remote_function("resetpicontrol", default_value=-1)
|
||||||
|
if ec == 0:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"piControl reset executed successfully"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"piControl reset could not be executed successfully"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_disconnect_triggered(self):
|
||||||
|
"""Close actual connection."""
|
||||||
|
helper.cm.pyload_disconnect()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtWidgets.QAction)
|
||||||
|
def on_men_connections_triggered(self, action: QtWidgets.QAction):
|
||||||
|
"""A connection is selected in the men_connections menu."""
|
||||||
|
if not helper.cm.pyload_connect(action.data()):
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not connect to RevPi XML-RPC Service! \n\n"
|
||||||
|
"This could have the following reasons: The RevPi is not "
|
||||||
|
"online, the XML-RPC service is not running or the ACL "
|
||||||
|
"permission is not set for your IP!!!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_webpage_triggered(self):
|
||||||
|
"""Open project page in default browser of operating system."""
|
||||||
|
webbrowser.open("https://revpimodio.org")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_act_info_triggered(self):
|
||||||
|
"""Open info window of application."""
|
||||||
|
self.diag_info.exec()
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Button events
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_plc_start_clicked(self):
|
||||||
|
"""Start plc program on revolution pi."""
|
||||||
|
helper.cm.call_remote_function("plcstart")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_plc_stop_clicked(self):
|
||||||
|
"""Start plc program on revolution pi."""
|
||||||
|
if helper.cm.simulating:
|
||||||
|
helper.cm.pyload_disconnect()
|
||||||
|
else:
|
||||||
|
helper.cm.call_remote_function("plcstop")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_plc_restart_clicked(self):
|
||||||
|
"""Restart plc program on revolution pi."""
|
||||||
|
if helper.cm.simulating:
|
||||||
|
rc = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Reset to piCtory defaults..."), self.tr(
|
||||||
|
"Do you want to reset your process image to piCtory default values?\n"
|
||||||
|
"You have to stop other RevPiModIO programs before doing that, "
|
||||||
|
"because they could reset the outputs."
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
if rc:
|
||||||
|
# Set piCtory default values in process image
|
||||||
|
helper.cm.reset_simulator()
|
||||||
|
else:
|
||||||
|
helper.cm.call_remote_function("plcstop")
|
||||||
|
helper.cm.call_remote_function("plcstart")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(bool)
|
||||||
|
def on_btn_plc_debug_toggled(self, state: bool):
|
||||||
|
"""Start plc watch mode."""
|
||||||
|
if not (state or self.wid_debugcontrol is None):
|
||||||
|
# Remove widget
|
||||||
|
self.gl.removeWidget(self.wid_debugcontrol)
|
||||||
|
self.wid_debugcontrol.deleteLater()
|
||||||
|
self.wid_debugcontrol = None
|
||||||
|
|
||||||
|
elif "psstart" not in helper.cm.xml_funcs:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"The watch mode ist not supported in version {0} "
|
||||||
|
"of RevPiPyLoad on your RevPi! You need at least version "
|
||||||
|
"0.5.3! Maybe the python3-revpimodio2 module is not "
|
||||||
|
"installed on your RevPi at least version 2.0.0."
|
||||||
|
).format(helper.cm.call_remote_function("version", "-"))
|
||||||
|
)
|
||||||
|
self.btn_plc_debug.setChecked(False)
|
||||||
|
self.btn_plc_debug.setEnabled(False)
|
||||||
|
|
||||||
|
elif helper.cm.xml_mode < 1 and not helper.cm.simulating:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not load this function, because your ACL level is to low!\n"
|
||||||
|
"You need at least level 1 to read or level 3 to write."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.btn_plc_debug.setChecked(False)
|
||||||
|
|
||||||
|
elif helper.cm.connected or helper.cm.simulating:
|
||||||
|
debugcontrol = DebugControl(self.centralwidget)
|
||||||
|
if debugcontrol.reload_devices():
|
||||||
|
self.wid_debugcontrol = debugcontrol
|
||||||
|
self.gl.addWidget(self.wid_debugcontrol, 7, 0)
|
||||||
|
else:
|
||||||
|
debugcontrol.deleteLater()
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not load piCtory configuration. \n"
|
||||||
|
"Did you create a hardware configuration? "
|
||||||
|
"Please check this in piCtory!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.btn_plc_debug.setChecked(False)
|
||||||
|
|
||||||
|
self.centralwidget.adjustSize()
|
||||||
|
self.adjustSize()
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
app = QtWidgets.QApplication(sys.argv)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Setup translation from file with system language
|
||||||
|
locale = QtCore.QLocale.system().name()
|
||||||
|
translator = QtCore.QTranslator()
|
||||||
|
translator.load("locale/revpicommander_{0}".format(locale), suffix=".qm")
|
||||||
|
app.installTranslator(translator)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Prepare workers
|
||||||
|
helper.cm.start()
|
||||||
|
|
||||||
|
win = RevPiCommander()
|
||||||
|
win.show()
|
||||||
|
exit_code = app.exec_()
|
||||||
|
|
||||||
|
# Clean up workers
|
||||||
|
helper.cm.requestInterruption()
|
||||||
|
helper.cm.wait()
|
||||||
|
|
||||||
|
sys.exit(exit_code)
|
||||||
299
revpicommander/revpidevelop.py
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""PLC developer to upload your active development."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
import os
|
||||||
|
from enum import IntEnum
|
||||||
|
from os import DirEntry
|
||||||
|
from xmlrpc.client import Binary
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from helper import WidgetData
|
||||||
|
from ui.revpidevelop_ui import Ui_wid_develop
|
||||||
|
|
||||||
|
|
||||||
|
class NodeType(IntEnum):
|
||||||
|
FILE = 1000
|
||||||
|
DIR = 1001
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiDevelop(QtWidgets.QWidget, Ui_wid_develop):
|
||||||
|
"""Developer extension for RevPiCommander."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(RevPiDevelop, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.tree_files_counter = 0
|
||||||
|
self.tree_files_counter_max = 10000
|
||||||
|
self.lbl_path.setText(helper.cm.develop_watch_path or self.tr("Please select..."))
|
||||||
|
|
||||||
|
self.btn_all.setEnabled(False)
|
||||||
|
self.btn_upload.setEnabled(False)
|
||||||
|
|
||||||
|
if helper.cm.develop_watch_path:
|
||||||
|
self._load_path_files(True)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
pi.logger.debug("RevPiDevelop.__del__")
|
||||||
|
|
||||||
|
def _do_my_job(self, stop_restart=True):
|
||||||
|
"""
|
||||||
|
Upload the selected files and do a optionally restart.
|
||||||
|
|
||||||
|
:param stop_restart: True will restart program
|
||||||
|
"""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
if stop_restart and helper.cm.call_remote_function("plcstop") is None:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not stop plc program on Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get config to find actual auto start program for warnings
|
||||||
|
opt_program = helper.cm.call_remote_function("get_config", default_value={})
|
||||||
|
opt_program = opt_program.get("plcprogram", "none.py")
|
||||||
|
uploaded = True # Will be False, when opt_program was found in files
|
||||||
|
ec = 0
|
||||||
|
|
||||||
|
for file_name in self.file_list():
|
||||||
|
# todo: Check exception of local file
|
||||||
|
with open(file_name, "rb") as fh:
|
||||||
|
# Remove base dir of file to set relative for PyLoad
|
||||||
|
send_name = file_name.replace(helper.cm.develop_watch_path, "")[1:]
|
||||||
|
|
||||||
|
# Check whether this is the auto start program
|
||||||
|
if send_name == opt_program:
|
||||||
|
uploaded = False
|
||||||
|
|
||||||
|
# Transfer file
|
||||||
|
try:
|
||||||
|
upload_status = helper.cm.call_remote_function(
|
||||||
|
"plcupload", Binary(gzip.compress(fh.read())), send_name,
|
||||||
|
default_value=False
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.error(e)
|
||||||
|
ec = -2
|
||||||
|
break
|
||||||
|
|
||||||
|
if not upload_status:
|
||||||
|
ec = -1
|
||||||
|
break
|
||||||
|
|
||||||
|
if ec == 0:
|
||||||
|
# Tell user, we did not find the auto start program in files
|
||||||
|
if uploaded:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Information..."), self.tr(
|
||||||
|
"A PLC program has been uploaded. Please check the "
|
||||||
|
"PLC options to see if the correct program is "
|
||||||
|
"specified as the start program."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif ec == -1:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The Revolution Pi could not process some parts of the "
|
||||||
|
"transmission."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif ec == -2:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"),
|
||||||
|
self.tr("Errors occurred during transmission")
|
||||||
|
)
|
||||||
|
|
||||||
|
if stop_restart and helper.cm.call_remote_function("plcstart", default_value=1) != 0:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"Could not start the plc program on Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Tree management
|
||||||
|
|
||||||
|
def __insert_files(self, base_dir: str, child=None):
|
||||||
|
"""
|
||||||
|
Recursively add files to tree view.
|
||||||
|
|
||||||
|
:param base_dir: Directory to scan for files
|
||||||
|
:param child: Child widget to add new widgets
|
||||||
|
"""
|
||||||
|
for de in os.scandir(base_dir): # type: DirEntry
|
||||||
|
|
||||||
|
if self.tree_files_counter > self.tree_files_counter_max:
|
||||||
|
return
|
||||||
|
|
||||||
|
if de.is_dir(follow_symlinks=False):
|
||||||
|
item = QtWidgets.QTreeWidgetItem(NodeType.DIR)
|
||||||
|
item.setText(0, de.name)
|
||||||
|
item.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
|
||||||
|
if child:
|
||||||
|
child.addChild(item)
|
||||||
|
else:
|
||||||
|
self.tree_files.addTopLevelItem(item)
|
||||||
|
|
||||||
|
self.__insert_files(de.path, item)
|
||||||
|
|
||||||
|
elif de.is_file(follow_symlinks=False):
|
||||||
|
self.tree_files_counter += 1
|
||||||
|
|
||||||
|
item = QtWidgets.QTreeWidgetItem(NodeType.FILE)
|
||||||
|
item.setText(0, de.name)
|
||||||
|
item.setData(0, WidgetData.file_name, de.path)
|
||||||
|
item.setIcon(0, QtGui.QIcon(
|
||||||
|
":/file/ico/file-else.ico" if de.name.find(".py") == -1 else
|
||||||
|
":/file/ico/file-python.ico"
|
||||||
|
))
|
||||||
|
if child:
|
||||||
|
child.addChild(item)
|
||||||
|
else:
|
||||||
|
self.tree_files.addTopLevelItem(item)
|
||||||
|
|
||||||
|
item.setSelected(de.path in helper.cm.develop_watch_files)
|
||||||
|
self._parent_selected(item)
|
||||||
|
|
||||||
|
def __select_children(self, top_item: QtWidgets.QTreeWidgetItem, value: bool):
|
||||||
|
"""Recursive select files from directory."""
|
||||||
|
pi.logger.debug("RevPiDevelop.__select_children")
|
||||||
|
|
||||||
|
for i in range(top_item.childCount()):
|
||||||
|
item = top_item.child(i)
|
||||||
|
if item.type() == NodeType.DIR:
|
||||||
|
self.__select_children(item, value)
|
||||||
|
elif item.type() == NodeType.FILE:
|
||||||
|
item.setSelected(value)
|
||||||
|
|
||||||
|
def _load_path_files(self, silent=False):
|
||||||
|
"""
|
||||||
|
Refresh the file list.
|
||||||
|
|
||||||
|
:param silent: Do not show message boxes
|
||||||
|
"""
|
||||||
|
pi.logger.debug("RevPiDevelop._load_path_files")
|
||||||
|
|
||||||
|
self.tree_files_counter = 0
|
||||||
|
self.tree_files.blockSignals(True)
|
||||||
|
self.tree_files.clear()
|
||||||
|
self.tree_files.blockSignals(False)
|
||||||
|
|
||||||
|
self.__insert_files(helper.cm.develop_watch_path)
|
||||||
|
self.tree_files.sortItems(0, QtCore.Qt.AscendingOrder)
|
||||||
|
|
||||||
|
if not silent and self.tree_files_counter > self.tree_files_counter_max:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Stop scanning for files, because we found more than {0} files."
|
||||||
|
).format(self.tree_files_counter_max)
|
||||||
|
)
|
||||||
|
|
||||||
|
state = len(self.tree_files.selectedItems()) > 0
|
||||||
|
self.btn_all.setEnabled(state)
|
||||||
|
self.btn_upload.setEnabled(state)
|
||||||
|
|
||||||
|
def _parent_selected(self, item: QtWidgets.QTreeWidgetItem):
|
||||||
|
"""Check all children of a parent are selected."""
|
||||||
|
if item.parent():
|
||||||
|
all_selected = True
|
||||||
|
for i in range(item.parent().childCount()):
|
||||||
|
if not item.parent().child(i).isSelected():
|
||||||
|
all_selected = False
|
||||||
|
break
|
||||||
|
item.parent().setSelected(all_selected)
|
||||||
|
|
||||||
|
def file_list(self):
|
||||||
|
"""Generate a file list with full path of selected entries."""
|
||||||
|
pi.logger.debug("RevPiDevelop.file_list")
|
||||||
|
lst = []
|
||||||
|
for item in self.tree_files.selectedItems():
|
||||||
|
if item.type() == NodeType.DIR:
|
||||||
|
continue
|
||||||
|
lst.append(item.data(0, WidgetData.file_name))
|
||||||
|
|
||||||
|
return lst
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_tree_files_itemSelectionChanged(self):
|
||||||
|
item = self.tree_files.currentItem()
|
||||||
|
if item is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
pi.logger.debug("RevPiDevelop.on_tree_files_itemSelectionChanged")
|
||||||
|
|
||||||
|
# Block while preselect other entries
|
||||||
|
self.tree_files.blockSignals(True)
|
||||||
|
|
||||||
|
if item.type() == NodeType.DIR:
|
||||||
|
self.__select_children(item, item.isSelected())
|
||||||
|
|
||||||
|
elif item.type() == NodeType.FILE:
|
||||||
|
self._parent_selected(item)
|
||||||
|
|
||||||
|
self.tree_files.blockSignals(False)
|
||||||
|
|
||||||
|
state = len(self.tree_files.selectedItems()) > 0
|
||||||
|
self.btn_all.setEnabled(state)
|
||||||
|
self.btn_upload.setEnabled(state)
|
||||||
|
|
||||||
|
helper.cm.develop_watch_files = self.file_list()
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_all_pressed(self):
|
||||||
|
pi.logger.debug("RevPiDevelop.on_btn_all_pressed")
|
||||||
|
self._do_my_job(True)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_select_pressed(self):
|
||||||
|
pi.logger.debug("RevPiDevelop.on_btn_select_pressed")
|
||||||
|
|
||||||
|
diag_folder = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Select folder..."),
|
||||||
|
helper.cm.develop_watch_path,
|
||||||
|
)
|
||||||
|
diag_folder.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
|
||||||
|
if diag_folder.exec() != QtWidgets.QFileDialog.Accepted:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_dir = diag_folder.selectedFiles()[0]
|
||||||
|
|
||||||
|
if not os.access(selected_dir, os.R_OK):
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not access the folder '{0}' to read files."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
helper.cm.develop_watch_files = []
|
||||||
|
helper.cm.develop_watch_path = ""
|
||||||
|
return
|
||||||
|
|
||||||
|
self.lbl_path.setText(selected_dir)
|
||||||
|
helper.cm.develop_watch_path = selected_dir
|
||||||
|
helper.cm.develop_watch_files = []
|
||||||
|
|
||||||
|
self._load_path_files(False)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_refresh_pressed(self):
|
||||||
|
pi.logger.debug("RevPiDevelop.on_btn_refresh_pressed")
|
||||||
|
self._load_path_files(False)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_upload_pressed(self):
|
||||||
|
pi.logger.debug("RevPiDevelop.on_btn_upload_pressed")
|
||||||
|
self._do_my_job(False)
|
||||||
53
revpicommander/revpiinfo.py
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Program information of local an remote system."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
from ui.revpiinfo_ui import Ui_diag_revpiinfo
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiInfo(QtWidgets.QDialog, Ui_diag_revpiinfo):
|
||||||
|
"""Version information window."""
|
||||||
|
|
||||||
|
def __init__(self, parent, version: str):
|
||||||
|
super(RevPiInfo, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self._debug_load = False
|
||||||
|
|
||||||
|
self.lbl_version_control.setText(version)
|
||||||
|
self.lbl_version_control.mousePressEvent = self.lbl_version_mousePressEvent
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
self.lbl_version_pyload.setText(
|
||||||
|
"{0}.{1}.{2}".format(*helper.cm.pyload_version)
|
||||||
|
if helper.cm.connected else "-"
|
||||||
|
)
|
||||||
|
self._load_lst_files()
|
||||||
|
return super(RevPiInfo, self).exec()
|
||||||
|
|
||||||
|
def lbl_version_mousePressEvent(self, a0: QtGui.QMouseEvent):
|
||||||
|
if a0.button() == QtCore.Qt.MidButton:
|
||||||
|
self._debug_load = not self._debug_load
|
||||||
|
self._load_lst_files()
|
||||||
|
|
||||||
|
def _load_lst_files(self):
|
||||||
|
"""Load files from working dir on Revolution Pi."""
|
||||||
|
self.lst_files.clear()
|
||||||
|
|
||||||
|
if self._debug_load:
|
||||||
|
lst = helper.cm.xml_funcs
|
||||||
|
elif helper.cm.connected:
|
||||||
|
lst = helper.cm.call_remote_function(
|
||||||
|
"get_filelist",
|
||||||
|
default_value=[self.tr("Can not load file list")]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
lst = [self.tr("Not connected")]
|
||||||
|
|
||||||
|
lst.sort()
|
||||||
|
self.lst_files.insertItems(0, lst)
|
||||||
209
revpicommander/revpilogfile.py
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""View log files from Revolution Pi."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from ui.revpilogfile_ui import Ui_win_revpilogfile
|
||||||
|
|
||||||
|
|
||||||
|
class LogType(IntEnum):
|
||||||
|
NONE = 0
|
||||||
|
APP = 1
|
||||||
|
DAEMON = 2
|
||||||
|
|
||||||
|
|
||||||
|
class DataThread(QtCore.QThread):
|
||||||
|
|
||||||
|
error_detected = QtCore.pyqtSignal()
|
||||||
|
line_logged = QtCore.pyqtSignal(LogType, bool, str)
|
||||||
|
|
||||||
|
def __init__(self, parent=None, cycle_time=1000):
|
||||||
|
super(DataThread, self).__init__(parent)
|
||||||
|
|
||||||
|
self._cli = helper.cm.get_cli()
|
||||||
|
self._cycle_time = cycle_time
|
||||||
|
self._paused = True
|
||||||
|
self.error_count = 0
|
||||||
|
self.max_block = 16384
|
||||||
|
self.mrk_app = 0
|
||||||
|
self.mrk_daemon = 0
|
||||||
|
|
||||||
|
def _load_log(self, log_type: LogType, xmlcall, start_position: int):
|
||||||
|
"""
|
||||||
|
Get log text and put it to log viewer.
|
||||||
|
|
||||||
|
:param log_type: Type of log file <class 'LogType'>
|
||||||
|
:param xmlcall: XML-RPC call to get log text
|
||||||
|
:param start_position: Start position in log file to read from
|
||||||
|
:return: New position in log file
|
||||||
|
"""
|
||||||
|
buff_log = b''
|
||||||
|
while True:
|
||||||
|
# Load max data from start position to see the end of logfile
|
||||||
|
bytebuff = xmlcall(start_position, self.max_block).data
|
||||||
|
buff_log += bytebuff
|
||||||
|
start_position += len(bytebuff)
|
||||||
|
|
||||||
|
# If we get less than max_block, the log file is EOF for this time
|
||||||
|
if len(bytebuff) < self.max_block:
|
||||||
|
break
|
||||||
|
|
||||||
|
if bytebuff == b'\x16': # 'ESC'
|
||||||
|
# RevPiPyLoad could not access log file on Revolution Pi
|
||||||
|
self.line_logged.emit(log_type, False, "")
|
||||||
|
|
||||||
|
elif bytebuff == b'\x19': # 'EndOfMedia'
|
||||||
|
# The log file was rotated by log rotate on the Revolution Pi
|
||||||
|
start_position = 0
|
||||||
|
|
||||||
|
elif buff_log:
|
||||||
|
self.line_logged.emit(log_type, True, buff_log.decode("utf-8"))
|
||||||
|
|
||||||
|
return start_position
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
"""Stop checking new log lines, but leave thread alive."""
|
||||||
|
self._paused = True
|
||||||
|
|
||||||
|
def resume(self):
|
||||||
|
"""Start checking for new log lines."""
|
||||||
|
self._paused = False
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
pi.logger.debug("DataThread.run")
|
||||||
|
|
||||||
|
while not self.isInterruptionRequested():
|
||||||
|
if not self._paused:
|
||||||
|
try:
|
||||||
|
self.mrk_app = self._load_log(
|
||||||
|
LogType.APP,
|
||||||
|
self._cli.load_applog,
|
||||||
|
self.mrk_app,
|
||||||
|
)
|
||||||
|
self.mrk_daemon = self._load_log(
|
||||||
|
LogType.DAEMON,
|
||||||
|
self._cli.load_plclog,
|
||||||
|
self.mrk_daemon,
|
||||||
|
)
|
||||||
|
self.error_count = 0
|
||||||
|
except Exception:
|
||||||
|
if self.error_count == 0:
|
||||||
|
self.error_detected.emit()
|
||||||
|
self.error_count += 1
|
||||||
|
|
||||||
|
self.msleep(self._cycle_time)
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiLogfile(QtWidgets.QMainWindow, Ui_win_revpilogfile):
|
||||||
|
"""Log file viewer for daemon and plc program log."""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
u"""Init RevPiLogfile-Class."""
|
||||||
|
super(RevPiLogfile, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.th_data = DataThread()
|
||||||
|
self.err_daemon = 0
|
||||||
|
|
||||||
|
helper.cm.connection_established.connect(self.on_cm_connection_established)
|
||||||
|
helper.cm.connection_disconnecting.connect(self.on_cm_connection_disconnecting)
|
||||||
|
|
||||||
|
self._load_gui_settings()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
pi.logger.debug("RevPiLogfile.__del__")
|
||||||
|
self.th_data.deleteLater()
|
||||||
|
|
||||||
|
def _create_data_thread(self):
|
||||||
|
self.th_data.deleteLater()
|
||||||
|
|
||||||
|
self.th_data = DataThread()
|
||||||
|
self.th_data.line_logged.connect(self.on_line_logged)
|
||||||
|
self.th_data.start()
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
helper.settings.setValue("logfile/geo", self.saveGeometry())
|
||||||
|
helper.settings.setValue("logfile/stay_on_top", self.cbx_stay_on_top.isChecked())
|
||||||
|
|
||||||
|
def hideEvent(self, a0: QtGui.QHideEvent) -> None:
|
||||||
|
self.th_data.pause()
|
||||||
|
super(RevPiLogfile, self).hideEvent(a0)
|
||||||
|
|
||||||
|
def showEvent(self, a0: QtGui.QShowEvent) -> None:
|
||||||
|
self.th_data.resume()
|
||||||
|
super(RevPiLogfile, self).showEvent(a0)
|
||||||
|
|
||||||
|
def _load_gui_settings(self):
|
||||||
|
self.restoreGeometry(helper.settings.value("logfile/geo", b''))
|
||||||
|
self.cbx_stay_on_top.setChecked(helper.settings.value("logfile/stay_on_top", False, bool))
|
||||||
|
self.cbx_wrap.setChecked(helper.settings.value("logfile/wrap", False, bool))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_cm_connection_disconnecting(self):
|
||||||
|
"""Disconnecting form Revolution Pi."""
|
||||||
|
self.th_data.requestInterruption()
|
||||||
|
self.th_data.wait()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_cm_connection_established(self):
|
||||||
|
"""New connection established."""
|
||||||
|
self.txt_app.clear()
|
||||||
|
self.txt_daemon.clear()
|
||||||
|
|
||||||
|
self._create_data_thread()
|
||||||
|
if self.isVisible():
|
||||||
|
self.th_data.resume()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_daemon_pressed(self):
|
||||||
|
"""Clear the daemon log view."""
|
||||||
|
self.txt_daemon.clear()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_app_pressed(self):
|
||||||
|
"""Clear the app log view."""
|
||||||
|
self.txt_app.clear()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbx_stay_on_top_stateChanged(self, state: int):
|
||||||
|
"""Set flag to stay on top of all windows."""
|
||||||
|
self.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint, state == QtCore.Qt.Checked)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbx_wrap_stateChanged(self, state: int):
|
||||||
|
"""Line wrap mode."""
|
||||||
|
wrap_mode = QtWidgets.QPlainTextEdit.WidgetWidth if state == QtCore.Qt.Checked else \
|
||||||
|
QtWidgets.QPlainTextEdit.NoWrap
|
||||||
|
self.txt_daemon.setLineWrapMode(wrap_mode)
|
||||||
|
self.txt_app.setLineWrapMode(wrap_mode)
|
||||||
|
helper.settings.setValue("logfile/wrap", self.cbx_wrap.isChecked())
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(LogType, bool, str)
|
||||||
|
def on_line_logged(self, log_type: LogType, success: bool, text: str):
|
||||||
|
pi.logger.debug("RevPiLogfile.on_line_logged")
|
||||||
|
|
||||||
|
if log_type == LogType.APP:
|
||||||
|
textwidget = self.txt_app
|
||||||
|
elif log_type == LogType.DAEMON:
|
||||||
|
textwidget = self.txt_daemon
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
bar = textwidget.verticalScrollBar()
|
||||||
|
auto_scroll = bar.value() == bar.maximum()
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
textwidget.clear()
|
||||||
|
textwidget.setPlainText(self.tr("Can not access log file on the RevPi"))
|
||||||
|
elif text != "":
|
||||||
|
# Function will add \n automatically
|
||||||
|
textwidget.appendPlainText(text.strip("\n"))
|
||||||
|
if auto_scroll:
|
||||||
|
bar.setValue(bar.maximum())
|
||||||
320
revpicommander/revpioption.py
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""RevPiPyLoad options window."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from aclmanager import AclManager
|
||||||
|
from mqttmanager import MqttManager
|
||||||
|
from ui.revpioption_ui import Ui_diag_options
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||||
|
"""Set options of RevPiPyLoad."""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(RevPiOption, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setFixedSize(self.size())
|
||||||
|
|
||||||
|
self.dc = {}
|
||||||
|
self.acl_plcslave = ""
|
||||||
|
self.acl_xmlrpc = ""
|
||||||
|
self.mrk_xml_ask = True
|
||||||
|
|
||||||
|
self._dict_mqttsettings = {
|
||||||
|
"mqttbasetopic": "revpi01",
|
||||||
|
"mqttclient_id": "",
|
||||||
|
"mqttbroker_address": "127.0.0.1",
|
||||||
|
"mqttpassword": "",
|
||||||
|
"mqttport": 1883,
|
||||||
|
"mqttsend_on_event": 0,
|
||||||
|
"mqttsendinterval": 30,
|
||||||
|
"mqtttls_set": 0,
|
||||||
|
"mqttusername": "",
|
||||||
|
"mqttwrite_outputs": 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.diag_aclmanager = AclManager(self)
|
||||||
|
self.rejected.connect(self.diag_aclmanager.reject)
|
||||||
|
self.diag_mqttmanager = MqttManager(self)
|
||||||
|
self.rejected.connect(self.diag_mqttmanager.reject)
|
||||||
|
self.diag_mqttmanager.dc = self._dict_mqttsettings
|
||||||
|
|
||||||
|
def _apply_acl(self):
|
||||||
|
"""Set availability of controls depending on ACL level."""
|
||||||
|
allow = helper.cm.xml_mode >= 4
|
||||||
|
|
||||||
|
self.cbx_autostart.setEnabled(allow)
|
||||||
|
self.cbx_autoreload.setEnabled(allow)
|
||||||
|
self.sbx_autoreloaddelay.setEnabled(allow)
|
||||||
|
self.cbx_zeroonexit.setEnabled(allow)
|
||||||
|
self.cbx_zeroonerror.setEnabled(allow)
|
||||||
|
self.cbb_replace_io.setEnabled(allow)
|
||||||
|
self.txt_replace_io.setEnabled(allow and self.cbb_replace_io.currentIndex() == 3)
|
||||||
|
self.cbx_plcslave.setEnabled(allow)
|
||||||
|
self.cbx_mqtt.setEnabled(allow)
|
||||||
|
self.cbx_xmlrpc.setEnabled(allow)
|
||||||
|
|
||||||
|
if allow:
|
||||||
|
self.btn_box.setStandardButtons(
|
||||||
|
QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.btn_box.setStandardButtons(
|
||||||
|
QtWidgets.QDialogButtonBox.Cancel
|
||||||
|
)
|
||||||
|
|
||||||
|
def _changesdone(self):
|
||||||
|
"""
|
||||||
|
Check for unsaved changes in dialog.
|
||||||
|
|
||||||
|
:return: True, if unsaved changes was found
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
int(self.cbx_autostart.isChecked()) != self.dc.get("autostart", 0) or
|
||||||
|
int(self.cbx_autoreload.isChecked()) != self.dc.get("autoreload", 1) or
|
||||||
|
self.sbx_autoreloaddelay.value() != self.dc.get("autoreloaddelay", 5) or
|
||||||
|
int(self.cbx_zeroonexit.isChecked()) != self.dc.get("zeroonexit", 0) or
|
||||||
|
int(self.cbx_zeroonerror.isChecked()) != self.dc.get("zeroonerror", 0) or
|
||||||
|
self.txt_replace_io.text() != self.dc.get("replace_ios", "") or
|
||||||
|
# todo: self.dc.get("plcprogram_watchdog", 0)
|
||||||
|
# todo: self.dc.get("reset_driver_action", 2)
|
||||||
|
# todo: self.dc.get("rtlevel", 2)
|
||||||
|
|
||||||
|
int(self.cbx_plcslave.isChecked()) != self.dc.get("plcslave", 0) or
|
||||||
|
self.acl_plcslave != self.dc.get("plcslaveacl", "") or
|
||||||
|
|
||||||
|
int(self.cbx_mqtt.isChecked()) != self.dc.get("mqtt", 0) or
|
||||||
|
self._changesdone_mqtt() or
|
||||||
|
|
||||||
|
int(self.cbx_xmlrpc.isChecked()) != self.dc.get("xmlrpc", 0) or
|
||||||
|
self.acl_xmlrpc != self.dc.get("xmlrpcacl", "")
|
||||||
|
)
|
||||||
|
|
||||||
|
def _changesdone_mqtt(self):
|
||||||
|
"""
|
||||||
|
Check for unsaved changes in mqtt settings.
|
||||||
|
|
||||||
|
:return: True, if unsaved changes was found
|
||||||
|
"""
|
||||||
|
for key in self._dict_mqttsettings:
|
||||||
|
if key in self.dc:
|
||||||
|
if self._dict_mqttsettings[key] != self.dc[key]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _load_settings(self):
|
||||||
|
"""Load values to GUI widgets."""
|
||||||
|
pi.logger.debug("RevPiOption._load_settings")
|
||||||
|
|
||||||
|
self.mrk_xml_ask = True
|
||||||
|
|
||||||
|
self.cbx_autostart.setChecked(bool(self.dc.get("autostart", 0)))
|
||||||
|
self.cbx_autoreload.setChecked(bool(self.dc.get("autoreload", 1)))
|
||||||
|
self.sbx_autoreloaddelay.setValue(self.dc.get("autoreloaddelay", 5))
|
||||||
|
self.cbx_zeroonexit.setChecked(bool(self.dc.get("zeroonexit", 0)))
|
||||||
|
self.cbx_zeroonerror.setChecked(bool(self.dc.get("zeroonerror", 0)))
|
||||||
|
self.txt_replace_io.setText(self.dc.get("replace_ios", ""))
|
||||||
|
self.cbx_plcslave.setChecked(bool(self.dc.get("plcslave", 0)))
|
||||||
|
self.acl_plcslave = self.dc.get("plcslaveacl", "")
|
||||||
|
self.cbx_mqtt.setChecked(bool(self.dc.get("mqtt", 0)))
|
||||||
|
self.cbx_xmlrpc.setChecked(bool(self.dc.get("xmlrpc", 0)))
|
||||||
|
self.acl_xmlrpc = self.dc.get("xmlrpcacl", "")
|
||||||
|
|
||||||
|
# Find the right index of combo box
|
||||||
|
if self.txt_replace_io.text() == "":
|
||||||
|
self.cbb_replace_io.setCurrentIndex(0)
|
||||||
|
elif self.txt_replace_io.text() == "/etc/revpipyload/replace_ios.conf":
|
||||||
|
self.cbb_replace_io.setCurrentIndex(1)
|
||||||
|
elif self.txt_replace_io.text() == "replace_ios.conf":
|
||||||
|
self.cbb_replace_io.setCurrentIndex(2)
|
||||||
|
else:
|
||||||
|
self.cbb_replace_io.setCurrentIndex(3)
|
||||||
|
|
||||||
|
# Update directory with mqtt values to compare with dc values
|
||||||
|
for key in self._dict_mqttsettings:
|
||||||
|
if key in self.dc:
|
||||||
|
self._dict_mqttsettings[key] = self.dc[key]
|
||||||
|
|
||||||
|
def accept(self) -> None:
|
||||||
|
if not self._changesdone():
|
||||||
|
super(RevPiOption, self).accept()
|
||||||
|
return
|
||||||
|
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"The settings will be set on the Revolution Pi now.\n\n"
|
||||||
|
"ACL changes and service settings are applied immediately."
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if not ask:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dc["autostart"] = int(self.cbx_autostart.isChecked())
|
||||||
|
self.dc["autoreload"] = int(self.cbx_autoreload.isChecked())
|
||||||
|
self.dc["autoreloaddelay"] = self.sbx_autoreloaddelay.value()
|
||||||
|
self.dc["zeroonexit"] = int(self.cbx_zeroonexit.isChecked())
|
||||||
|
self.dc["zeroonerror"] = int(self.cbx_zeroonerror.isChecked())
|
||||||
|
self.dc["replace_ios"] = self.txt_replace_io.text()
|
||||||
|
|
||||||
|
# PLCSlave Settings
|
||||||
|
self.dc["plcslave"] = int(self.cbx_plcslave.isChecked())
|
||||||
|
self.dc["plcslaveacl"] = self.acl_plcslave
|
||||||
|
|
||||||
|
# MQTT Settings
|
||||||
|
self.dc["mqtt"] = int(self.cbx_mqtt.isChecked())
|
||||||
|
self.dc["mqttbasetopic"] = self._dict_mqttsettings["mqttbasetopic"]
|
||||||
|
self.dc["mqttsendinterval"] = int(self._dict_mqttsettings["mqttsendinterval"])
|
||||||
|
self.dc["mqttsend_on_event"] = int(self._dict_mqttsettings["mqttsend_on_event"])
|
||||||
|
self.dc["mqttwrite_outputs"] = int(self._dict_mqttsettings["mqttwrite_outputs"])
|
||||||
|
self.dc["mqttbroker_address"] = self._dict_mqttsettings["mqttbroker_address"]
|
||||||
|
self.dc["mqttport"] = int(self._dict_mqttsettings["mqttport"])
|
||||||
|
self.dc["mqtttls_set"] = int(self._dict_mqttsettings["mqtttls_set"])
|
||||||
|
self.dc["mqttusername"] = self._dict_mqttsettings["mqttusername"]
|
||||||
|
self.dc["mqttpassword"] = self._dict_mqttsettings["mqttpassword"]
|
||||||
|
self.dc["mqttclient_id"] = self._dict_mqttsettings["mqttclient_id"]
|
||||||
|
|
||||||
|
# XML Settings
|
||||||
|
self.dc["xmlrpc"] = int(self.cbx_xmlrpc.isChecked())
|
||||||
|
self.dc["xmlrpcacl"] = self.acl_xmlrpc
|
||||||
|
|
||||||
|
saved = helper.cm.call_remote_function(
|
||||||
|
"set_config", self.dc, ask,
|
||||||
|
default_value=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if saved:
|
||||||
|
super(RevPiOption, self).accept()
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The settings could not be saved on the Revolution Pi!\n"
|
||||||
|
"Try to save the values one mor time and check the log "
|
||||||
|
"files of RevPiPyLoad if the error rises again."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
if self._changesdone():
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if ask:
|
||||||
|
self.reject()
|
||||||
|
else:
|
||||||
|
a0.ignore()
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
# Reset class values
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return QtWidgets.QDialog.Rejected
|
||||||
|
|
||||||
|
self.dc = helper.cm.call_remote_function("get_config", default_value={})
|
||||||
|
if len(self.dc) == 0:
|
||||||
|
return QtWidgets.QDialog.Rejected
|
||||||
|
|
||||||
|
self._load_settings()
|
||||||
|
self._apply_acl()
|
||||||
|
|
||||||
|
running = helper.cm.call_remote_function("plcslaverunning", default_value=False)
|
||||||
|
self.lbl_slave_status.setText(
|
||||||
|
self.tr("running") if running else self.tr("stopped")
|
||||||
|
)
|
||||||
|
self.lbl_slave_status.setStyleSheet(
|
||||||
|
"color: green" if running else "color: red"
|
||||||
|
)
|
||||||
|
|
||||||
|
running = helper.cm.call_remote_function("mqttrunning")
|
||||||
|
if running is None:
|
||||||
|
# On older versions of RevPiPyLoad MQTT is not implemented
|
||||||
|
self.cbx_mqtt.setToolTip(self.tr(
|
||||||
|
"The MQTT service is not available on your RevPiPyLoad version."
|
||||||
|
))
|
||||||
|
self.btn_mqtt.setVisible(False)
|
||||||
|
self.lbl_mqtt_status.setText("N/A")
|
||||||
|
self.lbl_mqtt_status.setStyleSheet("")
|
||||||
|
else:
|
||||||
|
self.cbx_mqtt.setToolTip("")
|
||||||
|
self.btn_mqtt.setVisible(True)
|
||||||
|
self.lbl_mqtt_status.setText(
|
||||||
|
self.tr("running") if running else self.tr("stopped")
|
||||||
|
)
|
||||||
|
self.lbl_mqtt_status.setStyleSheet(
|
||||||
|
"color: green" if running else "color: red"
|
||||||
|
)
|
||||||
|
|
||||||
|
return super(RevPiOption, self).exec()
|
||||||
|
|
||||||
|
def reject(self) -> None:
|
||||||
|
"""Reject all sub windows and reload settings."""
|
||||||
|
self._load_settings()
|
||||||
|
super(RevPiOption, self).reject()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbb_replace_io_currentIndexChanged(self, index: int):
|
||||||
|
"""Update replace_io path in text field to compare values."""
|
||||||
|
if index == 0:
|
||||||
|
self.txt_replace_io.setText("")
|
||||||
|
elif index == 1:
|
||||||
|
self.txt_replace_io.setText("/etc/revpipyload/replace_ios.conf")
|
||||||
|
elif index == 2:
|
||||||
|
self.txt_replace_io.setText("replace_ios.conf")
|
||||||
|
else:
|
||||||
|
self.txt_replace_io.setText(self.dc.get("replace_ios", ""))
|
||||||
|
|
||||||
|
self.txt_replace_io.setEnabled(index == 3)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_aclplcslave_clicked(self):
|
||||||
|
"""Start ACL manager to edit ACL entries."""
|
||||||
|
self.diag_aclmanager.setup_acl_manager(self.acl_plcslave, {
|
||||||
|
0: self.tr("read only"),
|
||||||
|
1: self.tr("read and write"),
|
||||||
|
})
|
||||||
|
self.diag_aclmanager.read_only = helper.cm.xml_mode < 4
|
||||||
|
if self.diag_aclmanager.exec() == QtWidgets.QDialog.Accepted:
|
||||||
|
self.acl_plcslave = self.diag_aclmanager.get_acl()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_mqtt_clicked(self):
|
||||||
|
"""Open MQTT settings."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.diag_mqttmanager.read_only = helper.cm.xml_mode < 4
|
||||||
|
self.diag_mqttmanager.exec()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbx_xmlrpc_stateChanged(self, state: int):
|
||||||
|
if state == QtCore.Qt.Unchecked and self.mrk_xml_ask:
|
||||||
|
self.mrk_xml_ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Are you sure you want to deactivate the XML-RPC server? "
|
||||||
|
"You will NOT be able to access the Revolution Pi with "
|
||||||
|
"this program after saving the options!"
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.No
|
||||||
|
if self.mrk_xml_ask:
|
||||||
|
self.cbx_xmlrpc.setCheckState(QtCore.Qt.Checked)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_aclxmlrpc_clicked(self):
|
||||||
|
self.diag_aclmanager.setup_acl_manager(self.acl_xmlrpc, {
|
||||||
|
0: self.tr("Start/Stop PLC program and read logs"),
|
||||||
|
1: self.tr("+ read IOs in watch mode"),
|
||||||
|
2: self.tr("+ read properties and download PLC program"),
|
||||||
|
3: self.tr("+ upload PLC program"),
|
||||||
|
4: self.tr("+ set properties")
|
||||||
|
})
|
||||||
|
self.diag_aclmanager.read_only = helper.cm.xml_mode < 4
|
||||||
|
if self.diag_aclmanager.exec() == QtWidgets.QDialog.Accepted:
|
||||||
|
self.acl_xmlrpc = self.diag_aclmanager.get_acl()
|
||||||
367
revpicommander/revpiplclist.py
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Saved connections of Revolution Pi devices."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from helper import WidgetData
|
||||||
|
from ui.revpiplclist_ui import Ui_diag_connections
|
||||||
|
|
||||||
|
|
||||||
|
class NodeType(IntEnum):
|
||||||
|
CON = 1000
|
||||||
|
DIR = 1001
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||||
|
"""Manage your saved connections."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(RevPiPlcList, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setFixedSize(self.size())
|
||||||
|
self.__default_name = self.tr("New connection")
|
||||||
|
self.__default_port = 55123
|
||||||
|
|
||||||
|
self.__current_item = QtWidgets.QTreeWidgetItem() # type: QtWidgets.QTreeWidgetItem
|
||||||
|
self.changes = True
|
||||||
|
|
||||||
|
self.tre_connections.setColumnWidth(0, 250)
|
||||||
|
self.lbl_port.setText(self.lbl_port.text().format(self.__default_port))
|
||||||
|
self.sbx_port.setValue(self.__default_port)
|
||||||
|
|
||||||
|
def _load_settings(self):
|
||||||
|
"""Load values to GUI widgets."""
|
||||||
|
pi.logger.debug("RevPiPlcList._load_settings")
|
||||||
|
|
||||||
|
self.tre_connections.clear()
|
||||||
|
self.cbb_folder.clear()
|
||||||
|
self.cbb_folder.addItem("")
|
||||||
|
for i in range(helper.settings.beginReadArray("connections")):
|
||||||
|
helper.settings.setArrayIndex(i)
|
||||||
|
|
||||||
|
con_item = QtWidgets.QTreeWidgetItem(NodeType.CON)
|
||||||
|
con_item.setIcon(0, QtGui.QIcon(":/main/ico/cpu.ico"))
|
||||||
|
con_item.setText(0, helper.settings.value("name", "Revolution Pi", str))
|
||||||
|
con_item.setText(1, helper.settings.value("address", "127.0.0.1", str))
|
||||||
|
con_item.setData(0, WidgetData.port, helper.settings.value("port", self.__default_port, int))
|
||||||
|
|
||||||
|
con_item.setData(0, WidgetData.last_dir_upload, helper.settings.value("last_dir_upload"))
|
||||||
|
con_item.setData(0, WidgetData.last_file_upload, helper.settings.value("last_file_upload"))
|
||||||
|
con_item.setData(0, WidgetData.last_dir_pictory, helper.settings.value("last_dir_pictory"))
|
||||||
|
con_item.setData(0, WidgetData.last_dir_picontrol, helper.settings.value("last_dir_picontrol"))
|
||||||
|
con_item.setData(0, WidgetData.last_dir_selected, helper.settings.value("last_dir_selected"))
|
||||||
|
con_item.setData(0, WidgetData.last_pictory_file, helper.settings.value("last_pictory_file"))
|
||||||
|
con_item.setData(0, WidgetData.last_tar_file, helper.settings.value("last_tar_file"))
|
||||||
|
con_item.setData(0, WidgetData.last_zip_file, helper.settings.value("last_zip_file"))
|
||||||
|
con_item.setData(0, WidgetData.watch_files, helper.settings.value("watch_files"))
|
||||||
|
con_item.setData(0, WidgetData.watch_path, helper.settings.value("watch_path"))
|
||||||
|
con_item.setData(0, WidgetData.debug_geos, helper.settings.value("debug_geos"))
|
||||||
|
|
||||||
|
folder = helper.settings.value("folder", "", str)
|
||||||
|
if folder:
|
||||||
|
sub_folder = self._get_folder_item(folder)
|
||||||
|
if sub_folder is None:
|
||||||
|
sub_folder = QtWidgets.QTreeWidgetItem(NodeType.DIR)
|
||||||
|
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
|
||||||
|
sub_folder.setText(0, folder)
|
||||||
|
self.tre_connections.addTopLevelItem(sub_folder)
|
||||||
|
self.cbb_folder.addItem(folder)
|
||||||
|
|
||||||
|
sub_folder.addChild(con_item)
|
||||||
|
else:
|
||||||
|
self.tre_connections.addTopLevelItem(con_item)
|
||||||
|
|
||||||
|
helper.settings.endArray()
|
||||||
|
|
||||||
|
self.tre_connections.expandAll()
|
||||||
|
self.changes = True
|
||||||
|
|
||||||
|
if self.tre_connections.topLevelItemCount() == 0:
|
||||||
|
self._edit_state()
|
||||||
|
|
||||||
|
def accept(self) -> None:
|
||||||
|
pi.logger.debug("RevPiPlcList.accept")
|
||||||
|
|
||||||
|
def set_settings(node: QtWidgets.QTreeWidgetItem):
|
||||||
|
parent = node.parent()
|
||||||
|
helper.settings.setValue("address", node.text(1))
|
||||||
|
helper.settings.setValue("folder", parent.text(0) if parent else "")
|
||||||
|
helper.settings.setValue("name", node.text(0))
|
||||||
|
helper.settings.setValue("port", node.data(0, WidgetData.port))
|
||||||
|
|
||||||
|
if node.data(0, WidgetData.last_dir_upload):
|
||||||
|
helper.settings.setValue("last_dir_upload", node.data(0, WidgetData.last_dir_upload))
|
||||||
|
if node.data(0, WidgetData.last_file_upload):
|
||||||
|
helper.settings.setValue("last_file_upload", node.data(0, WidgetData.last_file_upload))
|
||||||
|
if node.data(0, WidgetData.last_dir_pictory):
|
||||||
|
helper.settings.setValue("last_dir_pictory", node.data(0, WidgetData.last_dir_pictory))
|
||||||
|
if node.data(0, WidgetData.last_dir_picontrol):
|
||||||
|
helper.settings.setValue("last_dir_picontrol", node.data(0, WidgetData.last_dir_picontrol))
|
||||||
|
if node.data(0, WidgetData.last_dir_selected):
|
||||||
|
helper.settings.setValue("last_dir_selected", node.data(0, WidgetData.last_dir_selected))
|
||||||
|
if node.data(0, WidgetData.last_pictory_file):
|
||||||
|
helper.settings.setValue("last_pictory_file", node.data(0, WidgetData.last_pictory_file))
|
||||||
|
if node.data(0, WidgetData.last_tar_file):
|
||||||
|
helper.settings.setValue("last_tar_file", node.data(0, WidgetData.last_tar_file))
|
||||||
|
if node.data(0, WidgetData.last_zip_file):
|
||||||
|
helper.settings.setValue("last_zip_file", node.data(0, WidgetData.last_zip_file))
|
||||||
|
if node.data(0, WidgetData.watch_files):
|
||||||
|
helper.settings.setValue("watch_files", node.data(0, WidgetData.watch_files))
|
||||||
|
if node.data(0, WidgetData.watch_path):
|
||||||
|
helper.settings.setValue("watch_path", node.data(0, WidgetData.watch_path))
|
||||||
|
if node.data(0, WidgetData.debug_geos):
|
||||||
|
helper.settings.setValue("debug_geos", node.data(0, WidgetData.debug_geos))
|
||||||
|
|
||||||
|
helper.settings.remove("connections")
|
||||||
|
helper.settings.beginWriteArray("connections")
|
||||||
|
|
||||||
|
counter_index = 0
|
||||||
|
for i in range(self.tre_connections.topLevelItemCount()):
|
||||||
|
root_item = self.tre_connections.topLevelItem(i)
|
||||||
|
if root_item.type() == NodeType.DIR:
|
||||||
|
for k in range(root_item.childCount()):
|
||||||
|
helper.settings.setArrayIndex(counter_index)
|
||||||
|
set_settings(root_item.child(k))
|
||||||
|
counter_index += 1
|
||||||
|
elif root_item.type() == NodeType.CON:
|
||||||
|
helper.settings.setArrayIndex(counter_index)
|
||||||
|
set_settings(root_item)
|
||||||
|
counter_index += 1
|
||||||
|
|
||||||
|
helper.settings.endArray()
|
||||||
|
|
||||||
|
self.changes = False
|
||||||
|
super(RevPiPlcList, self).accept()
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
pi.logger.debug("RevPiPlcList.closeEvent")
|
||||||
|
if self.changes:
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if ask:
|
||||||
|
self.reject()
|
||||||
|
else:
|
||||||
|
a0.ignore()
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
self._load_settings()
|
||||||
|
return super(RevPiPlcList, self).exec()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
|
||||||
|
def on_btn_box_clicked(self, button: QtWidgets.QAbstractButton):
|
||||||
|
if self.btn_box.buttonRole(button) == QtWidgets.QDialogButtonBox.DestructiveRole:
|
||||||
|
self.reject()
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Connection management
|
||||||
|
|
||||||
|
def _edit_state(self):
|
||||||
|
item = self.tre_connections.currentItem()
|
||||||
|
if item is None:
|
||||||
|
up_ok = False
|
||||||
|
down_ok = False
|
||||||
|
con_item = False
|
||||||
|
dir_item = False
|
||||||
|
else:
|
||||||
|
con_item = item.type() == NodeType.CON
|
||||||
|
dir_item = item.type() == NodeType.DIR
|
||||||
|
|
||||||
|
if item.parent():
|
||||||
|
index = item.parent().indexOfChild(item)
|
||||||
|
up_ok = index > 0
|
||||||
|
down_ok = index < item.parent().childCount() - 1
|
||||||
|
else:
|
||||||
|
index = self.tre_connections.indexOfTopLevelItem(item)
|
||||||
|
up_ok = index > 0
|
||||||
|
down_ok = index < self.tre_connections.topLevelItemCount() - 1
|
||||||
|
|
||||||
|
self.btn_up.setEnabled(up_ok)
|
||||||
|
self.btn_down.setEnabled(down_ok)
|
||||||
|
self.btn_delete.setEnabled(con_item)
|
||||||
|
self.txt_name.setEnabled(con_item)
|
||||||
|
self.txt_address.setEnabled(con_item)
|
||||||
|
self.sbx_port.setEnabled(con_item)
|
||||||
|
self.cbb_folder.setEnabled(con_item or dir_item)
|
||||||
|
|
||||||
|
def _get_folder_item(self, name: str):
|
||||||
|
"""Find the folder entry by name."""
|
||||||
|
for i in range(self.tre_connections.topLevelItemCount()):
|
||||||
|
tli = self.tre_connections.topLevelItem(i)
|
||||||
|
if tli.type() == NodeType.DIR and tli.text(0) == name:
|
||||||
|
return tli
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _move_item(self, count: int):
|
||||||
|
"""Move connection item up or down"""
|
||||||
|
item = self.tre_connections.currentItem()
|
||||||
|
if not item:
|
||||||
|
return
|
||||||
|
|
||||||
|
if item.parent():
|
||||||
|
dir_item = item.parent()
|
||||||
|
index = dir_item.indexOfChild(item)
|
||||||
|
new_index = index + count
|
||||||
|
if 0 <= new_index < dir_item.childCount():
|
||||||
|
item = dir_item.takeChild(index)
|
||||||
|
dir_item.insertChild(new_index, item)
|
||||||
|
else:
|
||||||
|
index = self.tre_connections.indexOfTopLevelItem(item)
|
||||||
|
new_index = index + count
|
||||||
|
if 0 <= index < self.tre_connections.topLevelItemCount():
|
||||||
|
item = self.tre_connections.takeTopLevelItem(index)
|
||||||
|
self.tre_connections.insertTopLevelItem(new_index, item)
|
||||||
|
|
||||||
|
self.tre_connections.setCurrentItem(item)
|
||||||
|
self._edit_state()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(QtWidgets.QTreeWidgetItem, QtWidgets.QTreeWidgetItem)
|
||||||
|
def on_tre_connections_currentItemChanged(
|
||||||
|
self, current: QtWidgets.QTreeWidgetItem, previous: QtWidgets.QTreeWidgetItem):
|
||||||
|
|
||||||
|
self._edit_state()
|
||||||
|
if current and current.type() == NodeType.CON:
|
||||||
|
self.__current_item = current
|
||||||
|
self.txt_name.setText(current.text(0))
|
||||||
|
self.txt_address.setText(current.text(1))
|
||||||
|
self.sbx_port.setValue(current.data(0, WidgetData.port))
|
||||||
|
if current.parent() is None:
|
||||||
|
self.cbb_folder.setCurrentIndex(0)
|
||||||
|
else:
|
||||||
|
self.cbb_folder.setCurrentText(current.parent().text(0))
|
||||||
|
elif current and current.type() == NodeType.DIR:
|
||||||
|
self.__current_item = current
|
||||||
|
self.cbb_folder.setCurrentText(current.text(0))
|
||||||
|
else:
|
||||||
|
self.__current_item = QtWidgets.QTreeWidgetItem()
|
||||||
|
self.cbb_folder.setCurrentText(current.text(0) if current else "")
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_up_pressed(self):
|
||||||
|
self._move_item(-1)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_down_pressed(self):
|
||||||
|
self._move_item(1)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_delete_pressed(self):
|
||||||
|
"""Remove selected entry."""
|
||||||
|
item = self.tre_connections.currentItem()
|
||||||
|
if item and item.type() == NodeType.CON:
|
||||||
|
dir_node = item.parent()
|
||||||
|
if dir_node:
|
||||||
|
dir_node.removeChild(item)
|
||||||
|
else:
|
||||||
|
index = self.tre_connections.indexOfTopLevelItem(item)
|
||||||
|
self.tre_connections.takeTopLevelItem(index)
|
||||||
|
|
||||||
|
self._edit_state()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_add_pressed(self):
|
||||||
|
"""Create new element."""
|
||||||
|
self.__current_item = QtWidgets.QTreeWidgetItem(NodeType.CON)
|
||||||
|
self.__current_item.setIcon(0, QtGui.QIcon(":/main/ico/cpu.ico"))
|
||||||
|
self.__current_item.setText(0, self.__default_name)
|
||||||
|
self.__current_item.setData(0, WidgetData.port, self.__default_port)
|
||||||
|
sub_folder = self._get_folder_item(self.cbb_folder.currentText())
|
||||||
|
if sub_folder:
|
||||||
|
sub_folder.addChild(self.__current_item)
|
||||||
|
else:
|
||||||
|
self.tre_connections.addTopLevelItem(self.__current_item)
|
||||||
|
|
||||||
|
self.tre_connections.setCurrentItem(self.__current_item)
|
||||||
|
self.txt_name.setFocus()
|
||||||
|
self.txt_name.selectAll()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_txt_name_textEdited(self, text):
|
||||||
|
if self.__current_item.type() != NodeType.CON:
|
||||||
|
return
|
||||||
|
self.__current_item.setText(0, text)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_txt_address_textEdited(self, text):
|
||||||
|
if self.__current_item.type() != NodeType.CON:
|
||||||
|
return
|
||||||
|
self.__current_item.setText(1, text)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_sbx_port_valueChanged(self, value: int):
|
||||||
|
if self.__current_item.type() != NodeType.CON:
|
||||||
|
return
|
||||||
|
self.__current_item.setData(0, WidgetData.port, value)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def on_cbb_folder_editTextChanged(self, text: str):
|
||||||
|
pi.logger.debug("RevPiPlcList.on_cbb_folder_editTextChanged({0})".format(text))
|
||||||
|
|
||||||
|
if self.__current_item.type() == NodeType.DIR:
|
||||||
|
# We just have to rename the dir node
|
||||||
|
self.__current_item.setText(0, text)
|
||||||
|
|
||||||
|
elif self.__current_item.type() == NodeType.CON:
|
||||||
|
sub_folder = self._get_folder_item(text)
|
||||||
|
dir_node = self.__current_item.parent()
|
||||||
|
if dir_node:
|
||||||
|
if dir_node.text(0) == text:
|
||||||
|
# It is the same folder
|
||||||
|
return
|
||||||
|
|
||||||
|
if text != "" and dir_node.childCount() == 1 and not sub_folder:
|
||||||
|
# The folder hold just one item, so we can rename that
|
||||||
|
for i in range(self.cbb_folder.count()):
|
||||||
|
if self.cbb_folder.itemText(i) == dir_node.text(0):
|
||||||
|
self.cbb_folder.setItemText(i, text)
|
||||||
|
break
|
||||||
|
dir_node.setText(0, text)
|
||||||
|
return
|
||||||
|
|
||||||
|
index = dir_node.indexOfChild(self.__current_item)
|
||||||
|
self.__current_item = dir_node.takeChild(index)
|
||||||
|
|
||||||
|
elif text != "":
|
||||||
|
# Move root to folder
|
||||||
|
index = self.tre_connections.indexOfTopLevelItem(self.__current_item)
|
||||||
|
self.__current_item = self.tre_connections.takeTopLevelItem(index)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Root stays root
|
||||||
|
return
|
||||||
|
|
||||||
|
if text == "":
|
||||||
|
self.tre_connections.addTopLevelItem(self.__current_item)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if sub_folder is None:
|
||||||
|
sub_folder = QtWidgets.QTreeWidgetItem(NodeType.DIR)
|
||||||
|
sub_folder.setIcon(0, QtGui.QIcon(":/main/ico/folder.ico"))
|
||||||
|
sub_folder.setText(0, text)
|
||||||
|
self.tre_connections.addTopLevelItem(sub_folder)
|
||||||
|
self.cbb_folder.addItem(text)
|
||||||
|
sub_folder.addChild(self.__current_item)
|
||||||
|
|
||||||
|
if dir_node and dir_node.childCount() == 0:
|
||||||
|
# Remove empty folders
|
||||||
|
for i in range(self.cbb_folder.count()):
|
||||||
|
if self.cbb_folder.itemText(i) == dir_node.text(0):
|
||||||
|
self.cbb_folder.removeItem(i)
|
||||||
|
break
|
||||||
|
index = self.tre_connections.indexOfTopLevelItem(dir_node)
|
||||||
|
self.tre_connections.takeTopLevelItem(index)
|
||||||
|
|
||||||
|
self.tre_connections.setCurrentItem(self.__current_item)
|
||||||
|
self.cbb_folder.setFocus()
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
772
revpicommander/revpiprogram.py
Normal file
@@ -0,0 +1,772 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Revolution Pi PLC program configuration."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
import os
|
||||||
|
import tarfile
|
||||||
|
import zipfile
|
||||||
|
from shutil import rmtree
|
||||||
|
from tempfile import mkdtemp, mkstemp
|
||||||
|
from xmlrpc.client import Binary
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
import helper
|
||||||
|
import proginit as pi
|
||||||
|
from ui.revpiprogram_ui import Ui_diag_program
|
||||||
|
|
||||||
|
|
||||||
|
class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||||
|
"""Program options of RevPiPyLoad."""
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(RevPiProgram, self).__init__(parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
self.setFixedSize(self.size())
|
||||||
|
|
||||||
|
self.dc = {}
|
||||||
|
self.lst_files = []
|
||||||
|
|
||||||
|
self.cbx_pictory.setEnabled(False)
|
||||||
|
self.recover_gui()
|
||||||
|
|
||||||
|
def _apply_acl(self):
|
||||||
|
"""Set availability of controls depending on ACL level."""
|
||||||
|
|
||||||
|
# Setting properties require level 4
|
||||||
|
self.cbb_plcprogram.setEnabled(helper.cm.xml_mode >= 4)
|
||||||
|
self.txt_plcarguments.setEnabled(helper.cm.xml_mode >= 4)
|
||||||
|
self.rbn_pythonversion_2.setEnabled(helper.cm.xml_mode >= 4)
|
||||||
|
self.rbn_pythonversion_3.setEnabled(helper.cm.xml_mode >= 4)
|
||||||
|
self.cbx_plcworkdir_set_uid.setEnabled(helper.cm.xml_mode >= 4)
|
||||||
|
|
||||||
|
# Downloads require level 2
|
||||||
|
self.btn_program_download.setEnabled(helper.cm.xml_mode >= 2)
|
||||||
|
self.btn_pictory_download.setEnabled(helper.cm.xml_mode >= 2)
|
||||||
|
self.btn_procimg_download.setEnabled(helper.cm.xml_mode >= 2)
|
||||||
|
|
||||||
|
# Uploads require level 3
|
||||||
|
self.btn_program_upload.setEnabled(helper.cm.xml_mode >= 3)
|
||||||
|
self.btn_pictory_upload.setEnabled(helper.cm.xml_mode >= 3)
|
||||||
|
|
||||||
|
def _changesdone(self):
|
||||||
|
"""
|
||||||
|
Check for unsaved changes in dialog.
|
||||||
|
|
||||||
|
:return: True, if unsaved changes was found
|
||||||
|
"""
|
||||||
|
return \
|
||||||
|
self.cbb_plcprogram.currentText() != self.dc.get("plcprogram", "") or \
|
||||||
|
self.txt_plcarguments.text() != self.dc.get("plcarguments", "") or \
|
||||||
|
self.rbn_pythonversion_2.isChecked() != (self.dc.get("pythonversion", 3) == 2) or \
|
||||||
|
self.rbn_pythonversion_3.isChecked() != (self.dc.get("pythonversion", 3) == 3) or \
|
||||||
|
int(self.cbx_plcworkdir_set_uid.isChecked()) != self.dc.get("plcworkdir_set_uid", 0)
|
||||||
|
|
||||||
|
def _load_settings(self, files_only=False):
|
||||||
|
"""Load values to GUI widgets."""
|
||||||
|
pi.logger.debug("RevPiProgram._load_settings")
|
||||||
|
|
||||||
|
if files_only:
|
||||||
|
mrk_program = self.cbb_plcprogram.currentText()
|
||||||
|
else:
|
||||||
|
mrk_program = ""
|
||||||
|
|
||||||
|
self.cbb_plcprogram.clear()
|
||||||
|
self.cbb_plcprogram.addItem("")
|
||||||
|
|
||||||
|
self.lst_files.sort()
|
||||||
|
is_in_list = False
|
||||||
|
for file in self.lst_files:
|
||||||
|
if file == ".placeholder":
|
||||||
|
continue
|
||||||
|
self.cbb_plcprogram.addItem(file)
|
||||||
|
check = mrk_program or self.dc.get("plcprogram", "")
|
||||||
|
if file == check:
|
||||||
|
is_in_list = True
|
||||||
|
|
||||||
|
if not is_in_list:
|
||||||
|
pi.logger.warning("File {0} is not in list".format(mrk_program or self.dc.get("plcprogram", "")))
|
||||||
|
|
||||||
|
if files_only:
|
||||||
|
self.cbb_plcprogram.setCurrentText(mrk_program)
|
||||||
|
else:
|
||||||
|
self.cbb_plcprogram.setCurrentText(self.dc.get("plcprogram", ""))
|
||||||
|
self.txt_plcarguments.setText(self.dc.get("plcarguments", ""))
|
||||||
|
self.rbn_pythonversion_2.setChecked(self.dc.get("pythonversion", 3) == 2)
|
||||||
|
self.rbn_pythonversion_3.setChecked(self.dc.get("pythonversion", 3) == 3)
|
||||||
|
self.cbx_plcworkdir_set_uid.setChecked(bool(self.dc.get("plcworkdir_set_uid", 0)))
|
||||||
|
|
||||||
|
def accept(self) -> None:
|
||||||
|
# todo: After upload ask for restart pcl program?
|
||||||
|
if not self._changesdone():
|
||||||
|
super(RevPiProgram, self).accept()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.cbb_plcprogram.currentText() == "":
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"You have to select a start program, before uploading the "
|
||||||
|
"settings."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"The settings will be set on the Revolution Pi now.\n\n"
|
||||||
|
"If you made changes on the 'PCL Program' section, your plc "
|
||||||
|
"program will restart now!"
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if not ask:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dc["plcprogram"] = self.cbb_plcprogram.currentText()
|
||||||
|
self.dc["plcarguments"] = self.txt_plcarguments.text()
|
||||||
|
self.dc["pythonversion"] = 2 if self.rbn_pythonversion_2.isChecked() else 3
|
||||||
|
self.dc["plcworkdir_set_uid"] = int(self.cbx_plcworkdir_set_uid.isChecked())
|
||||||
|
|
||||||
|
saved = helper.cm.call_remote_function(
|
||||||
|
"set_config", self.dc, ask,
|
||||||
|
default_value=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if saved:
|
||||||
|
super(RevPiProgram, self).accept()
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The settings could not be saved on the Revolution Pi!\n"
|
||||||
|
"Try to save the values one mor time and check the log "
|
||||||
|
"files of RevPiPyLoad if the error rises again."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
|
||||||
|
if self._changesdone():
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Question"), self.tr(
|
||||||
|
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||||
|
)
|
||||||
|
) == QtWidgets.QMessageBox.Yes
|
||||||
|
|
||||||
|
if ask:
|
||||||
|
self.reject()
|
||||||
|
else:
|
||||||
|
a0.ignore()
|
||||||
|
|
||||||
|
def exec(self) -> int:
|
||||||
|
# Reset class values
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return QtWidgets.QDialog.Rejected
|
||||||
|
|
||||||
|
self.dc = helper.cm.call_remote_function("get_config", default_value={})
|
||||||
|
self.lst_files = helper.cm.call_remote_function("get_filelist", default_value=[])
|
||||||
|
if len(self.dc) == 0 or len(self.lst_files) == 0:
|
||||||
|
return QtWidgets.QDialog.Rejected
|
||||||
|
|
||||||
|
self._load_settings()
|
||||||
|
self._apply_acl()
|
||||||
|
|
||||||
|
return super(RevPiProgram, self).exec()
|
||||||
|
|
||||||
|
def reject(self) -> None:
|
||||||
|
"""Reject all sub windows and reload settings."""
|
||||||
|
self._load_settings()
|
||||||
|
super(RevPiProgram, self).reject()
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: PLC program
|
||||||
|
|
||||||
|
def _upload_pictory(self, filename: str):
|
||||||
|
"""Upload piCtory configuration."""
|
||||||
|
fh = open(filename, "rb")
|
||||||
|
file_binary = Binary(fh.read())
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
ask = QtWidgets.QMessageBox.question(
|
||||||
|
self, self.tr("Reset driver..."), self.tr(
|
||||||
|
"Reset piControl driver after successful uploading new piCtory "
|
||||||
|
"configuration?\nThe process image will be interrupted for a "
|
||||||
|
"short time!"
|
||||||
|
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel
|
||||||
|
)
|
||||||
|
if ask == QtWidgets.QMessageBox.Cancel:
|
||||||
|
return
|
||||||
|
|
||||||
|
ec = helper.cm.call_remote_function(
|
||||||
|
"set_pictoryrsc", file_binary, ask == QtWidgets.QMessageBox.Yes
|
||||||
|
)
|
||||||
|
|
||||||
|
if ec is None:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Got an network error while send data to Revolution Pi.\n"
|
||||||
|
"Please try again."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ec == 0:
|
||||||
|
helper.cm.program_last_pictory_file = filename
|
||||||
|
if ask == QtWidgets.QMessageBox.Yes:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"The transfer of the piCtory configuration "
|
||||||
|
"and the reset of piControl have been "
|
||||||
|
"successfully executed."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"The piCtory configuration was successfully transferred."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif ec == -1:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not process the transferred file."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ec == -2:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Can not find main elements in piCtory file."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ec == -4:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Contained devices could not be found on Revolution "
|
||||||
|
"Pi. The configuration may be from a newer piCtory version!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ec == -5:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Could not load RAP catalog on Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ec < 0:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The piCtory configuration could not be "
|
||||||
|
"written on the Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ec > 0:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Warning"), self.tr(
|
||||||
|
"The piCtroy configuration has been saved successfully.\n"
|
||||||
|
"An error occurred on piControl reset!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_replacedir(self, rootdir: str):
|
||||||
|
"""
|
||||||
|
Return root dir of a extracted archive an piCtory configuration.
|
||||||
|
|
||||||
|
This function checks the root dir of a extracted directory. It
|
||||||
|
will check the existence of a piCtory configuration will will
|
||||||
|
return that information in the return tuple.
|
||||||
|
|
||||||
|
:param rootdir: Dir to check
|
||||||
|
:return: Tuple with corrected root dir and piCtory configuration
|
||||||
|
"""
|
||||||
|
lst_dir = os.listdir(rootdir)
|
||||||
|
if len(lst_dir) == 1 and \
|
||||||
|
os.path.isdir(os.path.join(rootdir, lst_dir[0])):
|
||||||
|
return os.path.join(rootdir, lst_dir[0]), None
|
||||||
|
|
||||||
|
if len(lst_dir) == 2:
|
||||||
|
rscfile = None
|
||||||
|
for fname in lst_dir:
|
||||||
|
if fname.find(".rsc"):
|
||||||
|
rscfile = os.path.join(rootdir, fname)
|
||||||
|
return os.path.join(rootdir, lst_dir[0]), rscfile
|
||||||
|
|
||||||
|
else:
|
||||||
|
return rootdir, None
|
||||||
|
|
||||||
|
def create_filelist(self, rootdir):
|
||||||
|
"""Create a file list of a directory.
|
||||||
|
|
||||||
|
:param rootdir: Root dir to crate file list
|
||||||
|
:return: File list with <class 'str'>
|
||||||
|
"""
|
||||||
|
filelist = []
|
||||||
|
for tup_dir in os.walk(rootdir):
|
||||||
|
for fname in tup_dir[2]:
|
||||||
|
filelist.append(os.path.join(tup_dir[0], fname))
|
||||||
|
return filelist
|
||||||
|
|
||||||
|
def recover_gui(self):
|
||||||
|
"""Load saved GUI states."""
|
||||||
|
self.cbb_format.setCurrentIndex(helper.settings.value("program/cbb_format_index", 0, int))
|
||||||
|
self.cbx_pictory.setChecked(helper.settings.value("program/cbx_pictory_checked", False, bool))
|
||||||
|
self.cbx_clear.setChecked(helper.settings.value("program/cbx_clear_checked", False, bool))
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(int)
|
||||||
|
def on_cbb_format_currentIndexChanged(self, index: int):
|
||||||
|
helper.settings.setValue("program/cbb_format_index", index)
|
||||||
|
self.cbx_pictory.setEnabled(index >= 2)
|
||||||
|
self.btn_program_download.setEnabled(index != 1)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_program_download_pressed(self):
|
||||||
|
"""Download plc program from Revolution Pi."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_dir = ""
|
||||||
|
|
||||||
|
if self.cbb_format.currentIndex() == 0:
|
||||||
|
# Save files to selected directory
|
||||||
|
diag_folder = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Select folder..."),
|
||||||
|
helper.cm.program_last_dir_selected,
|
||||||
|
)
|
||||||
|
diag_folder.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
|
||||||
|
self.rejected.connect(diag_folder.reject)
|
||||||
|
|
||||||
|
if diag_folder.exec() != QtWidgets.QFileDialog.Accepted:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_dir = diag_folder.selectedFiles()[0]
|
||||||
|
fh = open(mkstemp()[1], "wb")
|
||||||
|
|
||||||
|
helper.cm.program_last_dir_selected = selected_dir
|
||||||
|
|
||||||
|
elif self.cbb_format.currentIndex() == 2:
|
||||||
|
# Save files as zip archive
|
||||||
|
diag_save = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Save ZIP archive..."),
|
||||||
|
os.path.join(
|
||||||
|
helper.cm.program_last_zip_file,
|
||||||
|
"{0}.zip".format(helper.cm.name)
|
||||||
|
),
|
||||||
|
self.tr("ZIP archive (*.zip);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_save.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
|
||||||
|
diag_save.setDefaultSuffix("zip")
|
||||||
|
self.rejected.connect(diag_save.reject)
|
||||||
|
|
||||||
|
if diag_save.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_save.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
filename = diag_save.selectedFiles()[0]
|
||||||
|
fh = open(filename, "wb")
|
||||||
|
|
||||||
|
helper.cm.program_last_zip_file = filename
|
||||||
|
|
||||||
|
elif self.cbb_format.currentIndex() == 3:
|
||||||
|
# Save files as TarGz archive
|
||||||
|
diag_save = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Save TGZ archive..."),
|
||||||
|
os.path.join(
|
||||||
|
helper.cm.program_last_tar_file,
|
||||||
|
"{0}.tgz".format(helper.cm.name)
|
||||||
|
),
|
||||||
|
self.tr("TGZ archive (*.tgz);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_save.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
|
||||||
|
diag_save.setDefaultSuffix("tgz")
|
||||||
|
self.rejected.connect(diag_save.reject)
|
||||||
|
|
||||||
|
if diag_save.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_save.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
filename = diag_save.selectedFiles()[0]
|
||||||
|
fh = open(filename, "wb")
|
||||||
|
|
||||||
|
helper.cm.program_last_tar_file = filename
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Other indexes are not allowed for download
|
||||||
|
return
|
||||||
|
|
||||||
|
plcfile = helper.cm.call_remote_function(
|
||||||
|
"plcdownload",
|
||||||
|
"zip" if self.cbb_format.currentIndex() == 2 else "tar",
|
||||||
|
self.cbx_pictory.isChecked()
|
||||||
|
)
|
||||||
|
|
||||||
|
if plcfile is None:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Could not load PLC program from Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
fh.write(plcfile.data)
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
if self.cbb_format.currentIndex() == 0:
|
||||||
|
# Extract archive to selected directory
|
||||||
|
os.makedirs(selected_dir, exist_ok=True)
|
||||||
|
fh_pack = tarfile.open(fh.name)
|
||||||
|
|
||||||
|
for tar_item in fh_pack.getmembers():
|
||||||
|
if tar_item.name == "revpipyload":
|
||||||
|
# Ignore root folder in Tar file
|
||||||
|
continue
|
||||||
|
tar_item.name = tar_item.name.replace("revpipyload/", "")
|
||||||
|
fh_pack.extract(tar_item, selected_dir)
|
||||||
|
|
||||||
|
fh_pack.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
pi.logger.error(e)
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Coud not save the archive or extract the files!\n"
|
||||||
|
"Please retry.")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"Transfer successfully completed."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_program_upload_pressed(self):
|
||||||
|
"""Upload plc program to Revolution Pi."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
dirtmp = None
|
||||||
|
|
||||||
|
def remove_temp():
|
||||||
|
# Remove temp dir
|
||||||
|
if dirtmp is not None:
|
||||||
|
rmtree(dirtmp)
|
||||||
|
|
||||||
|
dirselect = ""
|
||||||
|
lst_files = []
|
||||||
|
folder_name = ""
|
||||||
|
rscfile = None
|
||||||
|
|
||||||
|
if self.cbb_format.currentIndex() == 0:
|
||||||
|
# Upload files
|
||||||
|
diag_open = QtWidgets.QFileDialog(
|
||||||
|
self,
|
||||||
|
self.tr("Upload plc files..."),
|
||||||
|
helper.cm.program_last_dir_upload,
|
||||||
|
self.tr("Python file (*.py);;Config file (*.conf);;;JSON file (*.json);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||||
|
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
|
||||||
|
self.rejected.connect(diag_open.reject)
|
||||||
|
|
||||||
|
if diag_open.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_open.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
lst_files = diag_open.selectedFiles()
|
||||||
|
if len(lst_files) > 0:
|
||||||
|
helper.cm.program_last_dir_upload = os.path.dirname(lst_files[0])
|
||||||
|
|
||||||
|
elif self.cbb_format.currentIndex() == 1:
|
||||||
|
# Select folder to upload
|
||||||
|
diag_folder = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Select folder to upload..."),
|
||||||
|
helper.cm.program_last_dir_upload,
|
||||||
|
)
|
||||||
|
diag_folder.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
|
||||||
|
self.rejected.connect(diag_folder.reject)
|
||||||
|
|
||||||
|
if diag_folder.exec() != QtWidgets.QFileDialog.Accepted:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_dir = diag_folder.selectedFiles()[0]
|
||||||
|
helper.cm.program_last_dir_upload = selected_dir
|
||||||
|
|
||||||
|
folder_name = os.path.basename(selected_dir)
|
||||||
|
lst_files = self.create_filelist(selected_dir)
|
||||||
|
|
||||||
|
elif self.cbb_format.currentIndex() == 2:
|
||||||
|
# Upload zip archive content
|
||||||
|
diag_open = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Upload content of ZIP archive..."),
|
||||||
|
helper.cm.program_last_file_upload,
|
||||||
|
self.tr("ZIP archive (*.zip);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||||
|
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||||
|
diag_open.setDefaultSuffix("zip")
|
||||||
|
self.rejected.connect(diag_open.reject)
|
||||||
|
|
||||||
|
if diag_open.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_open.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = diag_open.selectedFiles()[0]
|
||||||
|
helper.cm.program_last_file_upload = filename
|
||||||
|
if zipfile.is_zipfile(filename):
|
||||||
|
dirtmp = mkdtemp()
|
||||||
|
fhz = zipfile.ZipFile(filename)
|
||||||
|
fhz.extractall(dirtmp)
|
||||||
|
fhz.close()
|
||||||
|
|
||||||
|
lst_files = self.create_filelist(dirtmp)
|
||||||
|
dirselect, rscfile = self.check_replacedir(dirtmp)
|
||||||
|
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The selected file ist not a ZIP archive."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
elif self.cbb_format.currentIndex() == 3:
|
||||||
|
# Upload TarGz content
|
||||||
|
diag_open = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Upload content of TAR archive..."),
|
||||||
|
helper.cm.program_last_file_upload,
|
||||||
|
self.tr("TAR archive (*.tgz);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||||
|
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||||
|
diag_open.setDefaultSuffix("tgz")
|
||||||
|
self.rejected.connect(diag_open.reject)
|
||||||
|
|
||||||
|
if diag_open.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_open.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = diag_open.selectedFiles()[0]
|
||||||
|
helper.cm.program_last_file_upload = filename
|
||||||
|
if tarfile.is_tarfile(filename):
|
||||||
|
dirtmp = mkdtemp()
|
||||||
|
fht = tarfile.open(filename)
|
||||||
|
fht.extractall(dirtmp)
|
||||||
|
fht.close()
|
||||||
|
|
||||||
|
lst_files = self.create_filelist(dirtmp)
|
||||||
|
dirselect, rscfile = self.check_replacedir(dirtmp)
|
||||||
|
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The selected file ist not a TAR archive."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# No files selected
|
||||||
|
if len(lst_files) == 0:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("No files to upload..."), self.tr(
|
||||||
|
"Found no files to upload in given location or archive."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
remove_temp()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Clean up directory before upload
|
||||||
|
clean_revpi = helper.cm.call_remote_function("plcuploadclean", default_value=False)
|
||||||
|
if self.cbx_clear.isChecked() and not clean_revpi:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"There was an error deleting the files on the Revolution Pi.\n"
|
||||||
|
"Upload aborted! Please try again."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
remove_temp()
|
||||||
|
return
|
||||||
|
|
||||||
|
plc_program_not_in_upload = True
|
||||||
|
ec = 0
|
||||||
|
|
||||||
|
for file_name in lst_files:
|
||||||
|
|
||||||
|
if file_name == rscfile:
|
||||||
|
# Do not send piCtory configuration
|
||||||
|
continue
|
||||||
|
|
||||||
|
# fixme: Fehlerabfang bei Dateilesen
|
||||||
|
with open(file_name, "rb") as fh:
|
||||||
|
|
||||||
|
# Generate relative file name for transfer
|
||||||
|
if dirselect == "":
|
||||||
|
sendname = os.path.basename(file_name)
|
||||||
|
else:
|
||||||
|
# Append folder name on complete folder transfer to crate it on Revolution Pi
|
||||||
|
sendname = os.path.join(
|
||||||
|
folder_name,
|
||||||
|
file_name.replace(dirselect, "")[1:]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Try to find given plc start program in upload files
|
||||||
|
if sendname == self.dc.get("plcprogram", ""):
|
||||||
|
plc_program_not_in_upload = False
|
||||||
|
|
||||||
|
ustatus = helper.cm.call_remote_function(
|
||||||
|
"plcupload", Binary(gzip.compress(fh.read())), sendname
|
||||||
|
)
|
||||||
|
if ustatus is None:
|
||||||
|
ec = -2
|
||||||
|
break
|
||||||
|
elif not ustatus:
|
||||||
|
ec = -1
|
||||||
|
break
|
||||||
|
|
||||||
|
if ec == 0:
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"The PLC program was transferred successfully."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.lst_files = helper.cm.call_remote_function("get_filelist", default_value=[])
|
||||||
|
self._load_settings(files_only=True)
|
||||||
|
if plc_program_not_in_upload:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, self.tr("Information"), self.tr(
|
||||||
|
"Could not find the selected PLC start program in "
|
||||||
|
"uploaded files.\nThis is not an error, if the file "
|
||||||
|
"was already on the Revolution Pi. Check PLC start "
|
||||||
|
"program field"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.cbx_pictory.isChecked():
|
||||||
|
if rscfile is not None:
|
||||||
|
self._upload_pictory(rscfile)
|
||||||
|
else:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"There is no piCtory configuration in this archive."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif ec == -1:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"The Revolution Pi could not process some parts of the transmission."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif ec == -2:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Errors occurred during transmission."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
remove_temp()
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# region # REGION: Control files
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_pictory_download_pressed(self):
|
||||||
|
"""Download piCtory configuration."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
diag_save = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Save piCtory file..."),
|
||||||
|
os.path.join(
|
||||||
|
helper.cm.program_last_dir_pictory,
|
||||||
|
"{0}.rsc".format(helper.cm.name)
|
||||||
|
),
|
||||||
|
self.tr("piCtory file (*.rsc);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_save.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
|
||||||
|
diag_save.setDefaultSuffix("rsc")
|
||||||
|
self.rejected.connect(diag_save.reject)
|
||||||
|
|
||||||
|
if diag_save.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_save.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = diag_save.selectedFiles()[0]
|
||||||
|
helper.cm.program_last_dir_pictory = os.path.dirname(filename)
|
||||||
|
bin_buffer = helper.cm.call_remote_function("get_pictoryrsc") # type: Binary
|
||||||
|
if bin_buffer is None:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Could not load piCtory file from Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
fh = open(filename, "wb")
|
||||||
|
fh.write(bin_buffer.data)
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"piCtory configuration successfully loaded and saved to:\n{0}."
|
||||||
|
"".format(filename)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_pictory_upload_pressed(self):
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
diag_open = QtWidgets.QFileDialog(
|
||||||
|
self, self.tr("Upload piCtory file..."),
|
||||||
|
helper.cm.program_last_pictory_file,
|
||||||
|
self.tr("piCtory file (*.rsc);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||||
|
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||||
|
diag_open.setDefaultSuffix("rsc")
|
||||||
|
self.rejected.connect(diag_open.reject)
|
||||||
|
|
||||||
|
if diag_open.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_open.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._upload_pictory(diag_open.selectedFiles()[0])
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot()
|
||||||
|
def on_btn_procimg_download_pressed(self):
|
||||||
|
"""Download process image."""
|
||||||
|
if not helper.cm.connected:
|
||||||
|
return
|
||||||
|
|
||||||
|
diag_save = QtWidgets.QFileDialog(
|
||||||
|
self,
|
||||||
|
self.tr("Save piControl file..."),
|
||||||
|
os.path.join(
|
||||||
|
helper.cm.program_last_dir_picontrol,
|
||||||
|
"{0}.img".format(helper.cm.name)
|
||||||
|
),
|
||||||
|
self.tr("Process image file (*.img);;All files (*.*)")
|
||||||
|
)
|
||||||
|
diag_save.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
|
||||||
|
diag_save.setDefaultSuffix("img")
|
||||||
|
self.rejected.connect(diag_save.reject)
|
||||||
|
|
||||||
|
if diag_save.exec() != QtWidgets.QFileDialog.AcceptSave or len(diag_save.selectedFiles()) != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = diag_save.selectedFiles()[0]
|
||||||
|
helper.cm.program_last_dir_picontrol = os.path.dirname(filename)
|
||||||
|
bin_buffer = helper.cm.call_remote_function("get_procimg") # type: Binary
|
||||||
|
|
||||||
|
if bin_buffer is None:
|
||||||
|
QtWidgets.QMessageBox.critical(
|
||||||
|
self, self.tr("Error"), self.tr(
|
||||||
|
"Could not load process image from Revolution Pi."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
fh = open(filename, "wb")
|
||||||
|
fh.write(bin_buffer.data)
|
||||||
|
fh.close()
|
||||||
|
|
||||||
|
QtWidgets.QMessageBox.information(
|
||||||
|
self, self.tr("Success"), self.tr(
|
||||||
|
"Process image successfully loaded and saved to:\n{0}."
|
||||||
|
"".format(filename)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# endregion # # # # #
|
||||||
57
setup.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Setupscript fuer RevPiPyLoad."""
|
||||||
|
__author__ = "Sven Sager"
|
||||||
|
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||||
|
__license__ = "LGPLv3"
|
||||||
|
|
||||||
|
import distutils.command.install_egg_info
|
||||||
|
from distutils.core import setup
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
|
||||||
|
class MyEggInfo(distutils.command.install_egg_info.install_egg_info):
|
||||||
|
|
||||||
|
u"""Disable egg_info installation, seems pointless for a non-library."""
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
u"""just pass egg_info."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
version="0.9.0",
|
||||||
|
python_requires="~=3.4",
|
||||||
|
requires=["PyQt5", "zeroconf"],
|
||||||
|
|
||||||
|
scripts=["data/revpicommander"],
|
||||||
|
data_files=[
|
||||||
|
("share/applications", ["data/revpicommander.desktop"]),
|
||||||
|
("share/icons/hicolor/32x32/apps", ["data/revpicommander.png"]),
|
||||||
|
("share/revpicommander", glob("revpicommander/*.py")),
|
||||||
|
("share/revpicommander/revpimodio2", glob("lib/revpimodio2/revpimodio2/*.py")),
|
||||||
|
("share/revpicommander/ui", glob("include/ui/*.py")),
|
||||||
|
("share/revpicommander/locale/", glob("revpicommander/locale/*.qm")),
|
||||||
|
],
|
||||||
|
|
||||||
|
# Additional meta-data
|
||||||
|
name="revpicommander",
|
||||||
|
author="Sven Sager",
|
||||||
|
author_email="akira@narux.de",
|
||||||
|
maintainer="Sven Sager",
|
||||||
|
maintainer_email="akira@revpimodio.org",
|
||||||
|
url="https://revpimodio.org/revpipyplc/",
|
||||||
|
description="GUI for Revolution Pi to upload programs and do IO-Checks",
|
||||||
|
long_description=""
|
||||||
|
"Dieses Programm startet beim Systemstart ein angegebenes Python PLC\n"
|
||||||
|
"Programm. Es überwacht das Programm und startet es im Fehlerfall neu.\n"
|
||||||
|
"Bei Abstruz kann das gesamte /dev/piControl0 auf 0x00 gesettz werden.\n"
|
||||||
|
"Außerdem stellt es einen XML-RPC Server bereit, über den die Software\n"
|
||||||
|
"auf den RevPi geladen werden kann. Das Prozessabbild kann über ein Tool\n"
|
||||||
|
"zur Laufzeit überwacht werden.",
|
||||||
|
classifiers=[
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
"Operating System :: POSIX :: Linux",
|
||||||
|
],
|
||||||
|
license="GPLv3",
|
||||||
|
cmdclass={"install_egg_info": MyEggInfo},
|
||||||
|
)
|
||||||
6
stdeb.cfg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
Debian-Version=1
|
||||||
|
Depends3=python3-pyqt5, python3-zeroconf
|
||||||
|
Section=universe/x11
|
||||||
|
Suite=stable
|
||||||
|
X-Python3-Version: >=3.4
|
||||||
29
translate.pro
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
SOURCES = revpicommander/aclmanager.py \
|
||||||
|
revpicommander/avahisearch.py \
|
||||||
|
revpicommander/debugcontrol.py \
|
||||||
|
revpicommander/debugios.py \
|
||||||
|
revpicommander/mqttmanager.py \
|
||||||
|
revpicommander/revpidevelop.py \
|
||||||
|
revpicommander/revpiinfo.py \
|
||||||
|
revpicommander/revpilogfile.py \
|
||||||
|
revpicommander/revpioption.py \
|
||||||
|
revpicommander/revpiplclist.py \
|
||||||
|
revpicommander/revpiprogram.py \
|
||||||
|
revpicommander/revpicommander.py
|
||||||
|
|
||||||
|
FORMS = include/ui_dev/aclmanager.ui \
|
||||||
|
include/ui_dev/avahisearch.ui \
|
||||||
|
include/ui_dev/debugcontrol.ui \
|
||||||
|
include/ui_dev/debugios.ui \
|
||||||
|
include/ui_dev/mqttmanager.ui \
|
||||||
|
include/ui_dev/revpidevelop.ui \
|
||||||
|
include/ui_dev/revpiinfo.ui \
|
||||||
|
include/ui_dev/revpilogfile.ui \
|
||||||
|
include/ui_dev/revpioption.ui \
|
||||||
|
include/ui_dev/revpiplclist.ui \
|
||||||
|
include/ui_dev/revpiprogram.ui \
|
||||||
|
include/ui_dev/revpicommander.ui
|
||||||
|
|
||||||
|
TRANSLATIONS = revpicommander/locale/revpicommander_de.ts
|
||||||
|
|
||||||
|
CODECRORTR = UTF-8
|
||||||