-ѕоиск по дневнику

ѕоиск сообщений в predefglas

 -ѕодписка по e-mail

 

 -—татистика

—татистика LiveInternet.ru: показано количество хитов и посетителей
—оздан: 10.12.2014
«аписей:
 омментариев:
Ќаписано: 94

»спользование механизма SRR в приложени€х разработанных на Qt дл€ QNX

ƒневник

¬торник, 19 »юл€ 2016 г. 20:31 + в цитатник
‘реймворк Qt один из самых попул€рных и примен€емых при разработке кроссплатформенных настольных и мобильных приложений. Ёта попул€рность не могла рано или поздно не привести к использованию Qt в системах специального и ответственного назначени€. ƒостаточно давно существует возможность разработки на Qt дл€ QNX Neutrino. Ѕиблиотека Qt поддерживает платформу QNX, а среда разработки Qt Creator обеспечивает взаимодействие с системами на QNX. ќднако QNX, как система в том числе и дл€ встраиваемых решений, имеет в своЄм составе технологии, которые не требуютс€, а потому и отсутствуют в системах общего назначени€.  лючева€ дл€ ќ—–¬ QNX функциональность, на которой построена сама система и на которую нередко опираютс€ пользовательские задачи это передача сообщений. ќб особенност€х применени€ механизма SRR (Send/Receive/Replay), как ещЄ называют передачу сообщений в QNX, и о разработке двух примеров Qt-приложений Ч клиента и сервера Ч € и хотел бы рассказать сегодн€. ¬ заметке не делаетс€ каких-то открытий, предлагаетс€ в общем-то известна€ информаци€. “ем не менее Qt это относительно новый фреймворк дл€ разработчиков систем специального назначени€, где исторически наблюдаетс€ некотора€ инертность при внедрении новых технологий. –азработчики систем на QNX могут быть недостаточно знакомы с тонкост€ми Qt, а разработчики Qt-приложений зачастую могут не знать специфику QNX. –ешение задачи использовани€ в одном проекте и графических возможностей библиотеки Qt, и специфичных дл€ QNX технологий может потребовать затраты усилий, особенно на первых этапах. »менно поэтому и по€вилась данна€ заметка, цель которой в одном месте собрать информацию, котора€ может понадобитьс€ разработчикам при использовании Qt в QNX. “иповой пример использовани€ SRR в QNX ѕоскольку о сообщени€х QNX € уже писал раньше на ’абре, в том числе и о составных сообщени€х, то будем считать, что теори€ в каком-то виде уже известна и можно переходить к практике. ѕоэтому привожу ниже исходный код приложени€ клиента: qnx_client.c//////////////////////////////////////////////////////////////////////////////// // qnx_client.c // // demonstrates using input/output vector (IOV) messaging // //////////////////////////////////////////////////////////////////////////////// #include <string.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include "../iov_server.h" int main(int argc, char* argv[]) { int coid; // Connection ID to server cksum_header_t hdr; // msg header will specify how many bytes of data will follow int incoming_checksum; // space for server's reply int status; // status return value iov_t siov[2]; // create a 2 part iov if ( 2 d\n"); exit(EXIT_FAILURE); } // locate the server coid = name_open(CKSUM_SERVER_NAME, 0); if ( -1 > ѕрограмма довольно тривиальна€, € просто вз€л пример из курсов по QNX и немного его Ђпричесалї. Ёто консольное приложение, которое принимает на вход строку, пересылает еЄ на сервер и выводит на экран ответ сервера Ч контрольную сумму переданной ранее строки. ќбратите внимание, что в примере используютс€ составные сообщени€ Ч макрос SETIOV() и функци€ MsgSendvs() вместо MsgSend() Ч что позвол€ет избежать лишнего копировани€. —амое интересное здесь это использование функции name_open() дл€ поиска сервера и установлени€ соединени€ с ним. “еперь самое врем€ посмотреть исходный код сервера: qnx_server.c//////////////////////////////////////////////////////////////////////////////// // qnx_server.c // // demonstrates using input/output vector (IOV) messaging // //////////////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> #include "../iov_server.h" typedef union { uint16_t msg_type; struct _pulse pulse; cksum_header_t cksum_hdr; } msg_buf_t; int calculate_checksum(char *text) { char *c; int cksum = 0; for ( c = text; *c; c++ ) cksum >  од сервера немного поинтереснее. —ервер принимает и обрабатывает сообщени€ от клиента. Ќа самом деле в этом примере реализовано только одно сообщение Ч CKSUM_MSG_TYPE Ч подсчЄт контрольной суммы переданных данных. ƒругое сообщение Ч _IO_CONNECT Ч посылаетс€ серверу, когда клиент вызывает функцию name_open(). ѕомимо сообщений сервер умеет обрабатывать служебные импульсы _PULSE_CODE_DISCONNECT и _PULSE_CODE_UNBLOCK. ¬ этом простом примере обработка служебных сообщений в принципе не требуетс€. јлгоритм работы сервера достаточно прост. —начала выполн€етс€ инициализаци€, в данном случае это объ€вление имени при помощи функции name_attach(), после чего клиенты могут найти сервер. ƒальнейша€ работа сервера представл€ет собой Ђвечный циклї. ¬ самом начале цикла сервер блокируетс€ на вызове MsgReceive() ожида€ сообщений от клиента. ѕо приходу сообщени€ или пульса €дро QNX разблокирует сервер, который начнЄт обработку прин€того сообщени€. ¬ примере используетс€ объединение (union) msg_buf_t дл€ получени€ сообщени€. Ёто обычна€ практика дл€ QNX, когда возможные типы сообщений (а сообщение обычно описываютс€ структурой €зыка —и) объедин€ютс€ в union. Ќаше полезное сообщение CKSUM_MSG_TYPE мы получаем при помощи MsgReceive() не целиком, принимаетс€ только заголовок, в котором указан размер данных. ƒанные дочитываютс€ при помощи функции MsgRead(). ќтвет клиенту отправл€етс€ при помощи функции MsgReply(), а в случае ошибки Ч MsgError(). »мпульсы не требуют ответа. ƒл€ полноты картины привожу текст заголовочного файла. Ётот заголовочный файл используетс€ и сервером, и клиентом, и, как мы увидим дальше, Qt версии нашего сервера и клиента тоже используют этот файл. ќн предназначен дл€ подключени€ необходимых заголовочных файлов и объ€влени€ структуры заголовка сообщени€ CKSUM_MSG_TYPE. iov_server.h#ifndef _IOV_SERVER_H_ #define _IOV_SERVER_H_ #include <sys/dispatch.h> #include <sys/neutrino.h> #include <sys/iomsg.h> #include <errno.h> #define CKSUM_SERVER_NAME "cksum" #define CKSUM_MSG_TYPE (_IO_MAX + 2) typedef struct { uint16_t msg_type; unsigned data_size; } cksum_header_t; // checksum reply is an int #endif //_IOV_SERVER_H_ Ќа скриншоте ниже представлен пример работы консольных версий сервера и клиента: —начала запускаетс€ сервер, который ожидает сообщений от клиента. ѕотом запускаетс€ клиент, в качестве аргумента ему указываетс€ строка ЂHello, QNX!ї ¬о врем€ работы клиент и сервер вывод€т диагностические сообщени€ в консоль, по которым можно судить о работе программ. ѕрограммы работают как ожидалось, можно приступать к написанию графических вариантов на Qt. —начала адаптируем клиентское приложение. ѕример клиента на Qt –азрабатывать Qt приложени€ будем в Qt Creator. ¬ этом случае сам процесс разработки приложений дл€ QNX в общем не отличаетс€ от разработки приложений дл€ других ќ—. ¬едь Qt это кроссплатформенный фреймворк. “ребуетс€ только создать комплект (Kit) дл€ QNX в Qt Creator. —оздаЄм новый проект приложени€ типа Qt Widgets Application. ѕри этом Qt Creator подготовит все необходимые файлы, в том числе и форму дл€ окна. ƒл€ клиента форму окна приводим к следующему виду: Ќа форме размещены поле дл€ ввода текста (text), который передаЄтс€ серверу, кнопки подключени€ (connect) и отключени€ (disconnect) от сервера, кнопка (calc) отправки сообщени€ серверу, поле ввода (cksum), которое используетс€ дл€ вывода контрольной суммы полученной от сервера, и область вывода диагностических сообщений (status). ќсталось только написать код дл€ работы с сервером и обработки логики графической формы. ¬ результате получаем следующий класс MainWindow: mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "../iov_server.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void log(QString msg); void showCrc(QString crc); private slots: void qnxConnect(); void qnxDisconnect(); void calculate(); private: Ui::MainWindow *ui; int coid; // Connection ID to server }; #endif // MAINWINDOW_H mainwindow.cpp#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDateTime> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); coid = -1; connect(ui->connect, SIGNAL(clicked()), this, SLOT(qnxConnect())); connect(ui->disconnect, SIGNAL(clicked()), this, SLOT(qnxDisconnect())); connect(ui->calc, SIGNAL(clicked()), this, SLOT(calculate())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::qnxConnect() { // check if we already connected if ( coid t connect to server: ")).append(strerror(errno))); return; } log(tr("Connected to server")); ui->connect->setEnabled(false); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::qnxDisconnect() { // check if we already disconnected if ( coid < 0 ) return; // disconnect from the server int status = name_close(coid); if ( status < 0 ) { log(QString(tr("Can't disconnect from server: ")).append(strerror(errno))); return; } log(tr("Disconnected from server")); coid = -1; ui->calc->setEnabled(false); ui->disconnect->setEnabled(false); ui->connect->setEnabled(true); } void MainWindow::calculate() { ui->disconnect->setEnabled(false); ui->calc->setEnabled(false); // get the data QString data = ui->text->toPlainText(); log(QString(tr("Sending the following text to checksum server: %1")).arg(data)); // build the header cksum_header_t hdr; // msg header will specify how many bytes of data will follow hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = data.length() + 1; // setup the message as a two part iov, first the header then the data iov_t siov[2]; // create a 2 part iov SETIOV(&siov[0], &hdr, sizeof(hdr)); SETIOV(&siov[1], data.toAscii().data(), hdr.data_size); // and send the message off to the server int incoming_checksum; // space for server's reply int status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof(incoming_checksum)); if ( status < 0 ) { log(QString(tr("Can't send message to server: ")).append(strerror(errno))); return; } log(QString(tr("MsgSend return status: %1")).arg(status)); showCrc(QString::number(incoming_checksum)); } void MainWindow::showCrc(QString crc) { ui->cksum->setText(crc); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::log(QString msg) { ui->status->append(msg.prepend(QDateTime::currentDateTime().toString("hh:mm:ss "))); } ‘айл main.cpp осталс€ таким, каким его создал Qt Creator, поэтому приводить его содержимое не стану. »так, посмотрим, что же мы тут понаделали. —начала, как и в прошлом примере, запускаем сервер. «атем запускаем Qt версию клиента. Ќажимаем кнопку Connect, обращаем внимание, что сервер получает уведомление о подключении клиента в виде сообщени€ _IO_CONNECT. «атем пишем текст ЂHello, QNX!ї и нажимаем кнопку Calc, что приводит к отправке сообщени€ на на сервер. —обытие отправки также отображаетс€ на экране. ѕолученна€ от сервера контрольна€ сумма отображаетс€ в окне клиента. ѕример работает, сообщени€ отправл€ютс€ и принимаютс€, проблем не замечено. ЌоЕ Ќо € то знаю, что всЄ не должно так замечательно работать. ƒело в том, что после вызова MsgSendvs() клиент блокируетс€ как минимум до вызова сервером функции MsgReceive() (моет быть больше, если в системе есть более высокоприоритетные процессы). ƒл€ иллюстрации этой особенности в коде функции calculate_checksum() сервера добавлена задержка в виде вызова sleep(10). — такой задержкой в сервере клиент блокируетс€ на 10 секунд, что приводит к заметному Ђзамерзаниюї графического окна сервера. ¬ некоторых случа€х, особенно когда сервер сразу отвечает клиенту (т.е. информаци€ всегда доступна серверу, а не приходит извне), блокировка не €вл€етс€ проблемой. ¬ остальных случа€х, пользователь может начать нервничать, когда у него Ђзамерзаетї графический интерфейс. я бы не стал рисковать и выпускать программы, которые могут нервировать заказчиков. — ЂзамЄрзшимї интерфейсом клиент не сможет продолжить работу с приложением после отправки сообщени€ до получени€ ответа от сервера, а ведь в реальной жизни приложение может взаимодействовать с несколькими серверами и предоставл€ть другие функции управлени€. Ќет, текущий вариант клиентского приложени€ нас не может устроить. ѕоэтому давайте посмотрим на правильную реализацию клиента. ѕравильный пример клиента на Qt  ак же можно решить проблему с блокировкой клиента?  лиент не может не блокироватьс€ на MsgSendvs(). ќднако вполне допустимо выделить работу с сообщени€ми в отдельный поток. ¬ этом случае один поток обслуживает графический интерфейс, другой Ч реализует механизм SRR. ƒл€ работы с потоками в Qt будем использовать класс QThread. –еализацию SRR вынесем в отдельный класс Sender. —в€зь между классами Sender (работа с сообщени€ми) и MainWindow (графический интерфейс) организуем через сигналы и слоты Qt. ѕосмотрим, как изменилс€ класс MainWindow с учЄтом вышесказанного. ƒл€ нагл€дности старый код также оставлен, и добавлен макрос SENDER_THREAD, при объ€влении которого работа с сообщени€ми выполн€етс€ в отдельном потоке Qt. mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "../iov_server.h" #define SENDER_THREAD #ifdef SENDER_THREAD #include <QThread> #endif namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); #ifdef SENDER_THREAD signals: void calcCrc(int coid, QString data); #endif public slots: void log(QString msg); void showCrc(QString crc); private slots: void qnxConnect(); void qnxDisconnect(); void calculate(); private: Ui::MainWindow *ui; int coid; // Connection ID to server #ifdef SENDER_THREAD QThread senderThread; #endif }; #endif // MAINWINDOW_H mainwindow.cpp#include "mainwindow.h" #include "ui_mainwindow.h" #ifdef SENDER_THREAD #include "sender.h" #endif #include <QDateTime> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); coid = -1; connect(ui->connect, SIGNAL(clicked()), this, SLOT(qnxConnect())); connect(ui->disconnect, SIGNAL(clicked()), this, SLOT(qnxDisconnect())); connect(ui->calc, SIGNAL(clicked()), this, SLOT(calculate())); #ifdef SENDER_THREAD Sender *sender = new Sender; sender->moveToThread(&senderThread); connect(&senderThread, SIGNAL(finished()), sender, SLOT(deleteLater())); connect(this, SIGNAL(calcCrc(int, QString)), sender, SLOT(send(int, QString))); connect(sender, SIGNAL(result(QString)), this, SLOT(showCrc(QString))); connect(sender, SIGNAL(log(QString)), this, SLOT(log(QString))); senderThread.start(); #endif } MainWindow::~MainWindow() { #ifdef SENDER_THREAD senderThread.quit(); senderThread.wait(); #endif delete ui; } void MainWindow::qnxConnect() { // check if we already connected if ( coid t connect to server: ")).append(strerror(errno))); return; } log(tr("Connected to server")); ui->connect->setEnabled(false); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::qnxDisconnect() { // check if we already disconnected if ( coid < 0 ) return; // disconnect from the server int status = name_close(coid); if ( status < 0 ) { log(QString(tr("Can't disconnect from server: ")).append(strerror(errno))); return; } log(tr("Disconnected from server")); coid = -1; ui->calc->setEnabled(false); ui->disconnect->setEnabled(false); ui->connect->setEnabled(true); } void MainWindow::calculate() { ui->disconnect->setEnabled(false); ui->calc->setEnabled(false); // get the data QString data = ui->text->toPlainText(); #ifdef SENDER_THREAD emit calcCrc(coid, data); #else log(QString(tr("Sending the following text to checksum server: %1")).arg(data)); // build the header cksum_header_t hdr; // msg header will specify how many bytes of data will follow hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = data.length() + 1; // setup the message as a two part iov, first the header then the data iov_t siov[2]; // create a 2 part iov SETIOV(&siov[0], &hdr, sizeof(hdr)); SETIOV(&siov[1], data.toAscii().data(), hdr.data_size); // and send the message off to the server int incoming_checksum; // space for server's reply int status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof(incoming_checksum)); if ( status < 0 ) { log(QString(tr("Can't send message to server: ")).append(strerror(errno))); return; } log(QString(tr("MsgSend return status: %1")).arg(status)); showCrc(QString::number(incoming_checksum)); #endif } void MainWindow::showCrc(QString crc) { ui->cksum->setText(crc); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::log(QString msg) { ui->status->append(msg.prepend(QDateTime::currentDateTime().toString("hh:mm:ss "))); } ¬ объ€влении класса MainWindow по€вилс€ сигнал calcCrc(), при помощи которого сообщаемс€ экземпл€ру класса Sender кому и какое сообщение требуетс€ отправить. Ѕольшие изменени€ претерпела реализаци€ класса MainWindow. ¬ конструкторе по€вилс€ блок кода, в котором создаЄтс€ экземпл€р класса Sender и при помощи метода moveToThread() выдел€етс€ в отдельный поток. ¬ деструкторе ожидаем завершение потока (методы quit() и wait() класса QThread). ¬есь код метода calculate() перенесЄн в класс Sender и заменЄн на генерацию сигнала calcCrc(). ѕосле доработки MainWindow, можно перейти к классу Sender. sender.h#ifndef SENDER_H #define SENDER_H #include <QObject> #include "../iov_server.h" class Sender : public QObject { Q_OBJECT public: Sender() {} virtual ~Sender() {} signals: void result(QString data); void log(QString err); public slots: void send(int coid, QString data); }; #endif // SENDER_H sender.cpp#include "sender.h" void Sender::send(int coid, QString data) { emit log(QString(tr("Sending the following text to checksum server: %1")).arg(data)); // build the header cksum_header_t hdr; // msg header will specify how many bytes of data will follow hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = data.length() + 1; // setup the message as a two part iov, first the header then the data iov_t siov[2]; // create a 2 part iov SETIOV(&siov[0], &hdr, sizeof(hdr)); SETIOV(&siov[1], data.toAscii().data(), hdr.data_size); // and send the message off to the server int incoming_checksum; // space for server's reply int status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof(incoming_checksum)); if ( status < 0 ) { emit log(QString(tr("Can't send message to server: ")).append(strerror(errno))); return; } emit log(QString(tr("MsgSend return status: %1")).arg(status)); emit result(QString::number(incoming_checksum)); } ѕо сути это код, который был раньше в методе calculate() класса MainWindow. ¬ывод ошибок и результата в графическое окно приложени€ клиента реализован при помощи сигналов log() и result(). — такими доработками графический интерфейс клиента не Ђзамерзаетї, т.е. пока экземпл€р класса Sender блокируетс€ на 10 секунд в отдельном потоке, мы можем управл€ть графическим окном. ѕравда в представленном примере управл€ть особо нечем, но возможность то есть. ѕример сервера на Qt ѕоэксперементировав с клиентом будем сразу разрабатывать сервер правильно. ѕоскольку вызов MsgReceive() приводит к блокировке, то вынесем функциональность сервера в класс Server, который будет работать в отдельном потоке. ѕринципы те же, что и в клиенте. ‘орму главного окна по-честному Ђскомуниздимї у клиента Ч скопируем mainwindow.ui, откроем в редакторе, удалим ненужные кнопки и преобразуем класс QPlainTextEdit (объект text) в QTextBrowser (редактор это позвол€ет). ќбъ€вление и реализаци€ класса MainWindow сервера приведены ниже: mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThread> #include "../iov_server.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); signals: void startServer(name_attach_t* attach); public slots: void log(QString msg); void showCrc(QString crc); void showText(QString txt); void stopServer(void); private: Ui::MainWindow *ui; name_attach_t* attach; QThread serverThread; }; #endif // MAINWINDOW_H mainwindow.cpp#include "mainwindow.h" #include "ui_mainwindow.h" #include "server.h" #include <QDateTime> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); Server *server = new Server; server->moveToThread(&serverThread); connect(&serverThread, SIGNAL(finished()), server, SLOT(deleteLater())); connect(this, SIGNAL(startServer(name_attach_t*)), server, SLOT(process(name_attach_t*))); connect(server, SIGNAL(result(QString)), this, SLOT(showCrc(QString))); connect(server, SIGNAL(text(QString)), this, SLOT(showText(QString))); connect(server, SIGNAL(log(QString)), this, SLOT(log(QString))); attach = name_attach(NULL, CKSUM_SERVER_NAME, 0); if ( NULL t attach name: %1")).arg(strerror(errno))); } else { serverThread.start(); emit startServer(attach); } } MainWindow::~MainWindow() { stopServer(); serverThread.quit(); serverThread.wait(); delete ui; } void MainWindow::showText(QString txt) { ui->text->setText(txt); } void MainWindow::showCrc(QString crc) { ui->cksum->setText(crc); } void MainWindow::log(QString msg) { ui->status->append(msg.prepend(QDateTime::currentDateTime().toString("hh:mm:ss "))); } void MainWindow::stopServer() { if ( NULL > ƒл€ работы сервера создаЄм им€ в MainWindow использу€ функцию name_attach(). ѕри помощи сигнала передаЄм структуру attach в поток сервера, тем самым запуска€ его. ƒл€ остановки сервера удал€ем им€ Ч функци€ name_detach(). ¬ остальном очень похоже на то, что было сделано в клиенте. ѕосмотрим на код: server.h#ifndef SERVER_H #define SERVER_H #include <QObject> #include "../iov_server.h" typedef union { uint16_t msg_type; struct _pulse pulse; cksum_header_t cksum_hdr; } msg_buf_t; class Server : public QObject { Q_OBJECT public: Server() {} virtual ~Server() {} signals: void result(QString data); void text(QString text); void log(QString err); public slots: void process(name_attach_t* attach); private: int calculate_checksum(char *text); }; #endif // SERVER_H server.cpp#include "server.h" int Server::calculate_checksum(char *text) { int cksum = 0; for ( char *c = text; *c; c++ ) cksum >  ласс Server реализует две функции консольного сервера (qnx_server), изменилс€ только вывод сообщений (при помощи сигналов/слотов Qt) и регистраци€ имени выполн€етс€ в классе MainWindow. –абота графических вариантов клиента и сервера представлена на следующем скриншоте: —ервер получилс€ без элементов управлени€. Ќет ни кнопок, ни полей ввода. √рафическое окно сервера служит только дл€ контрол€ за его работой. «аключение ¬от и подошла к концу эта заметка. Ѕыл рассмотрен код нескольких примеров, стало пон€тно, как правильно использовать механизм сообщений QNX в приложени€х на Qt. ƒл€ тех же, кто захочет воспроизвести примеры € опубликовал их на Bitbucket. ѕредвид€ возможные замечани€ по коду, прошу учесть, что это только примеры, которые иллюстрируют работу SRR в Qt.  ое-что в рабочей системе € бы сделал иначе, но чтобы не перегружать примеры, их код был упрощЄн, и на некоторые моменты € закрыл глаза. “ем не менее, если у кого-то из читателей по€в€тс€ конкретные предложени€ по улучшению кода примеров или исправлению ошибок, то € их по возможности учту. ѕрошу по этим вопросам обращатьс€ в личные сообщени€.
—етевые технологии »нтернета вещей

ћетки:  

 —траницы: [1]