Seite 1 von 1

Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 12:56
von manlud80
Hallo zusammen,
ich habe ein Problem, Daten über eine Funktion einer DLL abzurufen. Ich will von einem RFID-Reader Daten auslesen, dazu habe ich mit einer DLL mit dem Befehl

Code: Alles auswählen

b = WinDLL("C:\\...\\nameofdll.dll")
verbunden.
Mit der Funktion Inventory_G2 kann man nun die RFID-Daten abrufen. Mein Befehl sieht so aus:

Code: Alles auswählen

b.Inventory_G2(c_ubyte(int("ff", 16)), c_ubyte(0), c_ubyte(0), c_ubyte(0), (c_ubyte*1000)(), None, None, c_long(int("33f0000", 16)))
Die Fehlermeldung dazu lautet:
WindowsError: exception: access violation reading 0x00000030

Vom Hersteller wurde Quellcode in C# bereitgestellt, der die Funktion wie folgt erfolgreich verwendet:

Code: Alles auswählen

byte AdrTID = 0;
byte LenTID = 0;
byte TIDFlag = 0;
CardNum=0;
Totallen = 0;
byte[] EPC=new byte[5000];
fComAdr = 0xFF;
fCmdRet = StaticClassReaderB.Inventory_G2(ref fComAdr,AdrTID,LenTID,TIDFlag, EPC, ref Totallen, ref CardNum, frmcomportindex);
Kann mir jemand helfen? Wie rufe ich die Funktion korrekt in Python auf?

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 13:15
von Sirius3
@manlud80: in der Signatur der Funktion gibt es mehrere Parameter, die eine Referenz (Pointer) auf die Variablen erwarten und nicht ihren Wert.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 13:30
von manlud80
Super, das hat schonmal geholfen... Mit

Code: Alles auswählen

b.Inventory_G2(byref(c_ubyte(int("ff", 16))), c_ubyte(0), c_ubyte(0), c_ubyte(0), test, None, byref(c_ubyte(0)), c_long(int("33f0000", 16)))
bekomme ich schonmal "nur noch" die Fehlermeldung "55" zurück, aber noch keine Daten.
Kann mir jemand sagen, was ich noch falsch mache?
Vielen Dank schonmal Sirius3.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 14:24
von BlackJack
@manlud80: Wo sollten die Daten denn stehen? Und darf man bei `Totallen` aus dem C#-Beispiel einen NULL-Pointer angeben? Ich weiss auch nicht ob es so gut ist bei `byref()` direkt ein erzeugtes `ctypes`-Objekt zu übergeben weil `byref()` in der Dokumentation als ”leichtgewichtige” Alternative zu `pointer()` speziell für Funktionsaufrufe beschrieben wird. Es kann also gut sein dass diese Funktion nicht dafür sorgt das es eine Referenz auf das Argument gibt solange der Rückgabewert der Funktion verwendet wird und man sich so Pointer auf nicht (mehr) existierende Objekte/Speicherbereiche schafft. Ich würde an der Stelle das C#-Beispiel ähnlicher Nachbauen und vor dem Aufruf die Werte an Namen binden die da als Pointer übergeben werden.

Dieses `int('...', 16)` für die Hexadezimalzahlen ist übrigens unnötig umständlich. Python versteht auch die Notation für Hex-Literale wie bei der C-Sprachfamilie, also beispielsweise einfach ``0xff`` statt ``int('ff', 16)``.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 15:49
von manlud80
Vielen Dank dafür.
Die Daten müssen in "test" stehen, wobei

Code: Alles auswählen

test=(c_ubyte*5000)()
ist.
Den Nullpointer habe ich jetzt durch ein c_ubyte(0) ersetzt und die byref durch pointer ersetzt. Leider immer noch der gleiche Rückgabewert ("55"). Kann es sein, dass meine test-Variable falsch ist?

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 16:21
von Sirius3
@manlud80: was heißt denn 55? Es muß doch irgendwo eine Fehlermeldungsbeschreibung geben.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 16:33
von manlud80
Nein, leider nicht. In der DLL-Beschreibung steht, dass bei erfolgreich ausgeführtem BEfehl die 0 zurückgegeben wird.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 16:42
von BlackJack
@manlud80: Was hast Du denn da genau? Welches Gerät? Wie heisst die DLL? Woher hast Du die Dokumentation?

Wenn ich nach RFID und dem Funktionsnamen im Netz suche, finde ich beispielsweise dieses PDF: http://www.rfidshop.com.hk/datasheet/UH ... 20v1.2.pdf

Da steht bei Fehlercode 55 (0x37) „Invalid Handle“, was mir auch sinnvoll erscheint, denn die 0x33f0000 fällt in Deinem Code ja irgendwie vom Himmel herab. Wo hast Du den Wert her? In der Dokumentation stehen die Funktionen mit denen man den tatsächlichen, dynamischen Wert bekommt.

Edit: Auch wenn das bei Windows nicht zwingend nötig ist, würde ich mir erst einmal die Funktionen aus der DLL holen und die Signaturen definieren. Dann sehen die Aufrufe etwas weniger überfrachtet aus und sind auch sicherer. Ausserdem kann man den Rückgabewert dann auch gleich in eine Ausnahme umwandeln lassen wenn etwas nicht geklappt hat und kann ”pythonischeren” Code schreiben. Eventuell auch das Handle in ein Objekt kapseln.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 17:03
von manlud80
Ja, ich habe so ein ähnliches Gerät, die Inventory-Funktion ist nahezu identisch.

Ups, die Fehlercodes hatte ich bisher noch nicht gesehen. Bisher kam ich auch noch nie bis dort.

Das handle habe ich von der WinDLL b(siehe mein erster Post):

<WinDLL 'C:\...\nameofdll.dll', handle 33f0000 at 2d15fd0>

Die Funktion AutoOpenComPort() soll eigentlich einen gültigen handle übergeben, ich bekomme aber nur eine 0 für "erfolgreich verbunden".

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 17:15
von BlackJack
@manlud80: Das ist das Handle für die DLL, Du brauchst eines für einen geöffneten COM-Port das mit einer der beiden Open-Funktionen zu bekommen ist. Der Rückgabewert dieser Funktionen ist ja nicht das einzige Ergebnis. Das scheint immer eine Art Fehlercode zu sein. Alle anderen Werte werden über die Zeiger zurückgegeben die als Argumente übergeben werden.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 17:34
von manlud80
Ok, danke. Kannst du mir sagen, wie ich auf diese Werte zugreifen kann?

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 19:03
von BlackJack
@manlud80: Du musst die Werte halt an Namen binden und beim Aufruf eine Referenz auf den Wert übergeben. Die C-Funktion ändert dann den Wert die von dem jeweiligen `ctypes`-Objekt gekapselt wird.

Re: Problem mit Daten in ctypes

Verfasst: Mittwoch 13. Januar 2016, 21:26
von Dav1d
Nicht direkt eine Lösung für dein Problem (und auch gerade unrelated), empfehle dir aber cffi anzuschauen. Effizienter als ctypes und um seeeehr viel einfacher zu handhaben.

Re: Problem mit Daten in ctypes

Verfasst: Donnerstag 14. Januar 2016, 00:30
von BlackJack
Vollkommen ungetestet:

Code: Alles auswählen

from ctypes import byref, c_long, c_uint8, POINTER, WinDLL

READER_DLL = WinDLL('nameofdll.dll')
(
    BAUD_9600,
    BAUD_19200,
    BAUD_38400,
    _,
    BAUD_56000,
    BAUD_57600,
    BAUD_115200
) = xrange(7)


class ReaderException(Exception):

    def __init__(self, error_code):
        Exception.__init__(self, 'Error code 0x{0:x}'.format(error_code))
        self.error_code = error_code


def _error_check(result, _func, _arguments):
    if result:
        raise ReaderException(result)
    return result


_auto_open_com_port = READER_DLL.AutoOpenComPort
_auto_open_com_port.argtypes = (
    POINTER(c_long), POINTER(c_uint8), POINTER(c_uint8), POINTER(c_long)
)
_auto_open_com_port.restype = c_long
_auto_open_com_port.errcheck = _error_check


def auto_open_com_port(port=0, com_address=0xff, baud=BAUD_57600):
    port = c_long(port)
    com_address = c_uint8(com_address)
    baud = c_uint8(baud)
    handle = c_long()
    _auto_open_com_port(
        byref(port), byref(com_address), byref(baud), byref(handle)
    )
    return port.value, com_address.value, baud.value, handle
Wobei man wie schon gesagt, dann am besten die entsprechenden Werte die immer wieder gebraucht werden in Objekten zusammenfasst statt die Funktionen tatsächlich als Funktionen 1:1 abzubilden.

Re: Problem mit Daten in ctypes

Verfasst: Freitag 15. Januar 2016, 11:35
von manlud80
Hallo Blackjack,

das hat ja schonmal super funktioniert mit dem Comport. Vielen Dank.

Wenn du mir jetzt noch helfen könntest, die Inventory_G2 erfolgreich auszuführen, dann wäre ich restlos glücklich :-)

Der folgende Code spuckt den Fehler 0x01 aus (Return before Inventory finished):

Code: Alles auswählen

from ctypes import byref, c_long, c_uint8, POINTER, WinDLL, c_ubyte, cast
 
READER_DLL = WinDLL('nameofdll.dll')
(
    BAUD_9600,
    BAUD_19200,
    BAUD_38400,
    _,
    BAUD_56000,
    BAUD_57600,
    BAUD_115200
) = xrange(7)
 
 
class ReaderException(Exception):
 
    def __init__(self, error_code):
        Exception.__init__(self, 'Error code 0x{0:x}'.format(error_code))
        self.error_code = error_code
 
 
def _error_check(result, _func, _arguments):
    if result:
        raise ReaderException(result)
    return result
 
 
_auto_open_com_port = READER_DLL.AutoOpenComPort
_auto_open_com_port.argtypes = (
    POINTER(c_long), POINTER(c_uint8), POINTER(c_uint8), POINTER(c_long)
)
_auto_open_com_port.restype = c_long
_auto_open_com_port.errcheck = _error_check
 

_Inventory_G2 = READER_DLL.Inventory_G2
_Inventory_G2.argtypes = (
    POINTER(c_ubyte), c_ubyte, c_ubyte, c_ubyte, POINTER(c_ubyte), POINTER(c_long), POINTER(c_long), c_long
)
_Inventory_G2.restype = c_long
_Inventory_G2.errcheck = _error_check

 
def auto_open_com_port(port=8, com_address=0xff, baud=BAUD_57600):
    port = c_long(port)
    com_address = c_uint8(com_address)
    baud = c_uint8(baud)
    handle = c_long()
    _auto_open_com_port(
        byref(port), byref(com_address), byref(baud), byref(handle)
    )
    return port, com_address, baud, handle

def Inventory_G2(com_address, handle): 
    AdrTID=c_ubyte(0)
    LenTID=c_ubyte(0)
    TIDFlag=c_ubyte(0)
    EPClenandEPC=(c_ubyte*5000)()
    Totallen=c_long(0)
    CardNum=c_long(0)
    _Inventory_G2(
        byref(com_address), AdrTID, LenTID, TIDFlag, cast(EPClenandEPC, POINTER(c_ubyte)), byref(Totallen), byref(CardNum), handle
    )
    print EPClenandEPC
    return com_address, AdrTID, LenTID, TIDFlag, EPClenandEPC, Totallen, CardNum, handle

[port, com_address, baud, handle]=auto_open_com_port()
[com_address, AdrTID, LenTID, TIDFlag, EPClenandEPC, Totallen, CardNum, handle] = Inventory_G2(com_address, handle)
Kannst du nochmal helfen?

Re: Problem mit Daten in ctypes

Verfasst: Freitag 15. Januar 2016, 13:51
von manlud80
Hallo zusammen,

ich wollte nur kurz mitteilen, dass ich die Lösung gefunden habe. Vielen Dank für eure Hilfe und Anregungen. Im Speziellen danke an BlackJack.

Re: Problem mit Daten in ctypes

Verfasst: Freitag 15. Januar 2016, 14:07
von BlackJack
@manlud80: Was war denn die Lösung? Die Scanzeit erhöhen?

Neben dem PDF für die DLL gibt's da auch noch dieses: http://www.rfidshop.com.hk/datasheet/UH ... 20V1.2.pdf

Das Englisch ist ziemlich grauenhaft und so richtig schlau werde ich dort aus der Beschreibung von Fehlercode 0x01 nicht.

Aber so wie es aussieht ist dort das serielle Protokoll beschrieben was die DLL ja letztendlich umsetzt. Ich glaube ich würde eher das Protokoll in Python implementieren statt mich mit einer Windows-DLL herum zu schlagen. :-)

Re: Problem mit Daten in ctypes

Verfasst: Freitag 15. Januar 2016, 14:37
von manlud80
Die Lösung war, dass ich beim Inventory-Scan nicht den error-check machen darf bzw. erst warten muss, bis der Inventory Scan fertig ist.

Hmm, das Protokoll in Python implementieren klingt gut, aber ich habe da leider keine Erfahrung mit.