boost::asio::serial_port чтение после повторного подключения устройства
-
22-08-2019 - |
Вопрос
У меня проблема с чтением класса boost::asio::serial_port с устройства GPS (USB-Serial).Подключение устройства и чтение с него работает нормально, но когда я отключаю и снова подключаю устройство, read_some не читает никаких байтов из порта.
Поскольку boost не может обнаружить, что последовательный порт пропал (is_open() возвращает true), я периодически отменяю(), закрываю() и открываю (GPS_PORT) устройство, когда не получаю данные, сбрасывая параметры порта в пути.Но и это не помогает, входной буфер остается пустым.
Я что-то упускаю или делаю что-то не так, или это ошибка в asio?Есть ли стандартный способ обнаружить, что порт пропал?
Решение
Трудно сказать, какова точная причина в вашем случае, но практика показывает, что отключать приходится часто. RTS
чувствительность вашего последовательного порта.
RTS
это настоящая булавка RS-232
интерфейс, который включается, когда включено устройство на другой стороне.
serial_port::read_some
вызывает лежащий в основе Windows API
функция, которая смотрит на этот сигнал.
Поскольку у тебя нет настоящего RS-323
устройства, вам нужно полагаться на эмуляцию драйвера этого сигнала, который может быть неисправен (и, к сожалению, часто так и есть).
Чтобы отключить его, вызовите serial_port::set_option(DCB)
с RTSControl
установлен в RTS_CONTROL_DISABLE
.
Если close()
обращение к вашему дескриптору не помогает, это может быть проблема с boost
.Исходный код для close()
выглядит так:
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;
}
, я.е.если CloseHandle()
по какой-то причине происходит сбой (или зависает), значение внутреннего дескриптора не присвоено INVALID_HANDLE_VALUE
и is_open()
всегда вернусь true
.
Чтобы обойти эту проблему, проверьте is_open()
сразу после close()
'ing, и если он вернется true
, уничтожить весь экземпляр boost::asio::serial_port
и создайте его снова.
Другие советы
Обычно вы должны получить исключение типа boost::system::system_error
когда read_some
больше не могу быть готов.Попробуйте использовать read
вместо этого, возможно, он возвращает ошибку, а не просто возвращается.Вы также можете попробовать асинхронные методы;в этом случае обработчик должен получить объект ошибки, когда устройство было отключено.
Альтернативно вы можете получить дескриптор порта, используя native()
функцию и вызовите для нее ClearCommError().Это может вернуть ошибку.
Несмотря на простоту управления asio boost::ip:tcp
, я думаю, улучшение управляемости serial_port
требует особой осторожности в Windows 7.
У меня аналогичная проблема и преодолел это, сбросив экземпляр boost::asio::io_service
, io_service_.reset()
.
Я могу читать данные асинхронно, но со второй попытки не получается сделать то же самое.
На самом деле, это не проблема самой функции чтения: регистрация сбоя асинхронного чтения привела к немедленному возврату из boost::asio::io_service::run()
со второй попытки.
Я не уверен, что это та же проблема, что и у оригинального постера, потому что в наши дни я использую более новую библиотеку повышения.
В любом случае вот мое решение:
// port open step
port_ = boost::shared_ptr<boost::asio::serial_port>
(new boost::asio::serial_port(io_service_));
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service_));
port_->async_read_some(....);
.......
// port close step
port_->cancel();
port_->close();
port_.reset();
io_service_.stop();
io_service_.reset(); // <-- IMPORTANT: this makes serial_port works in repeat use.