Question

J'essaie d'utiliser boost :: asio pour lire et écrire à partir d'un périphérique sur un port série. Les blocs boost :: asio: read () et boost :: asio :: serial_port :: read_some () sont bloqués lorsqu'il n'y a rien à lire. Au lieu de cela, j'aimerais détecter cette condition et écrire une commande sur le port pour lancer le périphérique.

Comment puis-je détecter qu'aucune donnée n'est disponible?

Si nécessaire, je peux tout faire de manière asynchrone, je préférerais simplement éviter la complexité supplémentaire si je le pouvais.

Était-ce utile?

La solution

En fait, vous avez plusieurs options. Vous pouvez utiliser la fonction async_read_some du port série ou la fonction autonome boost :: asio :: async_read (ou async_read_some ).

Vous rencontrerez toujours le cas où vous êtes effectivement "bloqué", car aucun de ceux-ci n'appellera le rappel à moins que (1) les données aient été lues ou (2) une erreur se produise. Pour résoudre ce problème, utilisez un objet delay_timer pour définir un délai d'expiration. Si le délai d'attente se déclenche en premier, aucune donnée n'était disponible. Sinon, vous aurez lu les données.

La complexité ajoutée n’est pas si grave. Vous allez vous retrouver avec deux rappels avec un comportement similaire. Si soit le " lu " ou le " timeout " callback se déclenche avec une erreur, vous savez que c'est le perdant de la course. Si l’un ou l’autre tire sans erreur, alors vous savez que c’est le vainqueur de la course (et vous devez annuler l’autre appel). À l’endroit où vous auriez eu votre appel bloquant à read_some , vous aurez maintenant un appel à io_svc.run () . Votre fonction sera toujours bloquée comme avant quand elle appelle run , mais cette fois, vous contrôlez la durée.

Voici un exemple:

void foo()
{
  io_service     io_svc;
  serial_port    ser_port(io_svc, "your string here");
  deadline_timer timeout(io_svc);
  unsigned char  my_buffer[1];
  bool           data_available = false;

  ser_port.async_read_some(boost::asio::buffer(my_buffer),
      boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout),
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
  timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>));
  timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),
                  boost::asio::placeholders::error));

  io_svc.run();  // will block until async callbacks are finished

  if (!data_available)
  {
    kick_start_the_device();
  }
}

void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
  if (error || !bytes_transferred)
  {
    // No data was read!
    data_available = false;
    return;
  }

  timeout.cancel();  // will cause wait_callback to fire with an error
  data_available = true;
}

void wait_callback(serial_port& ser_port, const boost::system::error_code& error)
{
  if (error)
  {
    // Data was read and this timeout was canceled
    return;
  }

  ser_port.cancel();  // will cause read_callback to fire with an error
}

Cela devrait vous permettre de commencer avec seulement quelques ajustements ici et là pour répondre à vos besoins spécifiques. J'espère que cela aide!

Autre remarque: aucun thread supplémentaire n'était nécessaire pour gérer les rappels. Tout est géré dans l'appel à run () . Je ne sais pas si vous étiez déjà au courant ...

Autres conseils

C’est beaucoup plus simple que ce que les réponses suggèrent, et vous pouvez le faire de manière synchrone:

Supposons que votre lecture bloquante ressemble à ceci:

size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);

Ensuite, vous le remplacez par

socket.non_blocking(true);
size_t len = 0;
error = boost::asio::error::would_block;
while (error == boost::asio::error::would_block)
    //do other things here like go and make coffee
    len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error);
std::cout.write(recv_buf.data(), len);

Vous utilisez la forme alternative surchargée de receive_from que possèdent presque toutes les méthodes d'envoi / réception. Ils prennent malheureusement un argument flags mais 0 semble fonctionner correctement.

Vous devez utiliser la fonction libre asio :: async_read.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top