Posts by 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

    Ö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

    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

    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

    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

    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

    Danke für den Tipp! Könnte durchaus für mich interessant sein, weil es geht dabei durchaus darum, so manche Dinge mehr oder weniger parallel zu tun. Aber jetzt treib ich das erstmal so voran, bis es getestet werden kann und dann schau mer mal, wo ggf die Grenzen dabei liegen, wie ich es mache. Vielleicht ist es auch gar kein Problem. Aber mein Ziel ist nicht allein, die Geschichte "irgendwie" zum Laufen zu kriegen, sondern durchaus mit dem Anspruch, einen halbwegs gut wartbaren Code zu schreiben, der vielleicht auf seine Art auch, nun ja, schön ist. Und deswegen nehm ich den Hinweis weiter oben hinsichtlich Konventionen bei Namensgebung von Variablen ernst.

    Grüße,

    Raspbuino

    Hallo nochmal,

    so...Feierabend und schon wieder vor der Kiste... :)

    Quote

    Na ja, das ist halt so, wenn man mit Funktionen programmiert. Das ist schon die richtige "Eleganz"Das

    Das nehm ich dann gerne mal so mit! Ich denke, so mach ich das erstmal. Allerdings ist es so, dass ich mehr als eine Aktion hab, die zyklisch durchgeführt wird (aktuell zwei und zwar mit unterschiedlichen Frequenzen). Das heißt, ich muss zwei Variablen allein schon deswegen an die Funktion übergeben.

    Das mit yield bzw einer Generatorfunktion kannte ich bisher gar nicht. Hab das in meinem schlauen Buch grad nachgelesen und den Code ausprobiert. Sehr interessant - wär ich ansonsten sicherlich nicht so ohne Weiteres drübergestolpert.

    Das mit dem i-Tüpfelchen, nämlich einer Klasse, würde mich trotzdem interessieren. Vielleicht krieg ichs irgendwann noch gebacken.


    Vielen Dank soweit für die Hilfe! :thumbup:


    BTW: ( Linus ) die Variablennamen überarbeite ich auch nochmal. Bin ich eh grad dabei; hab mir letzte Woche im Buchladen "Clean Code" gegönnt:

    "details matter"!


    Grüße,

    Raspbuino

    Hallo zusammen,

    auf die Schnelle erstmal ein Dankeschön für die ersten Antworten.

    Werd ich mir daheim dann genauer ansehen, vor allem den Ansatz mit der Coroutine von noisefloor find ich sehr interessant.

    Im Moment bin ich Brötchenverdienen und da ist leider nix mit Progrämmchen ausprobieren (so gerne ich das jetzt tun würde).


    Tell ,

    das mit dem Hin- und Herschieben der Variable war mir schon klar. Ich wollte das halt gerne vermeiden, weil ich nach einer eleganteren Methode Ausschau halten möchte. Gerne will ich auf dem Weg dorthin etwas lernen aber selbst mit der ganzen Literatur, die ich dazu habe (und auch lese...) fehlt mir da scheinbar der richtige Funke, um den Einstieg zu finden. Wenn die bessere Lösung also ein Klasse wäre...wie könnte sowas aussehen?


    Grüße,

    Raspbuino

    Hallo zusammen,

    in einem kleinen Steuerprogramm muss ich mit bestimmter Frequenz (z.B. 10 oder 50Hz) immer wieder Daten senden.

    Das wird in einer Funktion gemacht. Im Grunde krieg ich das hin, indem ich die Zeitdifferenz zwischen time.time() und einem Initialwert (STARTZEIT) prüfe.

    Zur Verdeutlichung hab ich nen kleinen Code unten angefügt, der den Kern meines Anliegens beinhaltet.

    Das Problem ist, dass beim Aufruf der Funktion der Wert von STARTZEIT irgendwoher kommen muss und ich das nur hinbekomme, wenn ich den Wert in einer globalen Variable führe. Zwischen zwei Aufrufen der Funktion muss der Wert halt irgendwo gespeichert bleiben. In sofern funktioniert es nicht, dass der Wert erst in der Funktion jedesmal neu initialisiert wird.

    Meine Frage ist: gibt es einen besseren Weg, dies zu tun? Globale Variablen soll man ja möglichst vermeiden und ich möchte gerne herausfinden, wie das ein Profi machen würde.

    Natürlich kann man den Wert (STARTZEIT) auch immer wieder über lokale Variablen an die Funktion übergeben und den (ggf) neuen Wert über return(STARTZEIT) rausholen. Allerdings sagt mir mein Bauchgefühl, das geht bestimmt eleganter, nur reichen meine Python-Skills (noch) nicht so weit. Über diesen Punkt komme ich im Moment irgendwie nicht hinweg.

    Das mit Objektorientierung hab ich noch nicht so ganz geschnallt aber wenn das der bessere Weg ist, wäre ich für ein paar Hinweise dankbar...

    Ich hoffe, ich konnte mein Problem verständlich beschreiben ::)


    So...wollte den Thread hier noch kurz mit einem kleinen Feedback beenden.
    Zunächst nochmal vielen Dank für die Diskussion und Hilfe!
    Es hat mir geholfen, etwas klarer zu sehen und so wußte ich auch etwas genauer, wonach ich suchen muss.
    Mein Webseiten-Paket bei meinem Hoster erlaubt nur FTP (Netbeat, Level 1).
    Da ich im Grunde auch nur ein paar einfache Daten (ja, langweilig, Wetterdaten etc.) und Diagramme ablegen möchte, sehe ich die Sache mit den unverschlüsselt übertragenen Daten unkritisch. Immerhin weiß ich jetzt, dass mir (ohne ein anderes Paket einzukaufen) nur FTP bleibt. Dafür gehts jetzt auch recht einfach, und zwar mit dem Modul "ftplib"

    Code
    1. import ftplib


    :thumbs1:

    :D :bravo2:
    BINGO! Hatte wohl noch zu wenig Kaffee...
    Hab den wegdererkenntnis mal auch an dieser Stelle (weiter unten im Code unter "DEF...") hinzugefügt:


    Der Graph wurde generiert, nachdem ich das Script dann so aufgerufen hatte:

    Code
    1. pi@raspberrypi:~ $ python3 /home/pi/SPRRD/Graph_cron_alle2.py
    2. pi@raspberrypi:~ $


    (ist ne Kopie von der ursprünglichen Datei, die nur nen Teil des Codes enthält, da ich nicht gleich alle relevanten Stellen ändern wollte, um das auszuprobieren. Deswegen die 2 am Ende...).
    So...da habt ihr mir ja echt heut was beigebracht.
    :danke_ATDE: :danke_ATDE: :danke_ATDE: ...vor allem für die Geduld!
    Sobald ich auch den cron job hinbekommen habe (muss vorher noch ein bissl den kompletten Code ergänzen), kann ich hoffentlich die finale Erfolgsmeldung bringen. Schau mer mal. Bin guter Dinge jetzt.
    Automatisch zusammengefügt:[hr]
    Subber!
    Crontab funktioniert jetzt:


    10 * * * * pi python3 /home/pi/SPRRD/Graph_cron_alle.py
    11 * * * * pi python3 /home/pi/SPRRD/ftp_upload_graphen.py


    Es lag nur an den absoluten Pfaden, die ich nicht überall berücksichtigt hatte.


    :thumbs1:

    Okay, der Reihe nach.
    Die Pfade und den Code hab ich oben veröffentlicht Ich machs nochmal hier an einer Stelle möglichst kompakt, so das alle beisammen ist:
    1.) das auszuführende Python3-Script liegt hier:
    /home/pi/SPRRD/Graph_cron_alle.py
    2.) In folgendem Verzeichnis sollen die Graphen abgelegt werden:
    /home/pi/SPRRD/UPLOAD
    3.)Der auszuführende Code in "Graph_cron_alle.py" sieht so aus:


    Da hab ich jetzt den relativen Pfad gegen einen absoluten eingetauscht:


    Das klappt nach Start direkt aus Geany genauso wie mit dem relativen Pfad.
    Also hab ich jetzt probiert, das Script mal so im LXTerminal mit absolutem Pfad zu starten.
    Klappt leider nicht, gibt ne Fehlermeldung:

    Code
    1. pi@raspberrypi:~ $ python3 /home/pi/SPRRD/Graph_cron_alle.py
    2. Traceback (most recent call last):
    3.  File "/home/pi/SPRRD/Graph_cron_alle.py", line 26, in <module>
    4.    "TEXTALIGN:left")
    5. rrdtool.OperationalError: opening 'RRD_tempout.rrd': No such file or directory
    6. pi@raspberrypi:~ $


    Das sind doch jetzt aber an den entscheidenden Stellen absolute Pfade, oder nicht??
    Aber die Fehlermeldung erinnert mich direkt an die Probleme, die ich hatte, als ich noch nicht die rrdtool-Variante für Python3 hatte...
    :s

    Tja..ich steh wohl aufm Schlauch.
    Die Sache mit den Verzeichnissen scheint ja nicht ganz so einfach zu sein
    Nun ja, ich hoffe, ihr habt noch ein wenig Geduld mit mir.
    Die erste auszuführende Datei hat am Anfang folgenden Inhalt (der Rest wiederholt sich im Grunde).

    Code
    1. #!/usr/bin/python3
    2. import rrdtool
    3. gbreit = "600"
    4. ghoch = "300"
    5. Zeitstart1 = "-3h"
    6. ....
    7. rrdtool.graph("UPLOAD/graph_03h_tempout.gif", "--start",Zeitstart1,...
    8.   ....)


    Das auszuführende Script (Graph_cron_alle.py) liegt im Verzeichnis [/home/pi/SPRRD]
    Da gibts dann einen weiteren Ordner "UPLOAD", in den die Graphen reingeschrieben werden, s. Code oben.
    In sofern hab ich im Code dann ja auch nur den relativen Pfad angegeben und das funktioniert auch.
    Es müßte aus crontab heraus doch möglich sein, ein Script an beliebiger Stelle mit python3 zu starten.
    Sorry, ich kann leider noch nicht erkennen, was da jetzt falsch ist. Aber irgendwas muss schließlich falsch sein.
    Noch was Interessantes dazu:
    Hab grad mal ausprobiert, ob ich das Script in Ordner SPRRD direkt vom home-Verzeichnis aus gestartet bekomme. Sieht ganz danach aus, dass zwar versucht wird, das Script zu starten, es wird also gefunden, aber offensichtlich wird versucht, mit Python2 zu starten, denn in diesem Fall bekomme ich ne Fehlermeldung, dass im Code was nicht stimmt.

    Code
    1. pi@raspberrypi:~/SPRRD $ cd
    2. pi@raspberrypi:~ $ cd SPRRD
    3. pi@raspberrypi:~/SPRRD $ python3 Graph_cron_alle.py
    4. pi@raspberrypi:~/SPRRD $ cd
    5. pi@raspberrypi:~ $ python3 SPRRD/Graph_cron_alle.py
    6. Traceback (most recent call last):
    7.  File "SPRRD/Graph_cron_alle.py", line 26, in <module>
    8.    "TEXTALIGN:left")
    9. rrdtool.OperationalError: opening 'RRD_tempout.rrd': No such file or directory


    Habs noch nicht hingekriegt. Krieg ich vielleicht noch nen Tip?
    Oder muss das auszuführende Python-3- Script woanders liegen, damit es von crontab gestartet werden kann?
    Die Graphen hab ich auch in keinem anderen Verzeichnis finden können (zumindest nicht da, wo ich denke, wo sie potentiell liegen könnten, wenn ich was mit den Pfadangaben falsch gemacht hatte).


    Das mit CD /tmp hab ich mal ausprobiert. Da kommt die selbe Fehlermeldung wie oben. Wird also wahrscheinlich auch versucht, in Python2 zu starten.

    Code
    1. pi@raspberrypi:~ $ cd /tmp
    2. pi@raspberrypi:/tmp $ python3 /home/pi/SPRRD/Graph_cron_alle.py
    3. Traceback (most recent call last):
    4.  File "/home/pi/SPRRD/Graph_cron_alle.py", line 26, in <module>
    5.    "TEXTALIGN:left")
    6. rrdtool.OperationalError: opening 'RRD_tempout.rrd': No such file or directory
    7. pi@raspberrypi:/tmp $


    Kann man dem Raspi irgendwo, ähnlich wie das bei Geany geht, defaultmäßig Python3 zu interpretieren und nicht Python2? Dann müßte das doch auch gehen....
    :denker:

    Hallo Andreas,
    vielen Dank für die schnelle Antwort!
    Okay - ich habs mal so jetzt versucht (die Minuten etwas angepasst, weil ich ja keine Stunde warten wollte):

    Code
    1. 37 * * * * /home/pi/SPRRD/python3 Graph_cron_alle.py
    2. 38 * * * * /home/pi/SPRRD/python3 ftp_upload_graphen.py


    Hat leider noch nicht funktioniert...aber ich hoffe, ich bin ganz nah dran :)
    Bei der Pfadangabe scheint jetzt immer noch was nicht zu stimmen.
    Muss vor das "/home..." noch was anderes vornedran?

    Hallo zusammen,
    die Sache mit den Cronjobs hört sich ja immer einfach an. Scheint in Wirklichkeit aber doch etwas tricky zu sein...
    Ich krieg zwei Python3-Scripte nicht automatisch gestartet.
    Wär super, wenn mir da mal jemand etwas auf die Sprünge helfen könnte... :)


    Hab hier zwei Python3-Progrämmchen.
    Diese sind mit [chmod +c] lauffähig gemacht und in der Kommandozeile oder direkt aus Geany heraus laufen die auch und machen was sie sollen.
    Dabei hab ich Geany extra auf default Python 3 gestellt und in der Kommandozeile mach ichs einfach so, dass ich zuvor ins entsprechende Verzeichnis wechsle und dort das jeweilige Kommando für die Programme eingeb:

    Code
    1. pi@raspberrypi:~ $ cd SPRRD
    2. pi@raspberrypi:~/SPRRD $ python3 Graph_cron_alle.py
    3. pi@raspberrypi:~/SPRRD $ python3 ftp_upload_graphen.py


    für die crontab-Datei Verzeichnis [/etc/crontb] gelingt es mir jedoch nicht, die richtige Syntax zu finden.
    Es passiert einfach nix. Im Moment hab ich dort folgende Einträge, wobei die letzten beiden Zeilen von mir sind, alles andere war scheinbar schon drin:


    Hatte auch schon probiert, das "python3" hinter das letzte Slash zu stellen. Ohne Erfolg.
    Kennt sich da jemand aus? Was ist das Geheimnis??
    ;)

    Danke für die bisherigen Antworten!
    Nach ner halben Ewigkeit hab ich heut mal wieder nach meiner brach liegenden Homepage bei meinem Hoster geschaut und musste dafür erst die Zugangsdaten wiederfinden. Hab sie gefunden.
    Soweit ich das erkennen konnte, ist da aber nach wie vor nur FTP möglich.
    Das hat dann (vom PC mit Filezilla) testweise auch direkt funktioniert.
    Ich hoffe, am Wochenende kann ich wenigstens FTP ausprobieren. Ich geb Feedback.
    Warum hat ein Tag eigentlich nur 24h :huh:
    N8

    Hallo Tell,
    boah - vielen Dank für die schnelle Antwort und den konkreten Tip mit SCP sowie die anderen Klarstellungen. Das mit SCP hatte ich so noch nicht im Fokus. Sieht durchaus vielversprechend aus und passt scheinbar gut mit dem zusammen, was mir so im Kopf rumgeht. Ich werd mir das genauer ansehen...(leider nicht mehr heute).
    :thumbs1: