ctypes & callback funktionen

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
ScooB
User
Beiträge: 54
Registriert: Donnerstag 28. April 2011, 10:47

Hi

ich hab da mal ne Frage

ich hab da ne C++ Funktion

Code: Alles auswählen

AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_SYM_VALBYHND, hUser, &adsNotificationAttrib, Callback, hUser, &hNotification);
dich ich über Ctypes aufrufe

Code: Alles auswählen

error = self.ads_library.AdsSyncAddDeviceNotificationReq(ctypes.pointer(self.padress),ads_struct.ADSIGRP_SYM_VALBYHND,handel, adsNotificationAttrib, tempcall ,handel, hNotification)
was auch soweit Funktioniert das ich keinen error zurück bekomme.

Problem an die Funktion wird ne Callback Funktion übergeben die dann immer Werte erhält und verarbeitet.

Meine Frage ist wie kann ich die Callback-Funktion umsetzen in Python
Den Orginal C++ Code kann man sich unter folgendem Link anschauen http://infosys.beckhoff.com/index.php?c ... m&id=11350

Die Callback Funktion ist nicht in einer der C++ Bibliothek enthalten sie ist nur in dem Bsp-Code enthalten und ich möchte in Python halt eine eigene schreiben.

Danke schon mal, bin über jeden Tip dankbar

Gruß ScooB
Benutzeravatar
snafu
User
Beiträge: 6753
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

ScooB
User
Beiträge: 54
Registriert: Donnerstag 28. April 2011, 10:47

Soweit bin ich auch schon gekommen

Code: Alles auswählen

    def callback2(self, pAddr, pNotification, hUser):
        print 'test', pAddr, pNotification, hUser
        print pNotification.data
        return 5

Code: Alles auswählen

cf=ctypes.CFUNCTYPE(None, ctypes.POINTER(ads_struct.PAmsAddr), ctypes.POINTER(ads_struct.AdsNotificationHeader), ctypes.POINTER(ctypes.c_ulong))
callback = cf(self.callback2)
und callback dann übergeben

aber tut sich leider nichts
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

So wie ich die Dokumentation verstehe sollte das so aussehen (vorrausgesetzt, dass deine Argumente richtig sind):

Code: Alles auswählen

cf=ctypes.CFUNCTYPE(c_int, ctypes.POINTER(ads_struct.PAmsAddr), ctypes.POINTER(ads_struct.AdsNotificationHeader), ctypes.POINTER(ctypes.c_ulong))
Und der Callback (self hat da nichts zu suchen):

Code: Alles auswählen

def callback(pAddr, pNotification, hUser):
    print 'test', pAddr, pNotification, hUser
    print pNotification.data
    return 5
the more they change the more they stay the same
Benutzeravatar
snafu
User
Beiträge: 6753
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@Dav1d (bzgl. `self`): Der OP scheint den Callback als Methode implementiert zu haben, wo ja dann schon ein `self` hin muss. Ob das einen Unterschied für `ctypes` macht, weiß ich nicht. Könnte man natürlich ausprobieren. Bin ich aber gerade zu faul für. :mrgreen:

Ansonsten halt mal abwarten, ob BlackJack oder so noch was dazu sagen kann. Ich hab offen gestanden zu wenig Ahnung von `ctypes`, um jetzt irgendeine weitergehende Problemanalyse zu machen...
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Hab das gerade mal getestet, bei mir funktioniert das auch mit Methoden, selbst der Zugriff auf ein anderes Attribut wird richtig ausgeführt:

Code: Alles auswählen

>>> from ctypes import *
>>> shared=cdll.LoadLibrary('libtest.so')
>>> f_proto = CFUNCTYPE(c_int, c_int)
>>> class Bar(object):
...   def __init__(self):
...     self.v = 10
...   def f(self, a):
...     return self.v + a
... 
>>> bar=Bar()
>>> f_obj = f_proto(bar.f)
>>> shared.test(10, f_obj)
20
>>> shared.test(0, f_obj)
10
Die c-Funktion:

Code: Alles auswählen

int test(int a, void *cb) {
    int (*f)(int);
    f = cb;
    return f(a);
}
Allerdings weiss ich nicht, ob bzw. wie gut die Benutzung von Methoden offiziell unterstützt wird. Das self scheint zumindest für die Deklaration kein Problem zu sein.
deets

Das ist prinzipiell egal. Es geht um ein callable, und eine bound method ist ein callable. Solange eine Referenz darauf existiert (und die haelt ctypes ja selbst), geht das also.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Interessant, ich dachte das ginge nicht.
the more they change the more they stay the same
ScooB
User
Beiträge: 54
Registriert: Donnerstag 28. April 2011, 10:47

Mal was anderes wie halte ich das "Programm" am laufen weil wenn ich es ausführe wird es danach beendet.

Oder soll ich die Funktion in nen eigenen Thread packen??

Ich steh echt auf'm Schlauch und komm nicht weiter mit der Sache.

Also nur damit man es versteht was ich da mache.
Ich möchte werte aus ner SPS lesen wenn sie sich verändern, dafür gibt es die Funktion AdsSyncAddDeviceNotificationReq(...) (welche in der Bibliothek enthalten ist, die Callback nicht) mit der man sozusagen eine Variable der SPS überwacht und falls sie sich ändert eine Funktion aufgerufen wird. In C++ Funktioniert das ganze ja auch, nur ich soll es halt in Python umsetzen.

Mein Status ist soweit das wenn ich die AdsSyncAddDeviceNotificationReq() aufrufe die Notification regestriert wird und was auch gut ist, nur wie vielleicht schon gemerkt steh ich halt bei der Callback Funktion auf dem schlau.

Falls nützlich hier mein "Test-herumspiel-Code", bei der Callback habe ich mal nen Return mal keinen nur damit ihr nicht drucheinander kommt is halt mein Testcode

Code: Alles auswählen

class Ads:
    #ADS Bibliothek Laden
    ads_library = ctypes.windll.LoadLibrary(r'C:\TwinCAT\ADS Api\TcAdsDll\TcAdsDll.dll')
    padress = 0
    def __init__(self, ip=None):
            self.connect()
            
    def __del__(self):
        self.disconnect()
        
        
    def connect(self):
        port = self.ads_library.AdsPortOpen()
        self.padress = ads_struct.PAmsAddr()
        self.ads_library.AdsGetLocalAddress(ctypes.pointer(self.padress))
        self.padress.port = ads_struct.AMSPORT_R0_PLC_RTS1
        return self.padress

    def disconnect(self):
        error = self.ads_library.AdsPortClose()

    def callback(self):
        print 'test callback'
        
    def callback2(self, pAddr, pNotification, hUser):
        print 'test', pAddr, pNotification, hUser
        print pNotification.data

    def notification(self, var_name):
        
        var_name = ctypes.create_string_buffer(var_name)
        adsNotificationAttrib = ctypes.pointer(ads_struct.AdsNotificationAttrib())
        
        adsNotificationAttrib.cbLength = 4
        adsNotificationAttrib.nTransMode = ads_struct.ADSTRANS_SERVERONCHA
        adsNotificationAttrib.nMaxDelay = 0
        adsNotificationAttrib.nCycleTime = 10000   #1000000
        adsNotificationAttrib.dwChangeFilter = 10000  #1000000
        
        handel = ctypes.c_ulong(0)
        hNotification = ctypes.c_ulong(0)
        
        cf=ctypes.CFUNCTYPE(None, ctypes.POINTER(ads_struct.PAmsAddr), ctypes.POINTER(ads_struct.AdsNotificationHeader), ctypes.POINTER(ctypes.c_ulong))
        tempcall = cf(self.callback2)


        error = self.ads_library.AdsSyncReadWriteReq(ctypes.pointer(self.padress),ads_struct.ADSIGRP_SYM_HNDBYNAME,0x0, ctypes.sizeof(handel), ctypes.pointer(handel), ctypes.sizeof(var_name),ctypes.pointer(var_name))
        print error, "error 1"

        error = self.ads_library.AdsSyncAddDeviceNotificationReq(ctypes.pointer(self.padress),ads_struct.ADSIGRP_SYM_VALBYHND,handel, adsNotificationAttrib, tempcall,handel, ctypes.pointer(hNotification))
        
        print error, "error 2"
        print hNotification , 'hNotification'
        
        error = self.ads_library.AdsSyncDelDeviceNotificationReq(ctypes.pointer(self.padress), hNotification)
        
        print error, "error 3"
        
        
test = Ads()
test.notification('MAIN.PLCVar0')
ScooB
User
Beiträge: 54
Registriert: Donnerstag 28. April 2011, 10:47

Keiner nen Tip ???
deets

Code: Alles auswählen

while True:
   time.sleep()
Ob das reicht? KA. Kenne deine Lib ja nicht, ob die einen mainloop call braucht. Oh, und __del__ gewoehn dir mal ganz schnell ab. Explizites resourcen-management ist zuverlaessiger, es gibt keine Garantie von Python das __del__ ueberhaupt aufgerufen wird.
ScooB
User
Beiträge: 54
Registriert: Donnerstag 28. April 2011, 10:47

Nein Das reicht aus leider
:(
deets

Ich nehme mal an du meinst "reicht *nicht* aus".

Dann musst du halt schauen, wie deine Lib periodisch aufgerufen werden will.
Antworten