User-Vorstellung mit Problem(en)

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Nokilausi
User
Beiträge: 1
Registriert: Samstag 7. Januar 2017, 09:44

Moin,

ich hätte da gerne mal ein Problem!

Ich, Feuerfurzer (der Name ist Programm :wink: ), Maschinenbautechniker mit div. Elektrokenntnissen, würde mich als neugierigen Neuling in Sachen (Python)-Programmierung bezeichnen. Nachdem ich Ende 2016 entdeckt habe, dass es da einen kleinen Taschencomputer zum spielen (und mehr) gibt, bin ich seither in diversen Foren unterwegs und lese fleißig mit, was mit dem Teil so geht. Diverse Fachliteratur eingeschlossen! Lange Rede, ich bin leicht infiziert, aber offnsichtlich zu alt oder zu resistent um das alles (programmiertechnisch) zu verarbeiten, was ich da so gesehen und gelesen habe. Muss auch gar nicht sein denke ich, man soll ja langsam anfangen, oder ist das schon der falsche Ansatz!? Dann lasse ich es lieber gleich bleiben, aber noch bin ich hoch motiviert!

Hab' mir also ein Projekt ausgedacht, welches ich nun partout umsetzen möchte und nun, man ahnt es schon, kläglich scheitere bzw. schon fest hänge!

Zum Thema:

Es soll so etwas wie ein "Sortierer" sein!? Teile in Karton zählen, Karton autom. wechseln, neu einzählen, vorherigen (vollen) Karton manuell entnehmen, neuen Karton bereitstellen, quittieren, neuen (vollen) Karton entnehmen, neuen bereitstellen, quittieren, etc. ...

Das ist erst einmal das "Gerüst", die Feinheiten hebe ich mir evtl. für später auf, will auch nicht mit zu viel Prosa langweilen!

Hier einmal mein Anfang und wenn ich Anfang sage, meine ich Anfang :wink: :

Code: Alles auswählen

#Sortierer.py

from RPi import GPIO
from time import sleep

GPIO.setmode(GPIO.BCM)

GPIO.setup(18, GPIO.IN)     #Lichtschranke "Kartusche vorhanden"
GPIO.setup(17, GPIO.OUT)    #Relais Karton "Umschalt Karton 1/2"
GPIO.setup(27, GPIO.OUT)    #Leuchte "Karton fast voll"
GPIO.setup(22, GPIO.OUT)    #Leuchte "Karton 1 voll"
GPIO.setup(6, GPIO.OUT)     #Leuchte "Karton 2 voll"
GPIO.setup(5, GPIO.IN)      #Taster "Quittieren"

GPIO.output(17, 0)          #Grundstellung = Karton 1 wird befuellt
GPIO.output(27, 0)          #Leuchte aus
GPIO.output(22, 0)          #Leuchte aus
GPIO.output(6, 0)           #Leuchte aus

count_Kart1=0
count_Kart2=0

while count_Kart1 == 0:
#Karton 1 wird befuellt
        while count_Kart1 <= 7:
                GPIO.output(17, 0)
                GPIO.output(27, 0)
                if GPIO.input(18):
                    count_Kart1 += 1
                    print(count_Kart1)
                    sleep(0.05)
                    while GPIO.input(18):
                        sleep(0.05)
                        GPIO.output(17,0)

#Warnung "Karton 1 fast voll"
        while count_Kart1 <= 9:
                GPIO.output(17, 0)
                GPIO.output(27, 1)
                if GPIO.input(18):
                    count_Kart1 += 1
                    print(count_Kart1)
                    sleep(0.05)
                    while GPIO.input(18):
                        sleep(0.05)
                        GPIO.output(17,0)
#Karton 2 wird befuellt
        while count_Kart2 <= 7:
                GPIO.output(17, 1)
                GPIO.output(22, 1)
                GPIO.output(27, 0)
                if GPIO.input(18):
                    count_Kart2 += 1
                    print(count_Kart2)
                    sleep(0.05)
                    while GPIO.input(18):
                        sleep(0.05)
                        GPIO.output(17,1)

#Warnung "Karton 2 fast voll"
        while count_Kart2 <= 9:
                GPIO.output(17, 1)
                GPIO.output(27, 1)
                if GPIO.input(18):
                    count_Kart2 += 1
                    print(count_Kart2)
                    sleep(0.05)
                    while GPIO.input(18):
                            sleep(0.05)
                            GPIO.output(17,1)
                        

#Stoerung: "Karton 1 und 2 voll"
        while count_Kart2 == 10:
                GPIO.output(17, 0)
                GPIO.output(22, 1)
                GPIO.output(27, 1)
                GPIO.output(6, 1)
               
GPIO.cleanup()
So weit, so gut, jetzt sollte es aber wieder von vorne losgehen, bzw. bevor der zweite Karton befüllt ist, sollte der erste entnommen worden sein und eine (Quittier-)Taste gedrückt werden, um count_Kart1 wieder auf 0 setzen. Hier hört es dann schon auf bzw. stehe vielleicht auch nur auf dem Schlauch :K !? Ich habe schon mit "if Taster gedrückt, dann Zähler Null" experimentiert, aber nix da, tut nicht!

Sollte sich jemand finden, der nicht mit "rtfm" daherkommt, sondern gewillt ist mir, mit dem ein oder anderen Tipp, zur Seite zu stehen, so würde mich das wieder sehr aufbauen!

Natürlich interessieren mich fragen wie:

- Würdet ihr genau so anfangen?
- Ist die Struktur (sofern man eine erkennen kann!) ok?
- Wie könnte es weiter gehen?
- etc.

So, jetzt lass ich mich mal überraschen :idea: !

Gruß
FF
BlackJack

@Nokilausi: Drei Sachen die mir am stärkesten aufgefallen sind:

1. Magische Zahlen: Wenn man den Code unten lesen/verstehen möchte, muss man immer wieder oben nachsehen was die Zahlen bedeuten. Da definiert man sich am Anfang am besten Konstanten mit guten Namen für die Pins. Dann braucht man auch weniger Kommentare an einigen Stellen.

Denn ``GPIO.output(27, 0)`` ist deutlich nichtssagender als ``GPIO.output(ALMOST_FULL_LED_PIN, GPIO.LOW)``. Oder auch `False` statt der 0, damit klarer ist das man da nicht irgendwelche Zahlen übergeben kann, sondern das das eine Ja/Nein-Information ist.

Wenn man den Pinnummern Namen gibt, kann man den Wert auch viel einfacher ändern, weil man ihn dann nicht überall im Programm suchen muss, und an jeder Stelle prüfen muss ob es sich bei der Zahl um die Pinnummer handelt oder eine andere Zahl mit zufällig dem gleichen Wert. Man weisst einfach an einer Stelle der Konstanten einen neuen Wert zu und fertig.

Ebenfalls magische Zahlen sind die Füllstandsgrenzen und die Obergrenze. Die sogar als 9 drin steht für 10 Gegenstände.

2. Kopierter Quelltext: Da ist vier mal fast identischer Quelltext. Programmierer sind Faul. Wenn man etwas kopiert und geringfügig ändert, macht man in der Regel etwas falsch. In der Regel möchte man dann die veränderten Werte aus dem Code heraus ziehen und eine Schleife über diese Werte um den Code setzen, oder den Code in eine Funktion auslagern und dann mit den Werten aufrufen, oder eine Mischung aus beidem.

Code und Daten sollte man unter anderem auch deswegen nicht wiederholen, weil Änderungen am Programm dann fehleranfällig sind. Wenn man den Code ändert, muss man immer daran denken alle ”Kopien” zu ändern, und man muss auch aufpassen, das man alle Kopien auf die gleiche Weise verändert. Sonst driften die Varianten irgendwann auseinander und wenn es dann Fehler gibt, sind die nicht einfach zu finden, weil sie beispielsweise immer nur bei einem Karton vorkommen wenn jeder Karton seinen eigenen Code hat.

3. Daten als Code modelliert: Du nummerierst die Namen für die Anzahl der Gegenstände in einem Karton und hast Code für jeden Karton, obwohl der im Grunde gleich ist. Namen durchnummerieren ist ein „code smell“. Fast immer möchte man sich an der Stelle entweder passendere Namen ausdenken, oder aber man will eigentlich gar keine Einzelnamen, sondern eine Datenstruktur. Meistens eine Liste. Wie auch in diesem Fall.

Also statt eigenem Namen für jeden Karton, eine Liste mit Objekten die alles repräsentieren was zu einem Karton, beziehungsweise ja eigentlich zu dem Platz an dem der Karton steht, gehört. Also Füllstand und Pin für die Lampe zum Beispiel. Dann braucht man sich in einer Variablen nur noch merken welcher der beiden Plätze gerade aktiv ist, und kann eine Funktion schreiben die allgemein einen Platz abarbeitet.

Eigentlich wäre das schon ein Fall für Klassen. Du kannst das aber vielleicht auch erst einmal mit einem Wörterbuch pro Platz lösen.

----

Wenn man schon dabei ist Code in Funktionen auszulagern, kann man auch gleich das Hauptprogramm in eine Funktion stecken. Und generell sind Funktionen eine tolle Sache.

In der jeweils innersten ``while``-Schleife wird Ausgang 17 auf den Wert geschaltet auf den er vor der Schleife schon mal geschaltet wurde. Das heisst diese Anweisung sollte nichts verändern und damit eigentlich wegfallen können‽

Die letzte Schleife ist eine Endlosschleife die man nur mit Strg-C noch ohne abschiessen des Prozesses beenden kann. Und in beiden Fällen wird das `cleanup()` nicht aufgerufen. Das sollte man in einen ``finally``-Zweig stecken, zu einem ``try``-Block der alles umfasst was die GPIO-Konfiguration ändert.

----

Zu den Fragen: Ich denke ich würde nicht so anfangen. Das sieht so ein bisschen so aus als wäre es linear runterprogrammiert mit der Annahme das am Anfang beide Kartons leer sind und dann nach und nach die Gegenstände eingeworfen werden. Die Codestruktur landet dann bei einem Zustandsautomaten der in solchem Code ”versteckt” ist. Ich hätte das eher als Ereignisschleife geschrieben, die auf die entsprechenden Ereignisse reagiert. Insbesondere weil man sich bei einer objektorientierten Lösung dann auch über die Flanken informieren lassen möchte statt selber dauernd die Pins zu prüfen, denn…

…das „busy waiting“ auf die Lichtschranke und später dann auch auf den Quittierschalter ist nicht schön oder effizient. Es gibt Funktionen um auf eine Flanke zu warten, das muss man nicht selbst ausprogrammieren.
Antworten