mirror of
https://github.com/naruxde/revpipycontrol.git
synced 2025-12-29 11:23:13 +01:00
mktemp auf mkstemp umgestellt
PLC monitor aktiviert revpicheckclient auf revpipyloader angepasst Module als einzelnes Fenster anzeigen
This commit is contained in:
@@ -9,28 +9,29 @@
|
||||
|
||||
import pickle
|
||||
import tkinter
|
||||
from argparse import ArgumentParser
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from time import sleep
|
||||
from xmlrpc.client import ServerProxy, Binary, MultiCall
|
||||
from threading import Lock
|
||||
from xmlrpc.client import ServerProxy
|
||||
|
||||
|
||||
class RevPiCheckClient(tkinter.Frame):
|
||||
|
||||
def __init__(self, master, xmlcli):
|
||||
def __init__(self, master, xmlcli, xmlmode=0):
|
||||
"""Instantiiert MyApp-Klasse."""
|
||||
super().__init__(master)
|
||||
self.pack(fill="both", expand=True)
|
||||
|
||||
# XML-Daten abrufen
|
||||
self.xmlmode = xmlmode
|
||||
self.cli = xmlcli
|
||||
self.cli.psstart()
|
||||
self.lst_devices = self.cli.ps_devices()
|
||||
self.dict_inps = pickle.loads(self.cli.ps_inps().data)
|
||||
self.dict_outs = pickle.loads(self.cli.ps_outs().data)
|
||||
|
||||
self.lst_devices = self.cli.get_devicenames()
|
||||
self.lst_group = []
|
||||
self.dict_inpvar = {}
|
||||
self.dict_outvar = {}
|
||||
self.lk = Lock()
|
||||
|
||||
self.autorw = tkinter.BooleanVar()
|
||||
self.fut_autorw = None
|
||||
self.dowrite = tkinter.BooleanVar()
|
||||
|
||||
# Fenster aufbauen
|
||||
self._createwidgets()
|
||||
@@ -38,33 +39,6 @@ class RevPiCheckClient(tkinter.Frame):
|
||||
# Aktuelle Werte einlesen
|
||||
self.readvalues()
|
||||
|
||||
def _autorw(self):
|
||||
dict_inp = {}
|
||||
dict_out = {}
|
||||
|
||||
while self.autorw.get():
|
||||
for dev in self.lst_devices:
|
||||
try:
|
||||
dict_out[dev] = [
|
||||
value[8].get() for value in self.dict_outvar[dev]
|
||||
]
|
||||
except:
|
||||
print("lasse {} aus".format(dev))
|
||||
|
||||
dict_inp = self.cli.refreshvalues(
|
||||
Binary(pickle.dumps(dict_out, 3))
|
||||
)
|
||||
dict_inp = pickle.loads(dict_inp.data)
|
||||
|
||||
for dev in dict_inp:
|
||||
for io in self.dict_inpvar[dev]:
|
||||
try:
|
||||
io[8].set(dict_inp[dev].pop(0))
|
||||
except:
|
||||
print("lasse {} aus".format(io[0]))
|
||||
|
||||
sleep(0.1)
|
||||
|
||||
def onfrmconf(self, canvas):
|
||||
canvas.configure(scrollregion=canvas.bbox("all"))
|
||||
|
||||
@@ -85,23 +59,34 @@ class RevPiCheckClient(tkinter.Frame):
|
||||
)
|
||||
|
||||
rowcount = 0
|
||||
for io in self.cli.get_iolist(device, iotype):
|
||||
# io = [name,default,anzbits,adressbyte,export,adressid,bmk,bitaddress,tkinter_var]
|
||||
|
||||
if iotype == "inp":
|
||||
lst_io = self.dict_inps[device]
|
||||
else:
|
||||
lst_io = self.dict_outs[device]
|
||||
|
||||
for io in lst_io:
|
||||
# io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
|
||||
|
||||
tkinter.Label(s_frame, text=io[0]).grid(
|
||||
column=0, row=rowcount, sticky="w"
|
||||
)
|
||||
|
||||
if io[7] >= 0:
|
||||
if io[4] >= 0:
|
||||
var = tkinter.BooleanVar()
|
||||
check = tkinter.Checkbutton(s_frame)
|
||||
check["command"] = \
|
||||
lambda device=device, io=io: self.chval(device, io)
|
||||
check["state"] = "disabled" if iotype == "inp" else "normal"
|
||||
check["text"] = ""
|
||||
check["variable"] = var
|
||||
check.grid(column=1, row=rowcount)
|
||||
else:
|
||||
var = tkinter.IntVar()
|
||||
txt = tkinter.Spinbox(s_frame, to=256)
|
||||
# FIXME: Mehrere Bytes möglich
|
||||
txt = tkinter.Spinbox(s_frame, to=255 * io[1])
|
||||
txt["command"] = \
|
||||
lambda device=device, io=io: self.chval(device, io)
|
||||
txt["state"] = "disabled" if iotype == "inp" else "normal"
|
||||
txt["width"] = 4
|
||||
txt["textvariable"] = var
|
||||
@@ -109,116 +94,127 @@ class RevPiCheckClient(tkinter.Frame):
|
||||
|
||||
# Steuerelementvariable in IO übernehmen
|
||||
io.append(var)
|
||||
if iotype == "inp":
|
||||
self.dict_inpvar[device].append(io)
|
||||
elif iotype == "out":
|
||||
self.dict_outvar[device].append(io)
|
||||
|
||||
rowcount += 1
|
||||
|
||||
def __hidewin(self, win, event=None):
|
||||
win.withdraw()
|
||||
|
||||
def __showwin(self, win):
|
||||
if win.winfo_viewable():
|
||||
win.withdraw()
|
||||
else:
|
||||
win.deiconify()
|
||||
|
||||
def _createwidgets(self):
|
||||
"""Erstellt den Fensterinhalt."""
|
||||
# Hauptfenster
|
||||
self.master.wm_title("RevPi Onlineview")
|
||||
|
||||
devgrp = tkinter.LabelFrame(self)
|
||||
devgrp["text"] = "Devices of RevPi"
|
||||
devgrp.pack(fill="y", side="left")
|
||||
|
||||
for dev in self.lst_devices:
|
||||
# Variablen vorbereiten
|
||||
self.dict_inpvar[dev] = []
|
||||
self.dict_outvar[dev] = []
|
||||
win = tkinter.Toplevel(self)
|
||||
win.wm_title(dev[1])
|
||||
win.protocol(
|
||||
"WM_DELETE_WINDOW",
|
||||
lambda win=win: self.__hidewin(win)
|
||||
)
|
||||
win.withdraw()
|
||||
|
||||
# Devicegruppe erstellen
|
||||
group = tkinter.LabelFrame(self)
|
||||
group["text"] = dev
|
||||
group = tkinter.LabelFrame(win)
|
||||
group["text"] = dev[1]
|
||||
group.pack(side="left", fill="both", expand=True)
|
||||
self.lst_group.append(group)
|
||||
|
||||
for iotype in ["inp", "out"]:
|
||||
frame = tkinter.Frame(group)
|
||||
frame.pack(side="left", fill="both", expand=True)
|
||||
self._createiogroup(dev, frame, iotype)
|
||||
self._createiogroup(dev[0], frame, iotype)
|
||||
|
||||
# self.btn_update = tkinter.Button(self)
|
||||
# self.btn_update["text"] = "UPDATE"
|
||||
# self.btn_update["command"] = self._autorw
|
||||
# self.btn_update.pack(anchor="s", side="bottom", fill="x")
|
||||
# Button erstellen
|
||||
btn = tkinter.Button(devgrp)
|
||||
btn["command"] = lambda win=win: self.__showwin(win)
|
||||
btn["text"] = dev[1]
|
||||
btn.pack(fill="x", padx=10, pady=5)
|
||||
|
||||
self.btn_write = tkinter.Button(self)
|
||||
self.btn_write["text"] = "SCHREIBEN"
|
||||
self.btn_write["command"] = self.writevalues
|
||||
self.btn_write.pack(side="bottom", fill="x")
|
||||
# Steuerungsfunktionen
|
||||
cntgrp = tkinter.LabelFrame(self)
|
||||
cntgrp["text"] = "Control"
|
||||
cntgrp.pack(fill="y", side="right")
|
||||
|
||||
self.btn_read = tkinter.Button(self)
|
||||
self.btn_read = tkinter.Button(cntgrp)
|
||||
self.btn_read["text"] = "LESEN"
|
||||
self.btn_read["command"] = self.readvalues
|
||||
self.btn_read.pack(side="bottom", fill="x")
|
||||
self.btn_read.pack(fill="x")
|
||||
|
||||
check = tkinter.Checkbutton(self)
|
||||
check = tkinter.Checkbutton(cntgrp)
|
||||
check["command"] = self.toggleauto
|
||||
check["text"] = "autoupdate"
|
||||
check["text"] = "autorefresh processimage"
|
||||
check["variable"] = self.autorw
|
||||
check.pack(side="bottom")
|
||||
check.pack(anchor="w")
|
||||
|
||||
def _readvaluesdev(self, device, iotype):
|
||||
"""Ruft alle aktuellen Werte fuer das Device ab."""
|
||||
# Multicall vorbereiten
|
||||
mc_values = MultiCall(self.cli)
|
||||
check = tkinter.Checkbutton(cntgrp)
|
||||
check["state"] = "disabled" if self.xmlmode < 3 else "normal"
|
||||
check["text"] = "write values to processimage"
|
||||
check["variable"] = self.dowrite
|
||||
check.pack(anchor="w")
|
||||
|
||||
if iotype == "inp":
|
||||
lst_ios = self.dict_inpvar[device]
|
||||
elif iotype == "out":
|
||||
lst_ios = self.dict_outvar[device]
|
||||
def chval(self, device, io):
|
||||
if self.dowrite.get():
|
||||
with self.lk:
|
||||
self.cli.ps_setvalue(device, io[0], io[5].get())
|
||||
|
||||
for io in lst_ios:
|
||||
mc_values.get_iovalue(device, io[0])
|
||||
# Alles neu einlesen wenn nicht AutoRW aktiv ist
|
||||
if not self.autorw.get():
|
||||
self.readvalues()
|
||||
|
||||
i = 0
|
||||
for value in mc_values():
|
||||
value = pickle.loads(value.data)
|
||||
if type(value) == bytes:
|
||||
value = int.from_bytes(value, byteorder="little")
|
||||
|
||||
lst_ios[i][8].set(value)
|
||||
i += 1
|
||||
|
||||
def _writevaluesdev(self, device):
|
||||
"""Sendet Werte der Outputs fuer ein Device."""
|
||||
# Multicall vorbereiten
|
||||
mc_values = MultiCall(self.cli)
|
||||
lst_ios = lst_ios = self.dict_outvar[device]
|
||||
|
||||
for io in lst_ios:
|
||||
mc_values.set_iovalue(device, io[0], pickle.dumps(io[8].get(), 3))
|
||||
|
||||
# Multicall ausführen
|
||||
mc_values()
|
||||
|
||||
def readvalues(self):
|
||||
def _readvalues(self):
|
||||
"""Alle Werte der Inputs und Outputs abrufen."""
|
||||
# Werte aus Prozessabbild einlesen
|
||||
self.cli.readprocimg()
|
||||
|
||||
# Werte abrufen
|
||||
with self.lk:
|
||||
ba_values = bytearray(self.cli.ps_values().data)
|
||||
|
||||
for dev in self.lst_devices:
|
||||
self._readvaluesdev(dev, "inp")
|
||||
self._readvaluesdev(dev, "out")
|
||||
# io = [name,bytelen,byteaddr,bmk,bitaddress,(tkinter_var)]
|
||||
|
||||
# IO Typ verarbeiten
|
||||
for iotype in [self.dict_inps, self.dict_outs]:
|
||||
# ios verarbeiten
|
||||
for io in iotype[dev[0]]:
|
||||
|
||||
# Bytes umwandeln
|
||||
int_byte = int.from_bytes(
|
||||
ba_values[io[2]:io[2] + io[1]], byteorder="little"
|
||||
)
|
||||
if io[4] >= 0:
|
||||
# Bit-IO
|
||||
io[5].set(bool(int_byte & 1 << io[4]))
|
||||
else:
|
||||
# Byte-IO
|
||||
io[5].set(int_byte)
|
||||
|
||||
if self.autorw.get():
|
||||
self.master.after(200, self._readvalues)
|
||||
|
||||
def readvalues(self):
|
||||
if not self.autorw.get():
|
||||
self._readvalues()
|
||||
|
||||
def toggleauto(self):
|
||||
self.btn_read["state"] = "disabled" if self.autorw.get() else "normal"
|
||||
self.btn_write["state"] = "disabled" if self.autorw.get() else "normal"
|
||||
if self.autorw.get() \
|
||||
and (self.fut_autorw is None or self.fut_autorw.done()):
|
||||
e = ThreadPoolExecutor(max_workers=1)
|
||||
self.fut_autorw = e.submit(self._autorw)
|
||||
if self.autorw.get():
|
||||
self._readvalues()
|
||||
|
||||
def writevalues(self):
|
||||
"""Alle Outputs senden."""
|
||||
pass
|
||||
#for dev in self.lst_devices:
|
||||
#self._writevaluesdev(dev)
|
||||
|
||||
# Werte in Prozessabbild schreiben
|
||||
#self.cli.writeprocimg()
|
||||
|
||||
# Testdrive
|
||||
if __name__ == "__main__":
|
||||
root = tkinter.Tk()
|
||||
myapp = RevPiCheckClient(root)
|
||||
myapp.mainloop()
|
||||
cli = ServerProxy("http://192.168.50.35:55123")
|
||||
cli.psstart()
|
||||
|
||||
tk = tkinter.Tk()
|
||||
RevPiCheckClient(tk, cli, 3)
|
||||
tk.mainloop()
|
||||
|
||||
@@ -17,7 +17,7 @@ from os import environ
|
||||
from os import makedirs
|
||||
from shutil import rmtree
|
||||
from sys import platform
|
||||
from tempfile import mktemp, mkdtemp
|
||||
from tempfile import mkstemp, mkdtemp
|
||||
from xmlrpc.client import Binary
|
||||
|
||||
|
||||
@@ -363,7 +363,7 @@ class RevPiProgram(tkinter.Frame):
|
||||
message="Die Übertragung der piCtory Konfiguration "
|
||||
"wurde erfolgreich ausgeführt")
|
||||
|
||||
#Einstellungen speichern
|
||||
# Einstellungen speichern
|
||||
self.opt["setpictoryrsc_dir"] = os.path.dirname(fh.name)
|
||||
self._savedefaults()
|
||||
elif ec < 0:
|
||||
@@ -382,7 +382,7 @@ class RevPiProgram(tkinter.Frame):
|
||||
|
||||
def picontrolreset(self):
|
||||
ask = tkmsg.askyesno(
|
||||
parent=self.master, title="Frage...",
|
||||
parent=self.master, title="Frage...",
|
||||
message="Soll piControlReset wirklich durchgeführt werden? \n"
|
||||
"Das Prozessabbild und die Steuerung werden dann unterbrochen!!!"
|
||||
)
|
||||
@@ -415,7 +415,7 @@ class RevPiProgram(tkinter.Frame):
|
||||
)
|
||||
|
||||
if type(dirselect) == str and dirselect != "":
|
||||
fh = open(mktemp(), "wb")
|
||||
fh = open(mkstemp(), "wb")
|
||||
|
||||
elif tdown == 1:
|
||||
# Zip
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# RevPiPyControl
|
||||
# Version: 0.2.12
|
||||
# Version: 0.4.0
|
||||
#
|
||||
# Webpage: https://revpimodio.org/revpipyplc/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
import revpicheckclient
|
||||
import revpilogfile
|
||||
import revpioption
|
||||
import revpiplclist
|
||||
@@ -50,6 +51,7 @@ class RevPiPyControl(tkinter.Frame):
|
||||
self.xmlmode = 0
|
||||
|
||||
# Globale Fenster
|
||||
self.tkcheckclient = None
|
||||
self.tklogs = None
|
||||
self.tkoptions = None
|
||||
self.tkprogram = None
|
||||
@@ -124,7 +126,7 @@ class RevPiPyControl(tkinter.Frame):
|
||||
# PLC Menü
|
||||
self.mplc = tkinter.Menu(self.mbar, tearoff=False)
|
||||
self.mplc.add_command(label="PLC log...", command=self.plclogs)
|
||||
#self.mplc.add_command(label="PLC monitor...", command=self.plcmonitor)
|
||||
self.mplc.add_command(label="PLC monitor...", command=self.plcmonitor)
|
||||
self.mplc.add_command(label="PLC options...", command=self.plcoptions)
|
||||
self.mplc.add_command(label="PLC program...", command=self.plcprogram)
|
||||
self.mbar.add_cascade(label="PLC", menu=self.mplc, state="disabled")
|
||||
@@ -167,6 +169,8 @@ class RevPiPyControl(tkinter.Frame):
|
||||
self.mbar.entryconfig("PLC", state="normal")
|
||||
|
||||
def _closeall(self):
|
||||
if self.tkcheckclient is not None:
|
||||
self.tkcheckclient.destroy()
|
||||
if self.tklogs is not None:
|
||||
self.tklogs.master.destroy()
|
||||
if self.tkoptions is not None:
|
||||
@@ -191,10 +195,17 @@ class RevPiPyControl(tkinter.Frame):
|
||||
self.tklogs.focus_set()
|
||||
|
||||
def plcmonitor(self):
|
||||
# TODO: Monitorfenster
|
||||
pass
|
||||
"""Startet das Monitorfenster."""
|
||||
if self.tkcheckclient is None or len(self.tkcheckclient.children) == 0:
|
||||
win = tkinter.Toplevel(self)
|
||||
self.tkcheckclient = revpicheckclient.RevPiCheckClient(
|
||||
win, self.cli, self.xmlmode
|
||||
)
|
||||
else:
|
||||
self.tkcheckclient.focus_set()
|
||||
|
||||
def plcoptions(self):
|
||||
"""Startet das Optionsfenster."""
|
||||
if self.xmlmode < 2:
|
||||
tkmsg.showwarning(
|
||||
parent=self.master, title="Warnung",
|
||||
|
||||
Reference in New Issue
Block a user