views:

1264

answers:

2

I have a problem with the boost::asio::serial_port class reading from a GPS device (USB-Serial). Connecting the device and reading from it works fine, but when I disconnect and reconnect the device, read_some doesn't read any bytes from the port.

As boost doesn't seam to detect that the serial port is gone ( is_open() returns true ), I periodically cancel(), close() and open( GPS_PORT ) the device when I don't get data, resetting the port options on the way. But this doesn't help either, the input buffer stays empty.

Am I missing something, or doing something wrong, or is this a bug in asio? Is there a standard way to detect that the port is gone?

+2  A: 

It's hard to say what is the exact reason in your case, but practice shows that you often need to disable RTS sensitivity on your serial port.

RTS is a pin on real RS-232 interface that is on when a device on the other side is on.

serial_port::read_some invokes underlying Windows API function that looks on this signal.

As you don't have the real RS-323 device, you need to rely on the driver emulation of this signal which may be faulty (and unfortunately often is).

To disable it, invoke serial_port::set_option(DCB) with RTSControl set to RTS_CONTROL_DISABLE.

If close()'ing your handle doesn't help, it may be a problem with boost. Source code for close() looks like this:

  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. if CloseHandle() fails for some reason (or hangs), the internal handle value is not beign assigned to INVALID_HANDLE_VALUE and is_open() will always return true.

To work around this, check is_open() right after close()'ing, and if it returns true, destroy whole instance of boost::asio::serial_port and create it again.

Quassnoi
Thanks, I'll try that - but shouldn't the read also fail when the device is first opened?
VolkA
Read will fail when the driver says RTS is off. When it happens depends on the driver implementation. BTW, when the read begins to succeed: ater reboot, after device reattachment, after timeout?
Quassnoi
Ok, I just tried to set RTS_CONTROL_DISABLE - it doesn't change the behaviour. The boost::serial_port::is_open returns true, but won't start reading.The behaviour is: connect GPS, start tool - reading, disconnect GPS, reading stops, connect again, still not reading. Restart tool: works again.
VolkA
See updated post
Quassnoi
Thanks, checking for is_open() after the close() and then creating a new instance fixed the problem!
VolkA
+1  A: 

Normally you should get an exception of type boost::system::system_error when read_some cannot ready anymore. Try using read instead, maybe it returns an error and doesn't just return. You could also try the async methods; in this case the handler should get an error object when the device was disconnected.

Alterantively you could get the handle to the port using the native() function and call ClearCommError() on that. It might return the error.

vividos
That looks good - I'm checking the error code, but I haven't noticed the native() method yet ... with that I should be able to somehow convice the serial port to read :)
VolkA
Maybe you just don't get to notice that a device was plugged out, due to the usb driver not reporting this to the OS. In that case: bad luck :/
vividos