Russian Qt Forum

Qt => Вопросы новичков => Тема начата: Alechin от Март 23, 2017, 13:06



Название: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 13:06
Задача такая. есть POS-принтер на COM порту. Необходимо иметь возможность передавать ему команды как из одного потока (основного) так и из дополнительного потока (фоновая печать).
QSerialPort создается и открывается в основном потоке. с вызовом функций write, waitForBytesWritten, wiatForReadyRead проблем нет - все работает (что логично).
Для фоновой печати записываю в очередь требуемые для передачи в принтер команды и вызываю метод фоновой печати через QtConcurrent::run. Соответственно тут все перестает работать - порт принадлежит другому потоку.
Как можно выкрутиться? или у меня что-то не так идеологически?
спасибо!


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 23, 2017, 13:13
Не так идеалогически. Используй асинхронную работу через сигналы/слоты. Ищи по форуму.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 13:44
я так понимаю неверна идеология реализации именно на Qt? потому как с точки зрения пользователя не вижу никакой ошибки в идеологии: готов ждать результата - выполняю в основном потоке. не хочу ждать результата - выполняю этот же самый код в другом потоке и потом смотрю результат. Код абсолютно линейный и "прозрачный".
через слоты и сигналы попытался сделать - получилось ну "очень много буков". Т.е. фоновую печать да - сделал. а как получить состояние в основном потоке когда мне нужно остановить выполнение до получения результата?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 23, 2017, 13:50
Не хочу ждать результата - это совершенно не означает, что нужен отдельный поток. Прочитай про асинхронное программирование.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 15:02
слово serial как бы намекает, что порт последовательный
из 2+ потоков можно, но по очереди )


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 15:37
не получается из двух потоков. если вызывать не из потока в котором порт был создан - не работают функции wait_  выскакивает сообщение о несовпадении потоков.
да - с асинхронным многопоточным программированием под Win на Builder знаком в течение 15 лет каждый рабочий день.
а вот перенести приемы потоков win на Qt не получилось :(
(по мне яснее и прозрачнее WaitForMultipleObjects ничего нет, на Qt какая-то полная каша выходит)


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 23, 2017, 15:45
Асинхронность и WaitFor* это взаимоисключающие вещи. Читай про сигнал readyRead.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 15:46
и как он поможет мне заблокировать основной поток на ожидании ответ?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 15:53
хочу сказать что вариант с портом в отдельном потоке и слот-сигнальной реализацией я реализовал в первую очередь. получился "страшный тормозной монстр".
например: передать два байта в порт в другом потоке напрямую я не могу, так как port->write "ругается". приходится формировать сигнал в поток порта, который QueuedConnection, и посему передача этих байт происходит не сразу, а где-то через 15-20 мсек. При этом мне надо иметь 2 события, на одном делаю wait в ожидании начала передачи, на другом я делаю wait для ожидания завершения передачи. И это только для того чтобы передать 2 байта и дождаться завершения их передачи. Чтобы подождать ответа снова заводим событие, на котором делаем wait, а событие взводится в вашем readyRead.
а всего-то надо передать 2 байта и получить в ответ 1.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 23, 2017, 15:55
и как он поможет мне заблокировать основной поток на ожидании ответ?
Его не надо блокировать!


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: kuzulis от Март 23, 2017, 16:07
Я даж комментировать не буду, и даже не зовите меня в тред. :)



Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 23, 2017, 16:15
Я даж комментировать не буду, и даже не зовите меня в тред. :)
Сам пришел, сам напросился. Не хочешь разве для человека поискать ответов по форуму?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 16:51
При этом мне надо иметь 2 события, на одном делаю wait в ожидании начала передачи, на другом я делаю wait для ожидания завершения передачи.

а зачем ?
плюнул в порт и ладно
пришли данные - событие


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: kuzulis от Март 23, 2017, 17:14
Цитировать
Не хочешь разве для человека поискать ответов по форуму?

Не, раз у человека 15-лет стажу в борланде - то он сам разберется (пущай сам ищет ответы.. оно уже обсуждалось много раз).


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 18:49
Хорошо, если мне надо пять раз плюнуть в порт получив ответ и по каждому ответу решить что и куда плевать дальше? Вполне себе линейная логика. как ее вписать в "асинхронную модель"? притянуть ее за уши? если то что плевать дальше зависит от ответа на предыдущий плевок?
зачем тут вообще асинхронная модель? почему я не могу вызвать из одного потока процедуру другого потока? что это вообще за дурь такая? почему запись в порт может осуществляется только из одного конкретного потока?
я просто не понимаю логики. не все равно из какого потока запись port->write()?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 18:56
и как он поможет мне заблокировать основной поток на ожидании ответ?
Его не надо блокировать!
а если мне надо его заблокировать? потому как не получив ответа я не могу знать что делать дальше.
набросайте мне для понимания скелет программы, цель такая: считать 4-мя разными командами состояния устройства. Ответ - просто один байт т.е. по ответу узнать на какой запрос он пришел нельзя). при этом ответ может не придти вообще (т.е. еще нужен тайм-аут).
при этом следующий запрос зависит от ответа на предыдущий. Я например не могу прорисовать в асинхронной модели что-то простое.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 21:23
Хорошо, если мне надо пять раз плюнуть в порт получив ответ и по каждому ответу решить что и куда плевать дальше?

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

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



Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 21:31
набросайте мне для понимания скелет программы, цель такая: считать 4-мя разными командами состояния устройства. Ответ - просто один байт т.е. по ответу узнать на какой запрос он пришел нельзя). при этом ответ может не придти вообще (т.е. еще нужен тайм-аут).

1. сделать очередь из 4х команд (QVector<MyCmd>)
2. при отправке стартует таймер, может жить и в MyCmd
3. если пришел таймер ранее ответа - значит таймаут и знаешь на какую команду
4. если ответ пришел раньше таймера - стоп таймера, обработка
5. переход к следущей команде


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


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


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 21:45
Цитировать
1. сделать очередь из 4х команд (QVector<MyCmd>)
сделать очередь нельзя так как последовательность команд не может быть определена заранее.
я правильно понимаю, что вызывать методы QSerialPort созданного в одном потоке из другого потока нельзя? У меня все проблемы (усложнения) именно из-за этого. Именно из-за этого вместо записи сообщения как port->write мне приходится самому себе посылать Queued сигнал (т.е. формировать сигнал в одном потоке, что бы он отработал в другом), теряя на этом минимум 20 мсек (переключение потоков).


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 21:47
это что rs232 принтер ? и без драйвера управляется из userspace ?

без понятия что это за железяка и как она управляется - сказать что то полезное нельзя
20 мсек потоки не переключаются, это баг в программе


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 21:57
это что rs232 принтер ? и без драйвера управляется из userspace ?
без понятия что это за железяка и как она управляется - сказать что то полезное нельзя
обычный POS-принтер висящий на UARTе (у меня не комп, у меня embedded железяка на iMX6 с Линуксом).
дело не в управлении. Долгие команды (печать графики) надо осуществлять в отдельном потоке, быстрые (управление) - в основном потоке.
при этом набор команд един, команды должно быть можно выполнять как из фонового потока (если есть некая последовательность команд - задание на печать), так и из основного, когда результат требуется немедленно. Вот получается либо так, ли так, либо офигенный огород на пустом месте....
20 мсек потоки не переключаются, это баг в программе
qDebud()<<Time()  показывает такие времена.


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


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 22:14
у меня не комп, у меня embedded железяка на iMX6 с Линуксом

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

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



Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 23, 2017, 22:30
хотя мне все же непонятно как из 2х потоков можно одновременно и печатать и получать статус по одному и тому же ком порту
про _одновременно_ никто не говорил, зачем одновременно. Но, например, перед запуском фоновой печати в отдельном потоке хорошо бы получить все возможные (а их 4) слова состояния принтера и разобрав их выполнить соотв.действия с принтером. Это надо делать в основном потоке, может фоновую печать и запускать не придется. Просто выходит что методы класса работающие в одном потоке недоступны _напрямую_ из другого потока. Код НЕ получается очень простым и наглядным. Когда есть куча сигналов-слотов - приходится все время скакать по коду чтобы понять что за чем выполнилось и почему.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 23, 2017, 23:55
Код НЕ получается очень простым и наглядным. Когда есть куча сигналов-слотов - приходится все время скакать по коду чтобы понять что за чем выполнилось и почему.

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



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


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 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. Попробуй разобраться в этом псевдокоде.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 08:36
а SendCommand у вас откуда будет вызываться? если из другого потока - этот код не будет работать, так как serialPort_.write
не работает из другого потока.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 08:44
Повторяю в последний раз - БЕЗ ПОТОКОВ! ВООБЩЕ!


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 08:44
http://www.prog.org.ru/topic_30899_0.html


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 24, 2017, 08:57
да, я так подозреваю что это QSeraiPort реализован кривовато. От него требуется всего-лишь write и read независимый от потока и все. Все _мои_ проблемы уйдут.

если нужно низкоуровневое управление компортом, то man termios - там и ждать через select/poll можно и читать откуда угодно


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 08:59
Повторяю в последний раз - БЕЗ ПОТОКОВ! ВООБЩЕ!
может я закостенел, НО: если программа не должна продолжать выполнение пока я не произведу некоторые действия:
пример: пользователь нажал кнопку "печать". При этом я должен:
1. проверить связь с принтером послав команду условно чтения "статуса 1".
2. при неполучении ответа за отведенное время (очень короткое, 10 мсек максимум) - извещаем пользователя что это невозможно, дальше возвращаемся к основной работе.
3. при получении ответа: анализируем его, в зависимости от установленных флагов либо выполняем новые запросы, либо выдаем управляющие команды и затем отправляем новые запросы.
4. в результате вышеуказанных действий можем либо снова известить пользователя что печать невозможна, либо начинаем набивать в очередь задания для печати и затем запускаем фоновую печать.

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

Вся логика перемещается в слот завершения передачи (для команд, на которые не присылает ответов) и в слот приема ответа. При этом опять же Я ВИЖУ приличный автомат состояний, потому как реакция на получение ответа на запрос, отправленный в разных ситуациях разная.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:06
Код
C++ (Qt)
void MainWindow::onPrintButtonClick()
{
   setEnabled(false);
   QEventLoop loop;
   connect (communicator, &DeviceCommunicator::commandSent, [&loop] (int index, const QByteArray &answer) {loop.quit();});
   communicator.sendCommand(....);
   loop.exec();
   setEnabled(true);
}
 

Вот тебе код, который даст необходимое тебе поведение. Но это хреновый подход. Печать должна идти в фоне, а пользователь должен иметь доступ к остальной функциональности программы.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:15
конечно печать будет идти в фоне. Но прежде чем начать формирование кучи графики для печати и постановки их в очередь заданий печати хорошо бы убедиться что принтер подключен и отвечает, что он в on-line, что в нем есть бумага, что автобрезчик бумаги закрыт и прочее. При этом почти все ситуации можно исправить отправкой соотв.команд. Примерный цикл 2 байта запрос-один байт ответ занимает ~500 мксек. Соответственно весь процесс ТАКОЙ инициализации печати около 3-5 мсек. В чем такой подход хреновый? Максимальное время блокировки потока (GUI! а не работа с железом! работа с кучей разного железа крутится в паре десятков своих потоков) - это тайм-аут неответа 10 мсек.
(ну просто привычка - основной поток это GUI. он не должен и не делает ничего кроме отображения (ну и управления) кучей (десятки) потоков связанных с разным железом. Потоки железа сами по себе им для работы основной поток и GUI нафиг не нужен. Если им есть что сказать ГУЮ они посылали свои WM_USER.)
а тут получается все в одном ГУЕвом потоке должно крутиться......


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:23
Готовность принтера можно проверить в фоне при запуске программы и перепроверять периодически по таймеру.

У меня сейчас сервис на Qt работает по сокетам с автомобильными трекерами, одновременно бывает подключено около 10К трекеров. Вся работа проводится в констексте одного потока и никакой просадки по производительности нет. Не надо пихать потоки туда, где они совершенно не нужны. Забудь про свой борланд и синхронную работу с IO, тут другая кухня.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Igors от Март 24, 2017, 09:26
При этом такая последовательность действий может исходить как от пользователя (кнопка на экране), так и от другого потока
Хотя никогда не работал с QSerialPort, но думаю добиться этого несложно. Заводите поток в котором сидит тот же DeviceCommunicator и общаетесь с ним сигналами (QueuedConnection)). Для записи делаете sendCommand слотом (вызываете сигналом, не напрямую). Для получения - ну наверное придется хранить id нитки и посылать его в ответе (чтобы получающий смог различить "ответ для меня").

Но совершенно неясно выдержит ли Ваша архитектура это испытание - ведь тогда разные нитки смогут писать чего хотят, запросы в очереди будут перемешаны произвольным образом, и как с этим разобраться - хз

Код
C++ (Qt)
QQueue<std::tuple<int, QByteArray, int>> commandsQueue_;
 
Видимо, последний писк моды... :) Типа "структуры устарели". Все-таки интересно, чего добились? Вместо имен теперь "по номерам" - разве это удобно?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:30
Igors, это псевдокод, мне лень было писать структуру. Если хочешь поговорить об этом, создавай отдельную ветку.

Alechin, представь, что ты строитель. Ты 15 лет скреплял дерево гвоздями и тут тебе дают шурупы и шуруповерт, и говорят - так удобнее. Ты берешь шуруповерт и начинаешь им забивать шурупы в дерево, недоумевая какого хрена они так плохо входят, ведь раньше все с гвоздями отлично работало.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:30
Готовность принтера можно проверить в фоне при запуске программы и перепроверять периодически по таймеру.

У меня сейчас сервис на Qt работает по сокетам с автомобильными трекерами, одновременно бывает подключено около 10К трекеров. Вся работа проводится в констексте одного потока и никакой просадки по производительности нет. Не надо пихать потоки туда, где они совершенно не нужны. Забудь про свой борланд и синхронную работу с IO, тут другая кухня.
Сокеты и оборудование это несколько разные вещи. Питанием принтера я управляю из программы. Когда что-то надо - я его включаю, надо убедиться что он на связи, потом дождаться его готовности, он может отвалиться в процессе, потом я его отключаю чтобы он аккумулятор не жрал.......
А вопрос в продолжение - а как вы контролируете пропадание самого LAN? ну типа кабель выдернули и снова воткнули? я помню под виндой у меня с этим была проблема.
И если есть USB-RS232 преобразователь его тоже могут вытащить и снова вставить. При этом программа должна автоматически перезапустить работу с ним (пользователя там нет - это автономное оборудование).


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:34
У QTcpSocket есть сигнал о закрытии соединения, на него подписываюсь и делаю реконнект. Посмотри сигналы у QSerialPort, там есть сигналы об ошибках.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:37
 с ним сигналами (QueuedConnection)).
передача в порт 2-ух байтов "напрямую" с ожиданием их выдачи и получением ответа занимает 300 мксек. У меня только прохождение Ququed сигнала начала передачи заняло 20 мсек! т.е. в 66 раз больше!
Для записи делаете sendCommand слотом (вызываете сигналом, не напрямую). Для получения - ну наверное придется хранить id нитки и посылать его в ответе (чтобы получающий смог различить "ответ для меня").
да, получается такое вот упрощение и повышения наглядности ПО.
Но совершенно неясно выдержит ли Ваша архитектура это испытание - ведь тогда разные нитки смогут писать чего хотят, запросы в очереди будут перемешаны произвольным образом, и как с этим разобраться - хз
почему вы все считаете что кто-то одновременно полезет? есть класс работы с принтеров. класс одни. все обращения к принтеру только через него. он сам разруливает кого пустить к принтеру а кого нет.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:40
> У меня только прохождение Ququed сигнала начала передачи заняло 20 мсек!

Что за прохождение? Чем замерял? Вызов слота при выдаче сигнала в однопоточном окружении не несет практически никаких накладных расходов (если используется Qt::AutoConnection)


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: sergek от Март 24, 2017, 09:41
У меня сейчас сервис на Qt работает по сокетам с автомобильными трекерами, одновременно бывает подключено около 10К трекеров. Вся работа проводится в констексте одного потока и никакой просадки по производительности нет.
Этот сервис использует DeviceCommunicator?
Идея этого класса хорошая, спасибо за науку  :)


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:42
У QTcpSocket есть сигнал о закрытии соединения, на него подписываюсь и делаю реконнект. Посмотри сигналы у QSerialPort, там есть сигналы об ошибках.
в Винде "закрытие соединения" (тем более которого нет в UDP) и "пропадание LAN" не одно и тоже. Для UDP с выдернутым кабелем сокет продолжал "успешно" пихать данные и говорить что все Ok. Пришлось заводить отдельный поток (не ругайтесь!) который раз в секунду пытался создать новое соединение и при создании при обрыве кабеля уже возвращалась ошибка.
Для USB-COM еще хуже. если выдернуть преобразователь - ошибка не возвращается. Более того - хендл порта продолжает существовать и по нему успешно "пишутся" данные. И если при восстановлении LAN хотябы хендлы сохраняются и работа просто возобновляется дальше, то для USB-COM при втыкании USB порту назначался тоже номер, но новый хэндл!


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:47
> У меня только прохождение Ququed сигнала начала передачи заняло 20 мсек!
Что за прохождение? Чем замерял? Вызов слота при выдаче сигнала в однопоточном окружении не несет практически никаких накладных расходов (если используется Qt::AutoConnection)
использовать Qt::AutoConnection не получается - потоки разные. Только Queued.
а замерял просто qDebug()<<Time().currentTime()


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:49
У меня сейчас сервис на Qt работает по сокетам с автомобильными трекерами, одновременно бывает подключено около 10К трекеров. Вся работа проводится в констексте одного потока и никакой просадки по производительности нет.
Этот сервис использует DeviceCommunicator?
Идея этого класса хорошая, спасибо за науку  :)
Нет, там более сложная архитектура. А это просто наколенный пример. :)


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Igors от Март 24, 2017, 09:50
передача в порт 2-ух байтов "напрямую" с ожиданием их выдачи и получением ответа занимает 300 мксек. У меня
только прохождение Ququed сигнала начала передачи заняло 20 мсек! т.е. в 66 раз больше!
20 миллисекунд? Т.е. я смогу в секунду послать не более 50 сигналов queued? Такой рез-т - явный абсурд. Когда-то сам мерял - все там норм, не выдумывайте  

да, получается такое вот упрощение и повышения наглядности ПО.
Ну "преждевременной оптимизации" никто не отменял. Если хотите печатать из разных потоков - придется пойти на какие-то накладные расходы, и использование слот/сигналов - вариант очень приличный

почему вы все считаете что кто-то одновременно полезет? есть класс работы с принтеров. класс одни. все обращения к принтеру только через него. он сам разруливает кого пустить к принтеру а кого нет.
Ну и сосредоточьте всю работу с принтером в одном потоке, типа "всегда фоновый". Главный или какой-то другой поток захотел печатать - общается с "печатающим" через QueuedConnection


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:50
> У меня только прохождение Ququed сигнала начала передачи заняло 20 мсек!
Что за прохождение? Чем замерял? Вызов слота при выдаче сигнала в однопоточном окружении не несет практически никаких накладных расходов (если используется Qt::AutoConnection)
использовать Qt::AutoConnection не получается - потоки разные. Только Queued.
а замерял просто qDebug()<<Time().currentTime()
Вот как раз из-за того, что потоки разные. Если делать один поток, то никакой просадки нет - делается прямой вызов.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:51
> У меня только прохождение Ququed сигнала начала передачи заняло 20 мсек!
Что за прохождение? Чем замерял? Вызов слота при выдаче сигнала в однопоточном окружении не несет практически никаких накладных расходов (если используется Qt::AutoConnection)
использовать Qt::AutoConnection не получается - потоки разные. Только Queued.
а замерял просто qDebug()<<Time().currentTime()
Вот как раз из-за того, что потоки разные. Если делать один поток, то никакой просадки нет - делается прямой вызов.
уточню: делать"не один поток" а делать все основном GUI потоке?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Igors от Март 24, 2017, 09:53
Вот как раз из-за того, что потоки разные. Если делать один поток, то никакой просадки нет - делается прямой вызов.
Та хоть и разные - до 20 миллисекунд там "дистанция огромного размера"


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:55
Вот как раз из-за того, что потоки разные. Если делать один поток, то никакой просадки нет - делается прямой вызов.
Та хоть и разные - до 20 миллисекунд там "дистанция огромного размера"
Если были какие-то действия и ивентлуп не прокручивался, то хоть час может пройти.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:56
> уточню: делать"не один поток" а делать все основном GUI потоке?

Делать все в контексте основного потока до тех пор, пока не столкнешься с проблемой в производительности и не докажешь, что эта проблема именно из-за однопоточности.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 09:56
20 миллисекунд? Т.е. я смогу в секунду послать не более 50 сигналов queued? Такой рез-т - явный абсурд. Когда-то сам мерял - все там норм, не выдумывайте  
да, qDebug()<<QTime показыват именно такие цифры. Я так понимаю это очень похоже квант времени потока.
Ну и сосредоточьте всю работу с принтером в одном потоке, типа "всегда фоновый". Главный или какой-то другой поток захотел печатать - общается с "печатающим" через QueuedConnection
да уже так и сделано. Все работает. Удивила монструозность и тормознутость решения. Плюс непрозрачность (при взгляде со стороны), например дерганье своего собственного сигнала что бы вызвать свой собственный слот только потому что обычный port->write (по сути обычная запись файла!) не работает из чужого потока.
потому и возник вопрос "все ли я делаю так".


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 09:58
20 миллисекунд? Т.е. я смогу в секунду послать не более 50 сигналов queued? Такой рез-т - явный абсурд. Когда-то сам мерял - все там норм, не выдумывайте   
да, qDebug()<<QTime показыват именно такие цифры. Я так понимаю это очень похоже квант времени потока.
Ну и сосредоточьте всю работу с принтером в одном потоке, типа "всегда фоновый". Главный или какой-то другой поток захотел печатать - общается с "печатающим" через QueuedConnection
да уже так и сделано. Все работает. Удивила монструозность и тормознутость решения. Плюс непрозрачность (при взгляде со стороны), например дерганье своего собственного сигнала что бы вызвать свой собственный слот только потому что обычный port->write (по сути обычная запись файла!) не работает из чужого потока.
потому и возник вопрос "все ли я делаю так".
Нет, ты делаешь не так.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Igors от Март 24, 2017, 10:02
да уже так и сделано. Все работает. Удивила монструозность и тормознутость решения. Плюс непрозрачность (при взгляде со стороны), например дерганье своего собственного сигнала что бы вызвать свой собственный слот только потому что обычный port->write (по сути обычная запись файла!) не работает из чужого потока.
потому и возник вопрос "все ли я делаю так".
Ну похоже что не так  :) Иначе не пришлось бы дергать свой же слот через queued


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:02
я уже понял что нет так :) в смысле выноса в отдельный поток.
в таком случает (насчет предыдущего ответа с непрокручивание event_loop) - возникает боязнь что работоспособность системы будет зависеть от event-Loopа основного потока, что не есть хорошо. Работа в отдельном потоке не зависит от event_loopa других модулей программы. Виндовому WaitForMultipleEvent было все равно, даже если остальные потоки повисли - текущий всегда отрабатывал "мгновенно" свой функционал.
более того - для каждого потока (считай устройства, потому как работы с каждым устройством обычно шла в своем потоке) всегда отображалась статистика занятой памяти и времени работы потока, что позволяло легко отследить "взбесившиеся" устройства. Плюс для каждого потока отслеживалось что поток крутится, события приходят. если этого не происходило - поток автоматически перезапускался, в системный журнал делалась запись об этом..
В общем в выделении отдельного потока на каждое устройство были свои плюсы....
в принципе я все понял.......


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:03
Ну похоже что не так  :) Иначе не пришлось бы дергать свой же слот через queued
в подходе с отдельным поток все так.
запись в порт из соседнего потока НЕ РАБОТАЕТ для QSerialPort.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Igors от Март 24, 2017, 10:09
в подходе с отдельным поток все так.
запись в порт из соседнего потока НЕ РАБОТАЕТ для QSerialPort.
"И это правильно". Запускаете "принтерный" поток (он и только он юзает QSerialPort), и пуляете в него сигналами - это все. Не нужно никаких доп EveтtLoop и.т.п.  Если у потока нет работы - он сам повиснет в своем событийном цикле. Ну и там флажков парочку накиньте - и все дела


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:11
я уже понял что нет так :) в смысле выноса в отдельный поток.
в таком случает (насчет предыдущего ответа с непрокручивание event_loop) - возникает боязнь что работоспособность системы будет зависеть от event-Loopа основного потока, что не есть хорошо. Работа в отдельном потоке не зависит от event_loopa других модулей программы. Виндовому WaitForMultipleEvent было все равно, даже если остальные потолки повисли - текущий всегда отрабатывал "мгновенно" свой функционал.
в принципе я все понял.......
В большинстве случаев, все будет хорошо. Мне в сервисе пришлось выносить в отдельный поток сохранение данных на диск, когда я столкнулся с просадкой по производительности, ибо дисковый IO тормозил основной поток. Я создал класс, у которого есть метод на прием данных файла и поместил его в отдельный поток. Получилось так: класс принимает данне для файла и помещает их в очередь (собственно, вызов метода не имеет просадки), а дальше по ивентлупу потока происходит реальная запись данных в файл. В итоге, основной поток разгрузился и все опять заработало как часы.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:12
в подходе с отдельным поток все так.
запись в порт из соседнего потока НЕ РАБОТАЕТ для QSerialPort.
"И это правильно". Запускаете "принтерный" поток (он и только он юзает QSerialPort), и пуляете в него сигналами - это все. Не нужно никаких доп EveтtLoop и.т.п.  Если у потока нет работы - он сам повиснет в своем событийном цикле. Ну и там флажков парочку накиньте - и все дела
Зачем отдельный поток? QSerialPort, как я понял, вполне себе заточен для работы в основном потоке, если использовать асинхронное общение.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: kuzulis от Март 24, 2017, 10:13
Цитировать
запись в порт из соседнего потока НЕ РАБОТАЕТ для QSerialPort.

Удивительно, правда? А с чего бы оно должно работать?
А документацию мы читаем, или пытаемся с наскоку
наделать потоков (понатыкать везде WaitFor, ну да, не резиновая),
а потом плачемся что оно тормозит и крешится?
Хотим вот так шлеп-шлеп и в продакшен?

PS: Интересно, удивлю, если скажу, что в Qt все классы QIODevice
не позволяют так делать - дергать методы из разныз потоков
(да и вообще все классы QObject по большому счету)!


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:14
"И это правильно". Запускаете "принтерный" поток (он и только он юзает QSerialPort), и пуляете в него сигналами - это все. Не нужно никаких доп EveтtLoop и.т.п.  Если у потока нет работы - он сам повиснет в своем событийном цикле.
а никакх event_loop и нет.
вот как выглядит метод передачи:
void POS_Printer::Reset(quint _type)
{
   char req[3] = { 0x1b, 0x4a };  // ESC J
   req[2] = _type;
   port->write(req, 3);    <----------  не работает при вызове Reset() из другого потока.
}
приходится заводить сигнал signal_Reset();
подключать его через Queued к слоту Reset();
и вместо Reset() делать emit signal_Reset()
т.е. дергать самого себя за яйца.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:17
В большинстве случаев, все будет хорошо. Мне в сервисе пришлось выносить в отдельный поток сохранение данных на диск, когда я столкнулся с просадкой по производительности, ибо дисковый IO тормозил основной поток. Я создал класс, у которого есть метод на прием данных файла и поместил его в отдельный поток. Получилось так: класс принимает данне для файла и помещает их в очередь (собственно, вызов метода не имеет просадки), а дальше по ивентлупу потока происходит реальная запись данных в файл. В итоге, основной поток разгрузился и все опять заработало как часы.
да - у меня у каждого устройства был свой журнал в памяти. Приходилось каждый журнал "закручивать" в своем потоке, что он не тормозил. А в каждом журнале был еще поток анализа файловой системы, так как журналы надо было чистить по времени устаревания. А сканирование файловой журнальной системы занимало десятки минут! (у нас в сутки система накапливает около 500 файлов объемом под 10ГБайт).


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:24
Удивительно, правда? А с чего бы оно должно работать?
Удивительно! а с чего оно не должно работать? Сделать системный вызов записи в файл из другого потока невозможно? с каких это пор? Система не позволяет? или все-таки Qt не позволяет?
А документацию мы читаем, или пытаемся с наскоку
наделать потоков (понатыкать везде WaitFor, ну да, не резиновая),
а потом плачемся что оно тормозит и крешится?
а с чего поток должен тормозить на WaitFor? поток на WaitFor должен уйти в спячку и никаким образом не нагружать систему. Или в Qt WaitFor сделан через цикл опроса?!
PS: Интересно, удивлю, если скажу, что в Qt все классы QIODevice
не позволяют так делать - дергать методы из разныз потоков
(да и вообще все классы QObject по большому счету)!
это действительно интересно. Чем объясняется? дуростью разработчиков? система позволяет, а среда разработки не дает?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:25
Жесть. У меня сервис, который принимает данные от 20К трекеров (каждый шлет по несколько сообщений в секунду, сообщение - это n байт бинарных или текстовых данных), парсит данне, формирует xml и отсылает их в другую программу. Так же, отправляет команды на трекеры, логгирует работу, сохраняет xml и пр. И все, за исключением работы с диском, происходит в одном потоке. ЧЯДНТ?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:27
> это действительно интересно. Чем объясняется? дуростью разработчиков? система позволяет, а среда разработки не дает?

Системе насрать на потоки, работа с ними ложится на плечи разработчика. И чтобы не получить полную хрень в данных, когда несколько потоков пишут в один файл, разработчику приходится разруливать, использовать мютексы или что-то еще. Так вот, у Кьюта своя логика работы с потоками (которая все разруливает за тебя). Она очень хорошо продумана и работает безотказно уже долгие годы. Если ты не понимаешь ее, это не значит, что проблема в ней.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:30
Жесть. У меня сервис, который принимает данные от 20К трекеров (каждый шлет по несколько сообщений в секунду, сообщение - это n байт бинарных или текстовых данных), парсит данне, формирует xml и отсылает их в другую программу. Так же, отправляет команды на трекеры, логгирует работу, сохраняет xml и пр. И все, за исключением работы с диском, происходит в одном потоке. ЧЯДНТ?
еще раз повторю - у вас нет двух десятков железяк (8 LAN, 10 RS485, 128IO) которые могут отвалится, зависнуть, начать "штормить". При этом поток данных только от одного устройства около 100КБ в секунду. Данные надо распарсить, проанализировать, сбросить в журнал. Проверка оборудования проста. Подходим, выдергиваем что-нибудь или меняем местами кабели. Система должна в течение секунды восстановить работу и записать в системный журнал что с ней произошло. пользователя нет, более того - монитора обычно тоже нет. Пожаловаться некому, а продолжить работу надо. При этог не повлиять на десяток соседних устройств. При этом машинка - слабенький пром.комп с пентиумом.
Что приозойдет с одним потоком когда одно из устройств "заштормит"? или вы начнете сканировать файловую систему из 50 тысяч файлов? так вот скажу вам что сканирование такой файловой системы занимает около 3-ех часов.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:35
Ну, отвалилась - поймали сигнал и переподключились. В чем проблема? Сокеты у меня тоже рвутся.
А если мне надо будет просканировать файловую, то сканирование я вынесу в отдельный поток. А еще лучше, воспользуюсь пулом потоков.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:36
Системе насрать на потоки, работа с ними ложится на плечи разработчика. И чтобы не получить полную хрень в данных, когда несколько потоков пишут в один файл, разработчику приходится разруливать, использовать мютексы или что-то еще. Так вот, у Кьюта своя логика работы с потоками (которая все разруливает за тебя). Она очень хорошо продумана и работает безотказно уже долгие годы. Если ты не понимаешь ее, это не значит, что проблема в ней.
я все понимаю. и атомарные очереди с критическими секциями и событиями записи для других потоков, и прочее. все это само собой на разработчике. но и разработчики не "студенты-писатели чатов".
зачем это перекладывать на Qt - это должно быть в руках разработчика, что бы он четко понимал что произойдет, если и как с этим бороться.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:38
Ну, отвалилась - поймали сигнал и переподключились. В чем проблема? Сокеты у меня тоже рвутся.
А если мне надо будет просканировать файловую, то сканирование я вынесу в отдельный поток. А еще лучше, воспользуюсь пулом потоков.
я выше указал две ситуации когда вы ничего поймаете у отвалившегося устройства.
а когда устройство "заштормит"? ваш один эвент луп справится?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:38
Системе насрать на потоки, работа с ними ложится на плечи разработчика. И чтобы не получить полную хрень в данных, когда несколько потоков пишут в один файл, разработчику приходится разруливать, использовать мютексы или что-то еще. Так вот, у Кьюта своя логика работы с потоками (которая все разруливает за тебя). Она очень хорошо продумана и работает безотказно уже долгие годы. Если ты не понимаешь ее, это не значит, что проблема в ней.
я все понимаю. и атомарные очереди с критическими секциями и событиями записи для других потоков, и прочее. все это само собой на разработчике. но и разработчики не "студенты-писатели чатов".
зачем это перекладывать на Qt - это должно быть в руках разработчика, что бы он четко понимал что произойдет, если и как с этим бороться.
А зачем? Ты велосипедишь в кажом проекте? Возможно, у тебя ест на это время, у меня его нет, мне надо работу делать. Поэтому я с радостью возложил данные проблемы на Qt и использую предоставленную мне архитектуру. Code less, create more!


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:39
Ну, отвалилась - поймали сигнал и переподключились. В чем проблема? Сокеты у меня тоже рвутся.
А если мне надо будет просканировать файловую, то сканирование я вынесу в отдельный поток. А еще лучше, воспользуюсь пулом потоков.
я выше указал две ситуации когда вы ничего поймаете у отвалившегося устройства.
а когда устройство "заштормит"? ваш один эвент луп справится?

По поводу вытыкания патчкорда. Ну, заведи ты еще один экземпляр QTcpSocker и по таймеру подключай его/отключай и проверяй подключился ли он. Зачем для этого отдельный поток?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:40
я не "вилосипедю". мои системы управляют в реальном времени движением поездов (велком ту ПостЭЦ пассажирской станции москва-ленинградская и можно посмтреть в реальном времени как управляется осаживание поездов в тупики.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:42
По поводу вытыкания патчкорда. Ну, заведи ты еще один экземпляр QTcpSocker и по таймеру подключай его/отключай и проверяй подключился ли он. Зачем для этого отдельный поток?
а таймер в Qt какой? WM_TIMER или WaitableTimer?
вы уверены что на QTimer "можно положится"?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:45
По поводу вытыкания патчкорда. Ну, заведи ты еще один экземпляр QTcpSocker и по таймеру подключай его/отключай и проверяй подключился ли он. Зачем для этого отдельный поток?
а таймер в Qt какой? WM_TIMER или WaitableTimer?
вы уверены что на QTimer "можно положится"?
Что там "под капотом" я не знаю, но доверять ему можно.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 10:51
Что там "под катом" я не знаю, но доверять ему можно.
в таком случае вы не можете утверждать что на него можно положиться.
а все же можно как-нибудь узнать что там? Ведь если там WM_TIMER, то на него _нельзя_ положиться.

Ладно, наш спор зашел в тупик. Я в общем все понял.
Qt - это подход быстрого написания несложного ПО не очень квалифицированным программистом (типа ВижлБеэйсика :)
для написания серьезного ПО Qt, конечно, тоже подходит, но придется поковыряться более чем основательно.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: kuzulis от Март 24, 2017, 10:53
Цитата: Alechin
Удивительно! а с чего оно не должно работать? Сделать системный вызов записи в файл из другого потока невозможно? с каких это пор? Система не позволяет? или все-таки Qt не позволяет?

Дело в том, что QSP (как и большинство других I/O классов Qt) спроектирован для асинхронной работы с данными.

* QSP::write() просто добавит данные во внутренний буфер/очередь класса и взведет уведомитель (так называемый Write Notifier). Когда Write Notifier сработает, это значит что у-во готово принять новую порцию данных для передачи, и только в этом случае внутри класса будет произведен вызов ::write(fd, data, len) в который передадутся данные из буфера/очереди класса. После того, как данные были переданы, выстреливает сигнал bytesWritten().

* QSP::read() просто возвратит все доступные данные из внутреннего буфера/очереди класса, он не будет вызывать ::read(fd, data, len)! Вызов этой системной функции осуществляется внутри самого класса когда другой нотификатор (Read Notifier) уведомляет о том, что в у-во поступили данные и их можно вычитать. После того как данные были прочитаны, они добавляются во внутренний буфер/очередь класса и выстреливается сигнал readyRead().

И аналогично работают большинство классов I/O в Qt. Я не понимаю, что тут может быть не понятно? И в чем, собственно проблема..

PS:

* Так, а зачем вообще был выбран Вами Qt в таком случае? Писали бы на любимом борланде (в реинкарнации Lasarus для Linux)
и плодили бы там потоки.

* Если не нравится, никто не запрещает накалякать свой велосипед, но не стоит лезть в чужой монастырь со своим уставом
и пытаться скрестить ежа и ужа.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 10:54
Что там "под катом" я не знаю, но доверять ему можно.
в таком случае вы не можете утверждать что на него можно положиться.
а все же можно как-нибудь узнать что там? Ведь если там WM_TIMER, то на него _нельзя_ положиться.

Ладно, наш спор зашел в тупик. Я в общем все понял.
Qt - это подход быстрого написания несложного ПО не очень квалифицированным программистом (типа ВижлБеэйсика :)
для написания серьезного ПО Qt, конечно, тоже подходит, но придется поковыряться более чем основательно.
Нет, ты совершенно не так все понял. Qt очень даже подходит для написания серьезного ПО, но только надо прочитать документацию по работе с этим фреймворком, а не пытаться забивать шурупы шуруповертом.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: kuzulis от Март 24, 2017, 11:01
Цитата: Alechin
Qt - это подход быстрого написания несложного ПО не очень квалифицированным программистом (типа ВижлБеэйсика Улыбающийся
для написания серьезного ПО Qt, конечно, тоже подходит, но придется поковыряться более чем основательно.

Или Вы тролль, или у меня монитор заплыл жирком.

Цитата: Alechin
велком ту ПостЭЦ пассажирской станции москва-ленинградская и можно посмтреть в реальном времени как управляется осаживание поездов в тупики

Ок, я теперь понял почему поезда опаздывают! ПОЛНЫЙ ТУПИК! Патамучто софт пишут профи с 15-ти летним опытом на теплом ламповом билдере/дельфяшечках под WinXP. Излюбленный подход - насоздавать потоков (чем больше тем лучше) и понатыкать блокировочек.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 24, 2017, 11:10
Сокеты и оборудование это несколько разные вещи.

Именно сто одинаковые, все они QIODevice
И принцип работы с ними одинаков, событиями
Если нужны свои огороды - man termios


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 11:11
Жесть. У меня сервис, который принимает данные от 20К трекеров (каждый шлет по несколько сообщений в секунду, сообщение - это n байт бинарных или текстовых данных), парсит данне, формирует xml и отсылает их в другую программу. Так же, отправляет команды на трекеры, логгирует работу, сохраняет xml и пр. И все, за исключением работы с диском, происходит в одном потоке. ЧЯДНТ?
А стрессовое тестирование делали? если по одному сокету зарядить непрерывный поток очень больших сообщений, что будет с другими сокетами?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 11:15
как закрыть тему?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 24, 2017, 11:16
как закрыть тему?

обиделся ?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 11:19
Жесть. У меня сервис, который принимает данные от 20К трекеров (каждый шлет по несколько сообщений в секунду, сообщение - это n байт бинарных или текстовых данных), парсит данне, формирует xml и отсылает их в другую программу. Так же, отправляет команды на трекеры, логгирует работу, сохраняет xml и пр. И все, за исключением работы с диском, происходит в одном потоке. ЧЯДНТ?
А стрессовое тестирование делали? если по одному сокету зарядить непрерывный поток очень больших сообщений, что будет с другими сокетами?

И пусть идет, главное, что я читаю и обрабатываю данные правильно.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 11:20
обиделся ?
конечно нет! у меня уже не тот возраст чтобы обижаться :)
просто все как-бы ясно.
просто ушли от темы. Я все понял про Qt. мне подход Qt понятен. я его понимаю.
я просто не уверен что перенос всего в один общий поток повысит надежность системы.
пусть все будет в одном потоке. И вдруг с порта например принтера поперли данные ("заштормило"). непрерывный поток.
апологеты "все в одном потоке" уверены что это не повлияет на работу, грубо, еще 20 подключенным принтеров?
а я сталкивался с такой ситуацией - неисправная радиостанция погнала непрерывный поток данных в систему.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 11:22
И пусть идет, главное, что я читаю и обрабатываю данные правильно.
а вы будете успевать их обрабатывать единым ивент лупом? я написал выше - видел как радиостанция непрерывным потоком данных забивала систему. Именно после этого пришлось в каждый поток добавить статистику времени активности, которое обычно единицы процентов и как только стало что-то к 10 и выше - значит оборудование "умерло". Только наличие своего потока для этой радиостанции не позволило "убить систему".


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 11:26
И пусть идет, главное, что я читаю и обрабатываю данные правильно.
а вы будете успевать их обрабатывать единым ивент лупом? я написал выше - видел как радиостанция непрерывным потоком данных забивала систему. Именно после этого пришлось в каждый поток добавить статистику времени активности, которое обычно единицы процентов и как только стало что-то к 10 и выше - значит оборудование "умерло". Только наличие своего потока для этой радиостанции не позволило "убить систему".
Буду, если это нормальное поведение. А если ненормальное, просто закрою подключение и заново его открою. Это уже решение под конкретную ситуацию. Я сталкивался с проблемами с железом, когда получал видео с камеры и управлял ею. Вот только потоки не решили проблему. Пришлось разносить на разные процессы - основной процесс управлял камерой и запускал дополнительный процесс, который читал видео и передавал его основному через stdout. Тут для каждой железки может быть свой подход, но это не обязательное бездумное вынесение в отдельный поток.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 11:47
Тут для каждой железки может быть свой подход, но это не обязательное бездумное вынесение в отдельный поток.
ну а сможете перключить то? я наблюдал ситуацию 100% нагрузки из-за одного канала, не позволявшей, вернее позволявшей с ОЧЕНЬ большой задержкой что-то делать остальному оборудованию. Этоже с выносом в отдельный поток полностью решило проблему - 100% загруз одного потока никак не влиял на остальное оборудование - время отклика остальных осталось прежним. А у меня есть программно реализованный протокол радиоканала с временным разделением канала: так вот EmWin на довольно слабом железе обеспечиват окна радиобмена в потоке данных 50 мсек только благодаря тому что все разделено на потоки и никак не мешает друг-другу.
а почему вы так против потоков? сами потоки систему никак не нагружают но здорово позволяют отвязать разнородное по нагружанию системы оборудование друг от друга. А всякие бугалки про "разделение данных" и "синхронизацию" - не более чем пугалки. Что сложного в защите данных критическими секциями? И добавления к данным событий чтобы можно было будить любой поток, ждущий этого события?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 11:51
1. Переключение между потоками это затраты.
2. Мютексы это затраты
3. Усложнение архитектуры
4. Большая вероятность допуска ошибки, которую крайне тяжело будет вычленить.
Вот почему потоки "by default" это плохо.

По поводу загрузки на 100% - загружает не канал, а обработка данных - обработка должна уметь правильно рулить ресурсами и не допускать такого поведения.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 12:00
Добавлю, если и выносить что-то в отдельный поток, то обработку данных от устройства, а не получение/передачу этих данных. Для этого есть такая вещь, как пул потоков. Получили данные - создали задачу, закинули ее в пул на выполнение.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 12:10
1. Переключение между потоками это затраты.
переключений между потоками нет. Поток "спит" на WaitFor..Objects ожмдая своих WaitableTimer, Event и прочее.
у меня, например, суммарная нагрузка от потоков, например двух GPS, не превышает тысячной процента! Поток получает управление один раз в секунду при приеме терминального символа и все. А поток отслеживания состояния светофор и стрелок и того реже - только когда эти переключения происходят. Откуда переключения?
 
2. Мютексы это затраты
никаких мьютексов. критические секции. она не является объектом ядра. затраты = 0.
для устранения взаимных блокировок одно строгое правило: внутри критической секции никаких других критических секций!

3. Усложнение архитектуры
внешний исходный код упрощается очень сильно. например работа с принтером в основном потоке:
POS_Printer printer("COM3");
(да - динамических объектов стараемся избегать для исключения сюрпризов с веделением памяти, все что нужно выделается сразу чтобы сразу знать что "непойдет", а не потом, когда уже поздно).
if(POS_Printer.Start() != true)
{
// все плохо! думаем что дальше.
}

где надо печатать:
if(printer.Get_State(&state) != true)
{
// все плохо! думаем что дальше.
}
else
{
  if(state.OffLine())....
  if(state.PaperEnd()).....
  настраиваем принтер..
  // с принтером все хорошо.
  // кидаем в принтер кучу заданий.
  // по первому заданию он сразу в фоне начинает печатать.
  for(;;) printer.AddTask();
}

все "прозрачно" - все использование в одном экране! Никаких "прыганий" по исходнику.

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

По поводу загрузки на 100% - загружает не канал, а обработка данных - обработка должна уметь правильно рулить ресурсами и не допускать такого поведения.
какая разница что загружает? мне при приеме пакета надо парсить = искать флаги, считать контрольную сумму, если не совпала - искать новый флаг, считать новую сумму. и так по всему буферу приема по каждому принятому байта. А загружало даже не это: периодически происходили ошибки порта, типа прием BREAK, ошибка parity и прочее. По каждой ошибке пытались что-то сделать, переинициализировать порт и прочее.......


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 12:24
дополню.
3. Усложнение архитектуры
4. Большая вероятность допуска ошибки, которую крайне тяжело будет вычленить.
тестируется написанный класс без привязки к системе. Т.е. классу все равно в какой системе он будет "крутиться".
т.е. если класс протестирован - он будет работать. Ошибка может быть только в использовании класса. Но обычно классы предоставляют минимум довольно простого интерфейса, что минимизирует вероятность ошибки. пример: класс GPS-приемника. Интерфейс класса только: конструктор, методы Start/Stop, методы доступа к потокозаищенным переменным state и cur_time. ну еще может метод ReStart. все. основной поток создает экземпляр, запускает.
те пользователи (потоки), которым надо узнать время читает значение time там где им нужно и когда им нужно.
Один из потоков контролирует работу системы (это может быть и GUI поток), если он видит что GPS.state "неправильный" - решает что дальше, сделать ReStart, открыть/закрыть или прочее.
сложность использования = примитив, поэтому
вероятность ошибки = 0.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 12:31
А если я этот класс помещу в один поток и одновременно дерну разные его методы из n других потоков?


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 12:44
А если я этот класс помещу в один поток и одновременно дерну разные его методы из n других потоков?
конкретно для класса GPS методы Start и Stop дергать не кто не будет. хотя ничто не запрещает это сделать, согласен.
с другой стороны метод старт не перезапускает уже запущенный поток. поэтому думаю ничего не произойдет.
совместно используются только данные. например time для GPS. читать его может кто угодно и откуда угодно - критическая секция у time разграничит доступ.
Для более сложных классов, например радиомодем все сделано через очереди. Если кто-то хочет передать что-то, он вызывает метод Put_Request_to_Quque. запрос кладется в потокзащищенную очередь с событием. Если поток спит - событие будит его и он начинает его передачу в нужном тайм-слоте.
с другой стороны у меня в системе архитектура несколько другая. например есть радар в тупике, он каждын 100 мсек кладет в свою потоказищенную очередь новое измеренное значение и взводит событие наличия новых данных. Класс самостоятельный, ему все-равно кто будет пользоваться его данными и будет ли вообще кто-то. Он тупо набивает очередь пока не придет команда остановиться или сбросить очередь. Есть класс "тупиковый путь" - ему нужны данные радара. Он ждет их на событии очереди данных радара. Как только у связанного с ним радара в очереди появляются новые данные (см. выше) он пробуждается, выбирает данные из очереди (с удалением или без - зависит от логики работы) что-то с ними делает, пересчитывает по критериям и прочее, сохраняет результат своей обработки в своей очереди обработанных данных . Класс радиомодема так-же самостоятельный, он четко с заданной синхрограммой пробегает по всем классам путей, на которых есть зарегистрированный им поезда, выбирает из них последнее значение и отправляет на поезд в нужном тайм-слоте. Т.е. взаимодействие практически только по данным. все классы независимы. им наплевать кто вокруг них, кто пользуется его данными и зачем. Т.е. фактичеки никаких методов между классами нет. Каждый класс статический, поэтому "пропасть" он не может. Например, если что-то не так с радаром (физически или классом) - просто перестает обновляться его очередь данных. класс пути видя что новых данных нет -начинает "беспокоится" извещая класс контроля системы что с его радаром что-то нет.....
сильно упрощенно все выглядит так.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Пантер от Март 24, 2017, 12:49
Я тебя на другое хотел вывести - любое АПИ основанно на контрактах. Если ты не выполняешь контракт, ты получаешь UB. Так вот, контракт у Кьюта - не дергать методы QObject из других потоков. Если тебе такой контракт не подходит, не используй QSerialPort, а напиши свою обертку над системными вызовами или возьми другое решение. И не надо осуждать такой контракт, так как у всех остальных все отлично работает, достаточно только соблюдать условия. Как уже говорили выше, со своим уставом в чужой монастырь лучше не лезть.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 24, 2017, 12:51
Я тебя на другое хотел вывести - любое АПИ основанно на контрактах. Если ты не выполняешь контракт, ты получаешь UB. Так вот, контракт у Кьюта - не дергать методы QObject из других потоков. Если тебе такой контракт не подходит, не используй QSerialPort, а напиши свою обертку над системными вызовами или возьми другое решение. И не надо осуждать такой контракт, так как у всех остальных все отлично работает, достаточно только соблюдать условия. Как уже говорили выше, со своим уставом в чужой монастырь лучше не лезть.
по моему от Qt мы давно отошли - мы обсуждаем что "иметь кучу потоков ооочень плохо!". а я пытаюсь понять почему.
как надо делать на Qt уже понял 3 страницы назад :)
потому и просил закрыть тему, так как последние минимум 3 страницы не по теме.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: sergek от Март 24, 2017, 20:11
Нет, там более сложная архитектура. А это просто наколенный пример. :)
Принципиальные отличия есть? В двух словах.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: qate от Март 24, 2017, 20:18
хорошая тема новичка, побольше бы таких
а то обычно спросят всякую фигню и еще за ответ поблагодарят )


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Март 30, 2017, 12:56
А еще если можно вопрос в продолжение: как прервать запущенную операцию записи или чтения?
у меня принтер работает с XON/OFF. когда он присылает XOFF передача останавливается и дальше я ничего не могу сделать если принтер не удается "оживить".
Принтер поддерживает запросы и команды вне буфера данных, т.е. когда его буфер заполнен он присылает XOFF, при этом я могу послать ему запрос данных что бы узнать, что его остановило. или послать ему команду сброса.
если я это делаю при "приостановленной" передаче - у меня происходит critical error.
по хорошему мне надо иметь возможность хотя бы просто прервать запущенную операцию записи.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: grego812 от Апрель 14, 2017, 17:30
я не "вилосипедю". мои системы управляют в реальном времени движением поездов (велком ту ПостЭЦ пассажирской станции москва-ленинградская и можно посмтреть в реальном времени как управляется осаживание поездов в тупики.
Прошу прощения, но позволю себе немного оффтопа. В реальном времени движением поездов управляют системы СЦБ, исполнительная часть которых (т.е. которая за безопасность отвечает) сделана на здоровенных реле и ещё очень нескоро будет заменена на электронные, я конечно же говорю про РФ. Вы скорее всего занимаетесь наборной частью и отображением поездной ситуации на мониторы дежурного по станции. При этом даже если компоктир дежурного взорвётся, у него всегда есть возможность перейти на резервное управление с древнего пульта.


Название: Re: Работа из QSerialPort из разных потоков
Отправлено: Alechin от Апрель 16, 2017, 14:48
нет, вы в чем-то правы. но не совсем. есть такая система Призма, которая контролируем заезд поезда в тупиковый путь. Там как раз все в реальном времени. Если по расчетному тормозному пути поезд не успевает остановится - его принудительно тормозят по радиоканалу. А тормозной путь рассчитывается по данным установленного в тупике радара (см. перроны Ленинградского вокзала - там радары стоят) или по данным лазерного дальномера (см. Павелецкий и Ярославский вокзалы).
т.е. если система "задумалась" не важно по какой причине - тормозить состав будет уже поздно. А случаи "выхода" состава на перрон были неоднократно (как просто удар в тупиковую призму электрички, так и с "выходом" крайнего вагона на перрон).