Bitte Erklärung, Hilfe für int.from_bytes(response...)

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
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Hallo,
kann mir einer mit einfachen Worten erklären was folgender Code macht.
Die Code-Teile, sind wie so oft aus dem Netz kopiert. Nun will ich ihn verstehen.
Mein Code läuft, ich kann am Geber drehen und der Absolutwert läuft mit.

Code: Alles auswählen

readNumberOfBytes = 4  # warum nicht gleich auf 7?
readNumberOfBytes += 3 # dann kann ich mir das doch hier schenken. Oder?

response = ser.read(readNumberOfBytes) # Ok, er liest die Schnittstelle
print(response)

position=int.from_bytes(response[2:6],'big',signed=True) #  Was macht  der Teil? Und was macht 2:6 ?
print (position)

Response --> b'@B\x05\xbcX\xafL'
Position --> 96229551

Danke :-)
Benutzeravatar
noisefloor
User
Beiträge: 4149
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

die Zeilen genau in der Reihenfolge

Code: Alles auswählen

readNumberOfBytes = 4  # warum nicht gleich auf 7?
readNumberOfBytes += 3
machen in der Tat wenig Sinn, weil man dann direkt `readNumberOfBytes=7` schreiben kann. BTW: der Name der Variablen hält sich nicht an die Python Namenskonvention. Richtig wäre `number_of_bytes_to_read`.

`response[2:6]` nennt sich "Slicing", das gehört zu den Python Grundlagen. Hier wird von `response` nur die 2. bis 5. Stelle benutzt. Kannst du auch im Python-Tutorial nachlesen, z.B. in der Sektion "Text" relativ weit unten.

Kurzes einfaches Beispiel hier:

Code: Alles auswählen

>>> name = 'kiaralle'
>>> some_slice = name[2:6:]
>>> some_slice
aral
>>>
Zu `signed` fällt mir gerade kein Beispiel ein, um das einfach zu erklären...

Gruß, noisefloor
Benutzeravatar
sparrow
User
Beiträge: 4501
Registriert: Freitag 17. April 2009, 10:28

Erste Anlaufstelle immer: die Dokumentation.
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

int.from_bytes macht genau das, was der Name sagt: eine Ganzzahl aus Binärdaten erzeugen. Big und signed geben an, wie die Daten zu interpretieren sind.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

"big" steht für „big endian“ im Gegensatz zu "little" für „littele endian“ und beschreibt die Byte-Reihenfolge. Und `signed` gibt an ob es sich um eine vorzeichenlose ganze Zahl handeln soll, oder eine die auch negative Werte annehmen kann und deshalb im Zweierkomplement gespeichert ist.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Danke an alle, hat mir gut geholfen :-)
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Hi,
ich bin wieder am Basteln von meinem Code.

Ich kann ja am Sick-Geber verschiedene Register auslesen. Das funktioniert beim Register 42h wunderbar und ich kann sogar den Wert kontinuierlich lesen und im Tkinter permanent aktualisieren lassen. Stolz wie Oskar :-)

Die Vorgabe laut Datenblatt
Command identifier42h
Access code-
Response time10 ms
Master data length0 bytes
Slave data length4 bytes
Master data values-
Der eigentliche Programmierer, nimmt dazu die "length 4 bytes" und hängt 3 an. Warum? Weil die Länge immer 7 sein muss? Was ja nicht stimmen kann, siehe weiter unten.
Hier passt es aber.
client_read_bytes = 4
client_read_bytes += 3


Command identifier46h
Access code-
Response time5 ms
Master data length0 bytes
Slave data length3 bytes
Hier hätte ich 3 Bytes und müsste 4 dazurechnen? Das funktioniert nicht. Ich muss mit 3 erhöhen und erhalte als Ausgabe 1283, was mir nichts sagt.
Je kleiner die da zugezählte Zahl, um so kleiner die Ausgabe.


Falls jemand Zeit hat, hier mal schauen. Das ist die Doku zum Hiperface... https://cdn.sick.com/media/docs/5/65/86 ... 064865.pdf

Code von mir, also wie immer , es darf kritisiert werden. :D

Code: Alles auswählen

from tkinter import *
import random
from random import *
import math

from crccheck.checksum import ChecksumXor8
import serial, time, binascii

#initialization and open the port
ser = serial.Serial()
# Choose based on your system/ linux will have sth. like /dev/ttty...
ser.port = "/dev/ttyUSB0"
ser.baudrate = 9600
ser.bytesize = serial.EIGHTBITS #number of bits per bytes
ser.parity = serial.PARITY_EVEN #set parity check: no parity
ser.stopbits = serial.STOPBITS_ONE #number of stop bits
ser.timeout = None  # block read
ser.xonxoff = False  # disable software flow control
ser.rtscts = False  # disable hardware (RTS/CTS) flow control
ser.dsrdtr = False  # disable hardware (DSR/DTR) flow control
ser.writeTimeout = 0.2  #timeout for write




class Updater():
    def __init__(self, lines_label, grad_label, meldung_label):
        self.lines_label = lines_label
        self.grad_label = grad_label
        self.meldung_label = meldung_label


    
    def position_lesen(self):
        

        try:
            ser.open()
            data = bytearray.fromhex("FF" + "42")
            data.append(ChecksumXor8.calc(data))
            
            client_read_bytes = 4
            client_read_bytes += 7 - client_read_bytes         

            ser.flushInput()  # flush input buffer, discarding all its contents
            ser.flushOutput()#flush output buffer, aborting current output 
            
            ser.write(data)
            response = ser.read(client_read_bytes)
            # print(binascii.hexlify(response))
            # Note that some slave answers include INT values of varying bit length within response. Configure the required bits, big/little endian and signed to your liking
            position=int.from_bytes(response[2:8:],'big',signed=False) # response[2:8]
            ser.close()
    
            """
            Absolute Position mit einer Auflösung
            von 32768 Schritten pro umdrehung
            sowie 4096 umdrehungen beim
            multiturn-System
            """                       

            inkrement = 2048
            lines = 4096   # Absolute position resolution
            counter_live = position
            counter = counter_live / inkrement
            voll_rest = divmod(counter, lines)
            
            winkel =  round(360 / lines * voll_rest[1],2)
            
 
            self.lines_label["text"] = counter_live
            self.grad_label["text"] = winkel
            self.meldung_label.config(bg="#1a233d", fg="#ffffff")
            self.meldung_label["text"] = ""
        except Exception as e1:
            self.meldung_label.config(
                bg="#990000", fg="#ffffff", padx=10, pady=10)
            self.meldung_label["text"] = f"Error...: " + str(e1)
        self.meldung_label.after(100, self.position_lesen)
        
 
def position_reset():
    ser.open()
    data = bytearray.fromhex("FF" + "46")
    data.append(ChecksumXor8.calc(data)) 
    client_read_bytes = 3
    client_read_bytes +=1         

    ser.flushInput()  # flush input buffer, discarding all its contents
    ser.flushOutput()#flush output buffer, aborting current output 
    
    ser.write(data)
    response = ser.read(client_read_bytes)  
    status =int.from_bytes(response[2:8:],'big',signed=False) 
    ser.close()    
    print(status)
        

def main():

    
    fenster = Tk()
    fenster.title("Montagehilfe für Servo-Geber")
    # Fenstergröße ermitteln
    w, h = fenster.winfo_screenwidth(), fenster.winfo_screenheight()
    # fenster.geometry("%dx%d+0+0" % (w, h))
    fenster.geometry("1024x750")
    fenster. minsize(width=800, height=700)
    fenster.config(bg="#1a233d")

    canvas = Canvas(fenster, width=256, height=47)
    canvas.place(x=750, y=20)
    mein_Bild = PhotoImage(file='./emb-schultheiss.png')
    canvas.create_image(0, 0, anchor=NW, image=mein_Bild)

    ueberschrift_label = Label(fenster, font=(
        'arial', 24, 'bold', 'italic'), text="Servoprüfplatz", bg="#1a233d", fg="#ffffff").place(x=20, y=20)

    option_label = Label(fenster, font=(
        'arial', 10, 'bold', 'italic'), text="Sick Hiperface", bg="#1a233d", fg="#ffffff").place(x=50, y=70)

    feld_lines_label = Label(fenster, font=(
        'arial', 24, 'bold', 'italic'), text="Position")
    feld_lines_label.config(bg="#1a233d", fg="#AAAAAA",
                    padx=10, pady=20)
    feld_lines_label.place(x=50, y=150)
    
    feld_grad_label = Label(fenster, font=(
        'arial', 24, 'bold', 'italic'), text="Winkel")
    feld_grad_label.config(bg="#1a233d", fg="#AAAAAA",
                    padx=10, pady=20)
    feld_grad_label.place(x=50, y=300)

    lines_label = Label(fenster, font=(
        'arial', 24, 'bold', 'italic'))
    lines_label.config(bg="#444444", fg="#ffffff",
                    borderwidth=5, relief="sunken", width=20, padx=10, pady=20)
    lines_label.place(x=250, y=150)
    
    grad_label = Label(fenster, font=(
        'arial', 24, 'bold', 'italic'))
    grad_label.config(bg="#444444", fg="#ffffff",
                    borderwidth=5, relief="sunken", width=20, padx=10, pady=20)
    grad_label.place(x=250, y=300)

    meldung_label = Label(fenster)
    meldung_label.place(x=50, y=450)

    exit_button = Button(fenster, font=(
        'arial', 16, 'bold', 'italic'), text="Beenden", command=fenster.quit)
    exit_button.place(x=50, y=600)
    exit_button.config(bg="#550000", activebackground="#AA0000",
                       cursor='hand2', fg="#cccccc", activeforeground="#FFFFFF", padx=5, pady=10)

    # start stop

    reset_button = Button(fenster, font=(
        'arial', 16, 'bold', 'italic'), text="Position Reset", command=lambda: position_reset())
    reset_button.place(x=200, y=600)
    reset_button.config(bg="#1a344d", activebackground="#2c355d",
                       cursor='hand2', fg="#cccccc", activeforeground="#FFFFFF", padx=5, pady=10)

    updater = Updater(lines_label, grad_label, meldung_label)
    updater.position_lesen()
    fenster.mainloop()


if __name__ == "__main__":
    main()


Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Erstmal zum Code: *-Importe benutzt man nicht, weil man sich damit unkontrolliert Namen in den eigenen Namensraum schaufelt.
Globale Variablen benutzt man nicht und benennt seine Variablen sinnvoll. Was soll ein `ser` sein?
Die Parameter zum Öffnen der Seriellen Schnittstelle gibt man beim Erzeugen an und setzt sie nicht nachträglich.
Außerdem sollte man die Schnittstelle nicht ständig öffnen und schließen.
place benutzt man nicht, weil das für verschiedene Auflösungen oder Betriebsysteme zu unbenutzbaren Oberflächen führt. Genausowenig sollte man eine fixe Fenstergröße vorgeben.

Das Protokoll ist ja in Deinem PDF beschrieben:
Addresse | Command | Nutzlast | Checksumme
Daraus wird ja offensichtlich, warum man drei zusätzliche Bytes braucht. Wobei man die Checksumme auch prüfen sollte. Am besten, in dem man dafür eine Funktion schreibt.
Warum Du jetzt versuchst 6 Bytes als Position zu interpretieren, bleibt Dein Geheimnis.
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Hallo Sirius3,
danke für deine Zeit und deine harte Kritik. Passt schon :-)
Erstmal zum Code: *-Importe benutzt man nicht,
Besser So?

Code: Alles auswählen

from tkinter import Tk, Canvas, PhotoImage, NW, Label, Button
Die Parameter zum Öffnen der Seriellen Schnittstelle gibt man beim Erzeugen an
Ok. sollte so aussehen?

Code: Alles auswählen

schnittstelle_rs485_usb = serial.Serial(port="/dev/ttyUSB0", baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, dsrdtr=False, writeTimeout=0.2)
Außerdem sollte man die Schnittstelle nicht ständig öffnen und schließen.
Werde ich überarbeiten. Da der umsetzter RS485-USB ständig angeschlossen ist, muss ich ihn ja nur beim starten des Prog. checken und ab und zu mit .open(). Richtig?
Daraus wird ja offensichtlich, warum man drei zusätzliche Bytes braucht. Wobei man die Checksumme auch prüfen sollte. Am besten, in dem man dafür eine Funktion schreibt.
Das ist absolutes Neuland für mich. Bin eher der Schrauber :-)
Warum Du jetzt versuchst 6 Bytes als Position zu interpretieren, bleibt Dein Geheimnis.
Beziehst du dich da auf response[2:8:]?
Hatte heute schon mal nur mit response gearbeitet, da war die Zahl länger.
Kann ich morgen in der Firma mal gegenlesen. Wir haben ein Gerät von Sick was mir aber nicht den Winkel anzeigt.
Deshalb hier meine Bastelei.
Benutzeravatar
Kebap
User
Beiträge: 760
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

kiaralle hat geschrieben: Sonntag 16. Februar 2025, 18:47
Das ist absolutes Neuland für mich. Bin eher der Schrauber :-)
Dafür bist du ja hier. :mrgreen: Ist dir denn klar, was da eine Checksumme bedeutet, und wie die zu prüfen wäre, und wie man bei negativem Ergebnis reagieren sollte?
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

ist dir denn klar, was da eine Checksumme bedeutet
Mit meinem Kenntnisstand... Es ist eine "Summe" der gesendeten oder empfangen Information um zu checken, das diese valide sind. Also vollständig und richtig, nicht getürkt.

Gemacht habe ich das noch nicht. Kommt aber noch.

Aktuell habe ich das Problem. das mein Script nicht auf dem Raspberry oder Windoofs läuft.
Auf meinen Linuxrechnern läuft es.

Der ttyAM0 (Raspberry) oder COM beim Windows auf Arbeit, sagt mir immer der ist Port schon offen.
Eventuell ist ja der Geber (gebraucht) defekt. Es bleibt spannen.

Gruß Ralf
kiaralle
User
Beiträge: 132
Registriert: Donnerstag 19. August 2021, 19:11

Die Checksumme muss doch im Response mit enthalten sein, oder?
Deshalb die Zerlegung mit dem response[...].
Der Aufbau vom Response sollte dann in der PDF der Implementierung vom Geber stehen.
Benutzeravatar
grubenfox
User
Beiträge: 593
Registriert: Freitag 2. Dezember 2022, 15:49

kiaralle hat geschrieben: Donnerstag 20. Februar 2025, 09:41 Die Checksumme muss doch im Response mit enthalten sein, oder?
Deshalb die Zerlegung mit dem response[...].
Der Aufbau vom Response sollte dann in der PDF der Implementierung vom Geber stehen.
Im Response ... und im Request
Each frame contains an address byte, command, and optionally data bytes of varying
length and a trailing checksum to detect transmission errors. This specification applies
both for Master and Slave transmissions.
(aus dem PDF)
Antworten