2009-02-12 9 views
2

J'ai un problème avec la lecture de la classe boost :: asio :: serial_port à partir d'un périphérique GPS (USB-Serial). La connexion du périphérique et la lecture de celui-ci fonctionnent correctement, mais lorsque je déconnecte et reconnecte le périphérique, read_some ne lit aucun octet du port. Comme boost ne permet pas de détecter que le port série est parti (is_open() renvoie true), j'annule(), ferme() et ouvre (GPS_PORT) l'appareil périodiquement lorsque je ne reçois pas de données, réinitialiser les options de port en cours de route. Mais cela n'aide pas non plus, le tampon d'entrée reste vide. Est-ce que je manque quelque chose, ou si je fais quelque chose de mal, ou est-ce un bogue dans asio? Existe-t-il un moyen standard de détecter que le port est parti?boost :: asio :: serial_port lecture après la reconnexion du périphérique

Répondre

3

Il est difficile de dire quelle est la raison exacte dans votre cas, mais la pratique montre que vous devez souvent désactiver la sensibilité RTS sur votre port série.

RTS est une broche sur l'interface réelle RS-232 qui est activée lorsqu'un périphérique de l'autre côté est activé.

serial_port::read_some appelle la fonction sous-jacente Windows API qui regarde ce signal.

Comme vous n'avez pas le vrai périphérique RS-323, vous devez vous fier à l'émulation de pilote de ce signal qui peut être défectueuse (et malheureusement souvent). Pour le désactiver, appelez serial_port::set_option(DCB) avec RTSControl défini sur RTS_CONTROL_DISABLE.

Si votre poignée ne vous aide pas, cela peut poser un problème avec boost. Le code source pour close() ressemble à ceci:

boost::system::error_code close(implementation_type& impl, 
     boost::system::error_code& ec) 
    { 
    if (is_open(impl)) 
    { 
     if (!::CloseHandle(impl.handle_)) 
     { 
     DWORD last_error = ::GetLastError(); 
     ec = boost::system::error_code(last_error, 
      boost::asio::error::get_system_category()); 
     return ec; 
     } 

     impl.handle_ = INVALID_HANDLE_VALUE; 
     impl.safe_cancellation_thread_id_ = 0; 
    } 

    ec = boost::system::error_code(); 
    return ec; 
    } 

, i. e. si CloseHandle() échoue pour une raison quelconque (ou se bloque), la valeur de handle interne n'est pas affectée à INVALID_HANDLE_VALUE et is_open() renverra toujours true.

Pour contourner ce problème, vérifiez is_open() juste après close() « ing, et si elle retourne true, par exemple détruire toute boost::asio::serial_port et créer à nouveau.

+0

Merci, je vais essayer - mais ne devrait pas la lecture échouera également lorsque l'appareil est d'abord ouvert? – VolkA

+0

Lecture échouera lorsque le conducteur dit que RTS est éteint. Quand cela arrive, cela dépend de l'implémentation du pilote. BTW, lorsque la lecture commence à réussir: après redémarrage, après le rattachement de l'appareil, après le délai? – Quassnoi

+0

Ok, j'ai juste essayé de mettre RTS_CONTROL_DISABLE - ça ne change pas le comportement. Le boost :: serial_port :: is_open renvoie true, mais ne commencera pas à lire. Le comportement est le suivant: connecter le GPS, démarrer l'outil - lire, déconnecter le GPS, arrêter la lecture, se connecter à nouveau, ne pas encore lire. Redémarrer l'outil: fonctionne à nouveau. – VolkA

1

Normalement, vous devriez obtenir une exception de type boost::system::system_error lorsque read_some ne peut plus tout faire. Essayez d'utiliser read à la place, peut-être qu'il renvoie une erreur et ne revient pas seulement. Vous pouvez également essayer les méthodes asynchrones; dans ce cas, le gestionnaire devrait recevoir un objet d'erreur lorsque l'appareil a été déconnecté.

Vous pourriez obtenir le handle du port en utilisant la fonction native() et appeler ClearCommError(). Cela pourrait renvoyer l'erreur.

+0

Cela semble bon - Je vérifie le code d'erreur, mais je n'ai pas encore remarqué la méthode native() ... avec cela je devrais être en mesure de convier en quelque sorte le port série pour lire :) – VolkA

+0

Peut-être que vous venez de don ' Je remarque que le périphérique a été débranché du fait que le pilote USB ne le signale pas au système d'exploitation. Dans ce cas: la malchance: / – vividos

1

Malgré la manipulation facile des asio boost::ip:tcp, je pense que la manipulation poussée serial_port nécessite des précautions particulières sur Windows 7.
Je suis le même problème et eu sur elle en remettant à zéro une instance de boost::asio::io_service, io_service_.reset().
Je peux lire des données de manière asynchrone, mais il ne parvient pas à faire la même chose à partir du second essai.
En fait, il n'y avait aucun problème de lecture de la fonction elle-même, l'enregistrement de la lecture asynchrone a échoué, ce qui conduit à un retour immédiat de boost::asio::io_service::run() lors de la deuxième tentative.

Je ne suis pas sûr que ce soit le même problème que l'affiche originale avait parce que j'utilise la nouvelle bibliothèque de boost de ces jours.
Quoi qu'il en soit, voici ma solution:

// port open step 
port_ = boost::shared_ptr<boost::asio::serial_port> 
     (new boost::asio::serial_port(io_service_)); 
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service_)); 
port_->async_read_some(....); 
....... 

// port close step 
port_->cancel(); 
port_->close(); 
port_.reset(); 

io_service_.stop(); 
io_service_.reset(); // <-- IMPORTANT: this makes serial_port works in repeat use.