#c #qt #qthread
Вопрос:
Мне нужно создать графический интерфейс с использованием QT, который будет взаимодействовать со встроенным программным обеспечением через последовательный порт. Пользователь установит частоту связи, и когда он нажмет кнопку Пуск, графический интерфейс будет периодически отправлять управляющие сообщения встроенному sw.
Встроенный sw отвечает на каждое сообщение ответом, содержащим информацию о его состоянии, которая отображается на экране с помощью некоторых виджетов отображения.
Большая часть информации не имеет критического значения по времени (я имею в виду, что экран не нужно обновлять при каждом входящем и исходящем сообщении). Однако информация о местоположении команды и обратной связи должна извлекаться из каждого сообщения и выводиться на экран, чтобы данные о местоположении не были потеряны.
Во-первых, я закодировал все приложение без использования каких-либо потоков, но, например, когда я запускаю его на частоте 100 Гц, графический интерфейс начинает зависать или пропускать некоторые сообщения. Поэтому я решил создать отдельный поток, который будет обрабатывать сообщение. Я впервые пытаюсь использовать потоки в QT, и у меня есть только некоторые теоретические знания о потоковой передаче.
Я проверил все примеры, в которых используются потоки, но не смог найти ни одного, который выполнял бы как периодическую задачу (отправка сообщений), так и запускаемую задачу (получение сообщений). Я не хочу ждать ответа после отправки сообщения, так как иногда для получения ответа требуется больше времени, чем период общения.
Моя идея состояла в том, чтобы использовать два двух мьютекса в функции запуска потока, и поток вызывает tryLock для каждого мьютекса, чтобы отправить или получить сообщение.
class CommunicationThread : public QThread
{
Q_OBJECT
public:
explicit CommunicationThread(QObject *parent = nullptr);
QMutex sendMutex;
QMutex receiveMutex;
QTimer *SendControlCommand_Timer;
QSerialPort *serialPort;
Communication_Packets_st *commManager_s; // the communication library struct
double SendAngle_d;
double ReceiveAngle_d;
uint32 LastSendIndex_u32 = 0;
double periodSec_d;
private slots:
void MessageReceived_Slot();
void SendControlCommand_Slot();
}
void CommunicationThread::run()
{
forever {
if(sendMutex.tryLock())
{
//calculate the sendAngle here and add it to the graph, the other fields of the structure will be filled by the main thread with a slower frequency
//...
CommandGraph->addData(LastSendIndex_u32*periodSec_d,SendAngle_d);
LastSendIndex_u32 ;
commManager_s->ControlCommandPacket_s.CommandAngle_i32 =
static_cast<int32>(SendAngle_d * COMMAND_ANGLE_MULTIPLIER);
Comm_SendControlCommand(commManager_s); //Function that serializes data and sends the message
sendMutex.lock();
}
if(receiveMutex.tryLock())
{
QByteArray ReceiveBuffer_ba;
quint32 length_u32;
if(serialPort->bytesAvailable())
{
ReceiveBuffer_ba.append(serialPort->readAll());
}
Comm_PacketParser(commManager_s); //Function that parses the serial message and fills the commManager_s struct.
//Put the feedback angle in the plot here so no data is lost, the other fields of the received message will be read and displayed by the main thread with a slower frequency
ReceiveAngle_d = static_cast<double>
(commManager_s->ControlCommandResponsePacket_s.AxisAngle_i32) * FEEDBACK_ANGLE_MULTIPLIER;
}
}
}
Я подключил сигнал готовности последовательного порта к разъему, который разблокирует модуль приема. И sendMutex разблокирован в SendControlCommand_Slot (), инициируемом сигналом тайм-аута SendControlCommand_Timer.
Конечно, я буду использовать мьютексы, которые защитят общие данные, но также использование этих двух мьютексов-единственная идея, которую я мог бы придумать для реализации этих периодических и запускаемых задач, которые должны выполняться одним и тем же потоком.
Подводя итог, мой вопрос заключается в следующем: каков правильный способ структурирования этого потока связи, который будет считывать и записывать данные из/в последовательный порт, используя один экземпляр структуры библиотеки связи, которая также будет использоваться основным потоком?
Комментарии:
1. Графический интерфейс застыл, потому что вам нужно обработать события (
QCoreApplication::processEvents()
), пока вы ждете. Но… почему бы вам просто не использовать aQTimer
и не подключить егоtimeout()
сигнал к разъему, который отправляет ваши управляющие сообщения ?2. Использование QTimer-это то, что я сделал в первую очередь, но, похоже, это работает не очень хорошо. Экран часто зависает, и мое приложение время от времени не получает некоторых сообщений. Я сравнил количество сообщений, которые я получил, с количеством сообщений, отправленных встроенным sw, в результате оказалось, что около 10% сообщений было потеряно. Я попытался использовать как сигнал readyRead, так и опрос последовательного порта с таймером с периодом 0, особой разницы не было.
3. Использование QCoreApplication::processEvents() мне не пришло в голову, хотя я попробую, спасибо.
4. для получения ответа требуется больше времени, чем период общения , для меня это не имеет особого смысла, если период опроса чаще, чем доступные ответы. Я бы не стал отправлять новое сообщение для опроса, не получив ответа от предыдущего.
5. @neko я больше думал об использовании
QTimer
для обработки периодических отправок. Опрос для ожидания ответа-это простоwhile
цикл, в котором вы вызываетеQCoreApplication::processEvents()
, чтобы графический интерфейс не зависал, пока вы ждете, пока не появятся доступные для чтения байты. И это должно сработать.
Ответ №1:
Вы уверены, что клиент теряет пакеты на компьютере, а не на подключенном устройстве?
Вы можете использовать адаптер USB -> UART вместо реального устройства, подключив контакты TX и RX с помощью перемычки для тестирования.
Я написал пример без отдельной темы и без использования QCoreApplication :: processEvents ()
.
Я запустил этот пример и не потерял ни одной посылки.
Хотя показания заметно отставали от передачи.
Вы также можете посмотреть на этот мой проект. Он использует протокол связи Modbus.
Комментарии:
1. Я уверен, что мы отлаживаем встроенное устройство и заставляем его подсчитывать, сколько сообщений было отправлено. Они оказываются равными количеству отправленных сообщений моего программного обеспечения. Но количество полученных сообщений моего программного обеспечения отстает от этих трех цифр.
2. И большое спасибо за код, в настоящее время я не работаю, но рассмотрю его, как только вернусь
3. Однако я не могу выполнить тест с обратной связью, так как командные и ответные сообщения сильно отличаются друг от друга, и я не учил свое программное обеспечение интерпретировать управляющее сообщение. (Управляющее сообщение составляет 13 байт, ответное сообщение-27 байт)