2017-09-28 2 views
0

J'ai un problème de compréhension ou d'utilisation de Dispatchgroup. J'ai beaucoup lu à leur sujet mais la plupart des exemples/documents sont très vagues ou ne ressemblent pas à ce que je veux faire, cependant à chaque fois que je mentionne mon problème, tout le monde dit "UTILISER DES GROUPES D'EXPÉDITION!"DispatchGroup.wait n'attend pas

Voici ce que je veux faire (NOTE: COMMANDER SEQUENTIAL EST CRUCIAL):

  • Envoyer Bluetooth écriture caractéristique.
  • dispositif reçoit la valeur et recrache quelque chose en réponse
  • Lire la réponse Bluetooth (via une lecture caractéristique)
  • Envoyer une nouvelle caractéristique d'écriture (une autre commande)
  • dispositif reçoit la commande NOUVEAU, recrache NOUVEAU réponse des données

Répéter deux fois (3 commandes au total, 3 réponses différentes au total).


Mon code:

func tPodInitialSetUp() 
    { 
     print ("* * * * * BEGIN SET-UP * * * * *") 
     let setupDispatchGroup = DispatchGroup() 

     setupDispatchGroup.enter() 
     self.writeValue(command: Data(CommandModeCmd)) //231: Put t-Pod in command mode, burst mode is OFF returns OK 
     setupDispatchGroup.leave() 

     setupDispatchGroup.wait() 

     setupDispatchGroup.enter() 
     deviceConnected?.readValue(for: deviceConnectedCh1n2Char!) 
     print("Sent command 231: returned: \(receivedString1)") 
     if receivedString1.lowercased() == "ok" 
     { 
      print("t-Pod burst mode is OFF") 
     } 
     setupDispatchGroup.leave() 

     setupDispatchGroup.wait() 

     setupDispatchGroup.enter() 
     self.writeValue(command: Data(loadProbeCalCmd)) //202: load calibration constants of probe, returns ok or 0 
     setupDispatchGroup.leave() 

     setupDispatchGroup.wait() 

     setupDispatchGroup.enter() 
     deviceConnected?.readValue(for: deviceConnectedCh1n2Char!) 
     print("Sent command 202: returned: \(receivedString1)") 
     if receivedString1.lowercased() == "ok" 
     { 
      print("Probe Constants loaded") 
     } 
     if receivedString1 == "0" 
     { 
      print("No probe connected") 
     } 
     setupDispatchGroup.leave() 

     setupDispatchGroup.wait() 

     setupDispatchGroup.enter() 
     self.writeValue(command: Data(probeSNCmd)) //205: load probe serial number 
     setupDispatchGroup.leave() 

     setupDispatchGroup.wait() 

     setupDispatchGroup.enter() 
     deviceConnected?.readValue(for: deviceConnectedCh1n2Char!) 
     print("Sent command 205: returned: \(receivedString1)") 
     if (receivedString1.count == 6) 
     { 
      print("received Probe SN: \(receivedString1)") 
      probeSN = receivedString1 
     } 
     setupDispatchGroup.leave() 

     setupDispatchGroup.notify(queue: .main) 
     { 
      tPodSN = String(describing: connectedDeviceName!.dropFirst(7)) 
      print ("* * * SET-UP COMPLETE * * *") 
      self.writeValue(command: Data(resetCmd)) //200: resets t-Pod 
      self.writeValue(command: Data(beaconOffCmd)) //211: turns beacon off (temperature output) 
     } 

     DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) 
     { 
      self.dataDisplaySubView.isHidden = false 
      print ("Adding observers!") 

      NotificationCenter.default.addObserver(self, selector: #selector(self.updateIncomingData), name: NSNotification.Name(rawValue: DATA_PARSED), object: nil) //Run every time you receive data from BLE 

      NotificationCenter.default.addObserver(self, selector: #selector(self.calculateTNU), name: NSNotification.Name(rawValue: TOGGLESWITCH_TOGGLED), object: nil) //Run in case the toggle switches change and data needs to be re-calculated 

      NotificationCenter.default.addObserver(self, selector: #selector(self.parseReceivedData), name: NSNotification.Name(rawValue: DEVICE_FINISHED_SENT_DATA), object: nil) //Run every time you receive the notification that the whole data has been sent 
     } 
    } 

Ceci appelle la commande d'écriture Bluetooth qui a le code suivant et confirmation:

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) { 
     guard error == nil else { 
      print("Error writing descriptor: " + (error?.localizedDescription)!) 
      return 
     } 
     print("Descriptor Value sent") 
    } 

Maintenant, Voici ma sortie:

* * * * * BEGIN SET-UP * * * * * 
***** WRITING ***** 
Wrote: 1 bytes 
***** WRITING ***** 
Wrote: 1 bytes 
Sent command 231: returned: **T-Pod-9Ch** 
***** WRITING ***** 
Wrote: 1 bytes 
Sent command 202: returned: **T-Pod-9Ch** 
***** WRITING ***** 
Wrote: 1 bytes 
Sent command 205: returned: **T-Pod-9Ch** 
* * * SET-UP COMPLETE * * * 
***** WRITING ***** 
Wrote: 1 bytes 
***** WRITING ***** 
Wrote: 1 bytes 
Characteristic Value sent 
Adding observers! 
Characteristic Value sent 
Characteristic Value sent 
Characteristic Value sent 
Characteristic Value sent 
Clearing TNU Array 

Maintenant, comme vous peut voir "Caractéristique Valeur Envoyée" est la confirmation que la fonction Bluetooth donne quand elle envoie la valeur, mais cette sortie est créée APRÈS avoir terminé l'exécution du code entier, donc fondamentalement, il a mis les commandes dans un pipeline, a oublié tout fait et Puis envoyé les commandes et donc la réponse que je suis en train de lire sont des absurdités! Comme vous pouvez voir toutes les chaînes reçues sont T-Pod-9Ch (qui est juste sa sortie en rafale normale), les réponses attendues que je devrais obtenir des commandes sont OK, OK et un nombre de 6 chiffres (dans cet ordre).
S'il vous plaît, aidez, j'ai lu tant de fois sur la façon dont les groupes de distribution sont censés fonctionner mais je ne peux pas les amener à faire ce que je veux.

+1

vous entrez, en laissant, et attendre le groupe sur un seul fil. C'est complètement inutile. Chaque fois que vous appelez 'wait()', le compteur du groupe est '0' car vous avez déjà quitté le groupe. –

+0

Pouvez-vous élaborer là-dessus? Je ne comprends pas vraiment comment il est censé être formaté, d'après ce que j'ai compris chaque tâche devait être dans les commandes d'entrée/sortie et j'ai besoin d'eux pour attendre chaque commande individuelle –

+0

Les files d'attente sont pour synchroniser les choses entre threads. Généralement vous les utilisez comme ceci: 1) appelez 'enter()' sur le thread courant, 2) démarrez une tâche asynchrone et/ou exécutez du code sur une autre file d'attente ou de distribution, 3) appelez la tâche async 'leave() 'quand c'est fait, 4) répéter pour toutes les autres tâches qui doivent être faites, et 5) utiliser' notify() 'pour faire exécuter du code quand tous les appels' enter() 'ont été équilibrés par' leave() ' appels. –

Répondre

0

Si j'ai bien compris votre question, vous devez attendre une réponse avant d'envoyer une nouvelle commande.

Mais vos écritures n'ont pas de bloc d'achèvement, c'est pourquoi dans votre cas, utiliser dispatchGroup n'a aucun sens.

Le code ci-dessous est un exemple courant d'utiliser des groupes d'expédition

func someMethod(completionHandler: @escaping()-> Void) { 


//we need to get or set some data in separated queue 
    DispatchQueue.global(qos: .background).async { 

    let group = DispatchGroup() 

    //let's say we have an array of urls and we need to load images and put them to an array 
    for url in someUrlArray { 
      group.enter() 
      SomeLoaderClass.load(url) { image in 
       //add received image 

       //leave the group 
       group.leave() 
      } 
    } 

    //now we need to wait until all images will be downloaded 
    group.wait() 

    //then we can finish and call the completion in the main queue 
    DispatchQueue.main.async { 
     completionHandler() 
    } 
    } 
} 

Dans votre situation, vous pouvez avoir plusieurs options:

d'abord, si vous savez que si vous envoyez une commande et recevoir une réponse exactement pour cette commande, vous pouvez appeler les méthodes ci-dessous pour:

  1. Appel d'une méthode pour envoyer la commande 1

  2. appeler une autre méthode après une réponse pour la commande 1 sera reçu

  3. appel encore une autre méthode pour envoyer la commande 2

  4. Et une méthode plus après avoir obtenu une réponse pour la commande 2 ...

n. Terminer la configuration

Comme si j'avais besoin d'enregistrer un utilisateur, je dois d'abord envoyer des informations d'identification définies, obtenir un jeton du serveur, puis exécuter quelque chose après.

Vous devrez ajouter une méthode supplémentaire pour chaque commande et vous les appelez selon l'ordre

Si vous ne pouvez pas reconnaître pour quelle commande que vous allez obtenir une réponse et vous assurer que vous « avez envoyer une seule commande et d'attendre qu'une seule réponse, vous pouvez utiliser le groupe d'expédition de la manière décrite ci-dessous:

typealias Callback =()->Void 
class SomeManagerClass { 
     var callback: Callback? 

     func initiateSetup(){ 
       DispatchQueue.global(qos: .background).async { [weak self] in 
        let group = DispatchGroup() 
        //now we can send commands 
        group.enter() 
        self?.sendCommand(SomeDataForTheFirstCommand) { 
          //when the first answer will be received, it will init the callback, so you can leave the group now 
         group.leave() 
        } 
        //sending the second command 
        group.enter() 
        self?.sendCommand(SomeDataForTheSecondCommand) { 
          //waiting for the second answer will be received 
         group.leave() 
        } 

        //.... more commands sending same way 
        group.wait() 
        //now all commands was send and you got an answer for each 

        //finishing setup 
         DispatchQueue.main.async{ 
         self?.finishSetup() 
        } 
       } 
     } 

     func sendCommand(_ command: Data, callback: Callback?){ 
      self.writeValue(command: command) 
      self.callback = callback 
     } 

     func answerReceived(){ 
      //this is just an example method that is called when you get an answer for any command 
      //now we can callback 
      self.callback?() 
     } 

     func finishSetup(){ 

      //do something 
     } 
} 

Laissez-moi savoir si vous avez besoin de plus de détails

+0

Je vais essayer de l'utiliser, il semble faire ce que je veux. J'ai juste une question rapide, comment le programme sait ce que le rappel est? Comme dire, j'ai une méthode qui appelle ce didwritevalue périphérique (voir mon code original). Comment le programme sait-il qu'il doit attendre jusqu'à ce qu'il reçoive une «confirmation» (ou cette déclaration d'impression que j'ai)? Je pensais si je mettais une notification là ou quelque chose. . Juste pour que je puisse m'assurer d'attendre jusqu'à ce qu'il l'écrive avant de continuer –

+0

@ dvd.Void callback est juste une fonction qui n'a aucun paramètre et ne retourne aucun objet. Vous l'appelez simplement pour quitter le groupe de distribution et lancer l'appel de la méthode suivante. Si personne n'appelle la méthode answerReceived(), l'application ne quittera pas le groupe et le processus d'installation ne sera pas terminé. Chaque appel sendCommand modifiera le rappel stocké pour quitter le groupe après une commande exacte. Vous pouvez utiliser des notifications, des délégués ou autre. Vous devriez juste appeler answerReceived() après que chaque commande a été envoyée au moment où vous êtes sûr que cette commande a été exécutée. – Woof

+0

@ dvd.Void Oh, attendez, dans votre cas, il semble que vous deviez stocker tous les blocs ... Je ne suis pas sûr mais si vous obtenez une commande écrire le résultat de manière asynchrone, donc il peut changer le callback stocké par la deuxième fonction , avant que le premier utilise ce rappel. Nous devons le tester et vérifier la sortie de la console comme vous l'avez montré dans votre question. Si les rappels auront brisé l'ordre, nous devrons utiliser DispatchWorkItem pour chaque commande pour les stocker dans le tableau de blocs, ou utiliser DispatchSemaphore. Mais de toute façon, laissez-moi savoir comment cela fonctionnera dans votre code. Nous pouvons également discuter plus tard pour faire fonctionner le code. – Woof