Masz kilka opcji. Możesz użyć wbudowanej funkcji portu szeregowego async_read_some
lub użyć autonomicznej funkcji boost::asio::async_read
(lub async_read_some
).
W dalszym ciągu napotkasz sytuację, w której jesteś skutecznie "zablokowany", ponieważ żadne z nich nie wywoła oddzwaniania, chyba że (1) dane zostały przeczytane lub (2) wystąpił błąd. Aby obejść ten problem, należy użyć obiektu deadline_timer
, aby ustawić limit czasu. Jeśli limit czasu zostanie uruchomiony jako pierwszy, żadne dane nie są dostępne. W przeciwnym razie będziesz mieć odczytane dane.
Dodatkowa złożoność nie jest aż tak zła. Otrzymasz dwa wywołania zwrotne o podobnym działaniu. Jeśli wywołanie zwrotne "odczyt" lub "limit czasu" zostanie wywołane z błędem, wiesz, że to przegrany wyścig. Jeśli któryś z nich strzela bez błędu, to wiesz, że to zwycięzca wyścigu (i powinieneś anulować drugie połączenie). W miejscu, w którym miałbyś połączenie blokujące z read_some
, będziesz mieć teraz połączenie z io_svc.run()
. Twoja funkcja będzie nadal blokować, tak jak poprzednio, gdy dzwoni run
, ale tym razem kontrolujesz czas trwania.
Oto przykład:
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
}
To powinno Ci zacząć z tu i tam tylko kilka usprawnień do indywidualnych potrzeb. Mam nadzieję, że to pomoże!
Kolejna uwaga: żadne dodatkowe wątki nie były potrzebne do obsługi wywołań zwrotnych. Wszystko jest obsługiwane w ramach połączenia z numerem run()
. Nie wiem, czy byłaś już sobie z tego sprawę ...
Użyłem tego podejścia i działa dobrze, ale tylko za pierwszym razem. Następne wywołanie funkcji io_svc.run() zwraca natychmiast. Czy muszę zrobić coś ekstra? Dzięki – Schiavini
najwyraźniej musiałem zadzwonić do io_svc.reset() po io_svc.run(). – Schiavini
świetna odpowiedź, bardzo przydatne. Jednak otrzymuję pewne dziwne zachowanie: Mam program zapisujący dane do portu szeregowego co 5 ms i kolejny odczyt z niego, jednak jeśli ustawię opóźnienie asynchroniczne na 5 ms na części czytnika, nadal otrzymuję pewne "dane niedostępne" połączenia mieszane z wartościami odczytu.Czy jest to spowodowane pewnym obciążeniem kodu, czy też czegoś mi brakuje? – joaocandre