2010-07-27 4 views
0

J'écris une application dans Qt pour être déployée sur la plate-forme Symbian S60. Malheureusement, il doit avoir la fonctionnalité Bluetooth - rien de vraiment avancé, juste une simple découverte du socket client et de l'appareil RFCOMM. Pour être exact, l'application devrait fonctionner sur deux plates-formes - Windows PC et S60 précité. Bien sûr, comme Qt n'a pas de support Bluetooth, il doit être codé en API native - Winsock2 sous Windows et Symbian C++ sur S60 - je suis en train de coder une couche d'abstraction simple. Et j'ai quelques problèmes avec la partie découverte sur Symbian.Symbian C++ - découverte Bluetooth synchrone avec timeout à l'aide de RHostResolver

L'appel de découverte dans la couche d'abstraction doit fonctionner de manière synchrone. Il bloque jusqu'à la fin de la découverte et renvoie tous les périphériques sous la forme QList. Je n'ai pas le code exact en ce moment, mais j'avais quelque chose comme ça:

RHostResolver resolver; 
TInquirySockAddr addr; 
// OMITTED: resolver and addr initialization 

TRequestStatus err; 
TNameEntry entry; 
resolver.GetByAddress(addr, entry, err); 
while (true) { 
    User::WaitForRequest(err); 
    if (err == KErrHostResNoMoreResults) { 
     break; 
    } else if (err != KErrNone) { 
     // OMITTED: error handling routine, not very important right now 
    } 

    // OMITTED: entry processing, adding to result QList 

    resolver.Next(entry, err); 
} 
resolver.Close(); 

Oui, je sais que User::WaitForRequest est mal, que le codage comme Symbian, j'utiliser des objets actifs, etc. sur. Mais ce n'est pas ce dont j'ai besoin. J'ai besoin d'une manière simple et synchrone de faire la découverte de l'appareil.

Et le code ci-dessus ne travail. Il y a une bizarrerie, cependant - j'aimerais avoir un temps mort pendant la découverte. C'est-à-dire, je veux que la découverte ne prenne pas plus de, disons, 15 secondes - paramétrée dans un appel de fonction. J'ai essayé de faire quelque chose comme ceci:

RTimer timer; 
TRequestStatus timerStatus; 
timer.CreateLocal(); 

RHostResolver resolver; 
TInquirySockAddr addr; 
// OMITTED: resolver and addr initialization 

TRequestStatus err; 
TNameEntry entry; 

timer.After(timerStatus, timeout*1000000); 

resolver.GetByAddress(addr, entry, err); 
while (true) { 
    User::WaitForRequest(err, timerStatus); 

    if (timerStatus != KRequestPending) { // timeout 
     resolver.Cancel(); 
     User::WaitForRequest(err); 
     break; 
    } 

    if (err == KErrHostResNoMoreResults) { 
     timer.Cancel(); 
     User::WaitForRequest(timerStatus); 
     break; 
    } else if (err != KErrNone) { 
     // OMITTED: error handling routine, not very important right now 
    } 

    // OMITTED: entry processing, adding to result QList 

    resolver.Next(entry, err); 
} 
timer.Close(); 
resolver.Close(); 

Et ce code Kinda œuvres. Plus encore, la façon dont cela fonctionne est fonctionnellement correct - le délai d'attente fonctionne, les périphériques découverts jusqu'à présent sont retournés, et si la découverte se termine plus tôt, alors il quitte sans attendre le minuteur. Le problème est - il laisse un fil perdu dans le programme. Cela signifie que lorsque je quitte mon application, son processus est toujours chargé en arrière-plan, ne rien faire. Et je ne suis pas le type de programmeur qui serait satisfait d'une "correction" comme faire le bouton "exit" tuer le processus au lieu de quitter gracieusement. Laisser un fil perdu semble une fuite de ressources trop sérieuse.

Y at-il un moyen de résoudre ce problème? Cela ne me dérange pas de tout réécrire à partir de zéro, même en utilisant des API totalement différentes (du moment que nous parlons d'API Symbian natives), je veux juste que ça fonctionne. J'ai lu un peu sur les objets actifs, mais cela ne semble pas être ce dont j'ai besoin, car j'ai juste besoin que ça fonctionne de manière synchrone ... Dans le cas de changements plus importants, j'apprécierais des explications plus détaillées, puisque je suis nouveau sur Symbian C++, et je n'ai pas vraiment besoin de le maîtriser - ce petit module Bluetooth est probablement tout ce dont j'ai besoin pour y écrire dans un avenir prévisible.

Merci d'avance pour toute aide! :)

Répondre

0

Le code que vous avez m'a l'air bien. Vous avez manqué l'écueil habituel de ne pas consommer toutes les demandes que vous avez émises. En supposant que vous annulez également la minuterie et que vous faites un User::WaitForRequest(timerStatus) à l'intérieur de votre condition de traitement des erreurs, cela devrait fonctionner.

Je suppose que ce dont vous vous inquiétez, c'est que votre thread principal n'a aucun moyen de demander la sortie de ce thread. Vous pouvez le faire à peu près comme suit:

  • Passez un pointeur sur un TRequestStatus dans le fil lorsqu'il est créé par votre fil principal. Appelez le exitStatus.
  • Lorsque vous faites le User::WaitForRequest, aussi attendre exitStatus.
  • Le fil conducteur fera un bluetoothThread.RequestComplete(exitStatus, KErrCancel) quand il veut la subthread pour quitter, où bluetoothThread est l'objet RThread que le thread principal créé.
  • dans le subthread, lorsque exitStatus est signalée, sortir de la boucle pour terminer le fil. Vous devez vous assurer d'annuler et de consommer les demandes de minuterie et Bluetooth.
  • le thread principal doit faire un bluetoothThread.Logon et d'attendre le signal d'attendre le fil Bluetooth pour quitter.

Il y aura probablement plus de subtilités pour traiter correctement tous les cas d'erreur et ainsi de suite.

J'espère que je ne suis pas aboyer le mauvais arbre tout à fait ici ...

+0

Bien sûr, il n'y a pas d'utilisateur :: WaitForRequest qui accepte les trois années TRequestStatus. Cependant, dans ce cas, vous pouvez utiliser User :: WaitForAnyRequest() car vous savez que seuls les trois TRequestStatus que vous avez seront utilisés pour signaler le thread. Pas tout à fait aussi joli mais devrait fonctionner. Il peut être préférable de le refaire à l'aide d'objets actifs, mais cela rend l'initialisation de exitStatus plus difficile (puisqu'il appartiendra à l'un de vos objets actifs, il devra donc être transmis du sous-thread au thread principal). – MathewI

+0

Merci pour votre aide ... Je pensais à créer un thread séparé pour effectuer la découverte, et en utilisant des objets actifs au lieu de 'User :: WaitForRequest' là ... Le thread principal l'attendrait alors. Que pensez-vous de ce genre d'approche? – kFYatek

+0

Oh, et en passant - pour autant que je comprends la documentation de l'API, il _is_ un 'WaitForRequest' pour trois ou plusieurs objets' TRequestStatus' - Je parle de 'WaitForNRequest' - il faut un tableau de pointeurs vers' TRequestStatus 'et la taille de ce tableau. Cependant, je ne comprends pas vraiment votre approche - suis-je censé créer un nouveau 'RThread' juste pour l'annuler plus tard? Quel est le point dans tout ça? Je suppose qu'il y a un thread implicitement créé par l'API, auquel je n'ai pas d'accès explicite ... – kFYatek

0

La question est déjà répondu, mais ... Si vous utilisez des objets actifs, je vous propose d'utiliser imbriquée scheduler actif (classe CActiveSchedulerWait). Vous pouvez ensuite le transmettre à vos objets actifs (CPeriodic pour timer et à un autre CActive pour Bluetooth), et l'un d'entre eux arrêtera ce planificateur imbriqué dans sa méthode RunL(). Plus que cela, avec cette approche votre appel devient synchrone pour l'appelant, et votre fil sera gracieusement fermé après avoir effectué l'appel.

Si vous êtes intéressé par la solution, la recherche des exemples de CActiveSchedulerWait, ou tout simplement me demander et je vais vous donner votre certains exemple de code.