Pyserial und lange Wartezeit

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
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Hallo zusammen.

besten dank nochmals für die Installationshilfe.

Nun Mein Programm funktioniert soweit, nur habe ich ein Kleines Problem, die Antwort lässt etwa 2 Sekunden auf sich warten. Wenn ich die gleiche Übetragung mittels dem Original Konfigurationsprogramm tätige ist sie Unverzüglich wieder da?

Was habe ich falsch eingegeben?


besten Dank für jeden Tipp im Voraus

Code: Alles auswählen

# -*- coding: cp1252 -*-
## API calls ##
import serial
import io
import time## Creates an object, Spectrometer ##


class SEW():

# Parameters
    
    baud = 9600
    byte = 8 # 17 bytes ## potential problem here##
    port=3

    def init( self, port ):
        self.port = port
        self.isOpen = False
        return

## Open and initize the serial port ##

    def start( self,):
        if not self.isOpen :
            self.ser = serial.Serial()
            self.ser.baudrate = self.baud
            self.ser.parity =serial.PARITY_EVEN
            self.ser.port = self.port
            self.ser.timeout = 2
            self.ser.open()
            print( "Opened port." )
            print (self.ser.portstr)
            self.isOpen = True
        return


    
## Close the serial port ##

    def close( self ):
        self.ser.close()
        self.isOpen = False
        print( "Closed port." )
        return

## Write ##

    def write( self, command ):
        self.start()
        #time.sleep( 0.1 )
        self.ser.write( command)
        #self.sio.flush()
        print( "{0:10}{1}".format( "Sent:", command ) )
        return

## Read ##

    def read( self, byte ):
        self.start()
        read = self.ser.read( byte )
        print( "{0:10}{1}".format( "Read:", read ) )
        return read


### Ende Class


    
### Aufbereiten Parameter SEW

#Fixe Parameter

SD2=0x02
TYP=0x86

#Variable Parameter

ADR=00
Index=10024
Subindex=1

### Aufbereiten Parameter SEW


#Variable Parameter

ADR=00
Index=10024
Subindex=1





def Movilink (ADR,Index,Subindex):

    #Fixe Parameter

    SD1=0x02
    TYP=0x86
    VERW=0x31

    PD3H=0
    PD3L=0
    PD4H=0
    PD4L=0

    #Index Aufarbeietn

    ind= Index
    ind2=float(ind)
    wert2=(ind2/256)
    str1 = repr(wert2)
    wert1= (str1.split('.', 2 ))
    IndHigh= int(wert1[0])
    text1="0."+(wert1[1])
    float1=float(text1)
    IndLow=int((float("0."+wert1[1])*256))
            

    Subind=(Subindex)

    print (ind)
    print (IndHigh)
    print (IndLow)
    print (Subindex)

    print int(SD1)

    test=int(SD1)

    print test
    #CRC Aufbereiten

    CRC=(int(SD1) ^ (ADR)^ (int(TYP))^ int(VERW)^ int(Subind) ^ (IndHigh)^ (IndLow)^ (PD3H)^ (PD3L)^ (PD4H)^ (PD4L))

    print (CRC)  


    String=str(bytearray([(int(SD1)),int(ADR),int(TYP),int(VERW),int(Subind),IndHigh,IndLow,PD3H,PD3L,PD4H,PD4H,CRC]))#.encode("utf-8")

    print String

    telegramm=String
    
    return (telegramm)

#INIT

objSEW=SEW()

result=objSEW.init(3)


#öffnen
Result=objSEW.start()


Parameter=[0,0,0]
Subparameter=[0,0,0]


Parameter[0]=8325
Subparameter[0]=0

Parameter[1]=8301
Subparameter[1]=0

for i in range(0,2):

    par1=Parameter[i]
    par2=Subparameter[i]

    Telegramm=Movilink(0,par1,par2)

    print Telegramm

    ### Parameterübertragung
     
    #senden
    send=objSEW.write(Telegramm)

    read=objSEW.read(25)

    print read

    leng=len(read)
    print "Länge:  "+str(leng)

    if read !="":
        Text=int(ord(read[1]))
        print Text
    else:
        print "Kein Empfang"


#Schliessen
Result=objSEW.close()
Zuletzt geändert von Anonymous am Montag 18. März 2013, 09:47, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@thomasgull: 2 Sekunden ist ja der Timeout, also würde ich mal auf das ``read(25)`` tippen. Damit sagst Du: Lies 25 Bytes und warte bis zur Zeitüberschreitung bis die Leseoperation abgebrochen wird, falls weniger als 25 Bytes in der Zeit gelesen werden.

Was erwartest Du denn dort als Antwort über die serielle Schnittstelle? Wie lang ist die? Oder wird die mit einem bestimmten Bytewert abgeschlossen? Vielleicht ist `readline()` hier die bessere Alternative?

Zum Quelltext: Der ist teilweise nicht besonders gut. Die Klasse ist zum Beispiel nicht wirklich so geschrieben wie die Sprache das vorsieht.

Die `init()`-Methode sollte `__init__()` heissen, dann braucht man sie auch nicht extra aufrufen. In dieser Methode sollten alle Attribute auf dem Objekt eingeführt werden. Wenn man in beliebigen anderen Methoden neue Attribute einführt, ist es schwierig sich ein Bild davon zu machen wann die Exemplare welche Attribute haben und wann noch nicht. Das macht Programme unübersichtlicher und Fehleranfälliger. Nach der `__init__()` sollte ein Objekt vollständig initialisiert und in einem konsistenten Zustand sein.

Wenn man das `Serial`-Exemplar schon in der `__init__()` erstellt, kann man für `isOpen` auch *dessen* `isOpen()`-Methode heran ziehen, statt das Flag selber zu verwalten.

Das was als Attribute auf der Klasse definiert wird gehört da nicht wirklich hin, sondern auch eher in die `__init__()`. Dann kann man auch Defaultwerte bei den Argumenten verwenden.

Eigentlich verstehe ich den Sinn der Klasse überhaupt nicht. Im Grunde ist das doch nur ein Wrapper um das Serial-Objekt bei dem man `read()` und `write()` aufrufen kann und dass dann die Verbindung sozusagen On-Demand aufbaut. Nur wird genau das im Quelltext überhaupt gar nicht verwendendet, sondern am Anfang explizit die `start()`-Methode aufgerufen. Damit macht diese Klasse keinen Sinn.

``return`` ohne Rückgabewert an das Ende von Funktionen oder Methoden zu schreiben ist sinnfrei. Ebenso sinnfrei ist es den semantisch nicht vorhandenen Rückgabewert bei den Aufrufen an einen Namen zu binden.

``print`` ist in Python 2 eine Anweisung und keine Funktion, also sollte man es auch nicht so schreiben als wäre es eine. Oder man importiert in aktuellen Python 2er-Versionen die Änderung zu einer `print()`-Funktion aus dem `__future__()`-Modul. Eins von beidem sollte man machen und dann innerhalb eines Moduls konsistent durchziehen.

Auf Moduleben wird Code zum erstellen von Konstanten (Werte, Module, Funktionen, Klassen, …) mit Variablen und Programm vermischt. Das ist unübersichtlich, führt unnötig globale Namen ein, die unbeabsichtigt in Funktionen oder Methoden verwendet werden können oder gar absichtlich, was undurchsichtige Abhängigkeiten zur Folge hat. Der Code sollte in einer Funktion verschwinden und mit dem ``if __name__ == '__main__':``-Idiom aufgerufen werden.

Es werden ein paar Namen im Code gar nicht benutzt. Da wären die importierten Module `io` und `time`; und `SEW.byte`, `SEW.port`, `Result`, `ADR`, `Index`, `SD2`, `Subindex`, `TYP`, und `float1`. Teilweise hängen da transitiv Namen dran, die auch nicht verwendet werden. `text1` wird nur zum berechnen des nicht verwendeten `float1` benutzt, ist also genau so überflüssig. Ausserdem werden Namen ohne Notwendigkeit eingeführt und an vorhandene Objekte mit anderen Namen gebunden. Warum? `ind` und `Index` in `Movilink()` wäre ein Beispiel. Und dann diese Unart jedes noch so kleine Zwischenergebnis an durchnummerierte Namen zu binden. Was soll das? Die 9 Zeilen nach ``# Index aufarbeiten`` bekäme man ohne die ganzen unnötigen Namen auf drei herunter. Wobei das Vorgehen dort ziemlich abenteuerlich ist, für eine Zahlenumwandlung die `repr()`-Form von Gleitkommezahlen und Zeichenkettenoperationen heran zu ziehen. Das ist glaube ich die umständlichste Art einen 16-Bit-Wert in Low- und High-Byte aufzuteilen, die ich bis jetzt gesehen habe. Das geht auch als Einzeiler und in verständlich: ``index_high, index_low = divmod(index, 256)``

Es macht wenig Sinn `int()` auf Werten aufzurufen, von denen man schon weiss, dass es ganze Zahlen sind. Das betrifft sowohl Konstante Werte, als auch Argumente und das Ergebnis der `ord()`-Funktion.

Statt jeden Wert für die CRC hinzuschreiben hätte man besser eine Funktion geschrieben, welche die CRC für eine Zeichenkette oder ein `bytearray` berechnet. Das berechnen von High-/Low-Bytes und zusammensetzen einer Datenstruktur liesse sich mit dem `struct`-Modul aus der Standardbibliothek besser lösen.

Der Präfix `obj*` wie bei `objSEW` macht in Python grundsätzlich keinen Sinn, weil *alles* was man an einen Namen binden kann ein Objekt ist. Durch so einen Präfix hat man also nichts gewonnen, nur einen unnötig längeren Namen.

Listen mit Dummywerten zu erstellen, die man dann durch die tatsächlich verwendeten Werte ersetzt ist in Python genau so ungewöhnlich wie der unnötige Umweg über einen Index, um über Werte in Sequenzen wie beispielsweise Listen zu iterieren. Man hätte die Parameterlisten auch *gleich* mit den passenden Werten füllen können. Ausserdem sollte man nicht zusammengehörige Daten in parallelen Datenstrukturen speichern. Das erhöht die Komplexität des Codes und Fehleranfälligkeit unnötig.

Kommentare sollten dem Leser einen Mehrwert über den Code bieten. Einen `close()`-Aufruf muss man nicht mit einem Kommentar ``# Schliessen`` versehen. Wer das nicht aus dem Code sehen kann, dem hilft auch der Kommentar nicht. In der Regel sollten Kommentare nicht erklären *was* gemacht wird, denn das steht da ja schon im Quelltext, sondern *warum* etwas gemacht wird, wenn das nicht offensichtlich ist und einer Erklärung bedarf. Wenn man einen Kommentar schreibt, kann man sich auch überlegen ob man den Quelltext stattdessen nicht verständlicher hinbekommt. Bei dem Kommentar ``# öffnen`` vor dem Aufruf der `start()`-Methode fällt zum Beispiel auf, dass die Aufgabe der `start()`-Methode tatsächlich einzig das öffnen der Verbindung enthält. Ein Zeichen das der Name `start()` vielleicht eher `open()` sein sollte. Dann ist der Kommentar auch überflüssig.

Die Namen und die Formatierung des Quelltextes halten sich nicht an den Style Guide for Python Code.

Anstelle der ganzen `print()`-Aufrufe die anscheinend hauptsächlich zum Verfolgen des Programmablaufs dienen, sollte man das `logging`-Modul verwenden. Dann kann man die Ausgaben in verschiedene Stufen einteilen, die man ausgeben lassen, in Protokolldateien schreiben lassen, oder auch ganz unterdrücken kann, ohne dass man überall im Quelltext Änderungen vornehmen muss.

Einiges von den Anmerkungen umgesetzt (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: cp1252 -*-
from __future__ import print_function
from contextlib import closing
import serial


class SEW(object):
    def __init__(self, port=3, baudrate=9600):
        self.serial = serial.Serial(
            baudrate=baudrate, parity=serial.PARITY_EVEN, timeout=2
        )
        self.serial.port = port
        self.isOpen = self.serial.isOpen

    def start( self,):
        if not self.isOpen():
            self.serial.open()
            print('Opened port.')
            print(self.serial.portstr)

    def close(self):
        self.serial.close()
        print('Closed port.')

    def write(self, command):
        self.start()
        self.serial.write(command)
        print('{0:10}{1!r}'.format('Sent:', command))

    def read(self, byte_count):
        self.start()
        result = self.serial.read(byte_count)
        print('{0:10}{1!r}'.format('Read:', result))
        return result


def movilink(adr, index, subindex):
    sd1 = 0x02
    typ = 0x86
    verw = 0x31
    pd3h = 0
    pd3l = 0
    pd4h = 0
    pd4l = 0

    index_high, index_low = divmod(index, 256)
    print(index)
    print(index_high)
    print(index_low)
    print(subindex)
    # 
    # TODO: Write CRC function.
    # 
    crc = (
        sd1 ^ adr ^ typ ^ verw ^ subindex ^ index_high ^ index_low ^ pd3h
        ^ pd3l ^ pd4h ^ pd4l
    )
    print(crc)
    # 
    # TODO: Use `struct` module.
    # 
    result = str(
        bytearray(
            [
                sd1, adr, typ, verw, subindex, index_high, index_low, pd3h,
                pd3l, pd4h, pd4l, crc
            ]
        )
    )
    print(repr(result))
    return result


def main():
    sew = SEW(3)
    sew.start()  # TODO: Either this call or the `SEW` class is unnecessary.
    with closing(sew):
        parameters = [(8325, 0), (8301, 0)]
        for index, subindex in parameters:
            telegram = movilink(0, index, subindex)
            print(repr(telegram))
            sew.write(telegram)
            response = sew.read(25)
            print(response)
            print('Länge: ', len(response))
            if response:
                print(ord(response[1]))
            else:
                print('Kein Empfang')


if __name__ == '__main__':
    main()
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Besten Dank für die Informationen, auch betreffend Programmstruktur, ich schaue mir das ganze mal durch und studiere es.

Danke und freundliche Grüsse

Thomas Gull
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Danke für den Tipp mit der Struct, grundsätzlich kann ich ja die Int-Werte direkt schreiben, wenn da nich das Problem mit Big und Litleendian problem wäre.

wie Tausche ich solche Bytes am ringsten?

Byteswap funktionier ja nicht für Int_werte?

Danke Thomas
BlackJack

@thomasgull: Man kann bei der Beschreibung einer Struktur doch mit '<' beziehungsweise '>' vorgeben ob big oder little endian genommen werden soll.
thomasgull
User
Beiträge: 48
Registriert: Samstag 2. Februar 2013, 18:52

Danke ja es geht so logisch.

Na irgendwie muss ich mir die Grundlagen erarbeiten. Welche Lektüren eignen sich? Da ich nicht der ultimative englischkundige bin was empfiehlt sich auf deutsch?

grüsse

Thomas
BlackJack

@thomasgull: Die Grundlagen von was genau? Python? Programmieren? Objektorientierung? Ich würde ja sagen: Englisch lernen. So schwer ist das Fachenglisch in der Regel nicht. Es geht ja nicht um hohe Literatur wo es schick ist sich in möglichst blumigen Worten und ”bildreich” auszudrücken, sondern um technische Themen bei denen möglichst klar und einfach formuliert wird. In der Regel jedenfalls.

IMHO sollte man das Tutorial in der Python-Dokumentation mal durchgearbeitet haben. Dann hat man einen ganz guten Überblick über die Sprache.
Gary123456
User
Beiträge: 318
Registriert: Dienstag 26. Februar 2013, 18:39

Na irgendwie muss ich mir die Grundlagen erarbeiten. Welche Lektüren eignen sich? Da ich nicht der ultimative englischkundige bin was empfiehlt sich auf deutsch?
Ich lese als Anfänger auch Englisch. Wenn ich einzelne Wörter nicht verstehe, schlage ich sie nach. Jedoch , wenn man darin länger liest und auch mit Hilfe dieser Übersetzer arbeitet, dann fällt einem das nicht mehr so schwer. Man müsste sich praktisch nur daran anpassen. Ich programmiere und bin in einem Training (AntiMalwaretraining - nein ich nenn sie nich mehr Ausbildung :wink: ) - alle beide Optionen sind auf Englisch - kann man das locker schaffen. Ich bin nicht der fitteste in Englisch (d.h. nicht schlecht, aber auch nicht gut => ganz okay) , aber dennoch kann man das mit etwas Mühe schaffen.

Als Lektüre nehm ich ein Buch für Kiddies her. Mir gefällt es gut, kann aber nicht wirklich beurteilen, ob das was vermittelt wird, richtig ist, aber wie es scheint, scheinen alle Kapitel korrekt zu sein (bisher). Der Autor hat sich da wirklich viel Mühe gegeben. http://www.amazon.de/Hello-World-Progra ... 3446421440 Ich habe vor dem Buch dieses Galieleo Computing Buch gehabt und kann es keinem empfehlen. Auch ohne dieses OOP Kapitel ist dieses Buch nicht gut, denn einige Sachen erscheinen ungenau und werden unter anderem dadurch auch zu schnell durchgenommen.

Dann kleiner Tipp von mir: Gib nich zu schnell auf. :) Wegen diesem Galieleo Drums hätte ich fast aufgegeben, aber das KiddieBuch gibt mir neuen Mut. :)

EDIT: Aber ... Wenn ich mir mal Deinen Code ansehe, sind das nicht schon Grundlagen in Deinem Code? Sieht ja gar ned schlecht aus.
Antworten