2009-01-18 16 views
11

Próbuję użyć boost :: asio do odczytu i zapisu z urządzenia na porcie szeregowym. Zarówno boost :: asio: read() i boost :: asio :: serial_port :: read_some() blokują, gdy nie ma nic do czytania. Zamiast tego chciałbym wykryć ten warunek i napisać polecenie do portu, aby uruchomić urządzenie.Jak wykonać odczyt bez blokowania za pomocą asio?

Jak mogę wykryć, że żadne dane nie są dostępne?

W razie potrzeby mogę zrobić wszystko asynchronicznie, wolałbym raczej uniknąć dodatkowej złożoności, jeśli mogę.

Odpowiedz

17

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ę ...

+0

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

+3

najwyraźniej musiałem zadzwonić do io_svc.reset() po io_svc.run(). – Schiavini

+0

ś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

1

Musisz użyć darmowej funkcji asio :: async_read.

1

Jego faktycznie dużo prostsze niż odpowiedzi tutaj oznaczałoby, a można to zrobić synchronicznie:

Załóżmy, że blokowanie odczytu było coś takiego:

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

Następnie należy zastąpić go

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); 

użyć alternatywnej formy przeciążony z receive_from które otrzymują niemal wszystkie send/metody mają. Niestety przyjmują argument flag, ale 0 wydaje się działać dobrze.

+0

Twój przykład obejmuje przykład gniazda, podczas gdy pytanie dotyczyło portu szeregowego. –

Powiązane problemy