2009-01-18 23 views
11

Tôi đang cố gắng sử dụng boost :: asio để đọc và ghi từ thiết bị trên cổng nối tiếp. Cả hai boost :: asio: read() và boost :: asio :: serial_port :: read_some() chặn khi không có gì để đọc. Thay vào đó, tôi muốn phát hiện tình trạng này và viết một lệnh vào cổng để khởi động thiết bị.Làm cách nào để thực hiện đọc không được sử dụng asio?

Làm cách nào để phát hiện thấy không có dữ liệu?

Nếu cần, tôi có thể làm mọi thứ không đồng bộ, tôi chỉ muốn tránh sự phức tạp thêm nếu có thể.

Trả lời

17

Bạn có một số tùy chọn. Bạn có thể sử dụng chức năng async_read_some tích hợp của cổng nối tiếp hoặc bạn có thể sử dụng chức năng độc lập boost::asio::async_read (hoặc async_read_some).

Bạn sẽ vẫn gặp phải tình huống bạn bị "chặn" một cách hiệu quả, vì cả hai sẽ không gọi lại trừ khi (1) dữ liệu đã được đọc hoặc (2) xảy ra lỗi. Để giải quyết vấn đề này, bạn sẽ muốn sử dụng đối tượng deadline_timer để đặt thời gian chờ. Nếu hết thời gian chờ, không có sẵn dữ liệu. Nếu không, bạn sẽ có dữ liệu đọc.

Độ phức tạp thêm không thực sự tồi tệ. Bạn sẽ kết thúc với hai cuộc gọi lại với hành vi tương tự. Nếu cuộc gọi lại "đọc" hoặc "thời gian chờ" kích hoạt với lỗi, bạn biết đó là cuộc đua thua cuộc. Nếu một trong hai đám cháy mà không có một lỗi, sau đó bạn biết đó là người chiến thắng cuộc đua (và bạn nên hủy bỏ cuộc gọi khác). Ở nơi bạn sẽ có cuộc gọi chặn đến read_some, bây giờ bạn sẽ có cuộc gọi đến io_svc.run(). Chức năng của bạn sẽ vẫn chặn như trước khi nó gọi run, nhưng lần này bạn kiểm soát thời lượng.

Dưới đây là một ví dụ:

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 
} 

Điều đó sẽ giúp bạn bắt đầu với chỉ một vài điều chỉnh ở đây và ở đó cho phù hợp với nhu cầu cụ thể của bạn. Tôi hi vọng cái này giúp được!

Lưu ý khác: Không cần thêm chuỗi chủ đề nào để xử lý cuộc gọi lại. Mọi thứ được xử lý trong cuộc gọi đến run(). Bạn không chắc chắn nếu bạn là đã nhận thức được điều này ...

+0

Tôi đã sử dụng phương pháp này và phương pháp này hoạt động tốt nhưng chỉ ở lần đầu tiên. Cuộc gọi tiếp theo tới io_svc.run() trả về ngay lập tức. Tôi có cần phải làm gì thêm không? Cảm ơn – Schiavini

+3

dường như tôi đã gọi io_svc.reset() sau io_svc.run(). – Schiavini

+0

câu trả lời tuyệt vời, rất hữu ích. Tuy nhiên, tôi nhận được một số hành vi lạ: Tôi có một chương trình ghi dữ liệu vào cổng nối tiếp sau mỗi 5 ms và một số khác đọc, tuy nhiên nếu tôi đặt độ trễ async thành 5 ms trên phần đầu đọc, tôi vẫn nhận được một số dữ liệu không có sẵn các cuộc gọi được trộn lẫn với các giá trị đọc.Đây có phải là do một số chi phí trên mã hoặc tôi thiếu một cái gì đó? – joaocandre

1

Bạn phải sử dụng chức năng miễn phí asio :: async_read.

1

của nó thực sự đơn giản hơn rất nhiều so với các câu trả lời ở đây đã ngụ ý, và bạn có thể làm điều đó đồng bộ:

Giả đọc chặn của bạn là một cái gì đó như thế này:

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

Sau đó, bạn thay thế nó với

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

bạn sử dụng các hình thức quá tải thay thế receive_from mà hầu như tất cả các gửi/nhận phương pháp có. Họ không may có một cuộc tranh luận cờ nhưng 0 dường như làm việc tốt.

+0

Ví dụ của bạn bao gồm ví dụ socket, trong khi câu hỏi là về cổng nối tiếp. –

Các vấn đề liên quan