socket - Datenformat ankommend

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Fe-X
User
Beiträge: 4
Registriert: Sonntag 22. Juni 2014, 11:56

Hallo zusammen.

Bin Neuling bez. Python und versuche, ein eigenes Skript aus AutoIt auf Linux zu portieren. Das Skript nimmt Daten eines Gerätes entgegen, welche im LAN über ein Siena Nemo10 RS232-Ethernet-Modul verschickt werden. Die Daten kommen in Python auch an, doch habe ich Probleme bei der Umwandlung.

Verschickt werden die Hex-Daten

06 3B 04 9B 06 9C 04 D6 05 05 00 00 0D BA 02 00 D4 F7

und empfangen kann ich

Received '\x06"\x04\x9b\x06\x9c\x04\xd6\x05\x05\x00\x00\r\xba\x02\x00\xd5\x10'

Man erkennt die Umwandlung von 3B zu " und 0D zu \r . Die restlichen Daten müsste ich mit binascii.unhexlify umwandeln können, nicht?

Wie komme ich an die korrekten Daten? Gefragt ist schlussendlich der Dezimalwert von immer vier Hex-Ziffern, also 063B wird 1595, danach führt eine Teilung durch 64 zum vom Gerät gemessenen Temperaturwerte (hier 24.9°), mit Ausnahme der letzten 4 Hex-Ziffern, das sind Statuscodes und eine Prüfziffer.

Mit folgendem Beispielprogramm empfange ich die Daten:

Code: Alles auswählen

# Echo client program
import socket
import sys

HOST = '192.168.20.103'    # The remote host
PORT = 6001                # The same port as used by the server
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except socket.error as msg:
        s = None
        continue
    try:
        s.connect(sa)
    except socket.error as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print 'could not open socket'
    sys.exit(1)
data = s.recv(1024)
s.close()
print 'Received', repr(data)
Irgendein Tipp? Wo muss ich suchen?
Den Beitrag http://www.python-forum.de/viewtopic.php?f=3&t=33921 habe ich gesehen, aber dort ist (noch) keine Lösung erkennbar.

Danke.
Felix
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@Fe-X: die Daten kommen korrekt als Byte-String an. Was Du da siehst ist die Darstellung eines Strings, bei dem alle nicht-druckbaren Zeichen durch ihre Escape-Sequenz dargestellt werden. Zum Umwandeln in 16bit Werte hilft Dir struct.unpack.
BlackJack

@Fe-X: Nebenbei bemerkt finde ich den Kontrollfluss mit den ``continue``\s und dem ``break`` am Ende der Schleife sehr unübersichtlich.
Fe-X
User
Beiträge: 4
Registriert: Sonntag 22. Juni 2014, 11:56

Sirius3 hat geschrieben:@Fe-X: die Daten kommen korrekt als Byte-String an. Was Du da siehst ist die Darstellung eines Strings, bei dem alle nicht-druckbaren Zeichen durch ihre Escape-Sequenz dargestellt werden. Zum Umwandeln in 16bit Werte hilft Dir struct.unpack.
Perfekt! Danke!

Felix
Fe-X
User
Beiträge: 4
Registriert: Sonntag 22. Juni 2014, 11:56

BlackJack hat geschrieben:@Fe-X: Nebenbei bemerkt finde ich den Kontrollfluss mit den ``continue``\s und dem ``break`` am Ende der Schleife sehr unübersichtlich.
:-)

Das ist das Beispiel aus der Hilfe. Ich habe das 1:1 übernommen um sicher zu gehen, dass ich nicht woanders Fehler im Skript habe. Jetzt, wo ich die Daten so vorliegen habe wie ich sie brauche, werde ich die gewünschte Funktion programmieren. Kann es dann ja mal einstellen und eure Meinung einholen. Wie gesagt: Bin Neuling in Sachen Python und lerne gerne etwas.

Felix
BlackJack

So könnte man das ohne ``continue`` formulieren (ungetestet):

Code: Alles auswählen

from __future__ import print_function
import socket
import sys
from contextlib import closing

HOST = '192.168.20.103'  # The remote host.
PORT = 6001  # The same port as used by the server.


class NoSocket(Exception):
    pass


def get_connected_socket(host, port):
    for family, socktype, proto, _, sockaddress in socket.getaddrinfo(
        host, port, socket.AF_UNSPEC, socket.SOCK_STREAM
    ):
        try:
            sock = socket.socket(family, socktype, proto)
        except socket.error:
            pass
        else:
            try:
                sock.connect(sockaddress)
            except socket.error:
                sock.close()
            else:
                return sock
    raise NoSocket


def main():
    try:
        with closing(get_connected_socket(HOST, PORT)) as sock:
            data = sock.recv(1024)

        print('Received:', repr(data))
    except NoSocket:
        print('could not open socket')
        sys.exit(1)


if __name__ == '__main__':
    main()
BlackJack

@Fe-X: Wenn ich so drüber nachdenke erscheint mir diese Schleife komisch. Wozu soll die gut sein? Man weiss doch normalerweise über welches Protokoll man kommuniziert und im Grunde kommt doch hier nur TCP in Frage oder? Die beiden anderen Varianten die man üblicherweise bekommt, zumindest unter unixoiden Systemem, sind UNIX-Domain-Sockets wo sicher kein Gerät angeschlossen ist, und UDP, wo das `connect()` fehlschlägt.

Für TCP kann man im echten Einsatz dann das ``recv(1024)`` nicht einfach *so* verwenden, denn da muss man im Zweifelsfall damit klar kommen, das bei jedem dieser Aufrufe nur ein einzelnes Byte gelesen wird, oder auch das mehr als nur *eine* Nachricht gelesen wird.
Fe-X
User
Beiträge: 4
Registriert: Sonntag 22. Juni 2014, 11:56

BlackJack hat geschrieben:@Fe-X: Wenn ich so drüber nachdenke erscheint mir diese Schleife komisch. Wozu soll die gut sein? Man weiss doch normalerweise über welches Protokoll man kommuniziert und im Grunde kommt doch hier nur TCP in Frage oder? Die beiden anderen Varianten die man üblicherweise bekommt, zumindest unter unixoiden Systemem, sind UNIX-Domain-Sockets wo sicher kein Gerät angeschlossen ist, und UDP, wo das `connect()` fehlschlägt.

Für TCP kann man im echten Einsatz dann das ``recv(1024)`` nicht einfach *so* verwenden, denn da muss man im Zweifelsfall damit klar kommen, das bei jedem dieser Aufrufe nur ein einzelnes Byte gelesen wird, oder auch das mehr als nur *eine* Nachricht gelesen wird.
Danke für diese Inputs. Das Gerät spuckt jede Minute einen ganzen Datensatz aus. Das RS232-Ethernet-Modul sendet nach dem Empfang der Daten von der seriellen Schnittstelle das ganze Paket und beendet die Kommunikation. In der Regel funktioniert es also, einfach zu warten, bis die Daten kommen - quick n' dirty halt. Selbstverständlich hast du Recht, dass ein robustes Error-Handling nötig ist für Netzwerkausfall, Datenfehler etc. Die Daten von diesem Gerät sind allerdings nicht so heikel, als dass ich nicht mal eine Sequenz verwerfen könnte - zumal auch gar keine Möglichkeit besteht, bei einem Datenfehler z.B. zwischen RS232 und Ethernet-Modul die Sequenz nochmal anzufordern.
Antworten