mirror of
https://github.com/naruxde/revpicommander.git
synced 2026-08-16 05:45:53 +02:00
Merge tag '0.12.0' into pkg/debian
Release version 0.12.0
This commit is contained in:
Generated
+3
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11 (revpicommander)" />
|
||||
</component>
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
|
||||
@@ -3,11 +3,14 @@ MAKEFLAGS = --no-print-directory --no-builtin-rules
|
||||
.DEFAULT_GOAL = all
|
||||
|
||||
# Variables
|
||||
PACKAGE = revpicommander
|
||||
APP_NAME = RevPi\ Commander
|
||||
APP_IDENT = org.revpimodio.revpicommander
|
||||
PACKAGE = revpicommander
|
||||
APP_NAME = RevPi\ Commander
|
||||
APP_IDENT = org.revpimodio.revpicommander
|
||||
APPLE_SIG = "Developer ID Application: Sven Sager (U3N5843D9K)"
|
||||
|
||||
# Python interpreter to use for venv creation
|
||||
SYSTEM_PYTHON = python3
|
||||
|
||||
# Set path to create the virtual environment with package name
|
||||
ifdef PYTHON3_VENV
|
||||
VENV_PATH = $(PYTHON3_VENV)/$(PACKAGE)
|
||||
@@ -15,39 +18,37 @@ else
|
||||
VENV_PATH = venv
|
||||
endif
|
||||
|
||||
# If virtualenv exists, use it. If not, use PATH to find commands
|
||||
SYSTEM_PYTHON = python3
|
||||
PYTHON = $(or $(wildcard $(VENV_PATH)/bin/python), $(SYSTEM_PYTHON))
|
||||
|
||||
APP_VERSION = $(shell "$(PYTHON)" src/$(PACKAGE) --version | cut -d ' ' -f 2)
|
||||
|
||||
all: build_ui build_rc test build
|
||||
|
||||
# Set targets for "all"-target
|
||||
all: build-ui build-rc build
|
||||
.PHONY: all
|
||||
|
||||
## Environment
|
||||
venv-info:
|
||||
@echo Environment for $(APP_NAME) $(APP_VERSION)
|
||||
@echo Using path: "$(VENV_PATH)"
|
||||
exit 0
|
||||
|
||||
## Virtual environment creation with SYSTEM_PYTHON
|
||||
venv:
|
||||
# Start with empty environment
|
||||
"$(SYSTEM_PYTHON)" -m venv "$(VENV_PATH)"
|
||||
source "$(VENV_PATH)/bin/activate" && \
|
||||
python3 -m pip install --upgrade pip && \
|
||||
python3 -m pip install -r requirements.txt
|
||||
exit 0
|
||||
"$(VENV_PATH)/bin/pip" install --upgrade pip
|
||||
"$(VENV_PATH)/bin/pip" install --upgrade -r requirements.txt
|
||||
|
||||
venv-ssp:
|
||||
# Include system installed site-packages and add just missing modules
|
||||
"$(SYSTEM_PYTHON)" -m venv --system-site-packages "$(VENV_PATH)"
|
||||
source "$(VENV_PATH)/bin/activate" && \
|
||||
python3 -m pip install --upgrade pip && \
|
||||
python3 -m pip install -r requirements.txt
|
||||
exit 0
|
||||
"$(VENV_PATH)/bin/pip" install --upgrade pip
|
||||
"$(VENV_PATH)/bin/pip" install --upgrade -r requirements.txt
|
||||
|
||||
.PHONY: venv-info venv venv-ssp
|
||||
.PHONY: venv venv-ssp
|
||||
|
||||
# Choose python interpreter from venv or system
|
||||
PYTHON = $(or $(wildcard $(VENV_PATH)/bin/python), $(SYSTEM_PYTHON))
|
||||
|
||||
# Read app version from program
|
||||
APP_VERSION = $(shell "$(PYTHON)" src/$(PACKAGE) --version | cut -d ' ' -f 2)
|
||||
|
||||
# Environment info
|
||||
venv-info:
|
||||
@echo Environment for $(APP_NAME) $(APP_VERSION)
|
||||
@echo Using path: "$(VENV_PATH)"
|
||||
|
||||
.PHONY: venv-info
|
||||
|
||||
## Compile Qt UI files to python code
|
||||
build-ui:
|
||||
@@ -109,6 +110,7 @@ app-licenses:
|
||||
|
||||
app: build-ui build-rc app-licenses
|
||||
"$(PYTHON)" -m PyInstaller -n $(APP_NAME) \
|
||||
--collect-submodules=zeroconf \
|
||||
--add-data="src/$(PACKAGE)/locale:./$(PACKAGE)/locale" \
|
||||
--add-data="dist/bundled-libraries.md:$(PACKAGE)/open-source-licenses" \
|
||||
--add-data="dist/open-source-licenses.*:$(PACKAGE)/open-source-licenses" \
|
||||
@@ -123,6 +125,7 @@ app: build-ui build-rc app-licenses
|
||||
|
||||
app-mac: build-ui build-rc app-licenses
|
||||
"$(PYTHON)" -m PyInstaller -n $(APP_NAME) \
|
||||
--collect-submodules=zeroconf \
|
||||
--add-data="src/$(PACKAGE)/locale:./$(PACKAGE)/locale" \
|
||||
--add-data="dist/bundled-libraries.md:$(PACKAGE)/open-source-licenses" \
|
||||
--add-data="dist/open-source-licenses.*:$(PACKAGE)/open-source-licenses" \
|
||||
@@ -164,6 +167,8 @@ clean:
|
||||
rm -rf build dist src/*.egg-info
|
||||
# PyInstaller created files
|
||||
rm -rf *.spec
|
||||
# Pycaches
|
||||
find . -type d -name '__pycache__' -exec rm -r {} \+
|
||||
|
||||
distclean: clean
|
||||
# Virtual environment
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
set PACKAGE=revpicommander
|
||||
set APP_NAME=RevPi Commander
|
||||
|
||||
set PYTHON=venv\\Scripts\\python.exe
|
||||
set PYTHON=venv\Scripts\python.exe
|
||||
|
||||
if "%1" == "venv" goto venv
|
||||
if "%1" == "test" goto test
|
||||
@@ -24,7 +24,7 @@ goto end
|
||||
|
||||
:venv
|
||||
python -m venv venv
|
||||
venv\\Scripts\\pip.exe install -r requirements.txt
|
||||
venv\Scripts\pip.exe install -r requirements.txt
|
||||
goto end
|
||||
|
||||
:test
|
||||
@@ -41,7 +41,7 @@ goto end
|
||||
mkdir dist
|
||||
%PYTHON% -m piplicenses ^
|
||||
--format=markdown ^
|
||||
--output-file dist/bundled-libraries.md
|
||||
--output-file dist\\bundled-libraries.md
|
||||
%PYTHON% -m piplicenses ^
|
||||
--with-authors ^
|
||||
--with-urls ^
|
||||
@@ -49,7 +49,7 @@ goto end
|
||||
--with-license-file ^
|
||||
--no-license-path ^
|
||||
--format=json ^
|
||||
--output-file dist/open-source-licenses.json
|
||||
--output-file dist\\open-source-licenses.json
|
||||
%PYTHON% -m piplicenses ^
|
||||
--with-authors ^
|
||||
--with-urls ^
|
||||
@@ -57,18 +57,19 @@ goto end
|
||||
--with-license-file ^
|
||||
--no-license-path ^
|
||||
--format=plain-vertical ^
|
||||
--output-file dist/open-source-licenses.txt
|
||||
--output-file dist\\open-source-licenses.txt
|
||||
%PYTHON% -m PyInstaller -n "%APP_NAME%" ^
|
||||
--add-data="dist/bundled-libraries.md;%PACKAGE%\open-source-licenses" ^
|
||||
--add-data="dist/open-source-licenses.*;%PACKAGE%\open-source-licenses" ^
|
||||
--add-data="src\%PACKAGE%\locale;.\%PACKAGE%\locale" ^
|
||||
--add-data="data\%PACKAGE%.ico;." ^
|
||||
--collect-submodules=zeroconf ^
|
||||
--add-data="dist\\bundled-libraries.md;%PACKAGE%\\open-source-licenses" ^
|
||||
--add-data="dist\\open-source-licenses.*;%PACKAGE%\\open-source-licenses" ^
|
||||
--add-data="src\\%PACKAGE%\\locale;.\\%PACKAGE%\\locale" ^
|
||||
--add-data="data\\%PACKAGE%.ico;." ^
|
||||
--icon=data\\%PACKAGE%.ico ^
|
||||
--noconfirm ^
|
||||
--clean ^
|
||||
--onedir ^
|
||||
--windowed ^
|
||||
src\\%PACKAGE%\\__main__.py
|
||||
src\%PACKAGE%\__main__.py
|
||||
goto end
|
||||
|
||||
:distclean
|
||||
@@ -76,7 +77,7 @@ goto end
|
||||
|
||||
:clean
|
||||
rmdir /S /Q .pytest_cache
|
||||
rmdir /S /Q build dist src\%PACKAGE%.egg-info
|
||||
rmdir /S /Q build dist src\\%PACKAGE%.egg-info
|
||||
del /Q *.spec
|
||||
|
||||
:end
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@ wheel
|
||||
# Runtime dependencies, must match install_requires in setup.py
|
||||
keyring>=23.13.1
|
||||
PyQt5>=5.14.1
|
||||
paramiko>=2.12.0
|
||||
asyncssh>=2.14.0
|
||||
revpimodio2>=2.5.6
|
||||
zeroconf>=0.24.4
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "RevPi Commander"
|
||||
#define MyAppVersion "0.11.0"
|
||||
#define MyAppVersion "0.12.0"
|
||||
#define MyAppPublisher "Sven Sager"
|
||||
#define MyAppURL "https://revpimodio.org/"
|
||||
#define MyAppICO "data\revpicommander.ico"
|
||||
|
||||
@@ -19,7 +19,7 @@ setup(
|
||||
install_requires=[
|
||||
"keyring",
|
||||
"PyQt5",
|
||||
"paramiko",
|
||||
"asyncssh",
|
||||
"revpimodio2",
|
||||
"zeroconf"
|
||||
],
|
||||
|
||||
@@ -4,4 +4,4 @@ __author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023 Sven Sager"
|
||||
__license__ = "GPLv2"
|
||||
__package__ = "revpicommander"
|
||||
__version__ = "0.11.0"
|
||||
__version__ = "0.12.0"
|
||||
|
||||
@@ -57,10 +57,10 @@ class AclManager(QtWidgets.QDialog, Ui_diag_aclmanager):
|
||||
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."
|
||||
"ACL list contains errors.\nCheck rows highlighted in red. "
|
||||
"The ACL levels or IP addresses are invalid. "
|
||||
"If you save this dialog, invalid entries will be "
|
||||
"removed automatically."
|
||||
)
|
||||
)
|
||||
return True
|
||||
@@ -97,7 +97,7 @@ class AclManager(QtWidgets.QDialog, Ui_diag_aclmanager):
|
||||
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"
|
||||
"Quit without saving?\nUnsaved changes will be lost."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
|
||||
@@ -126,7 +126,7 @@ class AclManager(QtWidgets.QDialog, Ui_diag_aclmanager):
|
||||
while self.tb_acls.rowCount() > 0:
|
||||
self.tb_acls.removeRow(0)
|
||||
self.cbb_level.clear()
|
||||
self.cbb_level.addItem(self.tr("Select..."), -1)
|
||||
self.cbb_level.addItem(self.tr("Select"), -1)
|
||||
self.lbl_level_info.clear()
|
||||
|
||||
self.__re_ipacl = compile(
|
||||
@@ -186,9 +186,9 @@ class AclManager(QtWidgets.QDialog, Ui_diag_aclmanager):
|
||||
has_error = False
|
||||
tool_tip = ""
|
||||
else:
|
||||
brush = QtGui.QBrush(QtGui.QColor("red"))
|
||||
brush = QtGui.QBrush(QtGui.QColor("#CC6666"))
|
||||
has_error = True
|
||||
tool_tip = self.tr("This entry has an invalid ACL level or wrong IP format!")
|
||||
tool_tip = self.tr("Invalid ACL level or IP address format.")
|
||||
|
||||
for row in range(self.tb_acls.rowCount()):
|
||||
item_0 = self.tb_acls.item(row, 0)
|
||||
@@ -370,8 +370,8 @@ class AclManager(QtWidgets.QDialog, Ui_diag_aclmanager):
|
||||
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."
|
||||
"Cannot save ACL entry. Check IP address format "
|
||||
"and ACL level."
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class AvahiSearch(QtWidgets.QDialog, Ui_diag_search):
|
||||
return None
|
||||
|
||||
settings = RevPiSettings()
|
||||
settings.folder = self.tr("Auto discovered")
|
||||
settings.folder = self.tr("Automatically discovered")
|
||||
settings.name = item.data(WidgetData.host_name)
|
||||
settings.address = item.data(WidgetData.address)
|
||||
settings.port = item.data(WidgetData.port)
|
||||
|
||||
@@ -20,7 +20,7 @@ class BackgroundWorker(QtCore.QThread):
|
||||
|
||||
def __init__(self, parent=None, interruption_text: str = None):
|
||||
super().__init__(parent)
|
||||
self._interruption_text = interruption_text or self.tr("User requested cancellation...")
|
||||
self._interruption_text = interruption_text or self.tr("Cancellation requested")
|
||||
|
||||
def check_cancel(self) -> bool:
|
||||
"""
|
||||
|
||||
@@ -135,7 +135,7 @@ class DebugControl(QtWidgets.QWidget, Ui_wid_debugcontrol):
|
||||
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."),
|
||||
self.tr("piControl driver reset detected"),
|
||||
10000
|
||||
)
|
||||
self.reload_devices()
|
||||
@@ -175,7 +175,7 @@ class DebugControl(QtWidgets.QWidget, Ui_wid_debugcontrol):
|
||||
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."
|
||||
"Error while getting values from RevPi"
|
||||
), 5000)
|
||||
|
||||
return
|
||||
@@ -229,9 +229,9 @@ class DebugControl(QtWidgets.QWidget, Ui_wid_debugcontrol):
|
||||
win.set_value(io[0], value_procimg)
|
||||
|
||||
if self.cbx_refresh.isChecked():
|
||||
win.stat_bar.showMessage(self.tr("Auto update values..."), 1000)
|
||||
win.stat_bar.showMessage(self.tr("Updating values"), 1000)
|
||||
else:
|
||||
win.stat_bar.showMessage(self.tr("Values updated..."), 2000)
|
||||
win.stat_bar.showMessage(self.tr("Values updated"), 2000)
|
||||
|
||||
if self.driver_reset_detected:
|
||||
# Show values, which we can recover to empty process image
|
||||
@@ -269,7 +269,7 @@ class DebugControl(QtWidgets.QWidget, Ui_wid_debugcontrol):
|
||||
# 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"
|
||||
"Error setting value for 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])
|
||||
|
||||
@@ -266,12 +266,12 @@ class DebugIos(QtWidgets.QMainWindow, Ui_win_debugios):
|
||||
switching_cycles = helper.cm.call_remote_function(
|
||||
"ps_switching_cycles",
|
||||
sender.objectName(),
|
||||
default_value=self.tr("Can not display"),
|
||||
default_value=self.tr("Cannot display"),
|
||||
)
|
||||
if type(switching_cycles) is not list:
|
||||
switching_cycles = [switching_cycles]
|
||||
for i in range(len(switching_cycles)):
|
||||
relais_counter = self.tr(" Relais {0}").format(i + 1)
|
||||
relais_counter = self.tr(" Relay {0}").format(i + 1)
|
||||
if len(switching_cycles) == 1:
|
||||
relais_counter = ""
|
||||
men.addAction(
|
||||
@@ -284,29 +284,29 @@ class DebugIos(QtWidgets.QMainWindow, Ui_win_debugios):
|
||||
|
||||
if sender.property("byte_length") > 4:
|
||||
# Textbox needs format buttons
|
||||
act_as_text = QtWidgets.QAction(self.tr("as text"))
|
||||
act_as_text = QtWidgets.QAction(self.tr("As text"))
|
||||
men.addAction(act_as_text)
|
||||
act_as_number = QtWidgets.QAction(self.tr("as number"))
|
||||
act_as_number = QtWidgets.QAction(self.tr("As number"))
|
||||
men.addAction(act_as_number)
|
||||
men.addSeparator()
|
||||
else:
|
||||
act_as_text = None
|
||||
act_as_number = None
|
||||
|
||||
act_signed = QtWidgets.QAction(self.tr("signed"), men)
|
||||
act_signed = QtWidgets.QAction(self.tr("Signed"), men)
|
||||
act_signed.setCheckable(True)
|
||||
act_signed.setChecked(sender.property("signed") or False)
|
||||
if sender.property("bit_address") == -1:
|
||||
men.addAction(act_signed)
|
||||
|
||||
act_byteorder = QtWidgets.QAction(self.tr("big_endian"), men)
|
||||
act_byteorder = QtWidgets.QAction(self.tr("Big-endian"), men)
|
||||
act_byteorder.setCheckable(True)
|
||||
act_byteorder.setChecked(sender.property("big_endian") or False)
|
||||
if sender.property("bit_address") == -1:
|
||||
men.addAction(act_byteorder)
|
||||
|
||||
if sender.property("byte_length") > 2:
|
||||
act_wordorder = QtWidgets.QAction(self.tr("switch wordorder"))
|
||||
act_wordorder = QtWidgets.QAction(self.tr("Swap word order"))
|
||||
act_wordorder.setCheckable(True)
|
||||
act_wordorder.setChecked(sender.property("word_order") == "big")
|
||||
men.addAction(act_wordorder)
|
||||
@@ -338,7 +338,7 @@ class DebugIos(QtWidgets.QMainWindow, Ui_win_debugios):
|
||||
helper.cm.call_remote_function("ps_reset_counter", sender.objectName(), raise_exception=True)
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
QtWidgets.QMessageBox.critical(self, self.tr("Error"), self.tr("Could not reset the counter value"))
|
||||
QtWidgets.QMessageBox.critical(self, self.tr("Error"), self.tr("Cannot reset counter value."))
|
||||
|
||||
if sender.property("frm"):
|
||||
sender.setProperty("frm", "{0}{1}".format(
|
||||
@@ -451,8 +451,8 @@ class DebugIos(QtWidgets.QMainWindow, Ui_win_debugios):
|
||||
except UnicodeDecodeError:
|
||||
child.setProperty("struct_type", "number")
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, self.tr("Can not use format text"), self.tr(
|
||||
"Can not convert bytes {0} to a text for IO '{1}'. Switch to number format instead!"
|
||||
self, self.tr("Cannot use text format."), self.tr(
|
||||
"Cannot convert bytes {0} to text for I/O '{1}'. Switch to number format instead."
|
||||
).format(value, io_name)
|
||||
)
|
||||
if child.property("struct_type") == "number":
|
||||
|
||||
+147
-48
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Helper functions for this application."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023 Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023-2026 Sven Sager"
|
||||
__license__ = "GPLv2"
|
||||
|
||||
import pickle
|
||||
@@ -15,10 +15,11 @@ from queue import Queue
|
||||
from re import search
|
||||
from threading import Lock
|
||||
from uuid import uuid4
|
||||
from xmlrpc.client import Binary, ServerProxy
|
||||
from xmlrpc.client import Binary, ServerProxy, Transport
|
||||
from configparser import ConfigParser
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from paramiko.ssh_exception import AuthenticationException
|
||||
import asyncssh
|
||||
|
||||
from . import proginit as pi
|
||||
from .ssh_tunneling.server import SSHLocalTunnel
|
||||
@@ -32,6 +33,21 @@ homedir = environ.get("HOME", "") or environ.get("APPDATA", "")
|
||||
"""Home dir of user."""
|
||||
|
||||
|
||||
class UnixStreamTransport(Transport):
|
||||
"""Transport for xmlrpc to use unix domain sockets."""
|
||||
|
||||
def __init__(self, socket_path):
|
||||
super().__init__()
|
||||
self._socket_path = socket_path
|
||||
|
||||
def make_connection(self, host):
|
||||
import http.client
|
||||
conn = http.client.HTTPConnection("localhost")
|
||||
conn.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
conn.sock.connect(self._socket_path)
|
||||
return conn
|
||||
|
||||
|
||||
class ConnectionFail(IntEnum):
|
||||
NO_XML_RPC = 1
|
||||
SSH_CONNECT = 2
|
||||
@@ -91,6 +107,11 @@ class RevPiSettings:
|
||||
if load_index is not None:
|
||||
self.load_from_index(load_index)
|
||||
|
||||
@property
|
||||
def is_unix_socket(self) -> bool:
|
||||
"""Check if connection is a unix domain socket."""
|
||||
return self.address.startswith("/") or self.address.startswith("./")
|
||||
|
||||
def load_from_index(self, settings_index: int) -> None:
|
||||
"""Load settings from 'connections' index."""
|
||||
self._settings.beginReadArray("connections")
|
||||
@@ -129,8 +150,11 @@ class RevPiSettings:
|
||||
pass
|
||||
|
||||
# These values must exists
|
||||
if not (self.name and self.address and self.port):
|
||||
raise ValueError("Could not geht all required values from saved settings")
|
||||
if not (self.name and self.address):
|
||||
raise ValueError("Could not get all required values from saved settings")
|
||||
|
||||
if not self.is_unix_socket and not self.port:
|
||||
raise ValueError("Port is required for IP connections")
|
||||
|
||||
self._settings.endArray()
|
||||
|
||||
@@ -182,7 +206,8 @@ class RevPiSettings:
|
||||
self._settings.setValue("port", self.port)
|
||||
self._settings.setValue("timeout", self.timeout)
|
||||
|
||||
self._settings.setValue("ssh_use_tunnel", self.ssh_use_tunnel)
|
||||
# Disable SSH tunnel if unix socket is used. SSH will check the type on the remove system
|
||||
self._settings.setValue("ssh_use_tunnel", self.ssh_use_tunnel and not self.is_unix_socket)
|
||||
self._settings.setValue("ssh_port", self.ssh_port)
|
||||
self._settings.setValue("ssh_user", self.ssh_user)
|
||||
self._settings.setValue("ssh_saved_password", self.ssh_saved_password)
|
||||
@@ -342,10 +367,12 @@ class ConnectionManager(QtCore.QThread):
|
||||
|
||||
ssh_tunnel_server = None
|
||||
ssh_tunnel_port = 0
|
||||
ssh_tunnel_socket = None
|
||||
|
||||
socket.setdefaulttimeout(revpi_settings.timeout)
|
||||
|
||||
if revpi_settings.ssh_use_tunnel:
|
||||
# We first connect to find out which target to tunnel
|
||||
ssh_tunnel_server = SSHLocalTunnel(
|
||||
revpi_settings.port,
|
||||
revpi_settings.address,
|
||||
@@ -354,10 +381,68 @@ class ConnectionManager(QtCore.QThread):
|
||||
try:
|
||||
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(revpi_settings.ssh_user, ssh_pass)
|
||||
|
||||
if getattr(revpi_settings, "ssh_enable_revpipyload", False):
|
||||
ssh_tunnel_server.send_cmd("sudo systemctl enable --now revpipyload")
|
||||
# Check for Unix socket on remote system
|
||||
try:
|
||||
stdout, stderr, exit_code = ssh_tunnel_server.send_cmd("cat /etc/revpipyload/revpipyload.conf")
|
||||
if stdout:
|
||||
config = ConfigParser()
|
||||
config.read_string(stdout)
|
||||
if config.has_section("XMLRPC"):
|
||||
bindip = config.get("XMLRPC", "bindip", fallback="").strip()
|
||||
if bindip == "socket":
|
||||
ssh_tunnel_socket = "/run/revpipyload/xmlrpc.socket"
|
||||
elif bindip.startswith("/") or bindip.startswith("./"):
|
||||
ssh_tunnel_socket = bindip
|
||||
|
||||
except AuthenticationException:
|
||||
if ssh_tunnel_socket:
|
||||
log.debug("Using remote unix socket: %s", ssh_tunnel_socket)
|
||||
# Forward local port 0 (dynamic) to remote unix socket
|
||||
ssh_tunnel_server.disconnect()
|
||||
ssh_tunnel_server = SSHLocalTunnel(
|
||||
ssh_tunnel_socket,
|
||||
revpi_settings.address,
|
||||
revpi_settings.ssh_port
|
||||
)
|
||||
ssh_tunnel_port = ssh_tunnel_server.connect_by_credentials(
|
||||
revpi_settings.ssh_user, ssh_pass
|
||||
)
|
||||
else:
|
||||
log.debug("Using remote TCP socket: %s", bindip)
|
||||
|
||||
except Exception as e:
|
||||
log.warning(f"Could not check remote config for unix socket: {e}")
|
||||
|
||||
if getattr(revpi_settings, "ssh_enable_revpipyload", False):
|
||||
cmd_activate_pyload = "systemctl enable --now revpipyload"
|
||||
|
||||
# Test sudo requires password authentication
|
||||
_, _, exit_code = ssh_tunnel_server.send_cmd("sudo -n true")
|
||||
if exit_code == 0:
|
||||
# No password required
|
||||
ssh_tunnel_server.send_cmd(f"sudo {cmd_activate_pyload}")
|
||||
else:
|
||||
# Execute command with sudo password
|
||||
_, _, exit_code = ssh_tunnel_server.send_cmd(
|
||||
f"sudo -S {cmd_activate_pyload}",
|
||||
stdin=ssh_pass,
|
||||
)
|
||||
if exit_code != 0:
|
||||
log.error(
|
||||
"Sudo authentification failed for user %s",
|
||||
revpi_settings.ssh_user,
|
||||
)
|
||||
self.connect_error.emit(
|
||||
self.tr("Error"), self.tr(
|
||||
"Can not activate RevPiPyLoad on remote RevPi.\n"
|
||||
f"Sudo authentification failed for user {revpi_settings.ssh_user}. "
|
||||
"Please activate RevPiPyLoad manually via Cockpit or CLI."
|
||||
),
|
||||
ConnectionFail.NO_XML_RPC_VIA_TUNNEL,
|
||||
revpi_settings,
|
||||
)
|
||||
return False
|
||||
|
||||
except asyncssh.PermissionDenied:
|
||||
self.connect_error.emit(
|
||||
self.tr("Error"), self.tr(
|
||||
"The combination of username and password was rejected from the SSH server.\n\n"
|
||||
@@ -372,17 +457,17 @@ class ConnectionManager(QtCore.QThread):
|
||||
self._clear_settings()
|
||||
self.connect_error.emit(
|
||||
self.tr("Error"), self.tr(
|
||||
"Could not establish a SSH connection to server:\n\n{0}"
|
||||
"Cannot connect to SSH server:\n\n{0}"
|
||||
).format(str(e)),
|
||||
ConnectionFail.SSH_CONNECT,
|
||||
revpi_settings,
|
||||
)
|
||||
return False
|
||||
|
||||
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
|
||||
sp = create_server_proxy(revpi_settings, ssh_tunnel_port)
|
||||
|
||||
else:
|
||||
sp = ServerProxy("http://{0}:{1}".format(revpi_settings.address, revpi_settings.port))
|
||||
sp = create_server_proxy(revpi_settings)
|
||||
|
||||
# Load values and test connection to Revolution Pi
|
||||
try:
|
||||
@@ -398,11 +483,11 @@ class ConnectionManager(QtCore.QThread):
|
||||
if revpi_settings.ssh_use_tunnel:
|
||||
self.connect_error.emit(
|
||||
self.tr("Error"), self.tr(
|
||||
"Can not connect to RevPiPyLoad service through SSH tunnel!\n\n"
|
||||
"This could have the following reasons:\n"
|
||||
"- The RevPiPyLoad service is not running (activate it on your Revolution Pi)\n"
|
||||
"- The RevPiPyLoad XML-RPC service is NOT bind to localhost\n"
|
||||
"- The ACL permission is not set for 127.0.0.1!!!"
|
||||
"Cannot connect to RevPiPyLoad service through SSH tunnel.\n\n"
|
||||
"Possible reasons:\n"
|
||||
"- RevPiPyLoad service is not running. Activate service on your RevPi.\n"
|
||||
"- RevPiPyLoad XML-RPC service is not bound to localhost.\n"
|
||||
"- ACL permission is not set for 127.0.0.1."
|
||||
),
|
||||
ConnectionFail.NO_XML_RPC_VIA_TUNNEL,
|
||||
revpi_settings,
|
||||
@@ -410,14 +495,14 @@ class ConnectionManager(QtCore.QThread):
|
||||
else:
|
||||
self.connect_error.emit(
|
||||
self.tr("Error"), self.tr(
|
||||
"Can not connect to RevPiPyLoad XML-RPC service! \n\n"
|
||||
"This could have the following reasons:\n"
|
||||
"- The Revolution Pi is not online\n"
|
||||
"- The RevPiPyLoad service is not running (activate it on your Revolution Pi)\n"
|
||||
"- The RevPiPyLoad XML-RPC service is bind to localhost, only\n"
|
||||
"- The ACL permission is not set for your IP!!!\n\n"
|
||||
"Use 'Connect via SSH' to use an encrypted connection or run "
|
||||
"'sudo revpipyload_secure_installation' on Revolution Pi to setup direct remote access!"
|
||||
"Cannot connect to RevPiPyLoad XML-RPC service.\n\n"
|
||||
"Possible reasons:\n"
|
||||
"- RevPi is offline.\n"
|
||||
"- RevPiPyLoad service is not running. Activate service on your RevPi.\n"
|
||||
"- RevPiPyLoad XML-RPC service is bound to localhost only.\n"
|
||||
"- The ACL permission is not set for your IP.\n\n"
|
||||
"Use 'Connect via SSH' to use encrypted connection or run "
|
||||
"'sudo revpipyload_secure_installation' on RevPi to set up direct remote access."
|
||||
),
|
||||
ConnectionFail.NO_XML_RPC,
|
||||
revpi_settings,
|
||||
@@ -435,10 +520,7 @@ class ConnectionManager(QtCore.QThread):
|
||||
with self._lck_cli:
|
||||
self.ssh_tunnel_server = ssh_tunnel_server
|
||||
self._cli = sp
|
||||
self._cli_connect.put_nowait((
|
||||
"127.0.0.1" if revpi_settings.ssh_use_tunnel else revpi_settings.address,
|
||||
ssh_tunnel_port if revpi_settings.ssh_use_tunnel else revpi_settings.port
|
||||
))
|
||||
self._cli_connect.put_nowait((revpi_settings, ssh_tunnel_port))
|
||||
|
||||
self.connection_established.emit()
|
||||
|
||||
@@ -546,14 +628,14 @@ class ConnectionManager(QtCore.QThread):
|
||||
|
||||
if self._revpi is not None:
|
||||
sp = None
|
||||
self.status_changed.emit(self.tr("SIMULATING"), "yellow")
|
||||
self.status_changed.emit(self.tr("Simulating"), "#E3DE48")
|
||||
elif self._cli is None:
|
||||
sp = None
|
||||
self.status_changed.emit(self.tr("NOT CONNECTED"), "lightblue")
|
||||
self.status_changed.emit(self.tr("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))
|
||||
revpi_settings, ssh_tunnel_port = self._cli_connect.get()
|
||||
sp = create_server_proxy(revpi_settings, ssh_tunnel_port)
|
||||
self._cli_connect.task_done()
|
||||
|
||||
if sp:
|
||||
@@ -566,7 +648,7 @@ class ConnectionManager(QtCore.QThread):
|
||||
log.warning(e)
|
||||
except Exception as e:
|
||||
log.warning(e)
|
||||
self.status_changed.emit(self.tr("SERVER ERROR"), "red")
|
||||
self.status_changed.emit(self.tr("Server error"), "#CC6666")
|
||||
self._has_error = True
|
||||
self.connection_error_observed.emit("{0} | {1}".format(e, type(e)))
|
||||
|
||||
@@ -582,7 +664,7 @@ class ConnectionManager(QtCore.QThread):
|
||||
self.settings.ssh_user,
|
||||
self.ssh_pass
|
||||
)
|
||||
sp = ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
|
||||
sp = create_server_proxy(self.settings, ssh_tunnel_port)
|
||||
with self._lck_cli:
|
||||
self.ssh_tunnel_server = ssh_tunnel_server
|
||||
self._cli = sp
|
||||
@@ -596,19 +678,19 @@ class ConnectionManager(QtCore.QThread):
|
||||
self.connection_recovered.emit()
|
||||
|
||||
if plc_exit_code == -1:
|
||||
self.status_changed.emit(self.tr("RUNNING"), "green")
|
||||
self.status_changed.emit(self.tr("Running"), "green")
|
||||
elif plc_exit_code == -2:
|
||||
self.status_changed.emit(self.tr("PLC FILE NOT FOUND"), "red")
|
||||
self.status_changed.emit(self.tr("PLC file not found"), "#CC6666")
|
||||
elif plc_exit_code == -3:
|
||||
self.status_changed.emit(self.tr("NOT RUNNING (NO STATUS)"), "yellow")
|
||||
self.status_changed.emit(self.tr("Not running (no status)"), "#E3DE48")
|
||||
elif plc_exit_code == -9:
|
||||
self.status_changed.emit(self.tr("PROGRAM KILLED"), "red")
|
||||
self.status_changed.emit(self.tr("Program killed"), "#CC6666")
|
||||
elif plc_exit_code == -15:
|
||||
self.status_changed.emit(self.tr("PROGRAM TERMED"), "red")
|
||||
self.status_changed.emit(self.tr("Program terminated"), "#CC6666")
|
||||
elif plc_exit_code == 0:
|
||||
self.status_changed.emit(self.tr("NOT RUNNING"), "yellow")
|
||||
self.status_changed.emit(self.tr("Not running"), "#E3DE48")
|
||||
else:
|
||||
self.status_changed.emit(self.tr("FINISHED WITH CODE {0}").format(plc_exit_code), "yellow")
|
||||
self.status_changed.emit(self.tr("Finished with exit code {0}").format(plc_exit_code), "#E3DE48")
|
||||
|
||||
self.msleep(self._cycle_time)
|
||||
|
||||
@@ -669,12 +751,8 @@ class ConnectionManager(QtCore.QThread):
|
||||
|
||||
Use connection_recovered signal to figure out new parameters.
|
||||
"""
|
||||
if not self.settings.ssh_use_tunnel and self.settings.address and self.settings.port:
|
||||
return ServerProxy("http://{0}:{1}".format(self.settings.address, self.settings.port))
|
||||
if self.settings.ssh_use_tunnel and self.ssh_tunnel_server and self.ssh_tunnel_server.connected:
|
||||
return ServerProxy("http://127.0.0.1:{0}".format(self.ssh_tunnel_server.local_tunnel_port))
|
||||
|
||||
return None
|
||||
ssh_tunnel_port = self.ssh_tunnel_server.local_tunnel_port if self.ssh_tunnel_server else None
|
||||
return create_server_proxy(self.settings, ssh_tunnel_port)
|
||||
|
||||
@property
|
||||
def connected(self) -> bool:
|
||||
@@ -699,6 +777,27 @@ cm = ConnectionManager()
|
||||
"""Clobal connection manager instance."""
|
||||
|
||||
|
||||
def create_server_proxy(revpi_settings: RevPiSettings, ssh_tunnel_port: int = None) -> ServerProxy:
|
||||
"""
|
||||
Create a ServerProxy instance based on the given settings.
|
||||
|
||||
:param revpi_settings: Revolution Pi saved connection settings
|
||||
:param ssh_tunnel_port: Use this port if an SSH tunnel is already established
|
||||
:return: ServerProxy instance
|
||||
"""
|
||||
if not ssh_tunnel_port and revpi_settings.is_unix_socket:
|
||||
return ServerProxy("http://localhost", transport=UnixStreamTransport(revpi_settings.address))
|
||||
|
||||
if ssh_tunnel_port:
|
||||
return ServerProxy("http://127.0.0.1:{0}".format(ssh_tunnel_port))
|
||||
|
||||
if revpi_settings.ssh_use_tunnel:
|
||||
# This case is usually handled by passing ssh_tunnel_port after connecting the tunnel
|
||||
return ServerProxy("http://127.0.0.1:{0}".format(revpi_settings.port))
|
||||
|
||||
return ServerProxy("http://{0}:{1}".format(revpi_settings.address, revpi_settings.port))
|
||||
|
||||
|
||||
def all_revpi_settings() -> [RevPiSettings]:
|
||||
"""Get all revpi settings objects."""
|
||||
# Get length of array and close it, the RevPiSettings-class need it
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -81,7 +81,7 @@ class MqttManager(QtWidgets.QDialog, Ui_diag_mqtt):
|
||||
if self._changesdone():
|
||||
ask = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||
"Quit without saving?\nUnsaved changes will be lost."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
if ask:
|
||||
@@ -94,7 +94,7 @@ class MqttManager(QtWidgets.QDialog, Ui_diag_mqtt):
|
||||
if not self._load_settings():
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Can not load the MQTT settings dialog. Missing values!"
|
||||
"Cannot load the MQTT settings dialog. Missing values."
|
||||
)
|
||||
)
|
||||
return QtWidgets.QDialog.Rejected
|
||||
|
||||
@@ -100,8 +100,8 @@ def reconfigure_logger():
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
remove_record = False
|
||||
|
||||
# Remove paramiko ssh module
|
||||
remove_record = remove_record or record.name.startswith("paramiko")
|
||||
# Remove asyncssh module
|
||||
remove_record = remove_record or record.name.startswith("asyncssh")
|
||||
|
||||
return not remove_record
|
||||
|
||||
|
||||
@@ -129,7 +129,8 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
# region # REGION: Connection management
|
||||
|
||||
@QtCore.pyqtSlot(str, str, ConnectionFail, RevPiSettings)
|
||||
def on_cm_connect_error(self, title: str, text: str, fail_code: ConnectionFail, revpi_settings: RevPiSettings):
|
||||
def on_cm_connect_error(self, title: str, text: str, fail_code: ConnectionFail,
|
||||
revpi_settings: RevPiSettings):
|
||||
"""
|
||||
Slot to get information of pyload_connect connection errors.
|
||||
|
||||
@@ -138,13 +139,14 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
:param fail_code: Type of error
|
||||
:param revpi_settings: Settings of the revpi with the error
|
||||
"""
|
||||
if fail_code is ConnectionFail.NO_XML_RPC_VIA_TUNNEL:
|
||||
if (fail_code is ConnectionFail.NO_XML_RPC_VIA_TUNNEL
|
||||
and not getattr(revpi_settings, "ssh_enable_revpipyload", False)):
|
||||
# If RevPiPyLoad is not running, we can try to activate it via ssh
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Information"), self.tr(
|
||||
"Can not connect to RevPiPyLoad service through SSH tunnel!\n\n"
|
||||
"We are trying to activate this service now and reconnect. The settings can be "
|
||||
"changed at any time via 'webstatus'."
|
||||
"Cannot connect to the RevPiPyLoad service through the SSH tunnel.\n\n"
|
||||
"Service activation and reconnection in progress. The settings can be "
|
||||
"changed at any time via Cockpit."
|
||||
),
|
||||
)
|
||||
revpi_settings.ssh_enable_revpipyload = True
|
||||
@@ -259,9 +261,9 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
|
||||
diag_connecting = BackgroundWaiter(
|
||||
revpi_settings.timeout,
|
||||
self.tr("Establish a connection to the Revolution Pi..."),
|
||||
self.tr("Connecting to RevPi"),
|
||||
self,
|
||||
self.tr("Revolution Pi connected!"),
|
||||
self.tr("Connected to RevPi"),
|
||||
)
|
||||
helper.cm.connection_established.connect(diag_connecting.requestInterruption)
|
||||
|
||||
@@ -294,7 +296,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
th_connecting.finished.connect(diag_connecting.requestInterruption)
|
||||
th_connecting.start()
|
||||
|
||||
diag_connecting.exec_dialog(self.tr("Connecting..."), False)
|
||||
diag_connecting.exec_dialog(self.tr("Connecting"), False)
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_act_connections_triggered(self):
|
||||
@@ -328,18 +330,18 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
|
||||
if helper.cm.pyload_simulate(configrsc_file, procimg_file, diag.clean_procimg):
|
||||
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={0}\nconfigrsc={1}\n\n"
|
||||
"You can copy that from header textbox."
|
||||
self, self.tr("Simulator started"), self.tr(
|
||||
"Simulator is running.\n\nUse the additional "
|
||||
"RevPiModIO parameters:\nprocimg={0}\nconfigrsc={1}\n\n"
|
||||
"from the header text box."
|
||||
).format(procimg_file, configrsc_file)
|
||||
)
|
||||
else:
|
||||
log.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 have no write permissions for '{0}'."
|
||||
self, self.tr("Cannot start"), self.tr(
|
||||
"Cannot start the simulator. The PiCtory file might be invalid "
|
||||
"or you do not have write permissions for '{0}'."
|
||||
).format(procimg_file)
|
||||
)
|
||||
|
||||
@@ -354,8 +356,8 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
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."
|
||||
"This version of Log Viewer is not supported in version {0} "
|
||||
"of RevPiPyLoad on your RevPi. At least version 0.4.1 is required."
|
||||
).format(helper.cm.call_remote_function("version", default_value="-"))
|
||||
)
|
||||
return None
|
||||
@@ -375,7 +377,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, self.tr("Warning"), self.tr(
|
||||
"XML-RPC access mode in the RevPiPyLoad "
|
||||
"configuration is too small to access this dialog!"
|
||||
"configuration is too low to access this dialog."
|
||||
)
|
||||
)
|
||||
return
|
||||
@@ -384,9 +386,9 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
if helper.cm.pyload_version < (0, 6, 0):
|
||||
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!"
|
||||
"The version of RevPiPyLoad on your RevPi ({0}) is too old. "
|
||||
"This version of RevPi Commander requires at least version 0.6.0 "
|
||||
"of RevPiPyLoad. Update your RevPi."
|
||||
)
|
||||
)
|
||||
return
|
||||
@@ -405,7 +407,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, self.tr("Warning"), self.tr(
|
||||
"XML-RPC access mode in the RevPiPyLoad "
|
||||
"configuration is too small to access this dialog!"
|
||||
"configuration is too low to access this dialog."
|
||||
)
|
||||
)
|
||||
return
|
||||
@@ -414,7 +416,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_act_developer_triggered(self):
|
||||
"""Extent developer mode to main window."""
|
||||
"""Extend developer mode to main window."""
|
||||
if not helper.cm.connected:
|
||||
return
|
||||
|
||||
@@ -437,10 +439,10 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
|
||||
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!"
|
||||
"Are you sure you want to reset piControl?\n"
|
||||
"The PiCtory configuration will be reloaded. During that time, "
|
||||
"the process image will be interrupted and could cause errors "
|
||||
"on running control programs."
|
||||
)
|
||||
)
|
||||
if ask != QtWidgets.QMessageBox.Yes:
|
||||
@@ -450,14 +452,14 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
if ec == 0:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Success"), self.tr(
|
||||
"piControl reset executed successfully"
|
||||
"piControl reset completed successfully."
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"piControl reset could not be executed successfully"
|
||||
"piControl reset could not be completed."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -474,7 +476,7 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
@QtCore.pyqtSlot()
|
||||
def on_act_webpage_triggered(self):
|
||||
"""Open project page in default browser of operating system."""
|
||||
webbrowser.open("https://revpimodio.org")
|
||||
webbrowser.open("https://revpimodio2.readthedocs.io/en/latest/")
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_act_info_triggered(self):
|
||||
@@ -504,13 +506,13 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
"""Restart plc program on revolution pi."""
|
||||
if helper.cm.simulating:
|
||||
rc = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Reset to piCtory defaults..."), self.tr(
|
||||
self, self.tr("Reset to PiCtory defaults"), self.tr(
|
||||
"Do you want to reset your process image to {0} values?\n"
|
||||
"You have to stop other RevPiModIO programs before doing that, "
|
||||
"because they could reset the outputs."
|
||||
).format(
|
||||
self.tr("zero") if helper.settings.value("simulator/restart_zero", False, bool)
|
||||
else self.tr("piCtory default"))
|
||||
else self.tr("PiCtory defaults"))
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
if rc:
|
||||
# Set piCtory default values in process image
|
||||
@@ -531,10 +533,10 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
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."
|
||||
"The watch mode is not supported in version {0} "
|
||||
"of RevPiPyLoad on your RevPi. At least version 0.5.3 "
|
||||
"is required. The python3-revpimodio2 module may be missing "
|
||||
"or older than version 2.0.0."
|
||||
).format(helper.cm.call_remote_function("version", "-"))
|
||||
)
|
||||
self.btn_plc_debug.setChecked(False)
|
||||
@@ -543,8 +545,8 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
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."
|
||||
"Cannot load this function, because your ACL level is too low.\n"
|
||||
"At least level 1 to read or level 3 to write is required."
|
||||
)
|
||||
)
|
||||
self.btn_plc_debug.setChecked(False)
|
||||
@@ -558,9 +560,8 @@ class RevPiCommander(QtWidgets.QMainWindow, Ui_win_revpicommander):
|
||||
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!"
|
||||
"Cannot load PiCtory configuration.\n"
|
||||
"Check hardware configuration in PiCtory."
|
||||
)
|
||||
)
|
||||
self.btn_plc_debug.setChecked(False)
|
||||
|
||||
@@ -84,7 +84,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
self.dc_settings = {}
|
||||
self.tree_files_counter = 0
|
||||
self.tree_files_counter_max = 10000
|
||||
self.lbl_path_local.setText(helper.cm.settings.watch_path or self.tr("Please select..."))
|
||||
self.lbl_path_local.setText(helper.cm.settings.watch_path or self.tr("Select"))
|
||||
self.lbl_path_local.setToolTip(self.lbl_path_local.text())
|
||||
|
||||
self.btn_all.setEnabled(False)
|
||||
@@ -120,13 +120,13 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
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."
|
||||
"Cannot stop PLC program on RevPi."
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
uploader = UploadFiles(self.file_list_local(), self)
|
||||
if uploader.exec_dialog(self.tr("File transfer...")) == QtWidgets.QDialog.Rejected:
|
||||
if uploader.exec_dialog(self.tr("File transfer")) == QtWidgets.QDialog.Rejected:
|
||||
return
|
||||
|
||||
if uploader.ec == 0:
|
||||
@@ -134,7 +134,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
if not uploader.plc_program_included:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Information"), self.tr(
|
||||
"A PLC program has been uploaded. Please check the "
|
||||
"A PLC program has been uploaded. Check the "
|
||||
"PLC program settings to see if the correct program "
|
||||
"is specified as the start program."
|
||||
)
|
||||
@@ -143,7 +143,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
elif uploader.ec == -1:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"The Revolution Pi could not process some parts of the "
|
||||
"RevPi cannot process some parts of the "
|
||||
"transmission."
|
||||
)
|
||||
)
|
||||
@@ -151,13 +151,13 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
elif uploader.ec == -2:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"),
|
||||
self.tr("Errors occurred during transmission")
|
||||
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."
|
||||
"Cannot start the PLC program on RevPi."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -169,7 +169,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
if "set_plcprogram" in helper.cm.xml_funcs:
|
||||
self.btn_mark_plcprogram.setEnabled(False)
|
||||
self.btn_mark_plcprogram.setToolTip(self.tr(
|
||||
"Set as start file"
|
||||
"Set as start program."
|
||||
))
|
||||
if len(self.tree_files_revpi.selectedItems()) == 1:
|
||||
item = self.tree_files_revpi.selectedItems()[0]
|
||||
@@ -177,7 +177,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
else:
|
||||
self.btn_mark_plcprogram.setEnabled(False)
|
||||
self.btn_mark_plcprogram.setToolTip(self.tr(
|
||||
"Upgrade your Revolution Pi! This function needs at least 'revpipyload' 0.11.0"
|
||||
"Upgrade your RevPi. This function needs at least 'revpipyload' 0.11.0."
|
||||
))
|
||||
|
||||
self.btn_all.setEnabled(state_local)
|
||||
@@ -186,17 +186,17 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
if "plcdeletefile" not in helper.cm.xml_funcs:
|
||||
self.btn_delete_revpi.setEnabled(False)
|
||||
self.btn_delete_revpi.setToolTip(self.tr(
|
||||
"Upgrade your Revolution Pi! This function needs at least 'revpipyload' 0.9.5"
|
||||
"Upgrade your RevPi. This function needs at least 'revpipyload' 0.9.5."
|
||||
))
|
||||
else:
|
||||
self.btn_delete_revpi.setEnabled(state_revpi)
|
||||
self.btn_delete_revpi.setToolTip(self.tr(
|
||||
"Deletes selected files immediately on the Revolution Pi"
|
||||
"Deletes selected files immediately on RevPi."
|
||||
))
|
||||
if "plcdownload_file" not in helper.cm.xml_funcs:
|
||||
self.btn_to_left.setEnabled(False)
|
||||
self.btn_to_left.setToolTip(self.tr(
|
||||
"Upgrade your Revolution Pi! This function needs at least 'revpipyload' 0.9.5"
|
||||
"Upgrade your RevPi. This function needs at least 'revpipyload' 0.9.5."
|
||||
))
|
||||
elif not helper.cm.settings.watch_path:
|
||||
self.btn_to_left.setEnabled(False)
|
||||
@@ -275,7 +275,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
if not os.path.exists(base_dir):
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Can not open last directory '{0}'."
|
||||
"Cannot open last directory '{0}'."
|
||||
).format(base_dir)
|
||||
)
|
||||
return
|
||||
@@ -332,7 +332,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
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."
|
||||
"Stopped scanning for files because more than {0} files were found."
|
||||
).format(self.tree_files_counter_max)
|
||||
)
|
||||
|
||||
@@ -373,7 +373,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
lst_revpi = helper.cm.call_remote_function("get_filelist")
|
||||
self.dc_settings = helper.cm.call_remote_function("get_config", default_value={})
|
||||
self.lbl_path_revpi.setText(
|
||||
self.dc_settings.get("plcworkdir", self.tr("Could not load path of working dir"))
|
||||
self.dc_settings.get("plcworkdir", self.tr("Cannot load the working directory path."))
|
||||
)
|
||||
self.lbl_path_revpi.setToolTip(self.lbl_path_revpi.text())
|
||||
|
||||
@@ -439,7 +439,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
elif not silent:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Can not load file list from Revolution Pi."
|
||||
"Cannot load file list from RevPi."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -470,7 +470,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
log.debug("RevPiFiles.on_btn_select_clicked")
|
||||
|
||||
diag_folder = QtWidgets.QFileDialog(
|
||||
self, self.tr("Select folder..."),
|
||||
self, self.tr("Select folder"),
|
||||
helper.cm.settings.watch_path,
|
||||
)
|
||||
diag_folder.setFileMode(QtWidgets.QFileDialog.DirectoryOnly)
|
||||
@@ -482,7 +482,7 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
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."
|
||||
"Cannot access the folder '{0}' to read files."
|
||||
)
|
||||
)
|
||||
helper.cm.settings.watch_files = []
|
||||
@@ -531,17 +531,17 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
rc = rc.data
|
||||
if not rc:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error..."), self.tr(
|
||||
"Error while download file '{0}'."
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Error while downloading file '{0}'."
|
||||
).format(file_name)
|
||||
)
|
||||
else:
|
||||
file_name = os.path.join(helper.cm.settings.watch_path, file_name)
|
||||
if override is None and os.path.exists(file_name):
|
||||
rc_diag = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Override files..."), self.tr(
|
||||
"One or more files does exist on your computer! Do you want to override the existing"
|
||||
"files?\n\nSelect 'Yes' to override, 'No' to download only missing files."
|
||||
self, self.tr("Overwrite files"), self.tr(
|
||||
"One or more files already exist on your computer. Do you want to overwrite the existing "
|
||||
"files?\n\nSelect 'Yes' to overwrite, 'No' to download only missing files."
|
||||
),
|
||||
buttons=QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel
|
||||
)
|
||||
@@ -571,8 +571,8 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
lst_delete.append(item.data(0, WidgetData.file_name))
|
||||
|
||||
rc = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Delete files from Revolution Pi..."), self.tr(
|
||||
"Do you want to delete {0} files from revolution pi?"
|
||||
self, self.tr("Delete files from RevPi"), self.tr(
|
||||
"Do you want to delete {0} files from RevPi?"
|
||||
).format(len(lst_delete))
|
||||
)
|
||||
if rc != QtWidgets.QMessageBox.Yes:
|
||||
@@ -582,8 +582,8 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
rc = helper.cm.call_remote_function("plcdeletefile", file_name, default_value=False)
|
||||
if not rc:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error..."), self.tr(
|
||||
"Error while delete file '{0}'."
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Error while deleting file '{0}'."
|
||||
).format(file_name)
|
||||
)
|
||||
|
||||
@@ -601,9 +601,9 @@ class RevPiFiles(QtWidgets.QMainWindow, Ui_win_files):
|
||||
if saved is None:
|
||||
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."
|
||||
"Cannot save settings on RevPi.\n"
|
||||
"Try saving the values one more time and check the "
|
||||
"RevPiPyLoad log files if the error occurs again."
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class RevPiInfo(QtWidgets.QDialog, Ui_diag_revpiinfo):
|
||||
elif helper.cm.connected:
|
||||
lst = helper.cm.call_remote_function(
|
||||
"get_filelist",
|
||||
default_value=[self.tr("Can not load file list")]
|
||||
default_value=[self.tr("Cannot load file list.")]
|
||||
)
|
||||
else:
|
||||
lst = [self.tr("Not connected")]
|
||||
|
||||
@@ -207,7 +207,7 @@ class RevPiLogfile(QtWidgets.QMainWindow, Ui_win_revpilogfile):
|
||||
|
||||
if not success:
|
||||
textwidget.clear()
|
||||
textwidget.setPlainText(self.tr("Can not access log file on the RevPi"))
|
||||
textwidget.setPlainText(self.tr("Cannot access log file on RevPi."))
|
||||
elif text != "":
|
||||
# Function will add \n automatically
|
||||
textwidget.appendPlainText(text.strip("\n"))
|
||||
|
||||
@@ -187,7 +187,7 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
|
||||
ask = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"The settings will be set on the Revolution Pi now.\n\n"
|
||||
"Applying settings on RevPi.\n\n"
|
||||
"ACL changes and service settings are applied immediately."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
@@ -234,9 +234,9 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
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."
|
||||
"Cannot save settings on RevPi\n"
|
||||
"Try saving the values one more time and check the "
|
||||
"RevPiPyLoad log files if the error occurs again."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -244,7 +244,7 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
if self._changesdone():
|
||||
ask = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||
"Quit without saving?\nUnsaved changes will be lost."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
|
||||
@@ -273,7 +273,7 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
default_value=False
|
||||
)
|
||||
self.lbl_server_status.setText(
|
||||
self.tr("running") if running else self.tr("stopped")
|
||||
self.tr("Running") if running else self.tr("Stopped")
|
||||
)
|
||||
self.lbl_server_status.setStyleSheet(
|
||||
"color: green" if running else "color: red"
|
||||
@@ -292,7 +292,7 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
self.cbx_mqtt.setToolTip("")
|
||||
self.btn_mqtt.setVisible(True)
|
||||
self.lbl_mqtt_status.setText(
|
||||
self.tr("running") if running else self.tr("stopped")
|
||||
self.tr("Running") if running else self.tr("Stopped")
|
||||
)
|
||||
self.lbl_mqtt_status.setStyleSheet(
|
||||
"color: green" if running else "color: red"
|
||||
@@ -323,8 +323,8 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
def on_btn_aclplcserver_clicked(self):
|
||||
"""Start ACL manager to edit ACL entries."""
|
||||
self.diag_aclmanager.setup_acl_manager(self.acl_plcserver, {
|
||||
0: self.tr("read only"),
|
||||
1: self.tr("read and write"),
|
||||
0: self.tr("Read-only"),
|
||||
1: self.tr("Read/write"),
|
||||
})
|
||||
self.diag_aclmanager.read_only = helper.cm.xml_mode < 4
|
||||
if self.diag_aclmanager.exec() == QtWidgets.QDialog.Accepted:
|
||||
@@ -345,8 +345,8 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
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!"
|
||||
"RevPi will no longer be accessible from "
|
||||
"this program after saving settings."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.No
|
||||
if self.mrk_xml_ask:
|
||||
@@ -355,11 +355,11 @@ class RevPiOption(QtWidgets.QDialog, Ui_diag_options):
|
||||
@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")
|
||||
0: self.tr("Start/stop PLC program and read log files"),
|
||||
1: self.tr("+ Read I/Os 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:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Saved connections of Revolution Pi devices."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023 Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023-2026 Sven Sager"
|
||||
__license__ = "GPLv2"
|
||||
|
||||
from enum import IntEnum
|
||||
@@ -16,6 +16,7 @@ from . import proginit as pi
|
||||
from .helper import RevPiSettings, WidgetData
|
||||
from .ui.revpiplclist_ui import Ui_diag_connections
|
||||
|
||||
DEFAULT_SOCKET_ADDRESS = "/run/revpipyload/xmlrpc.socket"
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
@@ -40,6 +41,8 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
self.lbl_port.setText(self.lbl_port.text().format(self.__default_port))
|
||||
self.sbx_port.setValue(self.__default_port)
|
||||
|
||||
self._mrk_address = ""
|
||||
|
||||
# Dirty workaround to remove default button to prevent action on ENTER key, while user edit texts
|
||||
self.__btn_dummy = QtWidgets.QPushButton(self)
|
||||
self.__btn_dummy.setVisible(False)
|
||||
@@ -134,7 +137,7 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
if self.changes:
|
||||
ask = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||
"Quit without saving?\nUnsaved changes will be lost."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
|
||||
@@ -189,12 +192,21 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
up_ok = index > 0
|
||||
down_ok = index < self.tre_connections.topLevelItemCount() - 1
|
||||
|
||||
address = self.txt_address.text()
|
||||
is_unix_socket = address.startswith("/") or address.startswith("./")
|
||||
is_unix_default = address.lower() == DEFAULT_SOCKET_ADDRESS
|
||||
|
||||
# Value isn't saved in settings, resulting of address value
|
||||
with QtCore.QSignalBlocker(self.cbx_local_socket):
|
||||
self.cbx_local_socket.setChecked(is_unix_default)
|
||||
|
||||
self.btn_up.setEnabled(up_ok)
|
||||
self.btn_down.setEnabled(down_ok)
|
||||
self.btn_delete.setEnabled(con_item or dir_item)
|
||||
self.txt_name.setEnabled(con_item)
|
||||
self.txt_address.setEnabled(con_item)
|
||||
self.sbx_port.setEnabled(con_item)
|
||||
self.txt_address.setEnabled(con_item and not is_unix_default)
|
||||
self.cbx_local_socket.setEnabled(con_item)
|
||||
self.sbx_port.setEnabled(con_item and not is_unix_socket)
|
||||
self.sbx_timeout.setEnabled(con_item)
|
||||
self.cbb_folder.setEnabled(con_item or dir_item)
|
||||
self.cbb_folder.setEditable(dir_item)
|
||||
@@ -202,9 +214,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
# Disable auto complete, this would override a new typed name with existing one
|
||||
self.cbb_folder.setCompleter(None)
|
||||
|
||||
self.cbx_ssh_use_tunnel.setEnabled(con_item)
|
||||
self.sbx_ssh_port.setEnabled(con_item)
|
||||
self.txt_ssh_user.setEnabled(con_item)
|
||||
self.cbx_ssh_use_tunnel.setEnabled(con_item and not is_unix_socket)
|
||||
self.sbx_ssh_port.setEnabled(con_item and not is_unix_socket)
|
||||
self.txt_ssh_user.setEnabled(con_item and not is_unix_socket)
|
||||
|
||||
def _get_folder_item(self, name: str):
|
||||
"""Find the folder entry by name."""
|
||||
@@ -243,7 +255,6 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
def on_tre_connections_currentItemChanged(
|
||||
self, current: QtWidgets.QTreeWidgetItem, previous: QtWidgets.QTreeWidgetItem):
|
||||
|
||||
self._edit_state()
|
||||
self._load_cbb_folder()
|
||||
|
||||
if current and current.type() == NodeType.CON:
|
||||
@@ -271,6 +282,9 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
self.__current_item = QtWidgets.QTreeWidgetItem()
|
||||
self.cbb_folder.setCurrentText(current.text(0) if current else "")
|
||||
|
||||
self._mrk_address = ""
|
||||
self._edit_state()
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_btn_up_clicked(self):
|
||||
self._move_item(-1)
|
||||
@@ -303,8 +317,8 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
if item_to_remove.childCount():
|
||||
rc = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"If you remote this folder, all containing elements will be removed, too. \n\n"
|
||||
"Do you want to delete folder and all elements?"
|
||||
"If you remove this folder, all contained items will be removed as well.\n\n"
|
||||
"Do you want to delete the folder and all contained items?"
|
||||
),
|
||||
)
|
||||
if rc != QtWidgets.QMessageBox.Yes:
|
||||
@@ -319,6 +333,8 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
elif item_to_remove and item_to_remove.type() == NodeType.CON:
|
||||
remove_item(item_to_remove)
|
||||
|
||||
self._edit_state()
|
||||
|
||||
@QtCore.pyqtSlot()
|
||||
def on_btn_add_clicked(self, settings_preset: RevPiSettings = None):
|
||||
"""Create new element."""
|
||||
@@ -369,6 +385,7 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
settings = self.__current_item.data(0, WidgetData.revpi_settings) # type: RevPiSettings
|
||||
settings.address = text
|
||||
self.changes = True
|
||||
self._edit_state()
|
||||
|
||||
@QtCore.pyqtSlot(int)
|
||||
def on_sbx_port_valueChanged(self, value: int):
|
||||
@@ -386,6 +403,21 @@ class RevPiPlcList(QtWidgets.QDialog, Ui_diag_connections):
|
||||
settings.timeout = value
|
||||
self.changes = True
|
||||
|
||||
@QtCore.pyqtSlot(int)
|
||||
def on_cbx_local_socket_stateChanged(self, check_state: int):
|
||||
if self.__current_item.type() != NodeType.CON:
|
||||
return
|
||||
|
||||
if check_state == QtCore.Qt.CheckState.Checked:
|
||||
# Backup fields to restore the text if unchecked
|
||||
self._mrk_address = self.txt_address.text()
|
||||
self.txt_address.setText(DEFAULT_SOCKET_ADDRESS)
|
||||
self.on_txt_address_textEdited(self.txt_address.text())
|
||||
else:
|
||||
# Restore old address if it is not the default to unlock the address field
|
||||
self.txt_address.setText("" if self._mrk_address == DEFAULT_SOCKET_ADDRESS else self._mrk_address)
|
||||
self.on_txt_address_textEdited(self.txt_address.text())
|
||||
|
||||
@QtCore.pyqtSlot(int)
|
||||
def on_cbx_ssh_use_tunnel_stateChanged(self, check_state: int):
|
||||
if self.__current_item.type() != NodeType.CON:
|
||||
|
||||
@@ -105,7 +105,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if self.cbb_plcprogram.currentText() == "":
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"You have to select a start program, before uploading the "
|
||||
"You must select a start program before uploading the "
|
||||
"settings."
|
||||
)
|
||||
)
|
||||
@@ -113,9 +113,9 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
|
||||
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!"
|
||||
"Saving settings on RevPi.\n\n"
|
||||
"If you made changes in the 'PLC Program' section, your PLC "
|
||||
"program will be restarted."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
|
||||
@@ -137,9 +137,9 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
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."
|
||||
"Cannot save settings on RevPi.\n"
|
||||
"Try saving the values one more time and check the "
|
||||
"RevPiPyLoad log files if the error occurs again."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -147,7 +147,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if self._changesdone():
|
||||
ask = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Question"), self.tr(
|
||||
"Do you really want to quit? \nUnsaved changes will be lost."
|
||||
"Quit without saving?\nUnsaved changes will be lost."
|
||||
)
|
||||
) == QtWidgets.QMessageBox.Yes
|
||||
|
||||
@@ -186,10 +186,10 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
fh.close()
|
||||
|
||||
ask = QtWidgets.QMessageBox.question(
|
||||
self, self.tr("Reset driver..."), self.tr(
|
||||
"Reset piControl driver after successful uploading new piCtory "
|
||||
self, self.tr("Reset driver"), self.tr(
|
||||
"Reset piControl driver after successfully uploading the new PiCtory "
|
||||
"configuration?\nThe process image will be interrupted for a "
|
||||
"short time!"
|
||||
"short time."
|
||||
), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No | QtWidgets.QMessageBox.Cancel
|
||||
)
|
||||
if ask == QtWidgets.QMessageBox.Cancel:
|
||||
@@ -202,8 +202,8 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
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."
|
||||
"Network error while sending data to RevPi.\n"
|
||||
"Try again."
|
||||
)
|
||||
)
|
||||
elif ec == 0:
|
||||
@@ -211,7 +211,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if ask == QtWidgets.QMessageBox.Yes:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Success"), self.tr(
|
||||
"The transfer of the piCtory configuration "
|
||||
"The transfer of the PiCtory configuration "
|
||||
"and the reset of piControl have been "
|
||||
"successfully executed."
|
||||
),
|
||||
@@ -219,47 +219,47 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
else:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Success"), self.tr(
|
||||
"The piCtory configuration was successfully transferred."
|
||||
"The PiCtory configuration was successfully transferred."
|
||||
)
|
||||
)
|
||||
|
||||
elif ec == -1:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Can not process the transferred file."
|
||||
"Cannot process the transferred file."
|
||||
)
|
||||
)
|
||||
elif ec == -2:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Can not find main elements in piCtory file."
|
||||
"Cannot 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!"
|
||||
"Cannot find contained devices on RevPi.\n"
|
||||
"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."
|
||||
"Cannot load RAP catalog on RevPi."
|
||||
)
|
||||
)
|
||||
elif ec < 0:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"The piCtory configuration could not be "
|
||||
"written on the Revolution Pi."
|
||||
"Cannot write PiCtory configuration "
|
||||
"on RevPi."
|
||||
)
|
||||
)
|
||||
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!"
|
||||
"The PiCtory configuration has been saved successfully.\n"
|
||||
"An error occurred on piControl reset."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -321,7 +321,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if self.cbb_format.currentIndex() == 0:
|
||||
# Save files as zip archive
|
||||
diag_save = QtWidgets.QFileDialog(
|
||||
self, self.tr("Save ZIP archive..."),
|
||||
self, self.tr("Save ZIP archive"),
|
||||
helper.cm.settings.last_zip_file or "{0}.zip".format(helper.cm.settings.name),
|
||||
self.tr("ZIP archive (*.zip);;All files (*.*)")
|
||||
)
|
||||
@@ -339,7 +339,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
elif self.cbb_format.currentIndex() == 1:
|
||||
# Save files as TarGz archive
|
||||
diag_save = QtWidgets.QFileDialog(
|
||||
self, self.tr("Save TGZ archive..."),
|
||||
self, self.tr("Save TGZ archive"),
|
||||
helper.cm.settings.last_tar_file or "{0}.tgz".format(helper.cm.settings.name),
|
||||
self.tr("TGZ archive (*.tgz);;All files (*.*)")
|
||||
)
|
||||
@@ -367,7 +367,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if plcfile is None:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Could not load PLC program from Revolution Pi."
|
||||
"Cannot load PLC program from RevPi."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -380,8 +380,8 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
log.error(e)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Coud not save the archive or extract the files!\n"
|
||||
"Please retry.")
|
||||
"Cannot save the archive or extract the files.\n"
|
||||
"Try again.")
|
||||
)
|
||||
else:
|
||||
QtWidgets.QMessageBox.information(
|
||||
@@ -411,7 +411,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if self.cbb_format.currentIndex() == 0:
|
||||
# Upload zip archive content
|
||||
diag_open = QtWidgets.QFileDialog(
|
||||
self, self.tr("Upload content of ZIP archive..."),
|
||||
self, self.tr("Upload content of ZIP archive"),
|
||||
helper.cm.settings.last_file_upload,
|
||||
self.tr("ZIP archive (*.zip);;All files (*.*)")
|
||||
)
|
||||
@@ -437,7 +437,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"The selected file ist not a ZIP archive."
|
||||
"The selected file is not a ZIP archive."
|
||||
)
|
||||
)
|
||||
return
|
||||
@@ -445,7 +445,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
elif self.cbb_format.currentIndex() == 1:
|
||||
# Upload TarGz content
|
||||
diag_open = QtWidgets.QFileDialog(
|
||||
self, self.tr("Upload content of TAR archive..."),
|
||||
self, self.tr("Upload content of TAR archive"),
|
||||
helper.cm.settings.last_file_upload,
|
||||
self.tr("TAR archive (*.tgz);;All files (*.*)")
|
||||
)
|
||||
@@ -471,7 +471,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"The selected file ist not a TAR archive."
|
||||
"The selected file is not a TAR archive."
|
||||
)
|
||||
)
|
||||
return
|
||||
@@ -479,8 +479,8 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
# 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."
|
||||
self, self.tr("No files to upload"), self.tr(
|
||||
"No files found in the selected location or archive."
|
||||
)
|
||||
)
|
||||
remove_temp()
|
||||
@@ -491,8 +491,8 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
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."
|
||||
"Cannot delete files on RevPi.\n"
|
||||
"Upload aborted. Try again."
|
||||
)
|
||||
)
|
||||
remove_temp()
|
||||
@@ -545,10 +545,10 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
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"
|
||||
"Cannot find the selected PLC start program in "
|
||||
"the uploaded files.\nThis is not an error if the file "
|
||||
"already exists on RevPi. Check the PLC start program "
|
||||
"field."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -558,14 +558,14 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
else:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"There is no piCtory configuration in this archive."
|
||||
"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."
|
||||
"RevPi cannot process some parts of the transmission."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -590,12 +590,12 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
return
|
||||
|
||||
diag_save = QtWidgets.QFileDialog(
|
||||
self, self.tr("Save piCtory file..."),
|
||||
self, self.tr("Save PiCtory file"),
|
||||
os.path.join(
|
||||
helper.cm.settings.last_dir_pictory,
|
||||
"{0}.rsc".format(helper.cm.settings.name)
|
||||
),
|
||||
self.tr("piCtory file (*.rsc);;All files (*.*)")
|
||||
self.tr("PiCtory file (*.rsc);;All files (*.*)")
|
||||
)
|
||||
diag_save.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
|
||||
diag_save.setDefaultSuffix("rsc")
|
||||
@@ -610,7 +610,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if bin_buffer is None:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Could not load piCtory file from Revolution Pi."
|
||||
"Cannot load PiCtory file from RevPi."
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -620,7 +620,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Success"), self.tr(
|
||||
"piCtory configuration successfully loaded and saved to:\n{0}."
|
||||
"PiCtory configuration saved to:\n{0}."
|
||||
).format(filename)
|
||||
)
|
||||
|
||||
@@ -630,9 +630,9 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
return
|
||||
|
||||
diag_open = QtWidgets.QFileDialog(
|
||||
self, self.tr("Upload piCtory file..."),
|
||||
self, self.tr("Upload PiCtory file"),
|
||||
helper.cm.settings.last_pictory_file or "{0}.rsc".format(helper.cm.settings.name),
|
||||
self.tr("piCtory file (*.rsc);;All files (*.*)")
|
||||
self.tr("PiCtory file (*.rsc);;All files (*.*)")
|
||||
)
|
||||
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||
@@ -652,7 +652,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
|
||||
diag_save = QtWidgets.QFileDialog(
|
||||
self,
|
||||
self.tr("Save piControl file..."),
|
||||
self.tr("Save piControl file"),
|
||||
os.path.join(
|
||||
helper.cm.settings.last_dir_picontrol,
|
||||
"{0}.img".format(helper.cm.settings.name)
|
||||
@@ -673,7 +673,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
if bin_buffer is None:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Error"), self.tr(
|
||||
"Could not load process image from Revolution Pi."
|
||||
"Cannot load process image from RevPi."
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -683,7 +683,7 @@ class RevPiProgram(QtWidgets.QDialog, Ui_diag_program):
|
||||
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Success"), self.tr(
|
||||
"Process image successfully loaded and saved to:\n{0}."
|
||||
"Process image saved to:\n{0}."
|
||||
).format(filename)
|
||||
)
|
||||
|
||||
|
||||
@@ -77,9 +77,9 @@ class Simulator(QtWidgets.QDialog, Ui_diag_simulator):
|
||||
@QtCore.pyqtSlot()
|
||||
def on_btn_configrsc_clicked(self) -> None:
|
||||
diag_open = QtWidgets.QFileDialog(
|
||||
self, self.tr("Select downloaded piCtory file..."),
|
||||
self, self.tr("Select downloaded PiCtory file"),
|
||||
helper.settings.value("simulator/last_dir", ".", str),
|
||||
self.tr("piCtory file (*.rsc);;All files (*.*)")
|
||||
self.tr("PiCtory file (*.rsc);;All files (*.*)")
|
||||
)
|
||||
diag_open.setAcceptMode(QtWidgets.QFileDialog.AcceptOpen)
|
||||
diag_open.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||
|
||||
@@ -1,112 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Connect to a remote host and tunnel a port.
|
||||
|
||||
This was crated on base of the paramiko library demo file forward.py, see on
|
||||
GitHub https://github.com/paramiko/paramiko/blob/main/demos/forward.py
|
||||
"""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023 Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2023-2026 Sven Sager"
|
||||
__license__ = "GPLv2"
|
||||
|
||||
import select
|
||||
import asyncio
|
||||
import threading
|
||||
from logging import getLogger
|
||||
from socketserver import BaseRequestHandler, ThreadingTCPServer
|
||||
from threading import Thread
|
||||
from typing import Tuple, Union
|
||||
|
||||
from paramiko.client import MissingHostKeyPolicy, SSHClient
|
||||
from paramiko.rsakey import RSAKey
|
||||
from paramiko.ssh_exception import PasswordRequiredException
|
||||
from paramiko.transport import Transport
|
||||
import asyncssh
|
||||
|
||||
log = getLogger("ssh_tunneling")
|
||||
|
||||
|
||||
class ForwardServer(ThreadingTCPServer):
|
||||
daemon_threads = True
|
||||
allow_reuse_address = True
|
||||
|
||||
|
||||
class Handler(BaseRequestHandler):
|
||||
def handle(self):
|
||||
try:
|
||||
chan = self.ssh_transport.open_channel(
|
||||
"direct-tcpip",
|
||||
(self.chain_host, self.chain_port),
|
||||
self.request.getpeername(),
|
||||
)
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
return
|
||||
if chan is None:
|
||||
log.error("Could not create a ssh channel")
|
||||
return
|
||||
|
||||
while True:
|
||||
r, w, x = select.select([self.request, chan], [], [], 5.0)
|
||||
if self.request in r:
|
||||
data = self.request.recv(1024)
|
||||
if len(data) == 0:
|
||||
break
|
||||
chan.send(data)
|
||||
if chan in r:
|
||||
data = chan.recv(1024)
|
||||
if len(data) == 0:
|
||||
break
|
||||
self.request.send(data)
|
||||
|
||||
chan.close()
|
||||
self.request.close()
|
||||
|
||||
|
||||
class SSHLocalTunnel:
|
||||
|
||||
def __init__(self, remote_tunnel_port: int, ssh_host: str, ssh_port: int = 22):
|
||||
def __init__(self, remote_target: Union[int, str], ssh_host: str, ssh_port: int = 22):
|
||||
"""
|
||||
Connect to a ssh remote host and tunnel a port to your host.
|
||||
Connect to a ssh remote host and tunnel a port or unix socket to your host.
|
||||
|
||||
:param remote_tunnel_port: Port on the remote host to tunnel through ssh
|
||||
:param remote_target: Port or unix socket path on the remote host to tunnel through ssh
|
||||
:param ssh_host: ssh remote host address
|
||||
:param ssh_port: ssh remote host port
|
||||
"""
|
||||
self._remote_tunnel_port = remote_tunnel_port
|
||||
self._remote_target = remote_target
|
||||
self._ssh_host = ssh_host
|
||||
self._ssh_port = ssh_port
|
||||
|
||||
self._th_server = Thread()
|
||||
self._loop: asyncio.AbstractEventLoop | None = None
|
||||
self._stop_event: asyncio.Event | None = None
|
||||
self._thread: threading.Thread | None = None
|
||||
|
||||
self._ssh_client = SSHClient()
|
||||
self._ssh_client.set_missing_host_key_policy(MissingHostKeyPolicy())
|
||||
self._started = threading.Event()
|
||||
self._stopped = threading.Event()
|
||||
|
||||
self._ssh_transport = None # type: Transport
|
||||
self._forward_server = None # type: ThreadingTCPServer
|
||||
self._local_tunnel_port = None # type: int
|
||||
self._startup_error: Exception | None = None
|
||||
self._runtime_error: Exception | None = None
|
||||
|
||||
def __th_target(self):
|
||||
"""Server thread for socket mirror."""
|
||||
self._forward_server.serve_forever()
|
||||
self._conn: asyncssh.SSHClientConnection | None = None
|
||||
self._server: asyncssh.SSHForwarder | None = None
|
||||
self._local_tunnel_port: int | None = None
|
||||
|
||||
def _configure_forward_server(self) -> int:
|
||||
"""
|
||||
Configure forward server for port mirror.
|
||||
def _thread_main(self, username, password=None, client_keys=None, passphrase=None):
|
||||
try:
|
||||
asyncio.run(self._run(username, password, client_keys, passphrase))
|
||||
except Exception as exc:
|
||||
if not self._started.is_set():
|
||||
self._startup_error = exc
|
||||
self._started.set()
|
||||
else:
|
||||
self._runtime_error = exc
|
||||
finally:
|
||||
self._stopped.set()
|
||||
|
||||
:return: Local port on wich the remote port is connected
|
||||
"""
|
||||
self._ssh_transport = self._ssh_client.get_transport()
|
||||
async def _run(self, username, password=None, client_keys=None, passphrase=None):
|
||||
self._loop = asyncio.get_running_loop()
|
||||
self._stop_event = asyncio.Event()
|
||||
|
||||
class SubHandler(Handler):
|
||||
chain_host = "127.0.0.1"
|
||||
chain_port = self._remote_tunnel_port
|
||||
ssh_transport = self._ssh_transport
|
||||
try:
|
||||
async with asyncssh.connect(
|
||||
host=self._ssh_host,
|
||||
port=self._ssh_port,
|
||||
username=username,
|
||||
password=password,
|
||||
client_keys=client_keys,
|
||||
passphrase=passphrase,
|
||||
known_hosts=None, # Analog zu MissingHostKeyPolicy()
|
||||
config=None, # Do not parse local config
|
||||
) as conn:
|
||||
self._conn = conn
|
||||
# Forward local port 0 (dynamic) to remote target (port or unix socket)
|
||||
if isinstance(self._remote_target, int):
|
||||
self._server = await conn.forward_local_port(
|
||||
'127.0.0.1', 0, '127.0.0.1', self._remote_target
|
||||
)
|
||||
else:
|
||||
self._server = await conn.forward_local_port_to_path(
|
||||
'127.0.0.1', 0, self._remote_target
|
||||
)
|
||||
self._local_tunnel_port = self._server.get_port()
|
||||
|
||||
self._forward_server = ForwardServer(("127.0.0.1", 0), SubHandler)
|
||||
self._local_tunnel_port = self._forward_server.socket.getsockname()[1]
|
||||
self._started.set()
|
||||
await self._stop_event.wait()
|
||||
|
||||
self._th_server = Thread(target=self.__th_target)
|
||||
self._th_server.start()
|
||||
self._server.close()
|
||||
await self._server.wait_closed()
|
||||
|
||||
return self._local_tunnel_port
|
||||
except Exception as exc:
|
||||
if not self._started.is_set():
|
||||
self._startup_error = exc
|
||||
self._started.set()
|
||||
return
|
||||
raise
|
||||
|
||||
def connect_by_credentials(self, username: str, password: str) -> int:
|
||||
"""
|
||||
@@ -114,17 +102,31 @@ class SSHLocalTunnel:
|
||||
|
||||
:return: Local port on wich the remote port is connected
|
||||
"""
|
||||
if self._th_server.is_alive():
|
||||
if self._thread and self._thread.is_alive():
|
||||
raise RuntimeError("Already connected")
|
||||
|
||||
self._ssh_client.connect(
|
||||
hostname=self._ssh_host,
|
||||
port=self._ssh_port,
|
||||
username=username,
|
||||
password=password,
|
||||
)
|
||||
self._started.clear()
|
||||
self._stopped.clear()
|
||||
self._startup_error = None
|
||||
self._runtime_error = None
|
||||
|
||||
return self._configure_forward_server()
|
||||
self._thread = threading.Thread(
|
||||
target=self._thread_main,
|
||||
args=(username, password),
|
||||
daemon=True
|
||||
)
|
||||
self._thread.start()
|
||||
|
||||
if not self._started.wait(20.0):
|
||||
self.disconnect()
|
||||
raise TimeoutError("SSH connection timed out")
|
||||
|
||||
if self._startup_error:
|
||||
error = self._startup_error
|
||||
self.disconnect()
|
||||
raise error
|
||||
|
||||
return self._local_tunnel_port
|
||||
|
||||
def connect_by_keyfile(self, username: str, key_file: str, key_password: str = None) -> int:
|
||||
"""
|
||||
@@ -132,70 +134,92 @@ class SSHLocalTunnel:
|
||||
|
||||
:return: Local port on wich the remote port is connected
|
||||
"""
|
||||
if self._th_server.is_alive():
|
||||
if self._thread and self._thread.is_alive():
|
||||
raise RuntimeError("Already connected")
|
||||
|
||||
if self.key_file_password_protected(key_file):
|
||||
private_key = RSAKey.from_private_key_file(key_file, key_password)
|
||||
else:
|
||||
private_key = RSAKey.from_private_key_file(key_file)
|
||||
self._started.clear()
|
||||
self._stopped.clear()
|
||||
self._startup_error = None
|
||||
self._runtime_error = None
|
||||
|
||||
self._ssh_client.connect(
|
||||
hostname=self._ssh_host,
|
||||
port=self._ssh_port,
|
||||
username=username,
|
||||
pkey=private_key,
|
||||
look_for_keys=True,
|
||||
self._thread = threading.Thread(
|
||||
target=self._thread_main,
|
||||
args=(username, None, [key_file], key_password),
|
||||
daemon=True
|
||||
)
|
||||
self._thread.start()
|
||||
|
||||
return self._configure_forward_server()
|
||||
if not self._started.wait(20.0):
|
||||
self.disconnect()
|
||||
raise TimeoutError("SSH connection timed out")
|
||||
|
||||
if self._startup_error:
|
||||
error = self._startup_error
|
||||
self.disconnect()
|
||||
raise error
|
||||
|
||||
return self._local_tunnel_port
|
||||
|
||||
def disconnect(self):
|
||||
"""Close SSH tunnel connection."""
|
||||
if self._loop and self._stop_event:
|
||||
self._loop.call_soon_threadsafe(self._stop_event.set)
|
||||
|
||||
if self._thread and self._thread.is_alive():
|
||||
self._thread.join(timeout=5.0)
|
||||
|
||||
self._conn = None
|
||||
self._server = None
|
||||
self._local_tunnel_port = None
|
||||
if self._forward_server:
|
||||
self._forward_server.shutdown()
|
||||
self._forward_server.server_close()
|
||||
if self._ssh_transport:
|
||||
self._ssh_transport.close()
|
||||
self._ssh_client.close()
|
||||
self._thread = None
|
||||
self._loop = None
|
||||
self._stop_event = None
|
||||
|
||||
@staticmethod
|
||||
def key_file_password_protected(key_file: str) -> bool:
|
||||
# asyncssh doesn't have a direct equivalent without trying to load it.
|
||||
# But we can try to load it with an empty passphrase.
|
||||
try:
|
||||
RSAKey.from_private_key_file(key_file)
|
||||
except PasswordRequiredException:
|
||||
asyncssh.read_private_key(key_file, passphrase=None)
|
||||
return False
|
||||
except asyncssh.KeyImportError:
|
||||
return True
|
||||
except Exception:
|
||||
return True
|
||||
return False
|
||||
|
||||
def send_cmd(self, cmd: str, timeout: float = None) -> Union[Tuple[str, str], Tuple[None, None]]:
|
||||
def send_cmd(self, cmd: str, timeout: float = None, stdin: str = None) -> Union[Tuple[str, str, int], Tuple[None, None, None]]:
|
||||
"""
|
||||
Send simple command to ssh host.
|
||||
|
||||
The output of stdout and stderr is returned as a tuple of two elements.
|
||||
This elements could be None, in case of an internal error.
|
||||
|
||||
:param cmd: Shell command to execute on remote host
|
||||
:param timeout: Timeout for execution
|
||||
:return: Tuple with stdout and stderr
|
||||
:param stdin: Send this string to stdin
|
||||
:return: Tuple with stdout, stderr, exit status
|
||||
"""
|
||||
if not self._th_server.is_alive():
|
||||
if not self.connected:
|
||||
raise RuntimeError("Not connected")
|
||||
|
||||
try:
|
||||
_, stdout, stderr = self._ssh_client.exec_command(cmd, 1024, timeout)
|
||||
buffer_out = stdout.read()
|
||||
buffer_err = stderr.read()
|
||||
# Running async command from sync context
|
||||
async def _exec():
|
||||
result = await self._conn.run(cmd, timeout=timeout, input=stdin)
|
||||
return result.stdout, result.stderr, result.exit_status
|
||||
|
||||
return buffer_out.decode(), buffer_err.decode()
|
||||
try:
|
||||
future = asyncio.run_coroutine_threadsafe(_exec(), self._loop)
|
||||
stdout, stderr, exit_status = future.result(timeout=timeout)
|
||||
if type(stdout) is bytes:
|
||||
stdout = stdout.decode(errors="ignore")
|
||||
if type(stderr) is bytes:
|
||||
stderr = stderr.decode(errors="ignore")
|
||||
return stdout, stderr, exit_status
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
return None, None
|
||||
return None, None, None
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
"""Check connection state of ssh tunnel."""
|
||||
return self._ssh_transport and self._ssh_transport.is_active()
|
||||
return self._conn is not None and self._started.is_set() and not self._stopped.is_set()
|
||||
|
||||
@property
|
||||
def local_tunnel_port(self) -> int:
|
||||
|
||||
@@ -49,11 +49,11 @@ class SSHAuth(QtWidgets.QDialog, Ui_diag_sshauth):
|
||||
log.error(e)
|
||||
self._in_keyring = False
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, self.tr("Could not save password"), self.tr(
|
||||
"Could not save password to operating systems password save.\n\n"
|
||||
"Maybe your operating system does not support saving passwords. "
|
||||
self, self.tr("Cannot save password"), self.tr(
|
||||
"Cannot save password to operating system's password store.\n\n"
|
||||
"The operating system may not support saving passwords. "
|
||||
"This could be due to missing libraries or programs.\n\n"
|
||||
"This is not an error of RevPi Commander."
|
||||
"This is not a RevPi Commander error."
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'aclmanager.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -137,15 +137,15 @@ class Ui_diag_aclmanager(object):
|
||||
|
||||
def retranslateUi(self, diag_aclmanager):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_aclmanager.setWindowTitle(_translate("diag_aclmanager", "IP access control list"))
|
||||
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.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.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:"))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'avahisearch.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -84,25 +84,25 @@ class Ui_diag_search(object):
|
||||
|
||||
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..."))
|
||||
diag_search.setWindowTitle(_translate("diag_search", "RevPi device search"))
|
||||
self.lbl_search.setText(_translate("diag_search", "Searching for RevPi devices on the 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.setText(_translate("diag_search", "ZeroConf 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_connect.setText(_translate("diag_search", "&Connect to RevPi"))
|
||||
self.btn_save.setText(_translate("diag_search", "&Save connection"))
|
||||
self.act_copy_host.setText(_translate("diag_search", "Copy host name"))
|
||||
self.act_copy_ip.setText(_translate("diag_search", "Copy IP address"))
|
||||
self.act_open_pictory.setText(_translate("diag_search", "Open piCtory"))
|
||||
self.act_open_pictory.setText(_translate("diag_search", "Open PiCtory"))
|
||||
self.act_connect_ssh.setText(_translate("diag_search", "Connect via SSH (recommended)"))
|
||||
self.act_connect_ssh.setToolTip(_translate("diag_search", "Establish a connection via encrypted SSH tunnel"))
|
||||
self.act_connect_ssh.setToolTip(_translate("diag_search", "Connect via an encrypted SSH tunnel."))
|
||||
self.act_connect_xmlrpc.setText(_translate("diag_search", "Connect via XML-RPC"))
|
||||
self.act_connect_xmlrpc.setToolTip(_translate("diag_search", "You have to configure your Revolution Pi to accept this connections"))
|
||||
self.act_connect_xmlrpc.setToolTip(_translate("diag_search", "Enable these connections on RevPi."))
|
||||
self.act_connect.setText(_translate("diag_search", "Connect"))
|
||||
self.act_connect.setToolTip(_translate("diag_search", "Connect to Revoluton Pi"))
|
||||
self.act_connect.setToolTip(_translate("diag_search", "Connect to RevPi"))
|
||||
from . import ressources_rc
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'backgroundworker.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -19,7 +19,7 @@ class Ui_diag_backgroundworker(object):
|
||||
self.verticalLayout = QtWidgets.QVBoxLayout(diag_backgroundworker)
|
||||
self.verticalLayout.setObjectName("verticalLayout")
|
||||
self.lbl_status = QtWidgets.QLabel(diag_backgroundworker)
|
||||
self.lbl_status.setText("Status message...")
|
||||
self.lbl_status.setText("Status message")
|
||||
self.lbl_status.setObjectName("lbl_status")
|
||||
self.verticalLayout.addWidget(self.lbl_status)
|
||||
self.pgb_status = QtWidgets.QProgressBar(diag_backgroundworker)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'debugcontrol.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -65,21 +65,21 @@ class Ui_wid_debugcontrol(object):
|
||||
|
||||
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)\n"
|
||||
self.gb_devices.setTitle(_translate("wid_debugcontrol", "RevPi Devices"))
|
||||
self.cbx_stay_on_top.setText(_translate("wid_debugcontrol", "Keep window on top"))
|
||||
self.gb_control.setTitle(_translate("wid_debugcontrol", "I/O Control"))
|
||||
self.btn_read_io.setToolTip(_translate("wid_debugcontrol", "Read all I/O values and discard local changes (F4).\n"
|
||||
"\n"
|
||||
"Hold this button pressed and it will refresh the IOs every 200 ms."))
|
||||
self.btn_read_io.setText(_translate("wid_debugcontrol", "Read &all IO values"))
|
||||
self.btn_refresh_io.setToolTip(_translate("wid_debugcontrol", "Refresh all IO values which are locally not changed (F5)\n"
|
||||
"Hold this button to refresh the I/Os every 200 ms."))
|
||||
self.btn_read_io.setText(_translate("wid_debugcontrol", "Read &all I/O values"))
|
||||
self.btn_refresh_io.setToolTip(_translate("wid_debugcontrol", "Refresh all I/O values that have not been modified locally (F5).\n"
|
||||
"\n"
|
||||
"Hold this button pressed and it will refresh the IOs every 200 ms."))
|
||||
self.btn_refresh_io.setText(_translate("wid_debugcontrol", "&Refresh unchanged IOs"))
|
||||
self.btn_write_o.setToolTip(_translate("wid_debugcontrol", "Write locally changed output values to process image (F6)"))
|
||||
"Hold this button to refresh the I/Os every 200 ms."))
|
||||
self.btn_refresh_io.setText(_translate("wid_debugcontrol", "&Refresh unchanged I/Os"))
|
||||
self.btn_write_o.setToolTip(_translate("wid_debugcontrol", "Write locally modified output values to process image (F6)."))
|
||||
self.btn_write_o.setText(_translate("wid_debugcontrol", "&Write changed outputs"))
|
||||
self.cbx_refresh.setText(_translate("wid_debugcontrol", "&Auto refresh values"))
|
||||
self.cbx_write.setText(_translate("wid_debugcontrol", "and write outputs"))
|
||||
self.cbx_write.setText(_translate("wid_debugcontrol", "Write outputs"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'debugios.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'files.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -169,17 +169,17 @@ class Ui_win_files(object):
|
||||
|
||||
def retranslateUi(self, win_files):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
win_files.setWindowTitle(_translate("win_files", "File manager"))
|
||||
self.gb_select_local.setTitle(_translate("win_files", "Local computer"))
|
||||
win_files.setWindowTitle(_translate("win_files", "File Manager"))
|
||||
self.gb_select_local.setTitle(_translate("win_files", "Local Computer"))
|
||||
self.lbl_select_local.setText(_translate("win_files", "Path to development root:"))
|
||||
self.btn_select_local.setToolTip(_translate("win_files", "Open developer root directory"))
|
||||
self.btn_refresh_local.setToolTip(_translate("win_files", "Reload file list"))
|
||||
self.tree_files_local.setSortingEnabled(True)
|
||||
self.gb_select_revpi.setTitle(_translate("win_files", "Revolution Pi"))
|
||||
self.gb_select_revpi.setTitle(_translate("win_files", "RevPi"))
|
||||
self.lbl_select_revpi.setText(_translate("win_files", "RevPiPyLoad working directory:"))
|
||||
self.btn_refresh_revpi.setToolTip(_translate("win_files", "Reload file list"))
|
||||
self.tree_files_revpi.setSortingEnabled(True)
|
||||
self.btn_all.setText(_translate("win_files", "Stop - Upload - Start"))
|
||||
self.btn_all.setText(_translate("win_files", "Stop, upload, start"))
|
||||
from . import ressources_rc
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'mqttmanager.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -126,7 +126,7 @@ class Ui_diag_mqtt(object):
|
||||
_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", "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.\n"
|
||||
self.lbl_basetopic_description.setText(_translate("diag_mqtt", "Base topic is the prefix for MQTT topics published by RevPi. Use \"/\" to structure broker topics.\n"
|
||||
"\n"
|
||||
"For example: revpi0000/data"))
|
||||
self.lbl_basetopic.setText(_translate("diag_mqtt", "Base topic:"))
|
||||
@@ -136,10 +136,10 @@ class Ui_diag_mqtt(object):
|
||||
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"
|
||||
self.lbl_write_outputs.setText(_translate("diag_mqtt", "RevPi subscribes to an MQTT topic for setting output values. Publish the new I/O 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.cbx_write_outputs.setText(_translate("diag_mqtt", "Allow MQTT to set outputs on RevPi"))
|
||||
self.gb_broker.setTitle(_translate("diag_mqtt", "Broker settings"))
|
||||
self.lbl_broker_address.setText(_translate("diag_mqtt", "Broker address:"))
|
||||
self.lbl_port.setText(_translate("diag_mqtt", "Broker port:"))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'oss_licenses.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -59,14 +59,14 @@ class Ui_diag_oss_licenses(object):
|
||||
|
||||
def retranslateUi(self, diag_oss_licenses):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_oss_licenses.setWindowTitle(_translate("diag_oss_licenses", "Open-Source licenses"))
|
||||
diag_oss_licenses.setWindowTitle(_translate("diag_oss_licenses", "Open source licenses"))
|
||||
self.tb_oss_licenses.setSortingEnabled(True)
|
||||
item = self.tb_oss_licenses.horizontalHeaderItem(0)
|
||||
item.setText(_translate("diag_oss_licenses", "Software"))
|
||||
item = self.tb_oss_licenses.horizontalHeaderItem(1)
|
||||
item.setText(_translate("diag_oss_licenses", "License"))
|
||||
self.action_start.setText(_translate("diag_oss_licenses", "More licenses..."))
|
||||
self.action_start.setToolTip(_translate("diag_oss_licenses", "Show more open-source software licenses"))
|
||||
self.action_start.setText(_translate("diag_oss_licenses", "More licenses"))
|
||||
self.action_start.setToolTip(_translate("diag_oss_licenses", "Show more open source software licenses"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'revpicommander.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -177,17 +177,17 @@ class Ui_win_revpicommander(object):
|
||||
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.setText(_translate("win_revpicommander", "&Search RevPi..."))
|
||||
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_webpage.setText(_translate("win_revpicommander", "Visit &website"))
|
||||
self.act_info.setText(_translate("win_revpicommander", "&Info"))
|
||||
self.act_logs.setText(_translate("win_revpicommander", "PLC &logs..."))
|
||||
self.act_options.setText(_translate("win_revpicommander", "PLC &options..."))
|
||||
self.act_program.setText(_translate("win_revpicommander", "PLC progra&m..."))
|
||||
self.act_developer.setText(_translate("win_revpicommander", "PLC de&veloper..."))
|
||||
self.act_pictory.setText(_translate("win_revpicommander", "piCtory configuraiton..."))
|
||||
self.act_pictory.setText(_translate("win_revpicommander", "PiCtory configuration"))
|
||||
self.act_disconnect.setText(_translate("win_revpicommander", "&Disconnect"))
|
||||
self.act_reset.setText(_translate("win_revpicommander", "Reset driver..."))
|
||||
self.act_reset.setText(_translate("win_revpicommander", "Reset driver"))
|
||||
self.act_simulator.setText(_translate("win_revpicommander", "RevPi si&mulator..."))
|
||||
from . import ressources_rc
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'revpiinfo.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -75,7 +75,7 @@ class Ui_diag_revpiinfo(object):
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout.addItem(spacerItem, 5, 0, 1, 1)
|
||||
self.lbl_link = QtWidgets.QLabel(diag_revpiinfo)
|
||||
self.lbl_link.setText("<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_link.setText("<html><head/><body><p><a href=\"https://revpimodio2.readthedocs.io/en/latest/\"><span style=\" text-decoration: underline; color:#0000ff;\">https://revpimodio2.readthedocs.io/en/latest/</span></a></p></body></html>")
|
||||
self.lbl_link.setOpenExternalLinks(True)
|
||||
self.lbl_link.setObjectName("lbl_link")
|
||||
self.gridLayout.addWidget(self.lbl_link, 6, 0, 1, 2)
|
||||
@@ -87,13 +87,12 @@ class Ui_diag_revpiinfo(object):
|
||||
|
||||
def retranslateUi(self, diag_revpiinfo):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_revpiinfo.setWindowTitle(_translate("diag_revpiinfo", "Program information"))
|
||||
self.lbl_head.setText(_translate("diag_revpiinfo", "RevPi Python PLC - Commander"))
|
||||
diag_revpiinfo.setWindowTitle(_translate("diag_revpiinfo", "Program Information"))
|
||||
self.lbl_head.setText(_translate("diag_revpiinfo", "RevPi Commander (Python/PLC)"))
|
||||
self.lbl_lbl_version_pyload.setText(_translate("diag_revpiinfo", "RevPiPyLoad version on RevPi:"))
|
||||
self.lbl_lbl_version_control.setText(_translate("diag_revpiinfo", "Version:"))
|
||||
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"
|
||||
"All of them come with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.\n"
|
||||
"\n"
|
||||
"(c) Sven Sager, License: GPLv2"))
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'revpilogfile.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -79,13 +79,13 @@ class Ui_win_revpilogfile(object):
|
||||
|
||||
def retranslateUi(self, win_revpilogfile):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
win_revpilogfile.setWindowTitle(_translate("win_revpilogfile", "RevPi Python PLC Logfiles"))
|
||||
win_revpilogfile.setWindowTitle(_translate("win_revpilogfile", "RevPi Python PLC Log Files"))
|
||||
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.cbx_wrap.setText(_translate("win_revpilogfile", "Line wrap"))
|
||||
self.lbl_daemon.setText(_translate("win_revpilogfile", "RevPiPyLoad Log File"))
|
||||
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"))
|
||||
self.lbl_app.setText(_translate("win_revpilogfile", "Python PLC Program Log File"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'revpioption.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -123,34 +123,34 @@ class Ui_diag_options(object):
|
||||
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_zeroonerror.setText(_translate("diag_options", "... after exception and errors"))
|
||||
self.gb_plc.setTitle(_translate("diag_options", "PLC Start/Stop Behavior"))
|
||||
self.lbl_replace_io.setText(_translate("diag_options", "Replace I/O file:"))
|
||||
self.cbx_zeroonerror.setText(_translate("diag_options", "after exceptions or errors"))
|
||||
self.cbx_autostart.setText(_translate("diag_options", "Start PLC program automatically"))
|
||||
self.cbb_reset_driver_action.setItemText(0, _translate("diag_options", "Do nothing"))
|
||||
self.cbb_reset_driver_action.setItemText(1, _translate("diag_options", "Restart after piCtory changed"))
|
||||
self.cbb_reset_driver_action.setItemText(1, _translate("diag_options", "Restart after PiCtory changes"))
|
||||
self.cbb_reset_driver_action.setItemText(2, _translate("diag_options", "Always restart the PLC program"))
|
||||
self.lbl_reset_driver_action.setText(_translate("diag_options", "Driver reset action:"))
|
||||
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.lbl_plc_zero.setText(_translate("diag_options", "Set process image to NULL if the program terminates"))
|
||||
self.cbb_replace_io.setItemText(0, _translate("diag_options", "Do not replace I/O 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.cbx_zeroonexit.setText(_translate("diag_options", "... sucessfully without error"))
|
||||
self.cbb_replace_io.setItemText(3, _translate("diag_options", "Use custom path and file name"))
|
||||
self.cbx_zeroonexit.setText(_translate("diag_options", "without errors"))
|
||||
self.lbl_plc_delay.setText(_translate("diag_options", "Restart delay in seconds:"))
|
||||
self.cbx_autoreload.setText(_translate("diag_options", "Restart PLC program after exit or crash"))
|
||||
self.lbl_lbl_reset_driver_action.setText(_translate("diag_options", "PLC program behavior after piCtory driver reset clicked"))
|
||||
self.gb_server.setTitle(_translate("diag_options", "RevPiPyLoad server services"))
|
||||
self.lbl_lbl_reset_driver_action.setText(_translate("diag_options", "PLC program behavior after PiCtory driver reset"))
|
||||
self.gb_server.setTitle(_translate("diag_options", "RevPiPyLoad Server Services"))
|
||||
self.btn_aclplcserver.setText(_translate("diag_options", "Edit ACL"))
|
||||
self.cbx_mqtt.setText(_translate("diag_options", "MQTT process image publisher"))
|
||||
self.cbx_plcserver.setText(_translate("diag_options", "Start RevPi piControl server"))
|
||||
self.cbx_plcserver.setText(_translate("diag_options", "Start piControl server"))
|
||||
self.lbl_server_status.setText(_translate("diag_options", "status"))
|
||||
self.lbl_lbl_server_status.setText(_translate("diag_options", "piControl server is:"))
|
||||
self.lbl_lbl_server_status.setText(_translate("diag_options", "piControl server:"))
|
||||
self.lbl_mqtt_status.setText(_translate("diag_options", "status"))
|
||||
self.lbl_lbl_mqtt_status.setText(_translate("diag_options", "MQTT publish service is:"))
|
||||
self.lbl_lbl_mqtt_status.setText(_translate("diag_options", "MQTT publish service:"))
|
||||
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 RevPiCommander"))
|
||||
self.cbx_xmlrpc.setText(_translate("diag_options", "Activate XML-RPC for RevPi Commander"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'revpiplclist.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -37,7 +37,7 @@ class Ui_diag_connections(object):
|
||||
self.formLayout_2.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.txt_address)
|
||||
self.lbl_port = QtWidgets.QLabel(self.tab_connection)
|
||||
self.lbl_port.setObjectName("lbl_port")
|
||||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_port)
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_port)
|
||||
self.sbx_port = QtWidgets.QSpinBox(self.tab_connection)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -48,10 +48,10 @@ class Ui_diag_connections(object):
|
||||
self.sbx_port.setMaximum(65535)
|
||||
self.sbx_port.setProperty("value", 55123)
|
||||
self.sbx_port.setObjectName("sbx_port")
|
||||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.sbx_port)
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.sbx_port)
|
||||
self.lbl_timeout = QtWidgets.QLabel(self.tab_connection)
|
||||
self.lbl_timeout.setObjectName("lbl_timeout")
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.lbl_timeout)
|
||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lbl_timeout)
|
||||
self.sbx_timeout = QtWidgets.QSpinBox(self.tab_connection)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
@@ -61,17 +61,23 @@ class Ui_diag_connections(object):
|
||||
self.sbx_timeout.setMinimum(5)
|
||||
self.sbx_timeout.setMaximum(30)
|
||||
self.sbx_timeout.setObjectName("sbx_timeout")
|
||||
self.formLayout_2.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.sbx_timeout)
|
||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.sbx_timeout)
|
||||
self.lbl_folder = QtWidgets.QLabel(self.tab_connection)
|
||||
self.lbl_folder.setObjectName("lbl_folder")
|
||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.lbl_folder)
|
||||
self.formLayout_2.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.lbl_folder)
|
||||
self.cbb_folder = QtWidgets.QComboBox(self.tab_connection)
|
||||
self.cbb_folder.setEditable(True)
|
||||
self.cbb_folder.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
|
||||
self.cbb_folder.setObjectName("cbb_folder")
|
||||
self.cbb_folder.addItem("")
|
||||
self.cbb_folder.setItemText(0, "")
|
||||
self.formLayout_2.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.cbb_folder)
|
||||
self.formLayout_2.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.cbb_folder)
|
||||
self.lbl_local_socket = QtWidgets.QLabel(self.tab_connection)
|
||||
self.lbl_local_socket.setObjectName("lbl_local_socket")
|
||||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.lbl_local_socket)
|
||||
self.cbx_local_socket = QtWidgets.QCheckBox(self.tab_connection)
|
||||
self.cbx_local_socket.setObjectName("cbx_local_socket")
|
||||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.cbx_local_socket)
|
||||
self.tab_properties.addTab(self.tab_connection, "")
|
||||
self.tab_ssh = QtWidgets.QWidget()
|
||||
self.tab_ssh.setObjectName("tab_ssh")
|
||||
@@ -155,19 +161,20 @@ class Ui_diag_connections(object):
|
||||
|
||||
def retranslateUi(self, diag_connections):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_connections.setWindowTitle(_translate("diag_connections", "Revolution Pi connections"))
|
||||
diag_connections.setWindowTitle(_translate("diag_connections", "RevPi Connections"))
|
||||
self.lbl_name.setText(_translate("diag_connections", "Display name:"))
|
||||
self.lbl_address.setText(_translate("diag_connections", "Address (DNS/IP):"))
|
||||
self.lbl_port.setText(_translate("diag_connections", "Port (Default {0}):"))
|
||||
self.lbl_address.setText(_translate("diag_connections", "Address (DNS/IP/Socket):"))
|
||||
self.lbl_port.setText(_translate("diag_connections", "Port (default {0}):"))
|
||||
self.lbl_timeout.setText(_translate("diag_connections", "Connection timeout:"))
|
||||
self.sbx_timeout.setSuffix(_translate("diag_connections", " sec."))
|
||||
self.lbl_folder.setText(_translate("diag_connections", "Sub folder:"))
|
||||
self.sbx_timeout.setSuffix(_translate("diag_connections", " s"))
|
||||
self.lbl_folder.setText(_translate("diag_connections", "Subfolder:"))
|
||||
self.lbl_local_socket.setText(_translate("diag_connections", "Use default local socket:"))
|
||||
self.tab_properties.setTabText(self.tab_properties.indexOf(self.tab_connection), _translate("diag_connections", "Connection"))
|
||||
self.lbl_ssh_use_tunnel.setText(_translate("diag_connections", "Connect over SSH tunnel:"))
|
||||
self.lbl_ssh_port.setText(_translate("diag_connections", "SSH port:"))
|
||||
self.lbl_ssh_user.setText(_translate("diag_connections", "SSH user name:"))
|
||||
self.tab_properties.setTabText(self.tab_properties.indexOf(self.tab_ssh), _translate("diag_connections", "Over SSH"))
|
||||
self.tre_connections.headerItem().setText(0, _translate("diag_connections", "Connection name"))
|
||||
self.tre_connections.headerItem().setText(0, _translate("diag_connections", "Connection Name"))
|
||||
self.tre_connections.headerItem().setText(1, _translate("diag_connections", "Address"))
|
||||
from . import ressources_rc
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'revpiprogram.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -112,27 +112,27 @@ class Ui_diag_program(object):
|
||||
|
||||
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.lbl_plcarguments.setText(_translate("diag_program", "Program arguments:"))
|
||||
self.lbl_plcprogram_watchdog.setText(_translate("diag_program", "Software watchdog (0=disabled):"))
|
||||
self.cbx_plcworkdir_set_uid.setText(_translate("diag_program", "Set write permissions for plc program to workdirectory"))
|
||||
diag_program.setWindowTitle(_translate("diag_program", "PLC Program"))
|
||||
self.gb_plc.setTitle(_translate("diag_program", "PLC Program"))
|
||||
self.lbl_plcarguments.setText(_translate("diag_program", "Arguments:"))
|
||||
self.lbl_plcprogram_watchdog.setText(_translate("diag_program", "Software watchdog (0 = disabled):"))
|
||||
self.cbx_plcworkdir_set_uid.setText(_translate("diag_program", "Set write permissions for PLC program working directory"))
|
||||
self.lbl_plcprogram.setText(_translate("diag_program", "Python PLC start program:"))
|
||||
self.sbx_plcprogram_watchdog.setSuffix(_translate("diag_program", " sec."))
|
||||
self.cb_transfair.setTitle(_translate("diag_program", "Transfair PLC program"))
|
||||
self.sbx_plcprogram_watchdog.setSuffix(_translate("diag_program", " s"))
|
||||
self.cb_transfair.setTitle(_translate("diag_program", "Transfer PLC program"))
|
||||
self.cbb_format.setItemText(0, _translate("diag_program", "ZIP archive"))
|
||||
self.cbb_format.setItemText(1, _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.lbl_format.setText(_translate("diag_program", "Transfer format:"))
|
||||
self.cbx_pictory.setText(_translate("diag_program", "Include PiCtory configuration"))
|
||||
self.cbx_clear.setText(_translate("diag_program", "Remove all files on RevPi 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"))
|
||||
self.lbl_pictory.setText(_translate("diag_program", "PiCtory configuration"))
|
||||
self.lbl_procimg.setText(_translate("diag_program", "Process image from piControl"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'simulator.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -108,22 +108,22 @@ class Ui_diag_simulator(object):
|
||||
|
||||
def retranslateUi(self, diag_simulator):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_simulator.setWindowTitle(_translate("diag_simulator", "piControl simulator"))
|
||||
self.gb_settings.setTitle(_translate("diag_simulator", "Simulator settings"))
|
||||
diag_simulator.setWindowTitle(_translate("diag_simulator", "piControl Simulator"))
|
||||
self.gb_settings.setTitle(_translate("diag_simulator", "Simulator Settings"))
|
||||
self.lbl_history.setText(_translate("diag_simulator", "Last used:"))
|
||||
self.lbl_configrsc.setText(_translate("diag_simulator", "piCtory file:"))
|
||||
self.btn_configrsc.setText(_translate("diag_simulator", "select..."))
|
||||
self.lbl_configrsc.setText(_translate("diag_simulator", "PiCtory file:"))
|
||||
self.btn_configrsc.setText(_translate("diag_simulator", "Select"))
|
||||
self.lbl_procimg.setText(_translate("diag_simulator", "Process image:"))
|
||||
self.lbl_stop.setText(_translate("diag_simulator", "Stop action:"))
|
||||
self.lbl_restart.setText(_translate("diag_simulator", "Restart action:"))
|
||||
self.cbx_stop_remove.setText(_translate("diag_simulator", "Remove process image file"))
|
||||
self.rb_restart_pictory.setText(_translate("diag_simulator", "Restore piCtory default values"))
|
||||
self.rb_restart_zero.setText(_translate("diag_simulator", "Reset everything to ZERO"))
|
||||
self.gb_info.setTitle(_translate("diag_simulator", "RevPiModIO integration"))
|
||||
self.lbl_info.setText(_translate("diag_simulator", "You can work with this simulator if you call RevPiModIO with this additional parameters:"))
|
||||
self.btn_start_pictory.setText(_translate("diag_simulator", "Start with piCtory default values"))
|
||||
self.rb_restart_pictory.setText(_translate("diag_simulator", "Restore PiCtory default values"))
|
||||
self.rb_restart_zero.setText(_translate("diag_simulator", "Reset all values to NULL"))
|
||||
self.gb_info.setTitle(_translate("diag_simulator", "RevPiModIO Integration"))
|
||||
self.lbl_info.setText(_translate("diag_simulator", "To use this simulator, call RevPiModIO with the following additional parameters:"))
|
||||
self.btn_start_pictory.setText(_translate("diag_simulator", "Start with PiCtory default values"))
|
||||
self.btn_start_empty.setText(_translate("diag_simulator", "Start with empty process image"))
|
||||
self.btn_start_nochange.setText(_translate("diag_simulator", "Start without changing actual process image"))
|
||||
self.btn_start_nochange.setText(_translate("diag_simulator", "Start without changing the current process image"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# Form implementation generated from reading ui file 'sshauth.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.15.9
|
||||
# Created by: PyQt5 UI code generator 5.15.11
|
||||
#
|
||||
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
|
||||
# run again. Do not edit this file unless you know what you are doing.
|
||||
@@ -64,11 +64,11 @@ class Ui_diag_sshauth(object):
|
||||
def retranslateUi(self, diag_sshauth):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
diag_sshauth.setWindowTitle(_translate("diag_sshauth", "SSH authentication"))
|
||||
self.lbl_username.setText(_translate("diag_sshauth", "SSH username:"))
|
||||
self.lbl_username.setText(_translate("diag_sshauth", "SSH user name:"))
|
||||
self.lbl_password.setText(_translate("diag_sshauth", "SSH password:"))
|
||||
self.cbx_save_password.setToolTip(_translate("diag_sshauth", "Username and password will be saved in secured operating systems\'s password storage."))
|
||||
self.cbx_save_password.setText(_translate("diag_sshauth", "Save username and password"))
|
||||
self.lbl_info.setText(_translate("diag_sshauth", "Note: The default user for SSH is \"pi\" which differs from the web configuration. You can find the password on the sticker on the device."))
|
||||
self.cbx_save_password.setToolTip(_translate("diag_sshauth", "Save user name and password in secure password storage."))
|
||||
self.cbx_save_password.setText(_translate("diag_sshauth", "Save user name and password"))
|
||||
self.lbl_info.setText(_translate("diag_sshauth", "Default SSH user is \"pi\". The device password is on the RevPi housing sticker."))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>IP access control list</string>
|
||||
<string>IP Access Control List</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
@@ -51,7 +51,7 @@
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>IP Address</string>
|
||||
<string>IP address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
@@ -97,7 +97,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_edit">
|
||||
<property name="title">
|
||||
<string>Add / Edit access entry</string>
|
||||
<string>Add/Edit Access Entry</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Search Revolution Pi devices</string>
|
||||
<string>RevPi device search</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
@@ -19,7 +19,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_search">
|
||||
<property name="text">
|
||||
<string>Searching for Revolution Pi devices in your network...</string>
|
||||
<string>Searching for RevPi devices on the network</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -85,7 +85,7 @@
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Zero-conf name</string>
|
||||
<string>ZeroConf name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
@@ -98,7 +98,7 @@
|
||||
<item row="2" column="0">
|
||||
<widget class="QPushButton" name="btn_connect">
|
||||
<property name="text">
|
||||
<string>&Connect to Revolution Pi</string>
|
||||
<string>&Connect to RevPi</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -132,7 +132,7 @@
|
||||
</action>
|
||||
<action name="act_open_pictory">
|
||||
<property name="text">
|
||||
<string>Open piCtory</string>
|
||||
<string>Open PiCtory</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_connect_ssh">
|
||||
@@ -140,7 +140,7 @@
|
||||
<string>Connect via SSH (recommended)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Establish a connection via encrypted SSH tunnel</string>
|
||||
<string>Connect via an encrypted SSH tunnel.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_connect_xmlrpc">
|
||||
@@ -148,7 +148,7 @@
|
||||
<string>Connect via XML-RPC</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>You have to configure your Revolution Pi to accept this connections</string>
|
||||
<string>Enable these connections on RevPi.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_connect">
|
||||
@@ -156,7 +156,7 @@
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Connect to Revoluton Pi</string>
|
||||
<string>Connect to RevPi</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_status">
|
||||
<property name="text">
|
||||
<string notr="true">Status message...</string>
|
||||
<string notr="true">Status message</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+11
-11
@@ -32,7 +32,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="gb_devices">
|
||||
<property name="title">
|
||||
<string>Revolution Pi devices</string>
|
||||
<string>RevPi Devices</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="vl_devices"/>
|
||||
</widget>
|
||||
@@ -40,25 +40,25 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cbx_stay_on_top">
|
||||
<property name="text">
|
||||
<string>Open to stay on top</string>
|
||||
<string>Keep window 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>
|
||||
<string>I/O 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>Read all I/O values and discard local changes (F4).
|
||||
|
||||
Hold this button pressed and it will refresh the IOs every 200 ms.</string>
|
||||
Hold this button to refresh the I/Os every 200 ms.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Read &all IO values</string>
|
||||
<string>Read &all I/O values</string>
|
||||
</property>
|
||||
<property name="autoRepeat">
|
||||
<bool>true</bool>
|
||||
@@ -74,12 +74,12 @@ Hold this button pressed and it will refresh the IOs every 200 ms.</string>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_refresh_io">
|
||||
<property name="toolTip">
|
||||
<string>Refresh all IO values which are locally not changed (F5)
|
||||
<string>Refresh all I/O values that have not been modified locally (F5).
|
||||
|
||||
Hold this button pressed and it will refresh the IOs every 200 ms.</string>
|
||||
Hold this button to refresh the I/Os every 200 ms.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Refresh unchanged IOs</string>
|
||||
<string>&Refresh unchanged I/Os</string>
|
||||
</property>
|
||||
<property name="autoRepeat">
|
||||
<bool>true</bool>
|
||||
@@ -95,7 +95,7 @@ Hold this button pressed and it will refresh the IOs every 200 ms.</string>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_write_o">
|
||||
<property name="toolTip">
|
||||
<string>Write locally changed output values to process image (F6)</string>
|
||||
<string>Write locally modified output values to process image (F6).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Write changed outputs</string>
|
||||
@@ -112,7 +112,7 @@ Hold this button pressed and it will refresh the IOs every 200 ms.</string>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbx_write">
|
||||
<property name="text">
|
||||
<string>and write outputs</string>
|
||||
<string>Write outputs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+4
-4
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>File manager</string>
|
||||
<string>File Manager</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
@@ -28,7 +28,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_select_local">
|
||||
<property name="title">
|
||||
<string>Local computer</string>
|
||||
<string>Local Computer</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0">
|
||||
<item row="0" column="0">
|
||||
@@ -168,7 +168,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_select_revpi">
|
||||
<property name="title">
|
||||
<string>Revolution Pi</string>
|
||||
<string>RevPi</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,0">
|
||||
<item row="1" column="0" colspan="2">
|
||||
@@ -328,7 +328,7 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="btn_all">
|
||||
<property name="text">
|
||||
<string>Stop - Upload - Start</string>
|
||||
<string>Stop, upload, start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>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.
|
||||
<string>Base topic is the prefix for MQTT topics published by RevPi. Use "/" to structure broker topics.
|
||||
|
||||
For example: revpi0000/data</string>
|
||||
</property>
|
||||
@@ -113,7 +113,7 @@ For example: revpi0000/data</string>
|
||||
</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.
|
||||
<string>RevPi subscribes to an MQTT topic for setting output values. Publish the new I/O value as payload.
|
||||
|
||||
Publish values with topic: [basetopic]/set/[outputname]</string>
|
||||
</property>
|
||||
@@ -125,7 +125,7 @@ Publish values with topic: [basetopic]/set/[outputname]</string>
|
||||
<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>
|
||||
<string>Allow MQTT to set outputs on RevPi</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Open-Source licenses</string>
|
||||
<string>Open source licenses</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
@@ -87,10 +87,10 @@
|
||||
</layout>
|
||||
<action name="action_start">
|
||||
<property name="text">
|
||||
<string>More licenses...</string>
|
||||
<string>More licenses</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show more open-source software licenses</string>
|
||||
<string>Show more open source software licenses</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
|
||||
@@ -210,7 +210,7 @@
|
||||
</action>
|
||||
<action name="act_search">
|
||||
<property name="text">
|
||||
<string>&Search Revolution Pi...</string>
|
||||
<string>&Search RevPi...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+F</string>
|
||||
@@ -223,12 +223,12 @@
|
||||
</action>
|
||||
<action name="act_webpage">
|
||||
<property name="text">
|
||||
<string>Visit &webpage...</string>
|
||||
<string>Visit &website</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_info">
|
||||
<property name="text">
|
||||
<string>&Info...</string>
|
||||
<string>&Info</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_logs">
|
||||
@@ -265,7 +265,7 @@
|
||||
</action>
|
||||
<action name="act_pictory">
|
||||
<property name="text">
|
||||
<string>piCtory configuraiton...</string>
|
||||
<string>PiCtory configuration</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_disconnect">
|
||||
@@ -278,7 +278,7 @@
|
||||
</action>
|
||||
<action name="act_reset">
|
||||
<property name="text">
|
||||
<string>Reset driver...</string>
|
||||
<string>Reset driver</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="act_simulator">
|
||||
|
||||
+4
-5
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Program information</string>
|
||||
<string>Program Information</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="3">
|
||||
@@ -24,7 +24,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RevPi Python PLC - Commander</string>
|
||||
<string>RevPi Commander (Python/PLC)</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
@@ -114,8 +114,7 @@
|
||||
</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.
|
||||
All of them come with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.
|
||||
|
||||
(c) Sven Sager, License: GPLv2</string>
|
||||
</property>
|
||||
@@ -140,7 +139,7 @@ applicable law.
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QLabel" name="lbl_link">
|
||||
<property name="text">
|
||||
<string notr="true"><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>
|
||||
<string notr="true"><html><head/><body><p><a href="https://revpimodio2.readthedocs.io/en/latest/"><span style=" text-decoration: underline; color:#0000ff;">https://revpimodio2.readthedocs.io/en/latest/</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>RevPi Python PLC Logfiles</string>
|
||||
<string>RevPi Python PLC Log Files</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
@@ -25,7 +25,7 @@
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="cbx_wrap">
|
||||
<property name="text">
|
||||
<string>Linewrap</string>
|
||||
<string>Line wrap</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -42,7 +42,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_daemon">
|
||||
<property name="text">
|
||||
<string>RevPiPyLoad - Logfile</string>
|
||||
<string>RevPiPyLoad Log File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -89,7 +89,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_app">
|
||||
<property name="text">
|
||||
<string>Python PLC program - Logfile</string>
|
||||
<string>Python PLC Program Log File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+14
-14
@@ -17,20 +17,20 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_plc">
|
||||
<property name="title">
|
||||
<string>Start / Stop behavior of PLC program</string>
|
||||
<string>PLC Start/Stop Behavior</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>
|
||||
<string>Replace I/O file:</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>
|
||||
<string>after exceptions or errors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -50,7 +50,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Restart after piCtory changed</string>
|
||||
<string>Restart after PiCtory changes</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -70,7 +70,7 @@
|
||||
<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>
|
||||
<string>Set process image to NULL if the program terminates</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -78,7 +78,7 @@
|
||||
<widget class="QComboBox" name="cbb_replace_io">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Do not use replace io file</string>
|
||||
<string>Do not replace I/O file</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@@ -93,7 +93,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Give own path and filename</string>
|
||||
<string>Use custom path and file name</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
@@ -101,7 +101,7 @@
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QCheckBox" name="cbx_zeroonexit">
|
||||
<property name="text">
|
||||
<string>... sucessfully without error</string>
|
||||
<string>without errors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -135,7 +135,7 @@
|
||||
<item row="9" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lbl_lbl_reset_driver_action">
|
||||
<property name="text">
|
||||
<string>PLC program behavior after piCtory driver reset clicked</string>
|
||||
<string>PLC program behavior after PiCtory driver reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -145,7 +145,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_server">
|
||||
<property name="title">
|
||||
<string>RevPiPyLoad server services</string>
|
||||
<string>RevPiPyLoad Server Services</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="1">
|
||||
@@ -165,7 +165,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cbx_plcserver">
|
||||
<property name="text">
|
||||
<string>Start RevPi piControl server</string>
|
||||
<string>Start piControl server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -182,7 +182,7 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbl_lbl_server_status">
|
||||
<property name="text">
|
||||
<string>piControl server is:</string>
|
||||
<string>piControl server:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
@@ -202,7 +202,7 @@
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="lbl_lbl_mqtt_status">
|
||||
<property name="text">
|
||||
<string>MQTT publish service is:</string>
|
||||
<string>MQTT publish service:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
@@ -226,7 +226,7 @@
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="cbx_xmlrpc">
|
||||
<property name="text">
|
||||
<string>Activate XML-RPC for RevPiCommander</string>
|
||||
<string>Activate XML-RPC for RevPi Commander</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+22
-12
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Revolution Pi connections</string>
|
||||
<string>RevPi Connections</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="2">
|
||||
@@ -37,21 +37,21 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbl_address">
|
||||
<property name="text">
|
||||
<string>Address (DNS/IP):</string>
|
||||
<string>Address (DNS/IP/Socket):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="txt_address"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="lbl_port">
|
||||
<property name="text">
|
||||
<string>Port (Default {0}):</string>
|
||||
<string>Port (default {0}):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QSpinBox" name="sbx_port">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
@@ -70,14 +70,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="lbl_timeout">
|
||||
<property name="text">
|
||||
<string>Connection timeout:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QSpinBox" name="sbx_timeout">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
@@ -86,7 +86,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> sec.</string>
|
||||
<string> s</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
@@ -96,14 +96,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="lbl_folder">
|
||||
<property name="text">
|
||||
<string>Sub folder:</string>
|
||||
<string>Subfolder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="cbb_folder">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
@@ -118,6 +118,16 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lbl_local_socket">
|
||||
<property name="text">
|
||||
<string>Use default local socket:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="cbx_local_socket"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_ssh">
|
||||
@@ -194,7 +204,7 @@
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Connection name</string>
|
||||
<string>Connection Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
|
||||
+13
-13
@@ -11,33 +11,33 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>PLC program</string>
|
||||
<string>PLC Program</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_plc">
|
||||
<property name="title">
|
||||
<string>PLC program</string>
|
||||
<string>PLC Program</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lbl_plcarguments">
|
||||
<property name="text">
|
||||
<string>Program arguments:</string>
|
||||
<string>Arguments:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QLabel" name="lbl_plcprogram_watchdog">
|
||||
<property name="text">
|
||||
<string>Software watchdog (0=disabled):</string>
|
||||
<string>Software watchdog (0 = disabled):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" 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>
|
||||
<string>Set write permissions for PLC program working directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -57,7 +57,7 @@
|
||||
<item row="4" column="2">
|
||||
<widget class="QSpinBox" name="sbx_plcprogram_watchdog">
|
||||
<property name="suffix">
|
||||
<string> sec.</string>
|
||||
<string> s</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>600</number>
|
||||
@@ -70,7 +70,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="cb_transfair">
|
||||
<property name="title">
|
||||
<string>Transfair PLC program</string>
|
||||
<string>Transfer PLC program</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="1">
|
||||
@@ -104,21 +104,21 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_format">
|
||||
<property name="text">
|
||||
<string>Transfair format:</string>
|
||||
<string>Transfer 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>
|
||||
<string>Include 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>
|
||||
<string>Remove all files on RevPi before upload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -128,7 +128,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_control">
|
||||
<property name="title">
|
||||
<string>Control files</string>
|
||||
<string>Control Files</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="1">
|
||||
@@ -155,14 +155,14 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_pictory">
|
||||
<property name="text">
|
||||
<string>piCtory configuraiton</string>
|
||||
<string>PiCtory configuration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbl_procimg">
|
||||
<property name="text">
|
||||
<string>Process image from piControl0</string>
|
||||
<string>Process image from piControl</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
+10
-10
@@ -11,13 +11,13 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>piControl simulator</string>
|
||||
<string>piControl Simulator</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_settings">
|
||||
<property name="title">
|
||||
<string>Simulator settings</string>
|
||||
<string>Simulator Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
@@ -30,7 +30,7 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lbl_configrsc">
|
||||
<property name="text">
|
||||
<string>piCtory file:</string>
|
||||
<string>PiCtory file:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -47,7 +47,7 @@
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btn_configrsc">
|
||||
<property name="text">
|
||||
<string>select...</string>
|
||||
<string>Select</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -99,7 +99,7 @@
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QRadioButton" name="rb_restart_pictory">
|
||||
<property name="text">
|
||||
<string>Restore piCtory default values</string>
|
||||
<string>Restore PiCtory default values</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@@ -109,7 +109,7 @@
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QRadioButton" name="rb_restart_zero">
|
||||
<property name="text">
|
||||
<string>Reset everything to ZERO</string>
|
||||
<string>Reset all values to NULL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -119,13 +119,13 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_info">
|
||||
<property name="title">
|
||||
<string>RevPiModIO integration</string>
|
||||
<string>RevPiModIO Integration</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="lbl_info">
|
||||
<property name="text">
|
||||
<string>You can work with this simulator if you call RevPiModIO with this additional parameters:</string>
|
||||
<string>To use this simulator, call RevPiModIO with the following additional parameters:</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@@ -151,7 +151,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_start_pictory">
|
||||
<property name="text">
|
||||
<string>Start with piCtory default values</string>
|
||||
<string>Start with PiCtory default values</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+1</string>
|
||||
@@ -171,7 +171,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_start_nochange">
|
||||
<property name="text">
|
||||
<string>Start without changing actual process image</string>
|
||||
<string>Start without changing the current process image</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string notr="true">Ctrl+3</string>
|
||||
|
||||
+4
-4
@@ -26,7 +26,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="lbl_username">
|
||||
<property name="text">
|
||||
<string>SSH username:</string>
|
||||
<string>SSH user name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -53,10 +53,10 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbx_save_password">
|
||||
<property name="toolTip">
|
||||
<string>Username and password will be saved in secured operating systems's password storage.</string>
|
||||
<string>Save user name and password in secure password storage.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save username and password</string>
|
||||
<string>Save user name and password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -79,7 +79,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Note: The default user for SSH is "pi" which differs from the web configuration. You can find the password on the sticker on the device.</string>
|
||||
<string>Default SSH user is "pi". The device password is on the RevPi housing sticker.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
||||
Reference in New Issue
Block a user