Ctypes Hilfe

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
zhinek20
User
Beiträge: 10
Registriert: Mittwoch 15. Dezember 2010, 09:19

Hallo Leute,

ich bin Neuling im Umgang mit ctypes. Ich habe mir schon sämtliche Tutorials über ctypes durchgelesen und möchte gerne in meinem Programm auf eine dll mit ctypes zugreifen und von dort aus Funktionen benutzen.

Es geht um ein USBCAN modul, was ich gerne ansteuern will mit der Lib von den Herstellern.
Ich habe versucht mir z.B. die Funkton UcanInitHardware() aus der dll zu laden und auch richtig zu verwenden, bekomme aber immer wieder den Fehler: "ValueError:Procedur called with not enough arguments..."

Die funktion sieht folgender maßen aus (in C):

Code: Alles auswählen

[b]UcanInitHardware[/b]
[b]Syntax:[/b]

         UCANRET PUBLIC UcanInitHardware (
                                   tUcanHandle*                 pUcanHandle_p,
                                   BYTE                             bDeviceNr_p,
                                   tCallbackFkt                   fpCallbackFkt_p);
Die tCallbackFkt kann NULL sein.

genauere Informationen über die Funktion kann man dem Datenblatt entnehmen: http://www.systec-electronic.com/upload ... 87d_22.pdf

wenn ich die Funktion in Python aufrufen will, mache ich folgendes:

Code: Alles auswählen

from ctypes import *
from ctypes import util

cpath=util.find_library("USBCAN32")         #Pfad zur Bib.
libc = CDLL(cpath)                          #Lade Bib.

s="tUcanHandle"
byte = c_byte()
c_b = create_string_buffer(s)

InitHardware = libc.UcanInitHardware
InitHardware(byte,byref(c_b))

Als Fehler wird mir immer das ausgegeben:
[code=python]D:\eclipse\workspace\.metadata\.plugins\org.python.pydev.debug\.coverage
Traceback (most recent call last):
  File "E:\Program Files (x86)\eclipse\plugins\org.python.pydev.debug_1.6.3.2010100513\pysrc\coverage.py", line 1029, in <module>
    the_coverage.command_line(sys.argv[1:])
  File "E:\Program Files (x86)\eclipse\plugins\org.python.pydev.debug_1.6.3.2010100513\pysrc\coverage.py", line 405, in command_line
    execfile(sys.argv[0], __main__.__dict__)
  File "D:\eclipse\workspace\Ctype\src\Ctype_Test.py", line 22, in <module>
    InitHardware(byte,byref(c_b))
ValueError: Procedure called with not enough arguments (12 bytes missing) or wrong calling convention
kann mir da jemand helfen??

mfg zhinek20
BlackJack

@zhinek20: Also erst einmal musst Du alle Argumente übergeben. Das ein Argument auch ``NULL`` sein darf, bedeutet ja nicht, dass man es einfach weglassen darf, sondern dass man dort eben auch ``NULL`` als Wert übergeben kann, dass dann aber auch wirklich tun muss, wenn man den Wert übergeben möchte. In Python wäre dass dann `None`.

Dann schau Dir noch einmal die Reihenfolge der Argumente an.

Und zu guter Letzt muss die Aufrufkonvention stimmen, dass heisst je nach dem welche die Bibliothek benutzt, muss man cdll oder windll verwenden um sie zu laden.

Auch wenn man das unter Windows wohl nicht machen muss, würde ich immer die Typinformationen `argtypes` und `restype` auf den Funktionsobjekten setzen. Das dient der Dokumentation und man bekommt mehr Rückmeldung wenn man dabei etwas falsch macht.
zhinek20
User
Beiträge: 10
Registriert: Mittwoch 15. Dezember 2010, 09:19

Danke schon mal für die erste schnelle Antw.

Die genaue Reihenfolge sieht so aus: bsp. aus Usermanual:

Code: Alles auswählen

bRet = UcanInitHardware (&UcanHandle, USBCAN_ANY_MODULE, NULL);

-die Konstante: USBCAN_ANY_MODULE=255 (laut Datenblatt,öffnet ersten CAn Port der gefunden wird)
-Den Wert None(NULL) hab ich jetzt eingetragen.
-außerdem hab ich die WinDLL Anweisung benutzt.

in Python sieht das jetzt so aus bei mir:

Code: Alles auswählen

from ctypes import *
from ctypes import util

cpath=util.find_library("USBCAN32")         #Pfad zur Bib.
libc = WinDLL(cpath)                          #Lade Bib.
print "LibCAN:",libc

USBCAN_ANY_MODULE = c_int(255)
s="tUcanHandle"
byte = c_byte()
c_b = create_string_buffer(s)


InitHardware = libc.UcanInitHardware
init=InitHardware(c_b,USBCAN_ANY_MODULE,None)

print init

Jetzt werden keine Fehler angezeigt.
Auch der ausgegebene Wert von "init" ändert sich, wenn ich das CAN Modul anschließe.
Wenn es nicht Angeschlossen ist, ist c_b = 5
Wenn es angeschlossen ist, ist c_b=0

Frage:Habe ich die übergabe von c_b richtig gemacht??
BlackJack

@zhinek20: Vielleicht ist das "richtig", vielleicht aber auch nicht. Du müsstest herausfinden wie der Typ für das erste Argument aussieht, oder zumindest wie gross der ist, denn bei vielen anderen Funktionen muss man anscheinend den Wert selbst und keinen Zeiger darauf übergeben.

Wenn die Initialisierung fehlschlägt, dürfte der Wert von `c_b` (wofür auch immer der Name stehen soll) undefiniert sein. Du musst auf jeden Fall den Rückgabewert der Funktion betrachten.

Auf der Python-Seite würde ich dann aus Fehlerwerten Ausnahmen machen. `ctypes` bietet da auch die Möglichkeit eine Prüffunktion an Funktionen zu binden. Da die Bibliotheksfunktionen, die Du da hast, wenn sie einen Fehlerwert liefern, den aus der selben "Menge" von möglichen Werten liefern, kann man so eine Prüffunktion anscheinend ganz gut für verschiedene Bibliotheksfunktionen wiederverwenden.

Auf der Python-Seite solltest Du dann auch überlegen "pythonischere" Python-Funktionen, bzw. Klassen und Methoden anzubieten wo das Sinn macht, statt einfach nur die C-API 1:1 zu übernehmen. Bei der Init-Funktion würden sich zum Beispiel Default-Werte für die beiden letzten Argumente anbieten. Und da man das "handle" an viele andere Funktionen übergeben muss, wäre das ein Kandidat um eine Klasse darauf aufzubauen.
zhinek20
User
Beiträge: 10
Registriert: Mittwoch 15. Dezember 2010, 09:19

Das sind erste Versuche Kontakt mit dem Modul aufzunehmen und mit ctypes bisschen experementieren.
Später wollte ich auch natürlich Classen bilden und alles dokumentieren, damit ich es später leichter habe.

Ich habe gerade auch gefunden, was die beiden Werte bedeuten (die "0" und "5"), die mein c_b wider gibt.
Wenn eine "0x00" wider gegeben wird heisst das, dass die Funktion erfolgreich ausgeführt wurde.
Wird eine 0x05 zurückgegeben, wurde kein Gerät gefunden.
Scheint richtig zu sein!?!?

c_b ist einfach nur ein frei gewählter name:) . Ich gebs zu eimbisschen einfallslos.:)

Danke erst mal für deine Hilfe, wenn ich Hilfe brauche schreibe ich einfach wieder, wenn du nichts dagegen hast?;)

gruß zhinek20
lunar

@zhinek20: Ob das "richtig" ist, musst Du doch selbst wissen. Schließlich musst Du ja irgendeine Erwartung davon haben, was die Funktion zurückgibt. Im Zweifelsfall musst Du eben die Dokumentation der C-Funktion zu Rate ziehen.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Was ich auch Ändern würde: ``libc`` gegen einen anderen Namen welchseln, sonst denkt man man hat die ``libc`` angebunden, sowie keine *-Importe (auch wenn das leider so im ctypes Tutorial steht).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@zhinek20: Wo bekommst Du denn 0 und 5? Du musst den Rückgabewert der Funktion betrachten, nicht was in das erste Argument geschrieben wird. Wenn der Rückgabewert nicht der Konstanten `USBCAN_SUCCESSFUL` entspricht dann ist alles andere undefiniert.
zhinek20
User
Beiträge: 10
Registriert: Mittwoch 15. Dezember 2010, 09:19

@BlackJack

USBCAN_SUCCESSFUL ist ja eine in der C-Lib definierte Konstante, die Python gar nit als solche kennt.
Wenn man in die System Manual reinschaut welchen wert die Kontante USBCAN_SUCCESSFUL hat, kann man mit der ausgabe vergleichen. Bei mir gibt die Funktion eine "0" aus wenn das Modul angeschlossen ist und wenn nicht angeschlossen gibt diese den Wert "5" aus. Wenn von dieser Funktion eine "0" zurückgegeben wird, dann ist es gleich "USBCAN_SUCCESSFUL".
Bei rückgabe von "5" ist es gleich "USBCAN_ERR_ILLHW", laut Datenblatt S.124
BlackJack

@zhinek20: Das ist mir alles klar, aber Du hast geschrieben dass Du den Wert in Deinem `c_b` bekommst, und *da* steht nicht der Rückgabewert der Funktion, sondern ein Handle. Wenn Du *dort* eine 5 liest, dann ist das eben nicht der Rückgabewert 5 sondern das Handle mit dem Wert 5, allerdings auch nur wenn der Rückgabewert 0 war. Ansonsten ist was immer in `c_b` steht undefiniert.
zhinek20
User
Beiträge: 10
Registriert: Mittwoch 15. Dezember 2010, 09:19

@BlackJack

Sorry ich habe mich vertan. Ich meinte nicht "c_b" sondern "init" dort steht der Rückgabewert.
Du hast recht in c_b ist das handle.
Antworten