Schleife für bytes bzw. Hilfe für Neuling...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
dArkjON
User
Beiträge: 3
Registriert: Donnerstag 20. März 2014, 01:46

Moin moin,
bin noch recht neu in Python und bis jetzt klappt auch alles so wie ich will :)
Nur ein Problem habe ich mit den bytes Daten, diese kann ich einfach nicht auslesen bzw. verwenden.

Problem:
Am Com2 Port ist ein Zähler angeschlossen welcher mir Werte im folgenden Format ausgibt :

b'\x00' #Ist = 1

b'\x00\x00\x00' #Ist = 3

b'\x00\x00\x00\x00\x00\x00\x00' #Ist = 7

\x00 ist ja eigentlich eine "0" bzw ein "{SPACE}" und da hänge ich jetzt.
Habe mir jetzt eine Lösung gebastelt, bin dennoch damit nicht zufrieden da der Code einfach Müll ist...
Vielleicht mag mir ja jemand eine kleine Nachhilfe-Stunde für sauberen Code geben :)

Code: Alles auswählen

import serial
import time

ser = serial.Serial('COM2', 9600, timeout=1)

while 1:
 try:
         coiner = ser.readline()
         if coiner == bytes([0]):
             print (1)
         elif coiner == bytes([0,0]):
             print (2)
         elif coiner == bytes([0,0,0]):
             print (3)
         elif coiner == bytes([0,0,0,0]):
             print (4)
         elif coiner == bytes([0,0,0,0,0]):
             print (5)
         elif coiner == bytes([0,0,0,0,0,0]):
             print (6)
         elif coiner == bytes([0,0,0,0,0,0,0]):
             print (7)
         elif coiner == bytes([0,0,0,0,0,0,0,0]):
             print (8)
         elif coiner == bytes([0,0,0,0,0,0,0,0,0]):
             print (9)
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0]):
             print (10)
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0]):
             print (11)
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0]):
             print (12)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (13)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (14)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (15)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (16)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (17)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (18)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (19)   
         elif coiner == bytes([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]):
             print (20)   
                     
         time.sleep(1)
 except ser.SerialTimeoutException:
  print('Keine Daten gefunden')
  time.sleep(1)
BlackJack

@dArkjON: Funktioniert das überhaupt? Bei einem `readline()` würde ich ja nach den Nullbytes (die übrigens keine Leerzeichen/Space sind sondern Nullbytes) noch ein Zeilenendezeichen erwarten.

Schmeiss mal die ganzen ``if``/``elif``\s weg und lass Dir stattdessen zwei Informationen ausgeben: Die Länge der empfangenen Daten, und wieviele Bytes in den empfangenen Daten den Wert 0 haben. Sofern das Datenformat denn tatsächlich so aussieht, sollte Dir dabei etwas auffallen.

Sonstiges:

Die Einrückung entspricht nicht der Konvention von vier Leerzeichen pro Ebene.

Eine Endlosschleife ist etwas deutlicher als ``while True:``.

Die Bytes mit denen verglichen wird hätte man auch als Bytes-Literal schreiben können: b'\0', b'\0\0', b'\0\0\0', usw. beziehungsweise kann man auch ``*`` verwenden:

Code: Alles auswählen

In [6]: b'\0' * 10
Out[6]: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Damit hätte man die vielen ``if``/``elif`` durch eine Schleife ersetzen können.

Zwischen Funktionsname und öffnende Klammer der Argumente gehört kein Leerzeichen.

Das `time.sleep(1)` kann man geschickter im Programmablauf platzieren so dass man es nur *einmal* schreiben muss.
dArkjON
User
Beiträge: 3
Registriert: Donnerstag 20. März 2014, 01:46

Hallo BlackJack,
@dArkjON: Funktioniert das überhaupt? Bei einem `readline()` würde ich ja nach den Nullbytes (die übrigens keine Leerzeichen/Space sind sondern Nullbytes) noch ein Zeilenendezeichen erwarten.
Ja es klappt derzeit alles gut, ich habe im readline gelesen das er entweder auf ein Ende wartet, oder nach dem Timeout die Daten rauswirft...

Vielen Dank für deine Ausführliche Antwort. Hab grad die ersten Denkanstöße umgebaut und hoffe das es so klappt wie ich es mir Vorstelle :)
Würde mich aber freuen wenn später nochmals ein Blick auf den neuen Code wirst ob es da noch "Fehler" gibt oder er nun sauber ist.
dArkjON
User
Beiträge: 3
Registriert: Donnerstag 20. März 2014, 01:46

Danke nochmals für die Denkanstöße ! Hier der "neue" Code :

Code: Alles auswählen

import serial

ser = serial.Serial('COM2', 9600, timeout=1)
such = bytes([0])
ohne = 0

while 1:
    coiner = ser.readline()
    zahl = coiner.count(such)
    if zahl != ohne:
        print (zahl)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo dArkjON,

man könnte noch ein paar Kleinigkeiten an deinem Code verbessern:

Da es in Python keine Konstanten gibt, werden diese durch besondere Namen gekennzeichnet. Überlicherweise wird der ganze Namen in Großbuchstaben geschrieben, also SUCH oder OHNE. Damit kann jeder sofort erkennen, dass dieser Wert besser nicht angefasst werden sollte. Vielleicht wirst du mal einen Blick in PEP 8, da sind einige recht hilfreiche Dinge aufgelistet.

In Python gibt es Wahrheitswerte. Statt ``while 1`` solltest du ``while True`` schreiben. Dafür kannst du den Test ``if zahl != ohne`` kürzer schreiben: ``if zahl``. Das ohne ist hier überflüssig, da du den Wert von "ohne" wohl nie ändern wirst. Falls doch, dann wäre zumindest der Name sehr verwirrend und du müsstest diesen ändern.

Mittels with-Statement und contextlib kannst du dafür sorgen, dass der Port auch immer wieder ordentlich geschlossen wird. So kannst du es nicht vergessen und bist auch im Fehlerfall abgesichert. Zumindest in den Fehlerfällen, in denen der Interpreter eine Chance zur Fehlerbehandlung hat.

Nach Möglichkeit solltest du keinen Code auf modulebene stehen haben, dann kannst du das Programm nur schlecht wiederverwenden. Denn beim ersten Import wird der gesamte Code auf dieser Ebene ausgeführt, was automatisch zum Öffnen eines Ports führt. Was wohl in den seltensten Fällen erwünscht ist. Auf Modulebene sollten daher nur Importe und Definitionen stehen, der Rest sollte in Funktionen verpackt werden. Damit Code nur ausgeführt wird, wenn die Datei direkt gestartet wird, gibt es das ``if __name__ == "__main__"``-Idiom.

Ich habe deinen Code mal entsprechen der Kritik angepasst (ungetestet):

Code: Alles auswählen

import contextlib
import serial

SUCH = bytes([0])

def main():
    with contextlib.closing(serial.Serial('COM2', 9600, timeout=1)) as ser:
        while True:
            coiner = ser.readline()
            zahl = coiner.count(SUCH)
            if zahl:
                print(zahl)

if __name__ == "__main__":
    main()
Ich kenne nicht die Spezifikation der Schnittstelle, daher einfach mal so der Hinweis: ``coiner.count(such)`` liefert die Anzahl aller Vorkommen von "such" in "coiner" zurück. Das heißt aber nicht, dass nicht auch andere Werte in "coiner" enthalten sein können. Vielleicht bietet sich hier noch ein weiterer Test an. Es würde genügen, wenn du die Länge der Daten gegen die Anzahl der gefundenen Nullen abgleichst.
Das Leben ist wie ein Tennisball.
Antworten