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

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

Страниц: 1 [2] 3 4 ... 7   Вниз
  Печать  
Автор Тема: Работа из QSerialPort из разных потоков  (Прочитано 42777 раз)
Alechin
Гость
« Ответ #15 : Март 23, 2017, 18:56 »

и как он поможет мне заблокировать основной поток на ожидании ответ?
Его не надо блокировать!
а если мне надо его заблокировать? потому как не получив ответа я не могу знать что делать дальше.
набросайте мне для понимания скелет программы, цель такая: считать 4-мя разными командами состояния устройства. Ответ - просто один байт т.е. по ответу узнать на какой запрос он пришел нельзя). при этом ответ может не придти вообще (т.е. еще нужен тайм-аут).
при этом следующий запрос зависит от ответа на предыдущий. Я например не могу прорисовать в асинхронной модели что-то простое.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #16 : Март 23, 2017, 21:23 »

Хорошо, если мне надо пять раз плюнуть в порт получив ответ и по каждому ответу решить что и куда плевать дальше?

Ну так плюй первый раз и жди ответа асинхронно
Пришло событие с данными ответа, распарсил и плюнул второй раз и т.д.
Второй поток тут или не нужен или также принимает ответы и плюёт что ему вздумается
Зачем мыслить синхронно категориями dos ?

Тотже самый принцип и в сокетах - плюнул, получил ответ - все асинхронно.

Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #17 : Март 23, 2017, 21:31 »

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

1. сделать очередь из 4х команд (QVector<MyCmd>)
2. при отправке стартует таймер, может жить и в MyCmd
3. если пришел таймер ранее ответа - значит таймаут и знаешь на какую команду
4. если ответ пришел раньше таймера - стоп таймера, обработка
5. переход к следущей команде
Записан
Alechin
Гость
« Ответ #18 : Март 23, 2017, 21:37 »

Хорошо, если мне надо пять раз плюнуть в порт получив ответ и по каждому ответу решить что и куда плевать дальше?
Ну так плюй первый раз и жди ответа асинхронно
Пришло событие с данными ответа, распарсил и плюнул второй раз и т.д.
Второй поток тут или не нужен или также принимает ответы и плюёт что ему вздумается
второй поток нужен когда нужна фоновая печать - нужно вывести на принтер большой объем данных параллельно основной задаче.
а из основного потока нужно, например, проверить состояние принтера и сделать ему recovery, или сбросить ошибку, или сменить режим.
для этого нужна РАЗНАЯ последовательность команд, зависящая от предыдущих ответов и состояния основной программы.
т.е. при асинхронном подходе напрашивается довольно сложный "непрозрачный" автомат с кучей возможных состояний, вместо примитивной и наглядной цепочки if(Get_State()){ } else if(Get_Offline_Status) {} ......
дело в том что опрос состояния принтера занимает треть микросекунды. при асинхронном подходе сейчас на это уходит от 20 до 50 мсек только за счет нескольких сигнал-слот которые получаются Queued.
У меня не вопрос "как сделать", у меня вопрос "может я что-то не так делаю, что реализация примитивной задачи оказалась сложна и запутанна".
Записан
Alechin
Гость
« Ответ #19 : Март 23, 2017, 21:39 »

набросайте мне для понимания скелет программы, цель такая: считать 4-мя разными командами состояния устройства. Ответ - просто один байт т.е. по ответу узнать на какой запрос он пришел нельзя). при этом ответ может не придти вообще (т.е. еще нужен тайм-аут).
1. сделать очередь из 4х команд (QVector<MyCmd>)
2. при отправке стартует таймер, может жить и в MyCmd
3. если пришел таймер ранее ответа - значит таймаут и знаешь на какую команду
4. если ответ пришел раньше таймера - стоп таймера, обработка
5. переход к следущей команде
не надо забывать, что основная программа дойдя до этого места должна остановиться и ждать результата цепочки обращений. Для чего нужна такая асинхронность (см. мой ответ выше).
Записан
Alechin
Гость
« Ответ #20 : Март 23, 2017, 21:45 »

Цитировать
1. сделать очередь из 4х команд (QVector<MyCmd>)
сделать очередь нельзя так как последовательность команд не может быть определена заранее.
я правильно понимаю, что вызывать методы QSerialPort созданного в одном потоке из другого потока нельзя? У меня все проблемы (усложнения) именно из-за этого. Именно из-за этого вместо записи сообщения как port->write мне приходится самому себе посылать Queued сигнал (т.е. формировать сигнал в одном потоке, что бы он отработал в другом), теряя на этом минимум 20 мсек (переключение потоков).
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #21 : Март 23, 2017, 21:47 »

это что rs232 принтер ? и без драйвера управляется из userspace ?

без понятия что это за железяка и как она управляется - сказать что то полезное нельзя
20 мсек потоки не переключаются, это баг в программе
« Последнее редактирование: Март 23, 2017, 21:50 от qate » Записан
Alechin
Гость
« Ответ #22 : Март 23, 2017, 21:57 »

это что rs232 принтер ? и без драйвера управляется из userspace ?
без понятия что это за железяка и как она управляется - сказать что то полезное нельзя
обычный POS-принтер висящий на UARTе (у меня не комп, у меня embedded железяка на iMX6 с Линуксом).
дело не в управлении. Долгие команды (печать графики) надо осуществлять в отдельном потоке, быстрые (управление) - в основном потоке.
при этом набор команд един, команды должно быть можно выполнять как из фонового потока (если есть некая последовательность команд - задание на печать), так и из основного, когда результат требуется немедленно. Вот получается либо так, ли так, либо офигенный огород на пустом месте....
20 мсек потоки не переключаются, это баг в программе
qDebud()<<Time()  показывает такие времена.
Записан
sergek
Гипер активный житель
*****
Offline Offline

Сообщений: 870


Мы должны приносить пользу людям.


Просмотр профиля
« Ответ #23 : Март 23, 2017, 21:58 »

а если мне надо его заблокировать? потому как не получив ответа я не могу знать что делать дальше.
набросайте мне для понимания скелет программы, цель такая: считать 4-мя разными командами состояния устройства. Ответ - просто один байт т.е. по ответу узнать на какой запрос он пришел нельзя). при этом ответ может не придти вообще (т.е. еще нужен тайм-аут).
при этом следующий запрос зависит от ответа на предыдущий. Я например не могу прорисовать в асинхронной модели что-то простое.
У меня была подобная задача. Для себя сделал такой вот адаптер, отдаю на растерзание. Идею, думаю, понять можно.
Там и ожидание ответа, и таймаут, и асинхронная передача и прием данных между потоками.
Класс контроллера - это устройство с последовательным интерфейсом (modbus).
Пример использования ниже:
Код:
{
...
        // 3. передача запроса в контроллер и получение ответа
        CThreadAdapter adapter(controller, timeout);
        unitVars = adapter.sendRequest(requestId,params);
...
}
В адаптере упоминается сигнал CControllerInterface::responseSig - его можно считать эквивалентом readyRead.
Записан

Qt 5.13.0 Qt Creator 5.0.1
Win10, Ubuntu 20.04
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #24 : Март 23, 2017, 22:14 »

у меня не комп, у меня embedded железяка на iMX6 с Линуксом

сразу бы сказал что не комп, отсюда и тормоза
возможно придется делать чтото свое, прямую запись в порт, если переключение потоков там _так_ медленно

хотя мне всеже непонятно как из 2х потоков можно одновременно и печатать и получать статус по одному и томуже ком порту

Записан
Alechin
Гость
« Ответ #25 : Март 23, 2017, 22:30 »

хотя мне все же непонятно как из 2х потоков можно одновременно и печатать и получать статус по одному и тому же ком порту
про _одновременно_ никто не говорил, зачем одновременно. Но, например, перед запуском фоновой печати в отдельном потоке хорошо бы получить все возможные (а их 4) слова состояния принтера и разобрав их выполнить соотв.действия с принтером. Это надо делать в основном потоке, может фоновую печать и запускать не придется. Просто выходит что методы класса работающие в одном потоке недоступны _напрямую_ из другого потока. Код НЕ получается очень простым и наглядным. Когда есть куча сигналов-слотов - приходится все время скакать по коду чтобы понять что за чем выполнилось и почему.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #26 : Март 23, 2017, 23:55 »

Код НЕ получается очень простым и наглядным. Когда есть куча сигналов-слотов - приходится все время скакать по коду чтобы понять что за чем выполнилось и почему.

код именно получается простым и наглядным с сигналами и слотами
все методы и данные доступны из всех потоков ибо одно адресное пространство, но нужна синхронизация обычно
зачем qserialport сделан именно так, надо смотреть его код или модифицировать его т.к. задача и условия её работы специфичны
и этот принтер не имеет драйвера чтобы его видел cups ? он самоделка ?

Записан
Alechin
Гость
« Ответ #27 : Март 24, 2017, 07:53 »

код именно получается простым и наглядным с сигналами и слотами
при линейном коде все наоборот: вместо нескольких последовательно размещенных взаимосвязанных строк имеем несколько расположенных в разных местах функций, да еще приходится вводить дополнительные переменные, например сохранять код запроса что бы знать на какой запрос пришел ответ. Для тайм-аута выделять таймер, хотя  функции wait  так обеспечивают тайм-аут. где простота и наглядность?
все методы и данные доступны из всех потоков ибо одно адресное пространство, но нужна синхронизация обычно
методы моего класса доступны. но методы QSerialPort - нет. вернее они доступны но НЕ работают (ругаются на вызов из другого потока). Я могу понять почему не работают методы wait - они должны заблокировать поток, соотв. они блокируют свой родительский поток. Но обычная запись или чтение - с какого перепугу они связаны с потоком? Что, я не могу записать в файл из другого потока? ведь SerailPort это обычный файл!
зачем qserialport сделан именно так, надо смотреть его код или модифицировать его т.к. задача и условия её работы
специфичны
да, я так подозреваю что это QSeraiPort реализован кривовато. От него требуется всего-лишь write и read независимый от потока и все. Все _мои_ проблемы уйдут.
и этот принтер не имеет драйвера чтобы его видел cups ? он самоделка ?
обычный POS-принтер типа LTPV445 с платкой управления, реализующей поднабор команд ESC/Kanji.
еще раз повторю - у меня embedded платка на iMX6. в ней уже залито то что залито.
да и дело не в драйвере. у меня подобных задач впереди еще куча: прежде чем запустить в фоне длительный цикл обмена с внешним устройством сначала протестировать его из другого (не обязательно основного) потока. Все это годами отработано под WinEmbedded, крутится в режиме 365/7/24 годами, не вызывает никаких вопросов.
а тут на таком "пустом месте" затык вышел.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #28 : Март 24, 2017, 08:28 »

Alechin, я не уверен, что мой пример тебе поможет, так как ты закостенел в своей подходе и даже не пытаешься разобраться в других подходах. Ты пришел в новый фреймворк, так что не надо тут кричать как круто работать в борланде с конечными автоматами. У Qt есть свой подход и тебе придется его изучить, иначе тебе не будут помогать на данном форуме (я по крайней мере).

Код
C++ (Qt)
class DeviceCommunicator : public QObject
{
Q_OBJECT
 
public:
explicit DeviceCommunicator (QObject *parent)
: QObject (parent)
, serialPort_ {}
{
connect (serialPort_, &QSerialPort::readyRead, this, &DeviceCommunicator::readPort);
 
}
 
void sendCommand (int index, const QByteArray &command, int answerSize)
{
commandsQueue_.enqueue(std::make_tuple(index, command, answerSize));
if (commandsQueue_.size() == 1) {
serialPort_.write(std::get<1> (commandsQueue_.first()));
}
}
 
Q_SIGNALS:
void commandSent (int index, const QByteArray &answer);
 
private:
void readPort ()
{
answer_ += serialPort_.read(std::get<2> (commandsQueue_.first()) - answer_.size());
               if (answer_.size() == std::get<2> (commandsQueue_.first())) {
emit commandSent (std::get<0> (commandsQueue_), answer);
commandsQueue_.dequeue();
answer_.clear();
if (!commandsQueue_.isEmpty()) {
serialPort_.write(std::get<1> (commandsQueue_.first()));
}
               }
}
 
 
private:
QSerialPort serialPort_;
QQueue<std::tuple<int, QByteArray, int>> commandsQueue_;
       QByteArray answer_;
}
 

Вот класс коммуникации с портом. sendCommand используется для отправки команды, когда на команду придет ответ, вызовется сигнал commandSent. Попробуй разобраться в этом псевдокоде.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Alechin
Гость
« Ответ #29 : Март 24, 2017, 08:36 »

а SendCommand у вас откуда будет вызываться? если из другого потока - этот код не будет работать, так как serialPort_.write
не работает из другого потока.
Записан
Страниц: 1 [2] 3 4 ... 7   Вверх
  Печать  
 
Перейти в:  


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