[Release] RealTime OpenControl

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
haschtl
User
Beiträge: 8
Registriert: Mittwoch 7. November 2018, 08:08

Hallo zusammen!

Ich möchte euch mein letztes Projekt vorstellen: RealTime OpenControl
Geschrieben in Python mit PyQt5 und pyqtgraph.

Bild


RealTime OpenControl ermöglicht eine geräteübergreifende Messaufzeichnung. Außerdem kann man mit dem integrierten Python-Skript-Editor auf die Messdaten und Geräte zugreifen und mit diesen interagieren. Somit lassen sich langsame Regelungen zwischen mehreren Geräten realisieren. Z.B.: Temperaturmessung mit Multimeter (mit USB-Anschluss) und Regelung eines Heizelements auf eine Solltemperatur. Ideal zum Aufzeichnen, Testen und Optimieren von Regelungen. Eignet sich auch für Custom-HomeAutomation (z.B.: auf Raspberry Pi oder HomeServer) mit maximaler Flexibilität und Anpassbarkeit

Die Einbindung neuer Geräte ist einfach möglich:

Als Python-Plugin für RTOC (v.a. für lokale Geräte)
Als TCP-Client (v.a. für Netzwerkgeräte) (für weitere Infos siehe Wiki)

Beispiel Plugins:

Funktionsgenerator: Erzeugt Sinus, Square, Sawtooth, Random, AC, DC
System: Zur Aufzeichnung vieler Systemvariablen (CPU, Memory, Network,...)
Octoprint: Aufzeichnung für 3D-Drucker
DPS5020: Netzgerät-Aufzeichnung und Steuerung (evtl. auch DPS5005, ...)
HoldPeak VC820: Multimeter Messaufzeichnung (wahrsch. auch andere VC820)
NetWoRTOC: Datenaustausch zwischen mehreren RTOC's im Netzwerk

Die Oberfläche hat erweiterte Darstellungsoptionen und verschiedene Messtools für den Plot bereit.

Link zur Github-Repository:
https://github.com/Haschtl/RealTimeOpenControl

Ich freue mich über Feedback, Anregungen und Plugins, die ihr dafür schreibt!
Wenn ihr wollt, zeigt mir auch die Projekte, die ihr damit realisiert
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sieht gut aus!

Ich habe ein bisschen in den Code geschaut. IMHO verhedderst du dich da etwas mit QThreads. Da ist teilweise Toter Code drin. Teilweise Code, der wenig bis keinen Sinn hat. ZB der EventCallback. Dessen "run" emittiert einfach nur einmal ein Signal, danach ist der Thread tot. Das kann nicht sinnvoll sein. Der Updater ist auch so nicht sinnvoll, dafuer kann man einen QTimer benutzen. Der Sinn des Splash-Screens erschliesst sich mir nicht. Vielleicht denkst du, sowas gehoert sich. Tut es nicht. Es ist ein grauenvoller Workaround (den wir selbst benutzen...) um eine langsam ladende Software zu kaschieren. Du scheinst aus meiner Sicht nichts zu tun ausser einfach den Benutzer warten zu lassen. Wozu? Langsam startende Programme nerven.

Du hast auch deutlich zu viele nackte except:-Anweisungen. Die verschleiern oft schlimme Fehler. Versuch, nur die Fehler abzufangen, die *wirklich* auftauchen koennen.

Auch wenn du eine __main__-Sektion hast (gut), dir fehlt eine main-Funktion, die du darin aufrufst. Somit erzeugst du einen Haufen globaler Variablen. Ja, man sieht das oft. Nein, das ist immer noch nich gut ;)

Code wie https://github.com/Haschtl/RealTimeOpen ... tem.py#L64 ist verwirrend. Ich habe das gesehen, und mich ueber die Verwendung komischer magischer Zahlen gewundert. i ist ein schlechter Name. Nenn das checkbox_state, und dann versteht man das auch, ohne das man erst rauspulen muss, woher das kommt. Und ich bin mir sicher, irgendwo in Qt gibt es ein wrapping fuer http://doc.qt.io/qt-5/qt.html#CheckState-enum

Aber wie gesagt, erstmal ein gelungener Anfang.
haschtl
User
Beiträge: 8
Registriert: Mittwoch 7. November 2018, 08:08

Hi!
Vielen Dank für die schnelle Antwort und die nützlichen Anregungen!
Einige der Punkte, die du ansprichst, sind mir bewusst (exceptions abfangen vorallem ist ganz oben auf meiner ToDo-Liste)

Den QThread EventCallback habe ich eingebaut, da ich oft das Problem hatte, dass die GUI hängt, wenn der Python-code die GUI aktualisiert. Ist das hier nicht der Fall? Falls doch, welche Lösung ist dabei besser?
Den Updater Thread hab ich noch nicht durch einen QTimer ersetzt, weil der Nutzer die Möglichkeit hat, das Intervall während der Laufzeit zu verändern. Ich weiß - QTimer hat die Funktion 'setInterval', aber das hat nicht auf Anhieb funktioniert - also bin ich auf den mir gut bekannten QThread ausgewichen (mit Performance-Einbußen?)

Der Splashscreen ... ja, hab das feature entdeckt, fands cool und habs eingebaut. Meinst du der Splashscreen verzögert den Programmstart zusätzlich? Das Programm hat auch ohne den Splashscreen schon recht lange geladen, gibt es da Möglichkeiten das zu beschleunigen? Ich weiß auch nicht genau, ob das Laden der Bibliotheken oder das Laden der GUI so lange dauert

Das mit der main-function interessiert mich. Wird dadurch quasi die ganze GUI global, wenn ich den Code nicht in eine Funktion packe? Heißt, ich packe einfach alles, was unter __main__ steht in eine Funktion?

Magische Variablen sind da glaube ich noch häufiger drin ;) ... Da ist auf jedenfall noch ein Cleanup nötig!

Nochmals vielen Dank für die Infos!

PS: Respekt, wie zügig du den ganzen Code durchgeschaut hast!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn der GUI-Thread haengt, dann kann man natuerlich mit einem QThread im Hintergrund andere Taetigkeiten ausfuehren. So weit stimmt das schon. Nur was es bringen soll, genau EIN Signal zu feuern ist mir nicht klar. Das, was an dem Signal haengt, kann dann der Thread doch direkt ausfuehen, und muss nicht eine solche undurchsichtige Indirektion vornehmen?

Und ganz allgemein gilt: die Manipulation von allem, was GUI ist, MUSS ZWINGEND im Main-Thread geschehen! Das kracht sonst mit schweren Abstuerzen. Nicht immer, aber garantiert irgendwann. Womit auch der Updater seinen Sinn verliert: wenn du es richtig machst, dann liefert der Updater sein Signal per "queued connection" im Main-Thread ab. Womit der das dann abarbeitet. Da ist ein QTimer aber genauso gut. Wenn du es aber falsch machst, dann triggert dein Signal ein Stueck Code in der GUI im Updater-Thread, das zwingend im Mainthread ausgefuehrt werden will - und dann kracht es.

Und natuerlich verzoegert der Splashscreen nur. Du *tust* waehrenddessen doch nichts, ausser zu warten. Oder habe ich da was uebersehen? Der macht nur Sinn, wenn waehrend dessen das Programm irgendwas macht. Aber der langsame Start kommt bei dir wohl eher vom importieren der Qt Module, und die musst du nunmal erstmal haben, bevor du eine GUI inklusiver Splashscreen machen kannst.

Was main angeht: ja, aller Code wird ja auf modul-globaler Ebene ausgefuehrt. Das du da irgendwo ein if drum hast, macht ja keinen Unterschied.
haschtl
User
Beiträge: 8
Registriert: Mittwoch 7. November 2018, 08:08

Alles klar.
Hatte anfangs mal den Fall, dass ich mit einem normalen Thread die Gui beeinflussen wollte, das ging aber nicht, daher bin ich auf den QThread ausgewichen und dachte, dass ich den in solchen Fällen dann immer brauch.

Vielen Dank für die vielen Tipps! Hat mir sehr weitergeholfen!
Habe jetzt den EventCallback entfernt, den Updater als Queued connection gemacht (ich hoffe ich hab das richtig gemacht? Es funktioniert jedenfalls auch) , den SplashScreen entfernt, ne main-function gemacht, einige exceptions gehandlet und einige Qt-Warnungen behoben.

Kann man denn pauschal beurteilen, ob die Änderungen die Performance positiv beeinflussen?

Nochmals vielen Dank für deine Hilfe!
Ich hab das ganze nicht studiert, sondern nur selbst beigebracht, daher nimm ich auch gerne mal den einfachen Weg. Diese Queued connections kannte ich z.b. noch nicht.

Edit: den Updater-Thread hab ich gelöscht - wurde gar nicht mehr benutzt. Den "Callback"-Thread hab ich als queued-connection gemacht. In RTOC.py Zeile 333
Antworten