ctypes function callbacks, python-methode in c-struct

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
ddpf
User
Beiträge: 21
Registriert: Freitag 22. Februar 2013, 12:13

Ich habe mit Hilfe der ctypeslib c-Funktionen/Typen in Python definiert (ist ein großes Projekt) und eine dll erstellt. Es handelt sich hier um C-Code der auf embedded Targets läuft. Ein zentrales Element ist die Datenstruktur in Code1. Die Datenstruktur ist in Code2 via ctypeslib konvertiert. Hierbei sind die callback functions im C-code der dll nicht definiert (müssen später eingehangen werden). Es gibt bereits ein c-file (Code3) in denen die Funktionen eingehangen sind (Aber nicht in der dll).

Was ich bis jetzt habe ist in Code 4 definiert. Ich komme jetzt allerdings nicht weiter. Wie kann ich die callback Function einhängen? Sprich eine Python-methode als C-callback function einhängen. z.B. wie linke ich timer_cnfg_mode_primary_p auf timer_cnfg_mode_primary'?
Code1:

Code: Alles auswählen

typedef struct {
	...;
	volatile uint16_t huge *ad_prim[8];
	volatile uint16_t huge *ad_sec[8];
	...
	unsigned short_t (*timer_cnfg_mode_primary)(unsigned short channel, unsigned char mode);
	unsigned short (*timer_cnfg_input_primary)(unsigned short channel, unsigned char input);
	unsigned short (*timer_cnfg_mode_secondary)(unsigned short channel,  unsigned char mode);
	unsigned short (*timer_cnfg_input_secondary)(unsigned short channel,  unsigned char input);
	...
}data_t;
Code2:

Code: Alles auswählen


data_t(Structure):
    pass

data_t._fields_ = [
    ...,
    ('ad_prim', POINTER(c_uint16) * 8),
    ('ad_sec', POINTER(c_uint16) * 8),
    ...,
    ('timer_cnfg_mode_primary', CFUNCTYPE(c_uint16, c_uint16, c_ubyte)),
    ('timer_cnfg_input_primary', CFUNCTYPE(c_uint16, c_uint16, c_ubyte)),
    ('timer_cnfg_mode_secondary', CFUNCTYPE(c_uint16,c_uint16, c_ubyte)),
    ('timer_cnfg_input_secondary', CFUNCTYPE(c_uint16, c_uint16, c_ubyte)),
    ...,
]
Code3:

Code: Alles auswählen


static  timer_primary[8];
static  timer_primary[[8];

static const data_t data = {
	...,


	
	&timer_primary[0],
	&timer_primary[1],
	&timer_primary[2],
	&timer_primary[3],
	&timer_primary[4],
	&timer_primary[5],
	&timer_primary[6],
	&timer_primary[7],

	&timer_secondary[0],
	&timer_secondary[1],
	&timer_secondary[2],
	&timer_secondary[3],
	&timer_secondary[4],
	&timer_secondary[5],
	&timer_secondary[6],
	&timer_secondary[7],
	
	&timer_cnfg_mode_primary,
	&timer_cnfg_input_primary,

	&timer_cnfg_mode_secondary,
	&timer_cnfg_input_secondary,

	...

};

static unsigned short timer_cnfg_mode_primary(unsigned short channel, unsigned char mode)
{
	switch(mode)
	{
		case MODE_INIT:
			timer_primary[channel] = 0;
			break;

		default:
			break;
	}
	return(0);
}

static unsigned short timer_cnfg_input_primary(unsigned short channel, unsigned char input)
{
	return(0);
}



static unsigned short timer_cnfg_mode_secondary(unsigned short channel, unsigned char mode)
{
	switch(mode)
	{
		case MODE_INIT:
			timer_secondary[channel] = 0;
			break;

		default:
			break;
	}
	return(0);
}



static unsigned short timer_cnfg_input_secondary(unsigned short channel, unsinged short input)
{
	return(0);
}
Code4:

Code: Alles auswählen

class Error(Exception):
    pass

class Error_NOT_FOUND(Exception):
    pass

def _error_check(result, func, arguments):
    if result != 0
        if result ==  1:
            raise Error_NOT_FOUND
        else:        
            raise Error('%s%r returned %d' % (func, arguments, result))

....
dll.init.errcheck = _error_check

class IFXX(dll.data_t):    
    def __init__(self, mod_nr, type_module):
        dll.data_t.__init__(self)
        ...


    ...


    def timer_cnfg_mode_primary_p(channel, mode):
        if mode == dll.MODE_INIT:
            self.timer_primary[channel].value  
                        
        return 0

    def timer_cnfg_input_primary_p(channel, input):
        return 0


    def timer_cnfg_mode_secondary_p(channel, mode):
        if mode ==dll.INIT:
            self.timer_secondary[channel].value  
                        
        return 0

    def timer_cnfg_input_secondary_p(channel, input):
        return 0
    
        
Zuletzt geändert von ddpf am Mittwoch 29. Mai 2013, 10:05, insgesamt 3-mal geändert.
BlackJack

@ddpf: Die Objekte, die den C-Funktionsdatentyp repräsentieren, kann man mit einer Python-Funktion aufrufen um ein Objekt zu bekommen das als Callback an C-Funktionen übergeben werden kann. Wenn man aus dem Feldnamen automatisiert den Methodennamen erstellen kann, dann könnte man in der `__init__()` die Methoden in einer Schleife eintragen.

Ungetestet:

Code: Alles auswählen

class IFXX(data_t):    
    def __init__(self, mod_nr, type_module):
        data_t.__init__(self)
        # ...
        for name, c_type in self._fields_:
            if name.startswith('timer_cnfg_'):
                method = getattr(self, name + '_p', None)
                if method:
                    setattr(self, name, c_type(method))
ddpf
User
Beiträge: 21
Registriert: Freitag 22. Februar 2013, 12:13

Habe das jetzt wie folgt gelöst:

Code: Alles auswählen

self.timer_cnfg_mode_primary = CFUNCTYPE(c_uint16, c_uint16, c_ubyte)(self.timer_cnfg_mode_primary_p)        
  ...
(Ist für alle Methoden im Konstruktor zu machen)

Außerdem muss man noch die Pointer initialisieren

Sprich

Code: Alles auswählen

while i <  8:
            self.timer_primary[i].contents = c_ulong()
            self.timer_secondary[i].contents = c_ulong()
            i += 1      

Somit sieht die Pythonmethode 1 wie folgt aus:

Code: Alles auswählen

    def timer_cnfg_mode_primary_p(self, channel, mode):
        if mode == dll.MODE_INIT:
            self.timer_primary[channel][0]  = 0
        return 0

Zuletzt geändert von ddpf am Mittwoch 5. Juni 2013, 13:15, insgesamt 1-mal geändert.
BlackJack

@ddpf: Warum hast Du für die beiden Arrays denn eine ``while`` statt einer ``for``-Schleife genommen? Ich würde die 8 dort auch nicht hart kodieren, sondern die Länge des Arrays verwenden, oder es komplett ohne Index lösen. `c_ulong` dürfte für Felder die eigentlich Zeiger auf den C-Typ `uint16_t` aufnehmen auch etwas zu gross sein, da würde ich den tatsächlich passenden Typ verwenden. Ungetestet:

Code: Alles auswählen

        assert len(self.timer_primary) == len(self.timer_secondary)
        for primary_ptr, secondary_ptr in izip(
                self.timer_primary, self.timer_secondary
        ):
            primary_ptr.contents = c_uint16()
            secondary_ptr.contents = secondary_ptr._type_()
Die letzte Zeile wäre eine typsicherere Alternative zur Vorletzten. (`izip` ist aus `itertools`)
ddpf
User
Beiträge: 21
Registriert: Freitag 22. Februar 2013, 12:13

Ich habe es sowieso die while-Schleife mit len() begrenzt. War mal nur fürs schnelle posten gedacht.

PS: Gibts was eleganteres als self.timer_primary[channel][0] = 0? Mit dem [0] "dereferenziere" ich ja nur den Speicher (Oder wie man das dereferenzieren in Python ctypeslibs jetzt auch immer nennt).
BlackJack

@ddpf: Es sollte auch ``self.timer_primary[channel].contents.value = 0`` gehen. Da finde ich persönlich das ``[0]`` etwas besser, weil das syntaktisch in C auch so ginge wenn man den ``*`` zum dereferenzieren nicht nehmen dürfte.
Antworten