Название: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: mohax от Сентябрь 25, 2021, 08:28 Доброго всем дня
Версия Qt 5.14.2 Суть проблемы. Реализуется синхронный обмен данными с устройством. Необходим постоянный опрос данных по шине. Устройству посылаются команды, на которые получаются подтверждения выполнения, далее тут же идет опрос данных с их подтверждениями. Реализовать это с помощью механизма сигнал-слот чрезвычайно запутанно и геморрно. Наиболее подходящий метод - это реализация работы с последовательным портом с помощью waitForReadyRead() и waitForBytesWritten() в отдельном неграфическом потоке, в котором идет постоянный опрос шины(запись команд в порт и считывание данных с порта). Тем более, что есть в Qt пример работы Blocking Master и Blocking Slave. Так вот столкнулся с проблемой,что, послав запрос на пакет данных, waitForReadyRead() сообщает,что есть данные, но с помощью readAll() считывается только часть пришедшего пакета, а потом хоть сиди в этом waitForReadyRead() - он уже не сообщает о наличии данных. Хотя полностью весь пакет пришел - это видно с помощью шпиона компорта,который пришлось подключить для прослушки. Когда же я повторно запрашиваю данные, то пара waitForReadyRead() - readAll() выдает мне вместе вторую часть непришедшего вовремя пакета и часть следующего пакета. Но это для моей задачи уже бесполезно,т.к. я не могу дать подтверждение на пакетное получение данных. кусок проги такой Код: // запись данных Я вполне понимаю,что сейчас прибегут гуру и скажут, что надо работать с помощью механизма "сигнал-слот", но, извините, Qt задекларировала функцию waitForReadyRead(), и она должна работать нормально, а не через пень колоду. Да и механизм "сигнал-слот" в моем случае не подходит. Вопрос такой у меня: что можно сделать с этим waitForReadyRead(), чтобы все заработало? Если делаю вызов waitForReadyRead(-1), то просто поток блокируется на этом вызове, хотя данные из устройства в компорт пришли. Согласно справке, waitForReadyRead() блокируется до тех пор, пока новые данные не будут доступны для чтения и не будет выдан сигнал readyRead(). Получается,что на вторую часть пакета этот сигнал не выдается и waitForReadyRead() не разблокируется. Попробовал сделать опрос с помощью механизма "сигнал-слот", подготовив предварительно устройство к опросу с помощью другой программы, то в этом случае видно,что часто пакет считывается полностью после обработки двух последовательно пришедших сигналов readyRead() ПС. Эта проблема заметилась,когда начал читать пакеты с длиной более 250 байт. Когда надо было читать пакеты с длиной 64 байта, было все нормально Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: qate от Сентябрь 25, 2021, 11:59 бред в коде написан, везде
переделай на события ! Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: mohax от Сентябрь 25, 2021, 12:27 бред в коде написан, везде т.е. бред написан,значит, в примерах Qt? это во-первых.переделай на события ! Во-вторых, сама функция waitForReadyRead() разработчиками QSerialPort должна работать как заявлено и тогда код тоже будет работать окей Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: Igors от Сентябрь 25, 2021, 12:55 Так вот столкнулся с проблемой,что, послав запрос на пакет данных, waitForReadyRead() сообщает,что есть данные, но с помощью readAll() считывается только часть пришедшего пакета, а потом хоть .. А Вы рассчитывали что придет весь пакет, потому что "времени дали достаточно"? На это полагаться не стоит, ну хотя бы потому что может прийти и кусок следующего или вообще 100 пакетов. Когда же я повторно запрашиваю данные, то пара waitForReadyRead() - readAll() выдает мне вместе вторую часть непришедшего вовремя пакета и часть следующего пакета. Нет никакого "вовремя", когда придет - тогда и придет. И сколько придет - хз. Если хотите синхронку нужно буферировать самому и повторять "пару" до тех пор пока пакет не будет полностью вычитан. Начинать с проверки что пакет уже в Вашем буфере.Qt задекларировала функцию waitForReadyRead(), и она должна работать нормально, а не через пень колоду. Ну так обращайтесь к Qt :) И еще есть народная примета: если человек не поздоровался создавая тему - отвечать ему не стоит. Ладно, нарушим правило и посмотрим :)Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: sergek от Сентябрь 25, 2021, 13:54 Реализовать это с помощью механизма сигнал-слот чрезвычайно запутанно и геморрно. К счастью, это вина не самого механизма, а некоторой спутанности вашего сознания :) Цитировать Я вполне понимаю,что сейчас прибегут гуру и скажут, что надо работать с помощью механизма "сигнал-слот", но, Странный подход - спросить совета и тут же отказаться от помощи наиболее опытных участников форума.Вы сначала определитесь - хотите понять, как организовать синхронный обмен с портом или нет. Если появится желание, то поможем. Прошу прощения, себя к гуру никоим образом не отношу, но есть рабочие схемы. Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: mohax от Сентябрь 25, 2021, 14:22 Вы сначала определитесь - хотите понять, как организовать синхронный обмен с портом или нет. Если появится желание, то поможем. Очень хочу,но мне непонятно, как организовать синхронный обмен с помощью сигналов и слотов. Как идет работа с шиной у нас: операция чтения данных происходит в последовательности: 1. запрос данных 2. чтение данных 3. запрос статуса 4. чтение статуса для подтверждения чтения данных операция записи команд/данных происходит в последовательности: 1. запись команды 2.чтение статуса для подтверждения записи команд/данных внутри этих блоков все должно идти последовательно. Читаться данные должны как можно быстрее и постоянно. Сделал отдельный неграфический поток, в котором в функции run() идет постоянный опрос шины через QSerialPort, чтоб не зависал графич.интерфейс Если делать с помощью сигналов-слотов, то надо же как-то синхронизировать отправку сигнала с получением данных, да еще чтоб друг за другом все шло. Вот это непонятно,как сделать. Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: kuzulis от Сентябрь 25, 2021, 16:05 Жаль что кутешники не выкинули эти блокирующие вызовы вообще из АПИ. Эти вызовы только все усложняют, как реализацию самих кутешных модулей, так и всего прочего.
Проще все делается на сигналах/слотах, я не понимаю, в чем проблема вообще? 1. Методом QSP:::write() пиши данные (шли команды/запросы) 2. В обработчике Foo::onReadyRead() парси пришедшие данные/ответы. Код
это в общих чертах. Что тут сложного то? Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: sergek от Сентябрь 25, 2021, 16:32 Если делать с помощью сигналов-слотов, то надо же как-то синхронизировать отправку сигнала с получением данных, да еще чтоб друг за другом все шло. Вот это непонятно,как сделать. Так это отдельный вопрос ;) Это можно реализовать с помощью очереди запросов. А там свои заморочки. Я попробую подготовить упрощенный пример, заодно дам возможность попинать меня гуру))Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: sergek от Сентябрь 25, 2021, 16:47 это в общих чертах. Что тут сложного то? Вы нарисовали асинхронную схему обработки ответа. В этой схеме для решения задачи TC нужно связать запрос и ответ, замкнуть транзакцию с помощью какого-нибудь идентификатора.В случае, когда шина используется монопольно, это сделать просто - добавить, например, член Foo::idRequest. Соответственно, идентификатор генерируется перед передачей запроса и в doSomething определяется, на какой запрос пришел ответ. Может, уже и не надо делать пример квазисинхронного взаимодействия? Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: qate от Сентябрь 25, 2021, 20:09 ТС - научайся мыслить при обмене асинхронно - как с сокетами, так и с serial портом
пришли данные - обработай их отправил данные - и не жди когда они реально уйдут - ты (код юзер-спейса) не умнее ядра и не будешь никогда (а еще буфер отправки микросхемы есть) если ты пишешь waitFor* - это костыль, далее это обсуждать неинтересно Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: Igors от Сентябрь 26, 2021, 07:13 Вы нарисовали асинхронную схему обработки ответа. В этой схеме для решения задачи TC нужно связать запрос и ответ, замкнуть транзакцию с помощью какого-нибудь идентификатора. Да, та же песня возникает и в др задачах, напр при обмене командами плагин-хост. Однако это уже вопрос управления/диспетчеризации пакетов, а ТС спрашивал всего лишь как получить пакет :) В случае, когда шина используется монопольно, это сделать просто - добавить, например, член Foo::idRequest. Соответственно, идентификатор генерируется перед передачей запроса и в doSomething определяется, на какой запрос пришел ответ. Конечно асинхронка более привлекательна, но и синхронный прием (в своей нитке) - абсолютно законный и корректный способ работы. Да и вообще как-то несолидно, "тут не выходит - ну давайте попробуем так" :) Название: Re: Проблема с получением данных с помощью QSerialPort::waitForReadyRead() Отправлено: sergek от Сентябрь 26, 2021, 10:26 Хотел сделать упрощенный пример, но увлекся :) Получился вполне себе рабочий вариант с очередью и двумя типами запросов (асинхронным и синхронным). Цитировать его не буду, кому интересно - посмотрит исходники.
При использовании асинхронного запроса (sendAsyncRequest) обработчик данных основной программы нужно связать с сигналом responseReceived. Для идентификации ответа используется только идентификатор запроса. Возможно, этого будет мало и понадобится тип запроса, чтобы знать, что за данные поступили. Тогда нужно будет его добавить в параметры sendAsyncRequest и членом в класс CRequest, а также в сигнал responseReceived. В синхронном запросе я сделал одинаковыми таймауты ожидания ответа устройства на шине и в функции синхронного запроса Код На самом деле последний таймаут должен быть чуть больше, но в предлагаемом примере ответ при ошибке и таймауте одинаков (пустые данные). Правда, я тут не сделал проверку requestId запроса и ответа, но это просто. |