2016-04-01 11 views
-1

Ich versuche, die HappyTime ONVIF-Bibliothek von Swift aufrufen.Calling Objective C und C von Swift Passing Callback-Funktion

Ich habe die Bibliothek in meinem Projekt verknüpft und ich bin in der Lage, einige einfache Funktionen aufzurufen, aber ich habe Probleme beim Abrufen der Syntax in meinem Anruf, die meine Callback-Funktion übergibt. Hier

ist der Swift Code:

func discoverCameras() 
{ 
    HappyInterface.sharedInstance().startProb() 

//this line gives syntax error 
    HappyInterface.sharedInstance().setProbeCB(cameraDiscovered) 
} 

func cameraDiscovered(cameraFound:UnsafeMutablePointer<DEVICE_BINFO>) 
{ 
    table.reloadData() 
} 

mein setProbeCB Anruf diesen Fehler gibt:

Cannot convert value of type '(UnsafeMutablePointer) ->()' to expected argument type 'UnsafeMutablePointer' (aka 'UnsafeMutablePointer, UnsafeMutablePointer<()>) ->()>>')

Hier ist die Obj C Umsetzung:

- (void) setProbeCB:(onvif_probe_cb *)cb { 
    set_probe_cb(*cb, 0); 

} 

Dies ist Obj C Überschrift:

- (void) setProbeCB:(onvif_probe_cb *)cb; 

Dies ist der C-Header:

#ifndef __H_ONVIF_PROBE_H__ 
#define __H_ONVIF_PROBE_H__ 

#include "onvif.h" 


typedef void (* onvif_probe_cb)(DEVICE_BINFO * p_res, void * pdata); 

#ifdef __cplusplus 
extern "C" { 
#endif 

ONVIF_API void set_probe_cb(onvif_probe_cb cb, void * pdata); 
ONVIF_API void set_probe_interval(int interval); 
ONVIF_API int start_probe(int interval); 
ONVIF_API void stop_probe(); 
ONVIF_API void send_probe_req(); 


#ifdef __cplusplus 
} 
#endif 

#endif // __H_ONVIF_PROBE_H__ 

Dies ist der Code C:

/***************************************************************************************/ 
#define MAX_PROBE_FD 8 


/***************************************************************************************/ 
onvif_probe_cb g_probe_cb = 0; 
void * g_probe_cb_data = 0; 
pthread_t g_probe_thread = 0; 
int g_probe_fd[MAX_PROBE_FD]; 
int g_probe_interval = 30; 
BOOL g_probe_running = FALSE; 


/***************************************************************************************/ 
int onvif_probe_init(unsigned int ip) 
{ 
    int opt = 1; 
    SOCKET fd; 
    struct sockaddr_in addr; 
    struct ip_mreq mcast; 

    fd = socket(AF_INET, SOCK_DGRAM, 0); 
    if(fd < 0) 
    { 
     log_print(LOG_ERR, "socket SOCK_DGRAM error!\n"); 
     return -1; 
    } 

    addr.sin_family = AF_INET; 
    addr.sin_port = htons(3702); 
    addr.sin_addr.s_addr = ip; 

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 
    { 
     // if port 3702 already occupied, only receive unicast message 
     addr.sin_port = 0; 
     if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 
     { 
      closesocket(fd); 
      log_print(LOG_ERR, "bind error! %s\n", sys_os_get_socket_error()); 
      return -1; 
     } 
    } 

    /* reuse socket addr */ 
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt))) 
    { 
     log_print(LOG_WARN, "setsockopt SO_REUSEADDR error!\n"); 
    } 

    memset(&mcast, 0, sizeof(mcast)); 
    mcast.imr_multiaddr.s_addr = inet_addr("239.255.255.250"); 
    mcast.imr_interface.s_addr = ip; 

    if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast, sizeof(mcast)) < 0) 
    { 
#if __WIN32_OS__ 
     if(setsockopt(fd, IPPROTO_IP, 5, (char*)&mcast, sizeof(mcast)) < 0) 
#endif  
     { 
      closesocket(fd); 
      log_print(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP error! %s\n", sys_os_get_socket_error()); 
      return -1; 
     } 
    } 

    return fd; 
} 

char probe_req1[] = 
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>" 
    "<Envelope xmlns:tds=\"http://www.onvif.org/ver10/device/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">" 
    "<Header>" 
    "<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:%s</wsa:MessageID>" 
    "<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>" 
    "<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>" 
    "</Header>" 
    "<Body>" 
    "<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">" 
    "<Types>tds:Device</Types>" 
    "<Scopes />" 
    "</Probe>" 
    "</Body>" 
    "</Envelope>"; 

char probe_req2[] = 
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>" 
    "<Envelope xmlns:dn=\"http://www.onvif.org/ver10/network/wsdl\" xmlns=\"http://www.w3.org/2003/05/soap-envelope\">" 
    "<Header>" 
    "<wsa:MessageID xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">uuid:%s</wsa:MessageID>" 
    "<wsa:To xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>" 
    "<wsa:Action xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>" 
    "</Header>" 
    "<Body>" 
    "<Probe xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\">" 
    "<Types>dn:NetworkVideoTransmitter</Types>" 
    "<Scopes />" 
    "</Probe>" 
    "</Body>" 
    "</Envelope>"; 

int onvif_probe_req_tx(int fd) 
{ 
    int len; 
    int rlen; 
    char * p_bufs = NULL; 
    struct sockaddr_in addr; 

    int buflen = 10*1024; 

    p_bufs = (char *)malloc(buflen); 
    if (NULL == p_bufs) 
    { 
     return -1; 
    } 

    memset(p_bufs, 0, buflen); 
    sprintf(p_bufs, probe_req1, onvif_uuid_create()); 

    memset(&addr, 0, sizeof(addr)); 

    addr.sin_family = AF_INET; 
    addr.sin_addr.s_addr = inet_addr("239.255.255.250"); 
    addr.sin_port = htons(3702); 

    len = strlen(p_bufs); 
    rlen = sendto(fd, p_bufs, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); 
    if (rlen != len) 
    { 
     log_print(LOG_ERR, "onvif_probe_req_tx::rlen = %d,slen = %d\r\n", rlen, len); 
    } 

    usleep(1000); 

    memset(p_bufs, 0, buflen); 
    sprintf(p_bufs, probe_req2, onvif_uuid_create()); 

    len = strlen(p_bufs); 
    rlen = sendto(fd, p_bufs, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); 
    if (rlen != len) 
    { 
     log_print(LOG_ERR, "onvif_probe_req_tx::rlen = %d,slen = %d\r\n", rlen, len); 
    } 

    free(p_bufs); 

    return rlen; 
} 

BOOL onvif_parse_device_binfo(XMLN * p_node, DEVICE_BINFO * p_res) 
{ 
    XMLN * p_EndpointReference; 
    XMLN * p_Types; 
    XMLN * p_XAddrs; 

    p_EndpointReference = xml_node_soap_get(p_node, "EndpointReference"); 
    if (p_EndpointReference) 
    { 
     XMLN * p_Address = xml_node_soap_get(p_EndpointReference, "Address"); 
     if (p_Address && p_Address->data) 
     { 
      strncpy(p_res->EndpointReference, p_Address->data, sizeof(p_res->EndpointReference)-1); 
     } 
    } 

    p_Types = xml_node_soap_get(p_node, "Types"); 
    if (p_Types && p_Types->data) 
    { 
     p_res->type = parse_DeviceType(p_Types->data); 
    } 

    p_XAddrs = xml_node_soap_get(p_node, "XAddrs"); 
    if (p_XAddrs && p_XAddrs->data) 
    { 
     parse_XAddr(p_XAddrs->data, &p_res->XAddr); 

     if (p_res->XAddr.host[0] == '\0' || p_res->XAddr.port == 0) 
     { 
      return FALSE; 
     } 
    } 
    else 
    { 
     return FALSE; 
    } 

    return TRUE; 
} 

BOOL onvif_probe_res(XMLN * p_node, DEVICE_BINFO * p_res) 
{ 
    XMLN * p_body = xml_node_soap_get(p_node, "Body"); 
    if (p_body) 
    { 
     XMLN * p_ProbeMatches = xml_node_soap_get(p_body, "ProbeMatches"); 
     if (p_ProbeMatches) 
     { 
      XMLN * p_ProbeMatch = xml_node_soap_get(p_ProbeMatches, "ProbeMatch"); 
      while (p_ProbeMatch && soap_strcmp(p_ProbeMatch->name, "ProbeMatch") == 0) 
      { 
       if (onvif_parse_device_binfo(p_ProbeMatch, p_res)) 
       { 
        if (g_probe_cb) 
        { 
         g_probe_cb(p_res, g_probe_cb_data); 
        } 
       } 

       p_ProbeMatch = p_ProbeMatch->next; 
      } 
     } 
     else 
     { 
      XMLN * p_Hello = xml_node_soap_get(p_body, "Hello"); 
      if (p_Hello) 
      { 
       if (onvif_parse_device_binfo(p_Hello, p_res)) 
       { 
        if (g_probe_cb) 
        { 
         g_probe_cb(p_res, g_probe_cb_data); 
        } 
       } 
      } 
     } 
    } 

    return TRUE; 
} 

int onvif_probe_net_rx() 
{ 
    int i; 
    int ret; 
    int maxfd = 0; 
    int fd = 0; 
    char rbuf[10*1024]; 
    fd_set fdread; 
    struct timeval tv = {1, 0}; 

    FD_ZERO(&fdread); 

    for (i = 0; i < MAX_PROBE_FD; i++) 
    { 
     if (g_probe_fd[i] > 0) 
     { 
      FD_SET(g_probe_fd[i], &fdread); 

      if (g_probe_fd[i] > maxfd) 
      { 
       maxfd = g_probe_fd[i]; 
      } 
     } 
    } 

    ret = select(maxfd+1, &fdread, NULL, NULL, &tv); 
    if (ret == 0) // Time expired 
    { 
     return 0; 
    } 

    for (i = 0; i < MAX_PROBE_FD; i++) 
    { 
     if (g_probe_fd[i] > 0 && FD_ISSET(g_probe_fd[i], &fdread)) 
     { 
      int rlen; 
      int addr_len; 
      struct sockaddr_in addr; 
      unsigned int src_ip; 
      unsigned int src_port; 
      XMLN * p_node; 

      fd = g_probe_fd[i]; 

      addr_len = sizeof(struct sockaddr_in); 
      rlen = recvfrom(fd, rbuf, sizeof(rbuf), 0, (struct sockaddr *)&addr, (socklen_t*)&addr_len); 
      if (rlen <= 0) 
      { 
       log_print(LOG_ERR, "onvif_probe_net_rx::rlen = %d, fd = %d\r\n", rlen, fd); 
       continue; 
      } 

      src_ip = addr.sin_addr.s_addr; 
      src_port = addr.sin_port; 

      p_node = xxx_hxml_parse(rbuf, rlen); 
      if (p_node == NULL) 
      { 
       log_print(LOG_ERR, "onvif_probe_net_rx::hxml parse err!!!\r\n"); 
      } 
      else 
      { 
       DEVICE_BINFO res; 
       memset(&res, 0, sizeof(DEVICE_BINFO)); 

       onvif_probe_res(p_node, &res);  
      } 

      xml_node_del(p_node); 
     } 
    } 

    return 1; 
} 

void * onvif_probe_thread(void * argv) 
{ 
    int count = 0; 

    int i = 0; 
    int j = 0; 

    for (; i < get_if_nums() && j < MAX_PROBE_FD; i++, j++) 
    { 
     unsigned int ip = get_if_ip(i);  
     if (ip != 0 && ip != inet_addr("127.0.0.1")) 
     { 
      g_probe_fd[j] = onvif_probe_init(ip); 
     } 
    } 

    for (i = 0; i < MAX_PROBE_FD; i++) 
    { 
     if (g_probe_fd[i] > 0) 
     { 
      onvif_probe_req_tx(g_probe_fd[i]); 
     } 
    } 

    while (g_probe_running) 
    { 
     if (onvif_probe_net_rx() == 0) 
     { 
      count++; 
     } 

     if (count >= g_probe_interval) 
     { 
      count = 0; 

      for (i = 0; i < MAX_PROBE_FD; i++) 
      { 
       if (g_probe_fd[i] > 0) 
       { 
        onvif_probe_req_tx(g_probe_fd[i]); 
       } 
      }  
     } 

     usleep(1000); 
    } 

    g_probe_thread = 0; 

    return NULL; 
} 

ONVIF_API void set_probe_cb(onvif_probe_cb cb, void * pdata) 
{ 
    g_probe_cb = cb; 
    g_probe_cb_data = pdata; 
} 


ONVIF_API void send_probe_req() 
{ 
    int i; 
    for (i = 0; i < MAX_PROBE_FD; i++) 
    { 
     if (g_probe_fd[i] > 0) 
     { 
      onvif_probe_req_tx(g_probe_fd[i]); 
     } 
    } 
} 

ONVIF_API void set_probe_interval(int interval) 
{ 
    g_probe_interval = interval; 

    if (g_probe_interval < 10) 
    { 
     g_probe_interval = 30; 
    } 
} 

ONVIF_API int start_probe(int interval) 
{ 
    g_probe_running = TRUE; 

    set_probe_interval(interval); 

    g_probe_thread = sys_os_create_thread((void *)onvif_probe_thread, NULL); 
    if (g_probe_thread) 
    { 
     return 0; 
    } 

    return -1; 
} 

ONVIF_API void stop_probe() 
{ 
    int i; 

    g_probe_running = FALSE; 

    while (g_probe_thread) 
    { 
     usleep(1000); 
    } 

    for (i = 0; i < MAX_PROBE_FD; i++) 
    { 
     if (g_probe_fd[i] > 0) 
     { 
      closesocket(g_probe_fd[i]); 
      g_probe_fd[i] = 0; 
     } 
    } 
} 

Hier ist, was die DEVICE_BINFO Struktur wie folgt aussieht:

typedef struct 
{ 
    int  type;        // device type 
    char EndpointReference[100]; 

    onvif_XAddr XAddr;       // xaddr, include port host, url 
} DEVICE_BINFO; 

Antwort

0

Eine Sache, die sollte behoben werden, ist eine Diskrepanz in der Anzahl der Argumente für den Rückruf. Swift ruft die Objective-C setProbeCB()-Methode auf und gibt ihr einen Zeiger auf die cameraDiscovered()-Funktion, die ein einzelnes-Argument benötigt. Dann gibt setProbeCB() den Funktionszeiger an die C -Funktion, die einen Zeiger auf eine Funktion erwartet, die zwei Argumente benötigt.

Eine weitere Beobachtung ist, dass setProbeCB() nur onvif_probe_cb statt onvif_probe_cb* nehmen könnte und dann rufen Sie C-Code einfach als set_probe_cb(cb, 0). Ich denke jedoch nicht, dass es viel Unterschied macht.

Auch ich denke, die Frage hätte zu einer kleineren Größe destilliert werden können.

Das folgende ist ein vereinfachtes Beispiel, das auf Ihrem ursprünglichen Code basiert. Es zeigt, wie man einen Callback in Swift implementiert und C-Code aufrufen lässt, aber der eigentliche Spaß beginnt, wenn Daten über Callback-Parameter und Rückgabewerte übergeben werden. Es wird sehr schnell sehr schwierig, und deshalb zeigt das Beispiel nicht, wie man in Swift-Code mit DEVICE_BINFO umgehen kann. Es ist ein eigenständiges Thema.

Der Hinweis zur Verwendung von (Objektiven-) C-Funktionen und -Typen in Swift zeigt, wie diese in Swift importiert werden. Zum Beispiel, um herauszufinden, wie onvif_probe_cb importiert wird, geben Sie es auf einer Linie in der Swift-Code, setzen Sie den Cursor in ihm, und Quick Help zeigt Ihnen diese:

Declaration: typealias onvif_probe_cb = (UnsafeMutablePointer<DEVICE_BINFO>, UnsafeMutablePointer<Void>) -> Void 
Declared in: clib.h 

, die uns die Parameter teilt und Rückgabetypen in unserer Swift-Implementierung des Rückrufs zu verwenden. Das Beispiel ist keineswegs Produktionsqualität: Es gibt alle Arten von Dingen, die in Bezug auf Speicherverwaltung etc. drunter und drüber gehen können. Bitte beachten Sie die Code-Kommentare für zusätzliche Informationen.

Als erstes ist hier die C-Code-Header (clib.h):

#ifndef clib_h 
#define clib_h 

#include <stdio.h> 

typedef struct { 
    char hostname[50]; 
    int32_t port; 
    char url[200]; 
} onvif_XAddr; 

typedef struct 
{ 
    int  type;        // device type 
    char EndpointReference[100]; 

    onvif_XAddr XAddr;       // xaddr, include port host, url 
} DEVICE_BINFO; 

/** 
* This is the typedef of the function pointer to be used for our callback. 
* The function takes a pointer to DEVICE_BINFO and a pointer to some arbitrary 
* data meaningful to the code that provides the callback implementation. It will 
* be NULL in this example. 
*/ 
typedef void (* onvif_probe_cb)(DEVICE_BINFO * p_res, void * pdata); 

/** 
* A function to set the callback. 
*/ 
void set_probe_cb(onvif_probe_cb cb, void * pdata); 

/** 
* This is a function that calls the callback. 
*/ 
void find_device(); 

#endif /* clib_h */ 

Hier ist der Rest unserer C-Quelle (clib.c):

#include "clib.h" 
#include <string.h> 

onvif_probe_cb gCB = 0; // global variable to store the callback pointer 
void * gUserData = 0;  // global variable to store pointer to user data 
DEVICE_BINFO gDeviceInfo; // global variable to store device info struct 

void find_device() { 
    // Set up gDeviceInfo 
    gDeviceInfo.XAddr.port = 1234; 
    strcpy(gDeviceInfo.XAddr.hostname, "myhost"); 
    strcpy(gDeviceInfo.XAddr.url, "http://junk.com"); 
    gDeviceInfo.type = 777; 
    // ... and, if a callback is available, call it with the device info 
    if (gCB) gCB(&gDeviceInfo, gUserData); 
    else puts("No callback available"); 
} 

void set_probe_cb(onvif_probe_cb cb, void * pdata) { 
    gCB = cb; 
    gUserData = pdata; 
} 

Hier ist die Objective-C-Wrapper-Header (oclib.h):

#ifndef oclib_h 
#define oclib_h 

#import "clib.h" 
#import <Foundation/Foundation.h> 

/** 
* Interface of an Objective-C wrapper around C code in clib.*. We could have 
* gone straight to C from Swift, but I'm trying to keep the example close to the 
* code in the question. Also, this extra Objective C layer could be helpful in 
* translating data structures, such as DEVICE_BINFO, between C and Swift, since 
* Objective-C plays much nicer with C data types. This is no surprise: any C code 
* is valid Objective-C (Objective-C is a strict superset of C). 
*/ 
@interface MyWrapper : NSObject 

-(id)init; 
// Please note: this one takes a single argument, while the C function it wraps 
// takes 2; see the implementation. 
-(void) setProbeCB:(onvif_probe_cb) cb; 
-(void) findDevice; 

@end 

#endif /* oclib_h */ 

Und der Wrapper implementatio n (oclib.m):

#import "oclib.h" 

/** 
* Implementation of our Objective-C wrapper. 
*/ 
@implementation MyWrapper 

-(id)init { return self; } 

-(void) setProbeCB:(onvif_probe_cb) cb { 
    // We don't want anything other than device info to be passed back and 
    // forth via the callback, so this wrapper function takes a single argument 
    // and passes 0 as the 2nd argument to the wrapped C function. 
    set_probe_cb(cb, 0); 
} 

-(void) findDevice { 
    find_device(); 
} 

@end 

Schließlich ist hier der Swift Code, der den Rückruf implementiert (main.swift):

var w : MyWrapper = MyWrapper() 

/** 
* This is the callback implementation in Swift. We don't use the 2nd argument, userData, but it still 
* has to be present to satisfy the way the callback function pointer is specified in C code. 
*/ 
func cameraDiscovered(info : UnsafeMutablePointer<DEVICE_BINFO>, userData : UnsafeMutablePointer<Void>) { 
    print("Called the Swift callback!") 

    let devInfo : DEVICE_BINFO = info.memory; 
    print("The device type is \(devInfo.type)") 
    print("The device port is \(devInfo.XAddr.port)") 
} 

// Provide the callback to C code via Objective-C 
w.setProbeCB(cameraDiscovered) 

// ... and call a function that will cause the C code to invoke the callback. 
w.findDevice() 

Der überbrück Header hat nur #import oclib.h, damit der Inhalt der beiden C Belichten und Objective -C Header zu Swift.

Die erwartete Ausgabe:

Called the Swift callback! 
The device type is 777 
The device port is 1234 
+0

Ich denke, die Essenz meines Problems ist, dass ich nicht weiß, wie man Objective C. eine Funktion von Swift passieren Ist das überhaupt möglich? Einfacheres Beispiel ohne Argumente. Swift Code: objectivecfunction (swiftfunction) func swiftfunction() { } – user3561494

+0

Für diesen einfacheren Fall, legen Sie die folgenden in der Objective-C-Header: 'typedef void (* cb_t)(); void ocfunction (cb_t); 'In der Objective-C-Implementierung:' void ocfunction (cb_t cb) {cb(); } 'Stellen Sie sicher, dass der Obj C-Header in den Bridging-Header importiert wird. Dann in Swift: 'func swiftfunction() {print (" Hi from Swift callback! ")}; ocfunction (Schnellfunktion) ' – OmniProg