boost :: asio :: serial_port lendo após a reconexão do dispositivo
-
22-08-2019 - |
Pergunta
Eu tenho um problema com o boost :: asio :: leitura classe serial_port de um dispositivo GPS (USB-Serial). Ligar o dispositivo e leitura de funciona bem, mas quando eu desligar e voltar a ligar o dispositivo, read_some não lê bytes da porta.
Como impulso não costura para detectar que a porta serial está desaparecido (nome esta_aberta () retorna true), eu periodicamente cancelar (), close () e aberto (GPS_PORT) o dispositivo quando eu não obter dados, redefinindo as opções de porta no caminho. Mas isso não quer ajudar, a entrada tampão estadias esvaziar.
Estou faltando alguma coisa, ou fazendo algo errado, ou este é um bug no asio? Existe uma maneira padrão para detectar que a porta se foi?
Solução
É difícil dizer o que é a razão exata no seu caso, mas a prática mostra que muitas vezes você precisa desativar sensibilidade RTS
em sua porta serial.
RTS
é um pino na interface verdadeira RS-232
que é quando um dispositivo do outro lado está ligado.
invoca serial_port::read_some
subjacentes função Windows API
que olhares sobre esse sinal.
Como você não tem o dispositivo RS-323
real, você precisa contar com a emulação condutor deste sinal que pode estar com defeito (e, infelizmente, muitas vezes é).
Para desativá-lo, invoque serial_port::set_option(DCB)
com o conjunto RTSControl
para RTS_CONTROL_DISABLE
.
Se close()
'ing seu punho não ajudar, pode haver um problema com boost
. O código-fonte para olhares close()
assim:
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. se CloseHandle()
falhar por algum motivo (ou trava), o valor do identificador interno não é beign atribuído a INVALID_HANDLE_VALUE
e is_open()
sempre retornará true
.
Para contornar isso, certo verificação is_open()
após close()
'ing, e se ele retorna true
, destruir toda instância de boost::asio::serial_port
e criá-lo novamente.
Outras dicas
Normalmente, você deve obter uma exceção do tipo boost::system::system_error
quando read_some
não pode preparar mais. Tente usar read
em vez disso, talvez ele retorna um erro e não apenas retornar. Você também pode tentar os métodos assíncronos; neste caso, o manipulador deve obter um objeto de erro quando o dispositivo foi desconectado.
Alterantively você poderia obter o identificador para a porta usando a função native()
e chamar ClearCommError () sobre isso. Ele pode retornar o erro.
Apesar do fácil manuseio de asio boost::ip:tcp
, acho que lidar com impulso serial_port
requer um cuidado especial no Windows 7.
Eu tenho problema semelhante e tem sobre ele, redefinindo uma instância de boost::asio::io_service
, io_service_.reset()
.
Sou capaz de ler dados de forma assíncrona, mas não o faça mesma coisa a partir da segunda tentativa.
Na verdade, não foi um problema da própria função de leitura, registrar leitura assíncrona falhou, o que levou o retorno imediato de boost::asio::io_service::run()
na segunda tentativa.
Eu não estou certo de que este é o mesmo problema como o cartaz original tinha porque eu estou usando mais recente biblioteca de impulso destes dias.
De qualquer forma aqui está a minha solução:
// 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.