Falsche Verarbeitung je nach Position im Tupel

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
damianm
User
Beiträge: 6
Registriert: Donnerstag 6. Juni 2013, 16:35

Liebe Freunde,

bei einer seriellen Abfrage muss ich ein paar Bytes konvertieren und eine CRC-Summe prüfen,
es funktioniert soweit, allerdings habe ich einen komischen Fehler entdeckt, was die Abfrage verfälscht
(ein Byte mit Hex-Wert "bf" verschwindet).
Es sollen nur die Bytes:
"fe00" zu "fe", "fe12" zu "11" ...

Ändere ich die Reihenfolge im Tupel, kommt kein Fehler mehr.
Kann mir es jemand erklären?


Code-Ausschnitt mit vorgegebenem String:

Code: Alles auswählen

#!/usr/bin/env python
import binascii, struct

def READ_SERIAL(x):
    c = 0; r = ""; r1 = ""; l = 5; m = 0; i = 0; r2 = ""; 
    j = 0
    while True:
        #b = ser1.read(1);
        b = x[j]; j += 1

        c += 1; i = 0; r2 += b
        if r1 == "fe" and h(b) == "00": r += a("fe"); l += 1; m += 1; i = 1
        if r1 == "fe" and h(b) == "12": r += a("11"); l += 1; m += 1; i = 1
        if r1 == "fe" and h(b) == "14": r += a("13"); l += 1; m += 1; i = 1
        if r1 == "02" and h(b) == "00": r += a("02"); l += 1; m += 1; i = 1
        if r1 == "2b" and h(b) == "00": r += a("2b"); l += 1; m += 1; i = 1
        r1 = ""
        #if c >= 2 and c != l and h(b) in  "fe" "02" "2b" : r1 = h(b) # nutzt man diese Reihenfolge, ist alles ok
        if c >= 2 and c != l and h(b) in  "2b" "fe" "02" : r1 = h(b)  # hier eben nicht
        else:
            if i == 0: r += b 
        if c == 5: 
            try:
                l += struct.unpack('>H', (r[2:4]))[0]

            except: 
                print "LAENGE NICHT FESTGELEGT"
        if c == l and (h(b) == "fe" or h(b) == "02" or h(b) == "2b"): l += 1

        if c == l: return r2,l,r

def a(wert):
    a = binascii.a2b_hex(wert)
    return a

def h(wert):
    h = binascii.b2a_hex(wert)
    return h

def crc(s,a):
    c = 0; crc = 0
    while c < (len(s) - a):
        crc = crc ^ (ord(s[c]) ^ ((ord(s[c]) * 2 & 255)))
        c += 1
    return chr(crc)

def READ_AUSGABE(read):
    tel = read[0]; i = read[1] - 1
    telc = read[2]  
    print "RECV<", h(tel),
    if h(tel[i]) == h(crc(tel,1)): print "CRC OK" 
    else: print "NO CRC"
    print "RECV<", h(telc),
    if h(tel[i]) == h(crc(telc,1)): print "CRC OK" 
    else: print "NO CRC"
    return telc



b = a("02fd000c420100bf3cdcffffffffffff31")
read = READ_SERIAL(b)
telc = READ_AUSGABE(read)


Die Ausgabe ist:

Code: Alles auswählen

RECV< 02fd000c420100bf3cdcffffffffffff31 CRC OK
RECV< 02fd000c4201003cdcffffffffffff31 NO CRC

Soll aber:

Code: Alles auswählen

RECV< 02fd000c420100bf3cdcffffffffffff31 CRC OK
RECV< 02fd000c420100bf3cdcffffffffffff31 CRC OK
Zuletzt geändert von Anonymous am Donnerstag 6. Juni 2013, 17:05, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@damianm: Du solltest vielleicht wirklich mal Tupel verwenden und nicht *eine* Zeichenkette in der alle Werte hintereinander stehen, also auch welche gefunden werden wo die beiden Hex-Ziffern eigentlich in verschiedenen Werten stehen.

Code: Alles auswählen

In [3]: 'bf' in  "2b" "fe" "02"
Out[3]: True

In [4]: "2b" "fe" "02"
Out[4]: '2bfe02'

In [5]: 'bf' in  ("2b", "fe", "02")
Out[5]: False
Edit: Wobei der Quelltext insgesamt ziemlich gruselig aussieht und nicht wirklich nach Python.
damianm
User
Beiträge: 6
Registriert: Donnerstag 6. Juni 2013, 16:35

Vielen Dank für die Antwort, es stimmt,
ich habe jetzt

...
t = "2b", "fe", "02"
...
und

if c >= 2 and c != l and h(b) in t : r1 = h(b)

und es funktioniert !!!
Wobei der Quelltext insgesamt ziemlich gruselig aussieht und nicht wirklich nach Python
Stimmt, bin leider kein Programmierer, versuche nur meine Heizung auszulesen :-),
vielen Dank für super schnelle Hilfe

Muss der Thread geschlossen werden?
BlackJack

@damianm: Gerade wenn man kein Programmierer ist sollte man vielleicht mehr auf lesbaren und verständlichen Quelltext achten. Also zum Beispiel Namen verwenden die verraten wofür die Werte stehen und nicht kryptische ein bis zwei Buchstaben, wo man selbst nach ein paar Monaten nicht mehr weiss was das eigentlich bedeuten soll.

In dem ganzen Buchstabensalat gehen dann zum Beispiel so Dinge unter wie das `m`, welches zwar initialisiert und hochgezählt, dann aber nirgends benutzt wird. `j` ist auch überflüssig, weil das immer genau den gleichen Wert wie `c` hat. `c` selbst ist ebenfalls kein unabhängiger Wert, denn `c` ist immer das gleiche wie ``len(r2)``.

Hier mal eine erste Überarbeitung die mehr nach Python-Quelltext aussieht, ohne die ganzen unnötigen Konvertierungen von Bytes zu Hexadezimaldarstellungen und umgekehrt, und wo ich mal versucht habe vernünftige(re) Namen zu erraten:

Code: Alles auswählen

#!/usr/bin/env python
import struct
from io import BytesIO

BYTE_PAIR_TO_DECODED_BYTE = {
    ('\xfe', '\x00'): '\xfe',
    ('\xfe', '\x12'): '\x11',
    ('\xfe', '\x14'): '\x13',
    ('\x02', '\x00'): '\x02',
    ('\x2b', '\x00'): '\x2b',
}
MARKER_BYTES = set(x for x, _ in BYTE_PAIR_TO_DECODED_BYTE.iterkeys())


def read_serial(in_file):
    received_bytes = list()
    decoded_message = list()
    previous_byte = ''
    expected_length = 5
    while True:
        current_byte = in_file.read(1)
        received_bytes.append(current_byte)
        decoded_byte = BYTE_PAIR_TO_DECODED_BYTE.get(
            (previous_byte, current_byte)
        )
        if decoded_byte is not None:
            decoded_message.append(decoded_byte)
            expected_length += 1
        previous_byte = ''
        if (
            len(received_bytes) >= 2
            and len(received_bytes) != expected_length
            and current_byte in MARKER_BYTES
        ):
            previous_byte = current_byte
        elif decoded_byte is None:
            decoded_message.append(current_byte)
        if len(received_bytes) == 5: 
            try:
                expected_length += struct.unpack(
                    '>H', ''.join(decoded_message[2:4])
                )[0]
            except struct.error: 
                print 'LAENGE NICHT FESTGELEGT'
        if (
            len(received_bytes) == expected_length
            and current_byte in MARKER_BYTES
        ):
            expected_length += 1
        if len(received_bytes) == expected_length:
            return (
                ''.join(received_bytes),
                expected_length,
                ''.join(decoded_message)
            )


def calculate_crc_byte(data, skip_at_end_count):
    i = 0
    result = 0
    while i < (len(data) - skip_at_end_count):
        value = ord(data[i])
        result ^= value ^ (value * 2 & 255)
        i += 1
    return chr(result)


def read_ausgabe(read):
    tel, i, telc = read
    i -= 1
    print 'RECV< {0} {1}'.format(
        tel.encode('hex'),
        'CRC OK' if tel[i] == calculate_crc_byte(tel, 1) else 'NO CRC'
    )
    print 'RECV< {0} {1}'.format(
        tel.encode('hex'),
        'CRC OK' if tel[i] == calculate_crc_byte(telc, 1) else 'NO CRC'
    )


def main():
    in_file = BytesIO('02fd000c420100bf3cdcffffffffffff31'.decode('hex'))
    read_ausgabe(read_serial(in_file))


if __name__ == '__main__':
    main()
damianm
User
Beiträge: 6
Registriert: Donnerstag 6. Juni 2013, 16:35

@ BlackJack

Wow! Du hast dir wirklich viel Arbeit gemacht !

Code, was ich gepostet habe war ein Ausschnitt aus einem Script,
mit dem ich einen Pallet-Heizkessel der Fa Fröling auslese / versuche auszulesen.

Ich habe in Pastebin das ganze gepostet.
(wie füge ich Pastebin-Links ein? So?:)

http://www.python-forum.de/pastebin.php?mode=view&s=353

Die Ausgaben, was mir momentan mein Script rauswirft:

http://www.python-forum.de/pastebin.php?mode=view&s=354

Die diversen Werte der Heizung sollten abgefragt und dann teilweise zu einer Visualisierung weitergeschickt , wie auch von der Visu verändert werden (Gira Homeserver über KO-Gateway).

Ich versuche hier das Protokoll nach dieser Anleitung zu Entschlüsseln:

http://www.labviewforum.de/attachment.php?aid=43777


Die Heizung besitzt 2 serielle schnittstelle, die COM2 gibt die ganze zeit ca 30 Werte aus,
die habe ich auch schon vor ein paar Tagen visualisiert. (Wollte hier ein Bild von einfügen, wie füge ich Screenshots ein?)

Dieser Script soll aber die Service-Schnittstelle auslesen, was er auch schon teilweise tut.

Wie Du mir gezeigt hast, habe ich noch sehr viel Arbeit vor mir, nicht nur, um den Script erst mal lesbarer zu machen, aber auch um Python und Programmierung als solches zu lernen.

Danke noch mals
BlackJack

@damianm: Ich denke das empfangen von Frames kann man wesentlich einfacher lösen. Jeder Frame fängt mit der Bytefolge 02 FD an. Von dort bis zum nächsten Frame-Anfang kann diese Bytefolge unmöglich vorkommen weil jede 02 innerhalb eines Frames durch 02 00 ersetzt wird. Das heisst man kann den Eingabedatenstrom erst einmal ganz trivial an den 02 FDs in Frames zerlegen. Und danach macht man auf den Inhalt dann die Ersetzungen.
damianm
User
Beiträge: 6
Registriert: Donnerstag 6. Juni 2013, 16:35

@ BlackJack

Hm, habe jetzt Dein Code getestet, es berechnet zwar die CRC, irgendwie wandelt aber die zeichenfolgen nicht um:

Code: Alles auswählen

root@froeling:~/froeling# ./forum
RECV< 02fd0023470100924802001b2c070f050d537465756572756e67206e6575206765737461727465746c CRC OK
RECV< 02fd0023470100924802001b2c070f050d537465756572756e67206e6575206765737461727465746c CRC OK
müsste

Code: Alles auswählen

RECV< 02fd0023470100924802001b2c070f050d537465756572756e67206e6575206765737461727465746c CRC OK
RECV< 02fd00234701009248021b2c070f050d537465756572756e67206e6575206765737461727465746c CRC OK
und

Code: Alles auswählen

root@froeling:~/froeling# ./forum
RECV< 02fd002347010092c8042a2b000710050d537465756572756e67206e6575206765737461727465749d CRC OK
RECV< 02fd002347010092c8042a2b000710050d537465756572756e67206e6575206765737461727465749d CRC OK
root@froeling:~/froeling#
 
müsste

Code: Alles auswählen

RECV< 02fd002347010092c8042a2b000710050d537465756572756e67206e6575206765737461727465749d CRC OK
RECV< 02fd002347010092c8042a2b0710050d537465756572756e67206e6575206765737461727465749d CRC OK
so aussehen

wo
02fd - Header, immer gleich
0023 - Länge Bytes: Payload + CRC (nach Umwandlung)
47 - Befehl
010092c8042a2b0710050d537465756572756e67206e657520676573746172746574 - Payload
9d - CRC
damianm
User
Beiträge: 6
Registriert: Donnerstag 6. Juni 2013, 16:35

BlackJack hat geschrieben:@damianm: Ich denke das empfangen von Frames kann man wesentlich einfacher lösen. Jeder Frame fängt mit der Bytefolge 02 FD an. Von dort bis zum nächsten Frame-Anfang kann diese Bytefolge unmöglich vorkommen weil jede 02 innerhalb eines Frames durch 02 00 ersetzt wird. Das heisst man kann den Eingabedatenstrom erst einmal ganz trivial an den 02 FDs in Frames zerlegen. Und danach macht man auf den Inhalt dann die Ersetzungen.
ja, die Frames beginnen immer mit 02FD, dann kommt die Telegramlänge, (die nächsten 2 Bytes), mann soll schon bei Empfang wissen, wie lang ein Telegramm ist, danach muss evtl. wieder ein gültiges Telegramm gesendet werden, je nach dem, ob man nur ein wert lesen/senden oder eine Liste erhalten will (es gibt so Paar Befehle, die man nur über eine Schleife erhalten kann, z.B Fehlerspeicher), die Steuerung antwortet aber immer mit einem Frame, man muss die nächsten schon gezielt anfordern.

Man muss also nicht die Frames aus Datenstrom zerlegen sondern immer die Länge wissen. Die im Telegramm angegebene Länge betrifft Telegram nach Umrechnung, also 0200 wird als 1 Byte gerechnet.

Wie gesagt, bin eher schlechter Programmierer, ich bemühe mich, allerdings jedes Problem, heisst für mich suchen, suchen, suchen,

insbesondere, dass ich noch nicht mals weiss, was die Python Standard-Bibliothek so alles kann,
z.B auf die Lösung mit io.BytesIO wäre ich nicht gekommen
BlackJack

@damianm: Dann habe ich wohl beim umschreiben irgendwo einen Fehler gemacht. Ich finde das aber sowieso ganz furchtbar unübersichtlich das in einer Funktion zu lösen. Wenn man das Problem in kleinere Probleme zerlegt, die man dann einzeln lösen kann, wird es IMHO übersichtlicher. Also zum Beispiel lesen *eines* Bytes mit dekodieren/ersetzen. Und darauf aufbauend dann lesen von n Bytes mit dekodieren/ersetzen. Und damit kann man dann etwas schreiben was einen kompletten Frame liest.

Code: Alles auswählen

#!/usr/bin/env python
import struct
from itertools import imap
from io import BytesIO

BYTE_PAIR_TO_DECODED_BYTE = {
    ('\xfe', '\x00'): '\xfe',
    ('\xfe', '\x12'): '\x11',
    ('\xfe', '\x14'): '\x13',
    ('\x02', '\x00'): '\x02',
    ('\x2b', '\x00'): '\x2b',
}
MARKER_BYTES = set(x for x, _ in BYTE_PAIR_TO_DECODED_BYTE.iterkeys())


def calculate_crc_byte(data):
    result = 0
    for value in imap(ord, data):
        result ^= value ^ (value * 2 & 255)
    return chr(result)


class ReaderError(Exception):
    pass


class Reader(object):
    FRAME_HEADER = '\x02\xfd'

    def __init__(self, in_file):
        self.in_file = in_file

    def read_raw_byte(self):
        return self.in_file.read(1)

    def read_byte(self):
        result = self.read_raw_byte()
        if result in MARKER_BYTES:
            first_byte, second_byte = result, self.read_raw_byte()
            result = BYTE_PAIR_TO_DECODED_BYTE.get((first_byte, second_byte))
            if result is None:
                raise ReaderError(
                    'Invalid byte sequence {0} {1}'.format(
                        first_byte.encode('hex'), second_byte.encode('hex')
                    )
                )
        return result

    def read_bytes(self, count):
        return ''.join(self.read_byte() for _ in xrange(count))

    def read_uint16(self):
        return ord(self.read_byte()) * 256 + ord(self.read_byte())

    def read_frame(self):
        frame_header = self.read_raw_byte() + self.read_raw_byte()
        if frame_header != self.FRAME_HEADER:
            raise ReaderError(
                'unexpected frame header ' + frame_header.encode('hex')
            )
        frame_size = self.read_uint16()
        payload = self.read_bytes(frame_size)
        checksum = self.read_byte()
        if checksum != calculate_crc_byte(
            frame_header + struct.pack('>H', frame_size) + payload
        ):
            raise ReaderError('wrong frame checksum')
        return payload


def main():
    in_file = BytesIO(
        '02fd0023470100924802001b2c070f050d537465756572756e67206e657520'
        '6765737461727465746c'.decode('hex')
    )
    reader = Reader(in_file)
    payload = reader.read_frame()
    print payload.encode('hex')


if __name__ == '__main__':
    main()
damianm
User
Beiträge: 6
Registriert: Donnerstag 6. Juni 2013, 16:35

@BlackJack

ok, jetzt wird noch komplizierter (OOP ;-)) - habe die Klassen nie verstanden,

ich glaub, ich muss es mir übers Wochenende überlegen,
(ich muss den Code erst mal verstehen :-)
Antworten