В Windows, я наблюдаю, что если async_read
операция завершается успешно на последовательный порт, и я сразу же начать другую async_read
операцию чтения n
байт, то второй async_read
операции немедленно неожиданно завершается с успехом и передается 0 байт.повышение :: ASIO :: async_read заканчивается без выполнения условия завершения
после второй
async_read
операции, если третьяasync_read
операция инициируется для чтенияn
байт, то он будет в комплекте с успехом иn
байт переданы// where buffer_size(buffer) and n are both greater than 1 async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { // completes with error=success and bytes_transferred=n async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { // complete with error=success and bytes_transferred=0 async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { // completes with error=success and bytes_transferred=n }); }); });
, если 1 миллисекунды сна выполняется между первой и второй операциями
async_read
, то вторая операция завершается с успехом иn
переданных байт// where buffer_size(buffer) and n are both greater than 1 async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { // completes with error=success and bytes_transferred=n sleep_for(milliseconds(1)); async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { // completes with error=success and bytes_transferred=n }); });
Почему это происходит и как я могу его избежать?
Для конкретики, я использую Boost.Asio на Windows, общаться с микроконтроллером через RS232 эмулируется ATXMEGA-192A3U. Я посылаю команду запуска контроллеру и считываю вывод с таймаутом. Я прочитал вывод, вызвав функцию ReadPort
, код которой приведен ниже. Программа выполняет следующие задачи чтения подряд:
- Проверьте ответ микроконтроллера на команду запуска. Это чтение преуспеть в чтении трех символов, которые я ожидаю:
R\r\n
- Прочитано
n
байтов вывода контроллера на несколько сотен мс.
Операция async_read
на шаге 2 неожиданно завершается с успехом, несмотря на то, что не прочитала количество запрошенных байтов.
class BoostBasedCommunication
{
public:
BoostBasedCommunication();
~BoostBasedCommunication(void);
/*...*/
virtual int ReadPort(
int const numberOfCharacters, // maximum number of characters to be read
unsigned long const globalTimeout, // maximum time the operation is allowed to take in ms
unsigned long const intermediateTimeout, // maximum time allowed between two consequtive characters in ms
int& numberOfCharactersRead
);
/*...*/
private:
/*...*/
std::vector<unsigned char> inputBuffer; ///< buffer to save data to that is received
size_t numberOfBytesRead; ///< Number of bytes read
int lastErrorCode; ///< last error code
io_service my_io_service; ///< boost io service class
serial_port port; ///< boost serial port class
/*...*/
};
// Reads from the port until numberOfCharacters have been read, or the
// deadline_timer has expired, or the time between two consecutive calls of
// the completion condition is larger than intermediateTimeoutMS
int BoostBasedCommunication::ReadPort(
int const numberOfCharacters, // maximum number of characters to be read
unsigned long const globalTimeoutMS, // maximum time the operation is allowed to take in ms
unsigned long const intermediateTimeoutMS, // maximum time allowed between two consecutive characters in ms
int& numberOfCharactersRead // Actual number of characters read
)
{
try
{
OutputDebugStringA("ReadPort called\r\n");
my_io_service.reset();
deadline_timer gloabalTimeout(my_io_service);
inputBuffer.resize(numberOfCharacters);
timeoutHandler myGlobalTimeoutHandler(&port);
completion_handler_2 myHandler(&gloabalTimeout, numberOfBytesRead);
completion_condition_2 my_completion_condition(intermediateTimeoutMS, numberOfCharacters);
// Set the timer
gloabalTimeout.expires_from_now(boost::posix_time::milliseconds(globalTimeoutMS));
gloabalTimeout.async_wait(myGlobalTimeoutHandler);
async_read(port, boost::asio::buffer(inputBuffer, numberOfCharacters), my_completion_condition, myHandler);
my_io_service.run(); // run the io service
numberOfCharactersRead = numberOfBytesRead;
}
catch (std::exception&)
{
return COMMUNICATIONFAILED;
}
return NOERROR;
}
class completion_condition_2
{
public:
completion_condition_2(
long intermediateTimeOutTime,
size_t numberOfCharactersTobeRead
) :intermediateTimeOutTime(intermediateTimeOutTime),
numberOfCharactersTobeRead(numberOfCharactersTobeRead)
{}
std::size_t operator()(
const boost::system::error_code& error, // Result of latest async_read_some operation.
std::size_t bytes_transferred // Number of bytes transferred so far.
)
{
if (error)
{
OutputDebugStringA(("completion_condition received error code: " + error.message() + "\r\n").c_str());
if (error.value() == ERROR_OPERATION_ABORTED)
{
return 0;
}
}
/* ...Code concerning the intermediate timeout, which is commented out...*/
if (numberOfCharactersTobeRead <= bytes_transferred) // Enough data has been read
{
std::stringstream message;
message << "completion_condition: bytes transferred: " << bytes_transferred << " of " << numberOfCharactersTobeRead << " => done!" << std::endl;
OutputDebugStringA(message.str().c_str());
return 0;
}
else // More data should be read.
{
std::stringstream message;
message << "completion_condition: bytes transferred: " << bytes_transferred << " of " << numberOfCharactersTobeRead << " => continue!" << std::endl;
OutputDebugStringA(message.str().c_str());
return numberOfCharactersTobeRead - bytes_transferred;
}
}
private:
size_t numberOfCharactersTobeRead; ///< Number of characters to be read
};
class completion_handler_2 {
public:
completion_handler_2(
deadline_timer* _globalTimeout,
size_t& numberOfBytesRead
) :_globalTimeout(_globalTimeout),
numberOfBytesRead(numberOfBytesRead)
{
}
void operator()(
const boost::system::error_code& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes read.
)
{
OutputDebugStringA(("completion handler called with error code: " + error.message() + "\r\n").c_str());
if (error)
{
if (error.value() == ERROR_OPERATION_ABORTED)
{
numberOfBytesRead = bytes_transferred;
return;
}
else
{
BOOST_THROW_EXCEPTION(std::exception("Communication failed"));
}
}
OutputDebugStringA("completion handler: timeout cancelation.\r\n");
_globalTimeout->cancel();
numberOfBytesRead = bytes_transferred;
}
private:
deadline_timer* _globalTimeout; ///< global timeout deadline timer
size_t& numberOfBytesRead; ///< number of bytes read
};
Когда я выполнить первое чтение, который работает, как и ожидалось, я получаю следующий вывод:
ReadPort called
completion_condition: bytes transferred: 0 of 3 => continue!
completion_condition: bytes transferred: 3 of 3 => done!
completion handler called with error code: success
completion handler timeout cancelation.
timeoutHandler received error code: The I/O operation has been aborted because of either a thread exit or an application request
Если я сразу же выполнить другую считаны после первого завершил, операция завершается через 2 мс со следующим выходом:
ReadPort called
completion_condition: bytes transferred: 0 of 1024 => continue!
completion handler called with error code: success // Why is the completion handler called here, although the completion condition did not return 0?
completion handler timeout cancelation.
timeoutHandler received error code: The I/O operation has been aborted because of either a thread exit or an application request
а третьего чтения, сразу после последнего работает, как ожидалось:
ReadPort called
completion_condition: bytes transferred: 0 of 1024 => continue!
completion_condition: bytes transferred: 8 of 1024 => continue!
...
completion_condition: bytes transferred: 88 of 1024 => continue!
completion_condition: bytes transferred: 96 of 1024 => continue!
timeoutHandler called cancel of seriel port.
completion_condition received error code: The I/O operation has been aborted because of either a thread exit or an application request
completion handler called with error code: The I/O operation has been aborted because of either a thread exit or an application request
Имея это в виду, я смог установить стабильную связь между компьютером и микроконтроллером. Однако, я думаю, что я не понимаю эффект тайм-аута 1 мс. Микроконтроллер в некоторых точках действует как говорящий, который выводит 8 байт примерно каждые 7,5 мс. Я могу прочитать порт на несколько 100 мс, не нажимая тайм-аут. Почему связь не прерывается после первых 8 байтов, хотя требуется больше 1 мс, пока не появятся следующие 8 байтов? –
@PaulR. 'ReadFile' отключается после 8 байтов из-за его тайм-аута интервала времени ожидания 1 мс. Обратите внимание, как в вашем выводе при попытке прочитать 1024 байта с помощью операции 'async_read', каждая промежуточная операция' async_read_some' считывает 8 байтов. –
ОК, я понимаю. Итак, после 8 байтов вызывается мой обратный вызов условия завершения, что я и наблюдаю. –