zyklische Funktionsdurchführung mit vorgebenener Frequenz ohne globale Variable - wie geht das?

  • Hallo,


    Ansatz über kooperatives Multitasking via asyncio. Funktioniert so ab Python 3.5:

    Vorteil: man brauht keine Zeitstempel von Funktion zu Funktion reichen.


    Auch hier ist wie in meinem 1. Beispiel die Abfrage des Status (ok oder Störung) nicht implementiert - kann man aber leicht nochholen.


    Gruß, noisefloor

  • Hallo,


    Manul : kurz funktioniert nicht. Dazu ist das ein bisschen komplex bzw zu "anders" im Vergleich zu lineraren Programmierung, was die Denkweise angeht. Was grundsätzlich hilft ist, wenn man verstanden hat, wie Coroutinen funktionieren und wann man eine Idee hat, was ein "Future" ist.


    Drei Links:


    Gruß, noisefloor

  • noisefloor : Vielen Dank für die links! Als ich die beiden Youtubelinks gesehen habe, hatte ich ja schon Bedenken – ich hab's nicht so mit Videotutorials – aber das waren wirklich zwei Spitzenvorträge!

    Was schnell angeht, ist das ja auch immer relativ. Für mich war das schon deshalb schnell, weil ich mich nicht erst durch 100 Seiten und Videos wühlen musste, um die drei brauchbaren zu finden. Eigentlich meinte ich aber wohl eher praxisorientiert (hands-on heißt das wohl heute), und auch in dieser Hinsicht fand ich die von Dir verlinkten Videos sehr gut. Den ersten link schaue ich mir auch noch in Ruhe durch.

  • Vielen Dank für die tollen Beiträge! Vor allem auch die Beispielcodes. Vielleicht krieg ichs mit der Klasse ja doch nochmal hin ;-)

    Mittlerweile hatte ich Gelegenheit, den Kerncode der Steuerung auszuprobieren und das hat auch tatsächlich weitestgehend funktioniert. Ich muss aber gestehen, dass ich das jetzt doch erstmal mit ner globalen Variable gelöst hab.

    Das mag zwar nicht einem guten Stil entsprechen... andererseits war das deutlich eleganter umzusetzen als lokale Variablen über die Funktionen in zwei Ebenen auszutauschen. Ich hatte es ausprobiert und das sah deutlich unübersichtlicher aus und ich kann mir nicht vorstellen, dass das effizienter sein soll. Da lass ich mich aber gerne vom Gegenteil überzeugen. Schätze, da landen wir dann wieder bei einer Klasse.

    Da ich noch nicht fertig bin, werd ich an dem Thema aber dran bleiben und mir duchaus auch die Geschichte mit asyncio näher ansehen. Das könnte hier nämlich perfekt auf mein "Problem" passen.

    Grüße,

    Raspuino

  • Hallo,


    Zitat


    andererseits war das deutlich eleganter umzusetzen als lokale Variablen über die Funktionen in zwei Ebenen auszutauschen.

    100% falsch, es ist genau andersrum. Der explizite Austausch ist der elegante und richtige Weg. Sich drauf verlassen, dass die Funktionen "irgendwie" durch eine hoffentlich richtig typisierte globale Variable das machen, was sie sollen, ist falsch.

    `global` macht den Zustand des Programms schwer bis nicht durchschaubar und kann sehr schwer bis nicht auffindbaren Fehlern oder unerwartetem Verhalten führen. Die Verwendung von `global` ist in 99% der Fälle falsch - und dein Programm gehört definitiv nicht zum verbleibendem 1%.

    Vergiß' am besten, dass es `global` gibt. Das wird dir definitiv helfen, besser und ideomatischer in Python zu programmieren.


    Gruß, noisefloor

  • Ja, ich geh da voll mit.

    Aber step by step. Im Vergleich zu bisherigen Codes (kaum separate Funktionen) mach ich mittlerweile vieles ganz anders. Allein die relativ konsequente Verwendung von Funktionen macht schon sehr viel aus und macht den Code besser wartbar. KLassen sind dann der nächste Schritt. Und im Verlauf dieses Threads hab ich z.B. die Variablennamen konsequent auf die "Python-Norm" umgestellt, was gut war. Einiges konnte ich ausmisten und am Ende werd ich bestimmt irgendwie auch noch die globalen Variablen los. Im Moment jedenfalls hilfts und es funktioniert (und ich bin durchaus weit davon entfernt, den Überblick zu verlieren). Rein optisch ist es zum aktuellen Stand jedoch sehr wohl eleganter. Wie gesagt, ich arbeite noch dran ;-)

    Grüße,

    Raspbuino

  • Ist hier bereits jemand auf machwas() eingegangen? :conf:


    Ich persönlich betrachte das was in dieser Funktion ausgeführt wird als Kriterium für die Aufgabenstellung "mit bestimmter Frequenz (z.B. 10 oder 50Hz) immer wieder Daten senden."... Denn wenn die Funktion machwas() das Script derweil zu lange blockiert kann die Aufgabenstellung nicht erfüllt werden.




    :2cents:

  • So siehts aus! :)

    Kurz zum Hintergrund: da läuft ein CanBus (mit ner PICAN2 - Platine übrigens) und es müssen sowohl Daten gesendet und Daten empfangen werden. In beiden Fällen sind Messages dabei, die im Bereich zwischen 10 und 50Hz rein/rausgehen. Es sind also zwei mehr oder weniger "parallele" Prozesse, die eben abwechselnd laufen müssen. Das Senden enthält mindestens zwei Aktionen, die mit unterschiedlichen aber doch relativ genau einzuhaltenden Intervallen ausgeführt werden müssen.

    In der verbleibenden Zeit müssen dann möglichst alle Daten eingesammelt werden, die reinkommen. Da kommts dann also drauf an, die Sache möglichst verlustfrei zu gestalten. Ich konnte nur hoffen, dass der Raspi schnell genug ist.


    Wie gesagt, ich hatte bereits kurz Gelegenheit, den Code auszuprobieren....und offensichtlich lief der Code ausreichend schnell.

    Hätte ich selber nicht gedacht, dass das auf Anhieb (zumindest grundsätzlich) funktioniert. Ich hab sowas noch nie vorher gemacht aber manchmal klappt halt auch was direkt und das ist dann ein echtes Erfolgserlebnis. Deswegen hab ich ja auch so einen Riesenspass mit dem Raspi, Python und dem ganzen Rest.


    Die gesammelten Daten hab ich noch nicht analysiert um behaupten zu können, diese wären vollständig aber es sah auf den ersten Blick gut aus. Weil es bereits funktioniert, bin ich da jetzt schon etwas entspannter unterwegs. Trotzdem will ich versuchen, dies auch "sicherzustellen".


    Heut hab ich mich endlich mal vorsichtig an die Programmierung einer Klasse gewagt. Ich kann behaupten, den Einstieg hab ich jetzt. Feine Sache das.


    Grüße,

    Raspbuino

  • Naja auch ein 4000 MHz Computer könnte "zu langsam" sein wenn das Programm schlecht programmiert wurde...


    Alle bisherigen Beiträge hier befassen sich nur mit irgendeiner zyklischen Abfrage, aber keine dieser Abfragen führt tatsächlich besondere Aktionen aus.


    Dh eigentlich hättest Du dein Anliegen von Anfang an ausführlich(er) beschreiben sollen - da das nicht der Fall ist wurde nur auf das STARTZEIT Szenario eingegangen - hilft dir bei deinem Vorhaben aber nur wenig. Alle bisher gezeigten Scripts können nur ein Befehl zugleich ausführen, keine "parallelen Prozesse". Es ginge nur "Senden und derweil den Rest blockieren" oder "Empfangen und derweil den Rest blockieren"...


    Dh solange gesendet wird kann parallel nichts anderes verarbeitet werden. Dauert das Senden aber eine halbe Sekunde kannst du deine 10-50 Hz Bedingung vergessen.


    Damit parallel mehrere Zeitkritische Aktionen stattfinden können bedarf es eines weitaus komplexeren Programmes.

  • Da wir in der Tat nicht wissen, was in machwas() alles passieren soll, ist das weitgehend Spekulation. Ich denke aber, daß noisefloor s Ansatz aus #22 durchaus schon ausreichend sein könnte. Vielleicht mag Raspbuino sein Projekt ja mal komplett beschreiben, das wäre sicherlich hilfreich für den weiteren Ansatz.

  • Nun, also...

    im Grunde wollte ich tatsächlich "nur" die globale Variable loswerden (schlechter Programmierstil etc...)

    Was der Code im Kern machen soll, hab ich (zumindest glaube ich das) in #30 beschrieben.

    Knackpunkt bei der Geschichte ist, dass zwei (aus bestimmten Daten speziell "zusammengebaute") Messages in recht genau einzuhaltenden Intervallen zu senden sind (damit werden Motoren gesteuert). "Gleichzeitig" sind aber ebensolche Messages zu empfangen, die in ähnlichen Zeitintervallen über den CanBus laufen. Hier sind die Daten enthalten, die mich interessieren. Soweit ich mich mit dem CanBus auskenne, sind die Daten, egal ob ein- oder ausgehend, sowieso rein sequentiell, d.h. da ist nichts wirklich parallel und wenn ich es nicht zu schlecht programmiere, dürfte das ohne Multithreading trotzdem ganz gut funktionieren.

    Und im ersten Test hat ja auch funktioniert. Mittlerweile, also quasi im Verlauf dieses Threads hab ich doch einiges umgeschrieben und ich glaube, jetzt auch eine gut brauchbare Lösung gefunden zu haben. Durchaus in Anlehnung an den Beispielcode in #20 von Tell (vielen Dank!) und zwar direkt um eine wichtige Funktionalität ergänzt, denn ich benötige bei den gesendeten Messages einen "zyklischen Zähler", der die ausgehenden Messages mit einer fortlaufenden Nummer versieht, die aber wieder auf Null gesetzt wird, sobald ein festgelegter Maximalwert erreicht ist. Und zwar über die unterschiedlichen Messages hinweg.

    Diese verschiedenen Messages sind jetzt genau das, was ich unter unter machwas() behandeln möchte.


    Somit wäre grob umrissen der Kern des Codes :

    Code
    1. while Abbruchbedingung == False:
    2. 1) machwas_1() => Prüfen, ob es Zeit ist, Message1 zu senden. Falls ja, dann Message1 verfassen und senden
    3. 2) machwas_2() => Prüfen, ob es Zeit ist, Message2 zu senden. Falls ja, dann Message2 verfassen und senden
    4. 3) machwasanderes() => soviel wie möglich eingehende Messages sammeln und in einer Liste zwischenspeichern

    Schön ist, dass das nur für eine bestimmte Zeit (weniger als eine Minute) gemacht werden muss und danach ausreichend Zeit ist, die gesammelten Daten auszuwerten und zu speichern, bevor es wieder von vorne losgeht.

    Ich denke, es ist eine guteI dee, hier mit Objekten zu arbeiten. Dazu ein kleiner Beispielcode, mit dem ich das ausprobiert habe, was ich im Kern brauche. Und es klappt. Damit wird meine Code insgesamt auf jeden Fall übersichtlicher und so allmählich dämmerts mir, was Objektorientierung kann. Learning by doing halt.

    Die beiden Instanzen one, two wären dann also meine Sende-Messages. Das Lesen eingehender Daten ist da jetzt nicht explizit berücksichtigt. Das wäre ein separates Objekt. Jede Instanz hat hier ihren eigenen "Timer" und es gibt einen (!) gemeinsamen Zähler (Macher.counts) für beide Instanzen. Damit kann ich jetzt arbeiten, ich habe das Werkzeug, das ich brauche. Tagesziel ist erstmal erreicht. Ich bin ein bissl stolz auf mich aber gerne könnt ihr das jetzt auseinandernehmen (bitte seid gnädig: bis gestern wußte ich grad mal, wie man "Objekt" schreibt ;))


    Grüße,

    Raspbuino

  • Öhm,...sieht zwar schöner aus, aber self.counts zählt hier separat für jede erzeugte Instanz separat. Genau das soll ja nicht sein: ein einziger Zähler soll immer dann hochgezählt (oder bei Erreichen des Limits (hier 12) zurückgestellt) werden, sobald eine der Instanzen zur Aktion kommt.


    Ein anderes Detail ist mir aufgefallen. Da hätte ich ne Frage dazu.

    Vorher wurde ein Teil der Parameter beim Erzeugen der Instanzen übergeben (hier: die Intervalle) und ein weiterer Teil beim Aufrufen der Methoden (hier: die "Namen"). In Deinem optimierten Code nun werden beide Parameter schon beim Erzeugen der Instanzen übergeben.

    Kann ich das so sehen, dass es schlichtweg sauberer und effektiver ist, Parameter, die sich nicht ändern, immer beim Erzeugen der Instanz zu übergeben? Oder gibts noch andere Gründe? Danke jedenfalls für den Hinweis, das hat mir ein Stück weit die Augen geöffnet, wenn ichs richtig verstanden habe.

    Grüße,

    Raspbuino

  • So...

    ich muss sagen, nachdem ich mich jetzt am Wochenende, motiviert durch diesen Thread, mal so richtig reingekniet und versucht hab, in die objektorientierte Programmierung einzusteigen, glaube ich sagen zu können, dass mit der Einstieg jetzt gelungen ist 8)

    Ich bin dabei, meinen bisherigen Code komplett umzukrempeln und Stück für Stück funktioniert es sogar und die Sache ist insgesamt schon deeeutlich übersichtlicher geworden. Ich bin zwar noch lange nicht fertig aber das ist jetzt in erster Linie Fleißarbeit.


    Ganz nebenbei bin ich dabei meine globale Variable losgeworden :bravo2:


    Den Thread hier möchte ich somit als sauber abgeschlossen betrachten.

    Herzlichen Dank ans Forum für die vielen Beiträge, die mir die letzten Tage eine echt steile Lernkurve beschert haben.

    :danke_ATDE:

    Grüße,

    Raspbuino