QThread + QSerialPort! Работу с СOM-портом в отдельном потоке

В одном из своих проектов столкнулся с небольшой проблемой: задача заключается в том, что нужно работать с тремя разными портами одновременно (если быть точнее с двумя, а при настройке и калибровке оборудования 3-я), а самая большая неприятность в том, что все они общаются по разным протоколам… да в придачу два (в некоторых случаях 3) устройства “висят” на RS485 с протоколом DCON, это первый порт! Одно устройство на RS232 с жутким самопальным протоколом внеочередной переделкой Wake. И настроечно-калибровочное подключается также по RS232-му, протокол неизвестен (занимаюсь расшифровкой)… Ну это все прелюдия и ОФФТОП…

В такой задаче ИМХО целесообразно выполняит работу с каждым портом в отдельном потоке. Вот на этом месте и начались все проблемы… Проведя жуткоскучный и мучительный лит. поиск «ГУГЛ в помощь» результаты были весьма и весьма неоднозначны. Мои подзатыльники самому себе и хочу здесь привести на примере создания простейшей ГУЕвины терминалки с использованием QserialPort и Qthread. Надеюсь топик поможет многим, да и мне возможность получить дополнительных подзатыльников=)))

Постановка задачи: Рассмотреть возможные варианты реализации данной задачи и реализовать наиболее приемлемый и работоспособный. Привести пример простейшей терминалки. Получить подзатыльников и исправить свои ошибки (а их много).

Исходя из лит.анализа, основное: Поток должен быть независим, правильно унаследован. Все обращения к потоку и операции с потоком должны организовываться по сигнально-слотовой системе (никакого прямого обращения к методам в классе).

Краткие теоретические сведенья:

  • Кьют — бесплатный и кроссплатформенный инструмент для разработки ПО на C++.
  • QserialPort — дополнение к библиотеке Qt для работы с аппаратными и виртуальными СOM портами. Начиная с 5й версии Qt-а официально в него входит.
  • Qthread — какая-то жуткая магия для работы с потоками. Независимая задача, которая выполняется внутри процесса и разделяет вместе с ним общее адресное пространство, код и глобальные данные.
  • COM порт (RS232) — интерфейс с последовательной передачей данных стандарта RS232.
  • RS485 — стандарт физического уровня. Прикручивается к UART…
  • Цели: обобщить наработанные результаты и поделиться ими. Получить критический анализ и оценку от сообщества для повышения маны.
  • Моменты создания приложения в Qt пропускаю… Здесь же приведу код класса и то как его объявлять.

Создаем новый класс:

файл.h

#ifndef PORT_H
#define PORT_H
#include <QObject>
#include <QtSerialPort/QserialPort>//Обьявляем работу с портом

struct Settings {//Структура с настройками порта
QString name;
qint32 baudRate;
QSerialPort::DataBits dataBits;
QSerialPort::Parity parity;
QSerialPort::StopBits stopBits;
QSerialPort::FlowControl flowControl;
};
class Port : public QObject
{
Q_OBJECT
public:
explicit Port(QObject *parent = 0);
~Port();
QSerialPort thisPort;
Settings SettingsPort;
signals:
void finished_Port(); //Сигнал закрытия класса
void error_(QString err);//Сигнал ошибок порта
void outPort(QString data); //Сигнал вывода полученных данных
public slots:
void  DisconnectPort(); // Слот отключения порта
void ConnectPort(void); // Слот подключения порта
void Write_Settings_Port(QString name, int baudrate, int DataBits, int Parity, int StopBits, int FlowControl);// Слот занесение настроек порта в класс
void process_Port(); //Тело
void WriteToPort(QByteArray data); // Слот от правки данных в порт
private slots:
void handleError(QSerialPort::SerialPortError error);//Слот обработки ощибок
void ReadInPort(); //Слот чтения из порта по ReadyRead
public:
};
#endif // PORT_H

файл.с

#include «port.h»
#include <qdebug.h>
Port::Port(QObject *parent) :
QObject(parent)
{
}
Port::~Port()
{
qDebug(«By in Thread!»);
emit finished_Port();//Сигнал о завершении работы
}
void Port :: process_Port(){//Выполняется при старте класса
qDebug(«Hello World in Thread!»);
connect(&thisPort,SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError))); // подключаем проверку ошибок порта
connect(&thisPort, SIGNAL(readyRead()),this,SLOT(ReadInPort()));//подключаем   чтение с порта по сигналу readyRead()
}
void Port :: Write_Settings_Port(QString name, int baudrate,int DataBits,
int Parity,int StopBits, int FlowControl){//заносим параметры порта в структуру данных
SettingsPort.name = name;
SettingsPort.baudRate = (QSerialPort::BaudRate) baudrate;
SettingsPort.dataBits = (QSerialPort::DataBits) DataBits;
SettingsPort.parity = (QSerialPort::Parity) Parity;
SettingsPort.stopBits = (QSerialPort::StopBits) StopBits;
SettingsPort.flowControl = (QSerialPort::FlowControl) FlowControl;
}
void Port :: ConnectPort(void){//процедура подключения
thisPort.setPortName(SettingsPort.name);
if (thisPort.open(QIODevice::ReadWrite)) {
if (thisPort.setBaudRate(SettingsPort.baudRate)
&& thisPort.setDataBits(SettingsPort.dataBits)//DataBits
&& thisPort.setParity(SettingsPort.parity)
&& thisPort.setStopBits(SettingsPort.stopBits)
&& thisPort.setFlowControl(SettingsPort.flowControl))
{
if (thisPort.isOpen()){
error_((SettingsPort.name+ » >> Открыт!\r»).toLocal8Bit());
}
} else {
thisPort.close();
error_(thisPort.errorString().toLocal8Bit());
}
} else {
thisPort.close();
error_(thisPort.errorString().toLocal8Bit());
}
}
void Port::handleError(QSerialPort::SerialPortError error)//проверка ошибок при работе
{
if ( (thisPort.isOpen()) && (error == QSerialPort::ResourceError)) {
error_(thisPort.errorString().toLocal8Bit());
DisconnectPort();
}
}//
void  Port::DisconnectPort(){//Отключаем порт
if(thisPort.isOpen()){
thisPort.close();
error_(SettingsPort.name.toLocal8Bit() + » >> Закрыт!\r»);
}
}
void Port :: WriteToPort(QByteArray data){//Запись данных в порт
if(thisPort.isOpen()){
thisPort.write(data);
}
}
//
void Port :: ReadInPort(){//Чтение данных из порта
QByteArray data;
data.append(thisPort.readAll());
outPort(data);
//((QString)(adr.toInt())).toLatin1().toHex()
}

Теперь необходимо правильно обьявить поток и подключить к нему наш новый класс (здесь находится вся магия):

QThread *thread_New = new QThread;//Создаем поток для порта платы
Port *PortNew = new Port();//Создаем обьект по классу
PortNew->moveToThread(thread_New);//помешаем класс  в поток
PortNew->thisPort.moveToThread(thread_New);//Помещаем сам порт в поток

И соединяем все нужные нам сигналы и слоты:

connect(PortNew, SIGNAL(error_(QString)), this, SLOT(Print(QString)));//Лог ошибок
connect(thread_New, SIGNAL(started()), PortNew, SLOT(process_Port()));//Переназначения метода run
connect(PortNew, SIGNAL(finished_Port()), thread_New, SLOT(quit()));//Переназначение метода выход
connect(thread_New, SIGNAL(finished()), PortNew, SLOT(deleteLater()));//Удалить поток
connect(PortNew, SIGNAL(finished_Port()), thread_New, SLOT(deleteLater()));//Удалить поток
connect(this,SIGNAL(savesettings(QString,int,int,int,int,int)),PortNew,SLOT(Write_Settings_Port(QString,int,int,int,int,int)));//Слот — ввод настроек!
connect(ui->BtnConnect, SIGNAL(clicked()),PortNew,SLOT(ConnectPort()));//по нажатию кнопки подключить порт
connect(ui->BtnDisconect, SIGNAL(clicked()),PortNew,SLOT(DisconnectPort()));//по нажатию кнопки отключить порт
connect(PortNew, SIGNAL(outPort(QString)), this, SLOT(Print(QString)));//вывод в текстовое поле считанных данных
connect(this,SIGNAL(writeData(QByteArray)),PortNew,SLOT(WriteToPort(QByteArray)));//отправка в порт данных

И остается самое главное! Запустить поток:

В результате получаем вот такое окошко. Единственное что отправленные и принятые данные отображаются в ASCII кодах.

001

You may also like...

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>