Я пишу код, который читает и пишет на последовательное устройство с помощью boost::asio
учебный класс. Однако при отправке нескольких строк между программами я заметил, что в принимающей программе данные читаются в том порядке, в котором они были записаны в последовательный порт, а не в том случае, если данные отправляются из другой программы. Если я начну читать Через несколько секунд я получаю не те значения, которые отправляю в данный момент, а те, которые были отправлены ранее. Я предполагаю, что это вызвано тем, как я настраиваю boost::asio::serial_port
:
int main(int argc, char const *argv[]){
int baud=atoi(argv[1]);
std::string pty=argv[2];
printf("Virtual device: %s\n",pty.data());
printf("Baud rate: %d\n",baud);
boost::asio::io_service io;
boost::asio::serial_port port(io, pty);
port.set_option(boost::asio::serial_port_base::baud_rate(baud));
// counter that writes to serial port in 1s intervals
int val=0;
while (1){
std::string data=std::to_string(val);
data+='\n';
std::cout << data;
write(port,boost::asio::buffer(data.c_str(),data.size()));
sleep(1);
val++;
data.clear();
}
port.close();
return 0;
}
Есть ли способ заставить прошлые данные быть отброшенными, как только новое значение будет отправлено на последовательный порт (что, я полагаю, должно быть сделано в части кода write ())?
Boost.Asio не предоставляет высокоуровневую абстракцию для очистки буферов последовательного порта. Тем не менее, это часто может быть достигнуто с помощью конкретных платформ вызовов, таких как tcflush()
или же PurgeComm()
, работать на последовательном порту native_handle ().
Каждый последовательный порт имеет буфер приема и передачи, и очистка работает на один или оба буфера. Например, если два последовательных порта подключены (/dev/pts/3
а также /dev/pts/4
) и программа A
открывает и пишет /dev/pts/3
, тогда он может очищать только буферы, связанные с /dev/pts/3
(данные получены на /dev/pts/3
но не читать, а данные записывать /dev/pts/3
но не передается). Поэтому, если программа B
начинается, открывается /dev/pts/4
и хочет прочитать не устаревшие данные, затем запрограммировать B
необходимо очистить приемный буфер для /dev/pts/4
после открытия последовательного порта.
Вот полный пример работы с CentOs. Когда пример выполняется в качестве записывающего устройства, он будет записывать последовательно увеличивающееся число в последовательный порт один раз в секунду. Когда пример работает как писатель, он будет читать пять чисел, спать в течение 5 секунд и очищать свой буфер чтения каждую следующую итерацию:
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
/// @brief Different ways a serial port may be flushed.
enum flush_type
{
flush_receive = TCIFLUSH,
flush_send = TCOFLUSH,
flush_both = TCIOFLUSH
};
/// @brief Flush a serial port's buffers.
///
/// @param serial_port Port to flush.
/// @param what Determines the buffers to flush.
/// @param error Set to indicate what error occurred, if any.
void flush_serial_port(
boost::asio::serial_port& serial_port,
flush_type what,
boost::system::error_code& error)
{
if (0 == ::tcflush(serial_port.lowest_layer().native_handle(), what))
{
error = boost::system::error_code();
}
else
{
error = boost::system::error_code(errno,
boost::asio::error::get_system_category());
}
}
/// @brief Reads 5 numbers from the serial port, then sleeps for 5 seconds,
/// flushing its read buffer every other iteration.
void read_main(boost::asio::serial_port& serial_port)
{
std::vector<unsigned char> buffer(5);
for (bool flush = false;; flush = !flush)
{
std::size_t bytes_transferred =
read(serial_port, boost::asio::buffer(buffer));
for (std::size_t i = 0; i < bytes_transferred; ++i)
std::cout << static_cast<unsigned int>(buffer[i]) << " ";
boost::this_thread::sleep_for(boost::chrono::seconds(5));
if (flush)
{
boost::system::error_code error;
flush_serial_port(serial_port, flush_receive, error);
std::cout << "flush: " << error.message() << std::endl;
}
else
{
std::cout << "noflush" << std::endl;
}
}
}
/// @brief Write a sequentially increasing number to the serial port
/// every second.
void write_main(boost::asio::serial_port& serial_port)
{
for (unsigned char i = 0; ; ++i)
{
write(serial_port, boost::asio::buffer(&i, sizeof i));
boost::this_thread::sleep_for(boost::chrono::seconds(1));
}
}
int main(int argc, char* argv[])
{
boost::asio::io_service io_service;
boost::asio::serial_port serial_port(io_service, argv[2]);
if (!strcmp(argv[1], "read"))
read_main(serial_port);
else if (!strcmp(argv[1], "write"))
write_main(serial_port);
}
Создать виртуальные последовательные порты с socat
:
$ socat -d -d PTY: PTY:
2014/03/23 16:22:22 socat[12056] N PTY is /dev/pts/3
2014/03/23 16:22:22 socat[12056] N PTY is /dev/pts/4
2014/03/23 16:22:22 socat[12056] N starting data transfer loop with
FDs [3,3] and [5,5]
Начинаем читать и писать примеры:
$ ./a.out read /dev/pts/3 & ./a.out write /dev/pts/4
[1] 12238
0 1 2 3 4 noflush
5 6 7 8 9 flush: Success
14 15 16 17 18 noflush
19 20 21 22 23 flush: Success
28 29 30 31 32 noflush
33 34 35 36 37 flush: Success
Как показано в выводе, числа пропускаются только в последовательности, когда читатель очищает свой буфер чтения: 3 4 noflush 5 6 7 8 9 flush 14 15
,
Других решений пока нет …