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

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

Страниц: 1 ... 40 41 [42] 43 44 ... 88   Вниз
  Печать  
Автор Тема: Создаю библиотеку для работы с последовательными портами. [УШЕЛ ИЗ ПРОЕКТА].  (Прочитано 752864 раз)
pastor
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 2901



Просмотр профиля WWW
« Ответ #615 : Октябрь 08, 2011, 13:44 »

Можно как-то так
Записан

Integrated Computer Solutions, Inc. (ICS)
http://www.ics.com/
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #616 : Октябрь 08, 2011, 19:15 »

Я посмотрел. Спасибо за идейку.
Думаю объединить мнения всех трех сторон: твое, пастор, б-с-а и мое.

И в итоге сделаю что-то среднее. Уже есть идея как реализовать.
Попожжа скину результат ниже.

Думаю, что оно будет самое то!

PS:
Вот, прикрепил готовый компилябельный (под Win/WinCE/Linux/Symbian) проект, со всеми пожеланиями.
Конечно, нужно кое что подрихтовать (например методы canWriteNotification() и т.п.) - но это мелочи.

Блин, накосячил при упаковке: не добавил директорию include. Но теперь перезалил проект.
« Последнее редактирование: Октябрь 10, 2011, 21:29 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
b-s-a
Гость
« Ответ #617 : Октябрь 09, 2011, 16:45 »

Я не понял, зачем SerialPortNotifier сделан отдельным классом? Почему те методы и атрибуты нельзя сразу включить в SerialPortEngine? И вообще, зачем он нужен? Т.е. не привносит ли он что-либо ненужное, что слишком далеко находится от изначальной идеологии QIODevice?
Дальше, QIODevice изначально работает в неблокирующем режиме. Поэтому, зачем нужны методы setDataInterval() и setReadTimeout()?
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #618 : Октябрь 09, 2011, 18:59 »

Цитировать
Я не понял, зачем SerialPortNotifier сделан отдельным классом? Почему те методы и атрибуты нельзя сразу включить в SerialPortEngine? И вообще, зачем он нужен? Т.е. не привносит ли он что-либо ненужное, что слишком далеко находится от изначальной идеологии QIODevice?
Согласен, выкину его вообще.
Просто написал его от балды, типо чтоб понятно было, что эти методы от нотификатора, а вон те от енжины,

Цитировать
Дальше, QIODevice изначально работает в неблокирующем режиме. Поэтому, зачем нужны методы setDataInterval() и setReadTimeout()?
Ну, с setDataInterval() согласен, оно не нужно в принципе.
А вот  setReadTimeout() пусть останется, т.к. для QIODevice  есть флаг Unbuffered,
и я подразумевал, что в Unbuffered режиме (когда отключен внутренний буфер класса) можно
читать с таймаутами из порта (в блокирующем режиме).
Ну незнаю, кому-то оно необходимо (не факт что всех устроит неблокирующий режим), так что пусть setReadTimeout() останется.

ЗЫ: Ну а так, в целом, годится такая архитектура?
Т.к. удачно она решила проблему с некоторыми вещами и упростила.
Записан

ArchLinux x86_64 / Win10 64 bit
b-s-a
Гость
« Ответ #619 : Октябрь 09, 2011, 20:10 »

А вот  setReadTimeout() пусть останется, т.к. для QIODevice  есть флаг Unbuffered,
и я подразумевал, что в Unbuffered режиме (когда отключен внутренний буфер класса) можно
читать с таймаутами из порта (в блокирующем режиме).
Ну незнаю, кому-то оно необходимо (не факт что всех устроит неблокирующий режим), так что пусть setReadTimeout() останется.
Unbuffered - как я понял, это аналог опции O_SYNC у функции open стандарта POSIX. Т.е. данные идут на диск напрямую, минуя внутренние кэши.
Предлагаю, полностью отказаться от блокирующего режима для SerialPort. Так как это реализуется довольно просто.
Еще раз повторяюсь, блокирующий режим не достаточно точно работает, для меня. Например, мне необходимо отправить 1 байт с четностью Mark, затем дождаться отправки данных (но менее 5 мс), затем переключить порт в режим четности Space и отправить еще N байт. Затем дождаться фактической отправки всех данных (ОС с достаточно точностью это не поддерживают, на сколько я понял), затем ждать не более 20 мс первый байт ответа. Если он пришел, то ждать остальные. Между байтами интервал не более 5 мс.

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

Сообщений: 2812


Просмотр профиля
« Ответ #620 : Октябрь 09, 2011, 21:23 »

2 b-s-a

Цитировать
Предлагаю, полностью отказаться от блокирующего режима для SerialPort.
Ладно, хрен с ним, выкинуть не вопрос.

Цитировать
Так как это реализуется довольно просто.
Пример в студию.

Цитировать
Лично мне непонятно, зачем делать приватный класс для приватного класса?
Engine реализует нативный (платформо-зависимый) интерфейс, а также обеспечивает нотификацию, т.е. весь функционал в одном классе "движок", т.е. оно должно быть от QObject!!!

Теперь, SerialPort (не от QObject) предоставляет вспомогательный общий функционал (всякие промежуточные методы), а также связывает SerialPort и Engine. Это сделано для того чтобы не придумывать что-то свое, а взять готовую реализацию вспомогательных\промежуточных методов из сокетов и тупо их скопипастить, т.к. они уже проверены и отработаны годами.

Опять же, кроме как из сокетов, примеров больше взять неоткуда, ну нет в Qt4 больше классов с аналогичной функциональностью (есть жалкий аналог - это QProcess, но он не то). 
А если делать самому с нуля - то нужно дофига чего учитывать и не факт, что оно потом заработает как надо.

Предложи свой реальный компилябельный вариант "без приватных классов для приватных классов", а не просто на словах (хотя бы для одной платформы).

И допили для линукса обработку ошибок паритета, фрейма и политик.
Раз ты предложил эти методы - то тебе и флаг. Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
b-s-a
Гость
« Ответ #621 : Октябрь 09, 2011, 23:52 »

Пример в студию.
Код
C++ (Qt)
QByteArray ret;
while(qint64 remains = size - ret.size()) {
   serial.waitForReadyRead(-1);
   ret += serial.read(remains);
}
return ret;
Т.е. это простейший вариант блокирующего чтения.
Записан
b-s-a
Гость
« Ответ #622 : Октябрь 10, 2011, 10:58 »

И допили для линукса обработку ошибок паритета, фрейма и политик.
Раз ты предложил эти методы - то тебе и флаг. Улыбающийся
Жду, когда ты закончишь с реструктуризацией проекта, чтобы потом конфликты судорожно не решать.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #623 : Октябрь 10, 2011, 21:29 »

Ну вроде я закончил, см. gitorious
Записан

ArchLinux x86_64 / Win10 64 bit
b-s-a
Гость
« Ответ #624 : Октябрь 13, 2011, 12:11 »

При реализации поддержки политик обработки ошибок четности возникли некоторые сложности.
Во-первых, надо каким-то образом хранить информацию о местах ошибок в данных. Вариантов вижу не много - только 2. Первый вариант - хранение в виде, в котором они поступают со стороны Unix (ошибки и 255 экранируются кодом 255). Второй вариант - хранить список позиций ошибок в отдельном списке (гиморней делать, но быстрее будет работать).
Во-вторых, текущий вариант чтения не подходит, так как в *nix можно потерять второй байт данных в случае ошибки (когда экранирующий символ будет считан в буфер, а сами данные уже не поместятся в буфер).
В-третьих, QIODevice производит предварительное чтение данных в свой внутренний буфер. Ориентируется на bytesAvailable(). Думаю, необходимо будет отключить эту фичу, иначе будет проблема с тем видом ошибок.
В-четвертых, может ввести булевый метод parityError(), который будет принимать значение true, если последняя операция чтения завершилась из-за ошибки четности.
Итак, решение я вижу следующее:
1. добавить в engine метод чтения по одному байту: char nativeGetChar(bool *ok)
2. добавить внутренний буфер чтения и список положений сбойных байтов
3. побайтное чтение использовать только для случая StopReceivingPolicy
4. способ чтения должен выбирать класс SerialPortPrivate в зависимости от настроек (если четность есть и выбрана StopReceivingPolicy, то чтение по байтам через буфер, иначе - блоками напрямую)
4. попробовать принудительно выставлять флаг Unbuffered при вызове QIODevice::open(), чтобы отключить встроенную буферизацию.

Я только одного не понял, зачем нужны публичные методы с пиставкой native? Логичнее было бы, чтобы они были защищенными, а публичными были без этой приставки. А то лишняя какая-то писанина получается. Понятно что engine - это и так нативная реализация под конкретную платформу...

Жду конструктивную критику предложенного.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #625 : Октябрь 13, 2011, 21:20 »

Цитировать
Во-первых, надо каким-то образом хранить информацию о местах ошибок в данных. Вариантов вижу не много - только 2. Первый вариант - хранение в виде, в котором они поступают со стороны Unix (ошибки и 255 экранируются кодом 255). Второй вариант - хранить список позиций ошибок в отдельном списке (гиморней делать, но быстрее будет работать).
А зачем хранить эту информацию о местах ошибок? Т.е. ты планируешь добавить еще какой-то метод для получения списка этих позиций/мест?
Что это дает?
Не проще ли при обнаружении (при чтении) просто устанавливать в переменную ошибки последнее значение ошибки,
типа: ошибка паритета, ошибка фрейма и т.п?

Цитировать
В-третьих, QIODevice производит предварительное чтение данных в свой внутренний буфер. Ориентируется на bytesAvailable(). Думаю, необходимо будет отключить эту фичу, иначе будет проблема с тем видом ошибок.
А на что влияет bytesAvailable()? Оно сбрасывает ошибки или что?

Ведь можно сделать аналогично тому, как я сделал в винде, алгоритм примерно такой:

1. Если у нас установлена любая политика не равная "игнорировать", то в методе
WinSerialPortEngine::read(char *data, qint64 len) , когда приходит время читать, параметру len присваивается 1.
И метод read() читает ровно 1 байт.

2. Есть еще виндовый нотификатор, который кроме событий EV_RXCHAR и EV_TXEMPTY, отлавливает и EV_ERR.

3. Есть еще булевая переменная m_flagErrorFromCommEvent, которая устанавливается в true при возникновении любой ошибки по евенту EV_ERR
и сбрасывается в методе WinSerialPortEngine::read() после чтения и анализа байта.

Так вот, как это все работает:
при приходе в UART "битого" байта, срабатывает сначала нотифир по евенту EV_ERR (ХЗ почему, но вроде в винде оно так получается ) и устанавливает
через SerialPortPrivate::canErrorNotification() => m_engine->processIOErrors() одновременно в переменную ошибок - тип ошибки,
а также делает m_flagErrorFromCommEvent = true.
Далее, в этом же нотифире на этот же байт срабатывает событие  EV_RXCHAR и автоматом вызывается метод чтения,
через SerialPortPrivate::canReadNotification() => readFromPort() => read() => nativeRead().
Далее в nativeRead() смотрим: если политика есть "не игнорировать", то читаем один байт и далее,
смотрим: а булева переменная m_flagErrorFromCommEvent == true ?
И если да - то смотрим, а какая же политика установлена: SkipPolicy, PassZeroPolicy, StopReceivingPolicy
и в зависимости от того, какая политика - мы просто либо подменяем прочитанный байт на ноль либо еще что - то и т.п.
и в конце этого дела мы сбрасываем m_flagErrorFromCommEvent = false.

Т.к. все эти действия: нотификация по  EV_ERR, а потом по EV_RXCHAR происходят в одном потоке, то по идее, они должны и
соответствующие методы выполнять по порядку, т.е. сначала фиксируется факт ошибки, а потом происходит чтение байта.

Так вот, можно было б и что-то аналогичное для *nix сделать:
аналогом нотифира ошибок сделать QSocketNotifier(), настроенный на отлов ошибок.
Но тут проблема: неизвестно, реагирует ли он на ошибки паритета и фрейма,
и если даже и реагирует - то быстрее чем такойже нотифир но на событие о приходе байта?

Цитировать
В-четвертых, может ввести булевый метод parityError(), который будет принимать значение true, если последняя операция чтения завершилась из-за ошибки четности.
Итак, решение я вижу следующее:
1. добавить в engine метод чтения по одному байту: char nativeGetChar(bool *ok)
2. добавить внутренний буфер чтения и список положений сбойных байтов
Попробуй, т.к. я не особо понимаю как в *nix поймать ошибки четности и паритета.

Но насчет введения nativeGetChar(), а почему прочто не выставлять в методе nativeRead() размер = 1?

Цитировать
3. побайтное чтение использовать только для случая StopReceivingPolicy
А почему не сделать побайтное чтение для всех политик не равных "игнорировать"?

Цитировать
4. способ чтения должен выбирать класс SerialPortPrivate в зависимости от настроек (если четность есть и выбрана StopReceivingPolicy, то чтение по байтам через буфер, иначе - блоками напрямую)
4. попробовать принудительно выставлять флаг Unbuffered при вызове QIODevice::open(), чтобы отключить встроенную буферизацию.
Ну так если отключить буферизацию, то отключится и асинхронное чтение и (вроде так оно сейчас там сделано, т.е. если флаг m_isBuffered == true,
то вызывается SerialPortPrivate::readFromPort ).

Цитировать
Я только одного не понял, зачем нужны публичные методы с пиставкой native? Логичнее было бы, чтобы они были защищенными, а публичными были без этой приставки. А то лишняя какая-то писанина получается. Понятно что engine - это и так нативная реализация под конкретную платформу...
Ок, убрал все упоминания о "native" Улыбающийся

Записан

ArchLinux x86_64 / Win10 64 bit
b-s-a
Гость
« Ответ #626 : Октябрь 13, 2011, 23:10 »

А разве под Windows нельзя задать работу порта в режиме игнорирования, зануления и пропуска битых данных? Просто под *nix это можно сделать через настройки порта.
Дальше, отлов ошибок четности/фрейма под *nix возможен только в виде, когда перед сбойным байтом посылается байт 255 (и перед 255 тоже посылается): 255 0 - пришел 0 с ошибкой четности или фрейма (они не отличаются), 255 255 - пришел нормальный байт 255.

Почему я предложил getChar, так просто потому, что она позволяет получить информацию о битости данных. А в остальных случаях контроля ошибок все будет делать система.

Для реализации адекватной работы эмуляции четности mark/parity необходимо точное ожидание (именно на его основе необходимо делать реализацию метода flush, так как tcdrain и аналог под Windows работают с неадекватной точностью, поэтому данные прутся с ужасными промежутками - на осциллографе очень хорошо видно). Для этого был сделано изменение в QThread::usleep()/QThread::msleep(): https://qt.gitorious.org/qt/qt/merge_requests/1420 Поддержите по возможности. Кстати, критика приветствуется.
Цитировать
Ну так если отключить буферизацию, то отключится и асинхронное чтение и (вроде так оно сейчас там сделано, т.е. если флаг m_isBuffered == true,
то вызывается SerialPortPrivate::readFromPort ).
Что-то я эту область кода не заметил. Зачем оно нужно вообще? По идее, это реализует сам QIODevice. И я долго занимался сексом с отладчиком, пока не понял, что давая команду чтения одного байта, QIODevice вызывает readData для всего доступного на чтение.
Асинхронное чтение мне вообще не понятно. Зачем? По идее, все это делает ОС. Зачем дублировать?
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #627 : Октябрь 14, 2011, 09:40 »

Цитировать
А разве под Windows нельзя задать работу порта в режиме игнорирования, зануления и пропуска битых данных? Просто под *nix это можно сделать через настройки порта.
Можно, но я выше в топиках уже писал, что оно работает как-то странно, т.е. через одно место и приходится эмулировать вот таким образом.
см http://www.prog.org.ru/index.php?topic=9537.msg127752#msg127752
пост №565

Цитировать
Для этого был сделано изменение в QThread::usleep()/QThread::msleep(): https://qt.gitorious.org/qt/qt/merge_requests/1420 Поддержите по возможности. Кстати, критика приветствуется.
ОК, гляну.

Цитировать
Что-то я эту область кода не заметил. Зачем оно нужно вообще? По идее, это реализует сам QIODevice. И я долго занимался сексом с отладчиком, пока не понял, что давая команду чтения одного байта, QIODevice вызывает readData для всего доступного на чтение.
Ну да, на все 16386 (или сколько там)  Улыбающийся

Цитировать
Асинхронное чтение мне вообще не понятно. Зачем? По идее, все это делает ОС. Зачем дублировать?
в смысле? о чем ты?
« Последнее редактирование: Октябрь 14, 2011, 10:34 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
b-s-a
Гость
« Ответ #628 : Октябрь 14, 2011, 15:50 »

Я про это
Ну так если отключить буферизацию, то отключится и асинхронное чтение и (вроде так оно сейчас там сделано, т.е. если флаг m_isBuffered == true,
то вызывается SerialPortPrivate::readFromPort ).
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #629 : Октябрь 14, 2011, 16:56 »

Ну, все правильно.
Под асинхронным чтением я имею ввиду автоматическое чтение в буфер самого класса при появлении данных в UART.
Т.е. асинхронное == автоматическое  Смеющийся
Я не так выразился.

Цитировать
Что-то я эту область кода не заметил. Зачем оно нужно вообще? По идее, это реализует сам QIODevice.
Не реализует он это! Поэтому метод readFromPort() введен именно для автоматического чтения из UART в буфер класса при буферизованном режиме.
И если этот буф. режим отключить, то ничо в буфер класса читаться не будет.

Тем более, readFromPort() придуман не мной, а троллями (точнее метод readFromSocket() ), так что выкинуть его не получится.
Записан

ArchLinux x86_64 / Win10 64 bit
Страниц: 1 ... 40 41 [42] 43 44 ... 88   Вверх
  Печать  
 
Перейти в:  


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