Russian Qt Forum
Ноябрь 23, 2024, 10:29 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 ... 75 76 [77] 78 79 ... 88   Вниз
  Печать  
Автор Тема: Создаю библиотеку для работы с последовательными портами. [УШЕЛ ИЗ ПРОЕКТА].  (Прочитано 785368 раз)
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #1140 : Февраль 08, 2014, 17:40 »

В linux версии используются QSocketNotifier?
Под профайлером не пытались запускать?
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1141 : Февраль 08, 2014, 17:43 »

Цитата: Old
В linux версии используются QSocketNotifier?

Ага.

Цитата: Old
Под профайлером не пытались запускать?

Думаю толку не будет в профайлере, т.к. нужно и само Qt core тоже с ним (с опциями профилировки) пересобирать. Или я не прав?
Записан

ArchLinux x86_64 / Win10 64 bit
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #1142 : Февраль 08, 2014, 17:54 »

>>Юр, ну бы хоть посмотрел на ссылку что я дал
Я посмотрел, но честно говоря не сильно понял. "baud rate emulation" это что за зверь такой?

>>Но без эмуляции (когда скорость - по максимуму 38 мегабайт в секунду) - тут уже 40% набегает.
вот меня в  этом com0com напрягало какое-то  непонятное изобретение чего-то.
Есть COM-порт у него есть скорость, как её эмулировать можно? Типа едя я на запарожце под 40 км/час по городу и эмулирую буд-то еду под 40 км/час.
Ерунда какая-то.
Записан

Юра.
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #1143 : Февраль 08, 2014, 17:58 »

Не могу скачать тестовую программку. Что там, просто асинхронное чтение из порта?
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1144 : Февраль 08, 2014, 18:37 »

Цитата: lit-uriy
Я посмотрел, но честно говоря не сильно понял. "baud rate emulation" это что за зверь такой?

com0com в своем конфигураторе имеет галочку "эмулировать боды". Если она включена по com0com будет передавать данные между своими устройствами на скоростях, с которыми конфигурируются последовательные порты при открытии. Если было 9600 - то на 9600 и т.д Но если галочка снята то скорость игнорится и все данные передаются на максимально возможной которая поддерживается драйвером, это около 38 магабайт/сек.

Цитата: lit-uriy
Есть COM-порт у него есть скорость, как её эмулировать можно?

Ну дык это же виртуальное устройство, не привязано ни к какой хардвари, ни к каким клокам. Тут скорость любую можно задать.  Улыбающийся

Цитата:  Old
Не могу скачать тестовую программку. Что там, просто асинхронное чтение из порта?

Приаттачил тут, на всякий случай. Там две программки: читатель и писатель. Простые и банальные. Вся работа через сигналы/слоты.
Запускать так: <имя программки> <имя порта> <скорость в бодах>, например: stressreader.exe COM1 9600. Читатель также подсчитывает среднюю фактическую скорость приема: кол-во принятых байт за 10 секунд делит на 10.  Улыбающийся
« Последнее редактирование: Февраль 08, 2014, 18:39 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #1145 : Февраль 08, 2014, 18:57 »

Интересно еще смотреть на средний размер получаемого пакета данных. Т.е. сколько в среднем читается на один сигнал readyRead. Тогда будет понятно сколько раз срабатывает select в главном цикле.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1146 : Февраль 08, 2014, 19:12 »

где-то так: http://www.qtcentre.org/threads/57633-QSerialPort-read-delay?p=257542#post257542

Там и ниже еще есть дополнение (т.е. и для com0com и для реальных шнурков). Там приведено кол-во читаемых байт при каждом readyRead(). Но там результаты виндовые, но и для Linux результаты примерно те-же. Т.е. около 8~20 байт за раз, зависит от скорости, драйвера устройства, и прочих факторов.

UPD: Я думал уже о том, что нужно по-идее как-то размазать срабатывание событий во времени, т.е. с некоторой задержкой. Например, при первом событии RX_CHAR ничего не читать, а запускать таймер, например на 1-10 мсек, и только после срабатывания таймера читать все из устройства. Это должно разгрузить event-loop, но имеет и побочный эффект: если нужно прочитать всего один байт (или небольшое их кол-во), то он/они прочитаются с задержкой, что может быть в некоторых случаях неприемлемым.

Хотя я попробовал уже реализовать что-то подобное (ради эксперимента) и использовал QTimer, но нагрузка выросла до 90% при 9600 бод..  Наверно где-то накосячил. Улыбающийся

« Последнее редактирование: Февраль 08, 2014, 19:19 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #1147 : Февраль 09, 2014, 09:26 »

Набросал стресс-тестер на boost::asio.
Код
C++ (Qt)
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/program_options.hpp>
 
namespace ba = boost::asio;
namespace bs = boost::system;
namespace po = boost::program_options;
 
static const int timeInterval = 10;
 
// ====================================================================================
class reader
{
public:
reader( ba::io_service &srv, ba::serial_port &port ) :
m_srv( srv ),
m_port( port ),
m_timer( srv ),
m_buf( 1024 ),
m_total( 0 )
{
m_port.async_read_some( ba::buffer( m_buf ), boost::bind( &reader::on_readed, this, ba::placeholders::error, ba::placeholders::bytes_transferred ) );
 
m_timer.expires_from_now( boost::posix_time::seconds( timeInterval ) );
m_timer.async_wait( boost::bind( &reader::on_timeout, this, ba::placeholders::error ) );
}
 
protected:
void on_readed( const bs::error_code &error, size_t bytes_transferred )
{
if( error )
{
if( error == ba::error::operation_aborted )
return;
 
std::cerr << "Error: " << error.message() << std::endl;
}
else
{
m_total += bytes_transferred;
m_port.async_read_some( ba::buffer( m_buf ), boost::bind( &reader::on_readed, this, ba::placeholders::error, ba::placeholders::bytes_transferred ) );
}
}
 
void on_timeout( const bs::error_code &error )
{
if( error == ba::error::operation_aborted )
return;
 
std::cout << "Speed: " << double( m_total ) / timeInterval << std::endl;
m_total = 0;
 
m_timer.expires_from_now( boost::posix_time::seconds( timeInterval ) );
m_timer.async_wait( boost::bind( &reader::on_timeout, this, ba::placeholders::error ) );
}
 
ba::io_service &m_srv;
ba::serial_port &m_port;
ba::deadline_timer m_timer;
std::vector<char> m_buf;
int m_total;
};
 
// ====================================================================================
class writer
{
public:
writer( ba::io_service &srv, ba::serial_port &port ) :
m_srv( srv ),
m_port( port ),
m_buf( "Test string\n\r" )
{
ba::async_write( m_port, ba::buffer( m_buf ), boost::bind( &writer::on_writed, this, ba::placeholders::error ) );
}
 
protected:
void on_writed( const bs::error_code &error )
{
if( error )
{
if( error == ba::error::operation_aborted )
return;
 
std::cerr << "Error: " << error.message() << std::endl;
}
else
{
std::cout << "Writed data: " << m_buf.size() << " byte(s)." << std::endl;
ba::async_write( m_port, ba::buffer( m_buf ), boost::bind( &writer::on_writed, this, ba::placeholders::error ) );
}
}
 
ba::io_service &m_srv;
ba::serial_port &m_port;
std::string m_buf;
};
 
// ====================================================================================
int main( int argc, char *argv[] )
{
std::cout << "Serial port tester" << std::endl;
 
std::string mode;
std::string portname;
int speed;
 
po::options_description desc( "Options" );
desc.add_options()
( "help,h", "produce help message" )
( "speed,s", po::value<int>( &speed )->default_value( 9600 ), "port speed" )
( "mode", po::value<std::string>( &mode )->required(), "Mode: r | w" )
( "portname", po::value<std::string>( &portname )->required(), "port name" )
;
 
po::positional_options_description pos_options;
pos_options.add( "mode", 1 );
pos_options.add( "portname", 1 );
 
try
{
po::variables_map vm;
 
po::store( po::command_line_parser( argc, argv ).options( desc ).positional( pos_options ).run(), vm );
 
if( vm.count( "help" ) )
{
std::cout << desc << std::endl;
return 1;
}
 
po::notify( vm );
}
catch( po::error &e )
{
std::cout << e.what() << std::endl;
std::cout << desc << std::endl;
return 10;
}
 
std::cout << "Mode: " << mode << std::endl
 << "Port: " << portname << std::endl
 << "Speed: " << speed << std::endl;
 
try
{
ba::io_service io;
ba::serial_port port( io );
 
port.open( portname );
if( !port.is_open() )
{
std::cerr << "Error open serial port: " << portname << std::endl;
return 2;
}
 
port.set_option( ba::serial_port_base::baud_rate( speed ) );
 
if( mode[ 0 ] == 'w' || mode[ 0 ] == 'W' )
{
writer worker( io, port );
io.run();
}
else
{
reader worker( io, port );
io.run();
}
}
catch( std::exception &e )
{
std::cerr << e.what() << std::endl;
return 3;
}
 
return 0;
}
 

Сборка:
g++ main.cpp -lboost_system -lboost_program_options

Запуск:
main r /dev/ttyS0 --speed 9600     (читатель)
main w /dev/ttyS1 --speed 9600    (писатель)

Погонять смог только на socat, процессор жрет сильно. Интересно как будет работать на реальном железе.

Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1148 : Февраль 09, 2014, 17:53 »

Огромное спасибо за буст! Улыбающийся

Я немного переделал твой пример: разбил на два приложения (читатель/писатель), сделал из него qmake проект (в *.pro файл добавил чтобы удобнее было компилить), и увеличил размер передаваемого блока данных до 1024 байт (чтобы было также как и в Qt-шных тестах с QtSerialPort).

Проверил под Linux и результаты буста где-то в два раза лучше.. см. сводные таблички тут: https://bugreports.qt-project.org/browse/QTBUG-36684
также туда я приаттачил и бустовые тестовые проектики.
Записан

ArchLinux x86_64 / Win10 64 bit
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #1149 : Февраль 09, 2014, 18:53 »

Проверил под Linux и результаты буста где-то в два раза лучше..
boost для linux использует epoll.
Можно попробовать написать свой QSocketNotifier использующий epoll, потому что сильно ускорить select вряд ли получится. Что бы разгрузить процессор понадобиться как-то сократить количество вызовов select, например, что бы он вызывался не каждую итерацию mainloop.
Записан
Phoenix
Гость
« Ответ #1150 : Февраль 10, 2014, 10:46 »

Я думал уже о том, что нужно по-идее как-то размазать срабатывание событий во времени, т.е. с некоторой задержкой. Например, при первом событии RX_CHAR ничего не читать, а запускать таймер, например на 1-10 мсек, и только после срабатывания таймера читать все из устройства. Это должно разгрузить event-loop, но имеет и побочный эффект: если нужно прочитать всего один байт (или небольшое их кол-во), то он/они прочитаются с задержкой, что может быть в некоторых случаях неприемлемым.

Может быть задержку сделать настраиваемой? Для одной программы нужны отдельные байты, для другой нужны пакеты данных. Например в Moxa nport есть параметр, который задает паузу между пакетами данных. Если данные не приходят определенное время, то считается что это конец пакета и данные из буфера nport передаются драйверу nport. Если бы обмен данными по LAN между устройством nport и драйвером nport был по одному байту, то скорость была бы очень низкой.

Если продолжить аналогию с Moxa nport, то можно еще добавить критерии определения момента отправки данных получателю. Например отправлять данные если выполнено одно из трех условий:
1. данные не приходят определенной время;
2. в буфере уже есть определенное количество данных (например 5 байт);
3. встретилась определенная последовательность данных (это не всегда требуется, т.к. конец пакета иногда заканчивается CRC, а не конкретными данными).
Все критерии сделать настраиваемыми.
« Последнее редактирование: Февраль 10, 2014, 11:20 от Phoenix » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1151 : Февраль 10, 2014, 11:32 »

@Phoenix ,

ох, это уж слишком.

На крайний случай можно добавить некоторое апи get/set, которое может устанавливать задержку readyRead(), типа:

setReadyReadDeferred(int msec) / isReadyReadDeferred() const

хотя, хз.
Записан

ArchLinux x86_64 / Win10 64 bit
Phoenix
Гость
« Ответ #1152 : Февраль 10, 2014, 11:53 »

Отложенная (Deferred) отправка данных может не всегда сработать как нужно. Да и количество событий может не уменьшиться из за добавления событий таймера. Имхо нужно определять временной интервал именно между байтами.

Например так:
1. приняли байт, запустили таймер;
2. если таймер сработал, то отправляем данные;
3. если таймер не сработал, а мы приняли еще байт, то убиваем таймер и запускаем заново.
4. если таймер не сработал, а в буфере уже определенное количество данных, то убиваем таймер и отправляем данные.

Этим можно добиться уменьшение количества событий таймера, т.к. таймер будет убиваться если данные идут потоком (ну я так думаю).
« Последнее редактирование: Февраль 10, 2014, 11:56 от Phoenix » Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #1153 : Февраль 10, 2014, 15:19 »

@Phoenix ,

ох, это уж слишком.

На крайний случай можно добавить некоторое апи get/set, которое может устанавливать задержку readyRead(), типа:

setReadyReadDeferred(int msec) / isReadyReadDeferred() const

хотя, хз.
Можно добавить базовый класс Стратегии отправки/получения,  от него отнаследовать несколько предопределенный стратегий и дать возможность пользователю описывать свои.
Тогда пользователь сможет детально описывать поведение, а кому не надо - оставит стратегию по умолчанию.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1154 : Февраль 10, 2014, 16:41 »

Не прокатит. Слишком сложно, да и не пропустят, по крайней мере до Qt 5.3 или Qt 6
Записан

ArchLinux x86_64 / Win10 64 bit
Страниц: 1 ... 75 76 [77] 78 79 ... 88   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.096 секунд. Запросов: 23.