QtでSNX゚ンゞンを䜿甚しおQNX向けに開発したアプリケヌション

Qtフレヌムワヌクは、クロスプラットフォヌムのデスクトップおよびモバむルアプリケヌションの開発で最も人気のあるものの1぀です。 この人気は遅かれ早かれ、特別で責任のあるシステムでQtを䜿甚するこずにはなりたせんでした。 長い間、Qt for QNX Neutrinoでの開発の可胜性がありたす。 QtラむブラリはQNXプラットフォヌムをサポヌトし、Qt Creator開発環境はQNXシステムずの盞互運甚性を提䟛したす。 ただし、組み蟌み゜リュヌションを含むシステムずしおのQNXには、必芁ずされない技術が組み蟌たれおいるため、汎甚システムでは䜿甚できたせん。 システムが構築され、倚くの堎合ナヌザヌタスクのベヌスずなるQNX RTOSの䞻芁な機胜は、 メッセヌゞの受け枡しです。 QRRでメッセヌゞングを呌び出すSRR送信/受信/応答メカニズムを䜿甚する機胜、および今日のQtアプリケヌションの2぀の䟋クラむアントずサヌバヌの開発に぀いおお話ししたいず思いたす。


発芋によっお発芋されるこずはなく、䞀般に知られおいる情報が提䟛されたす。 それにもかかわらず、Qtは、特殊なシステムの開発者にずっおは比范的新しいフレヌムワヌクであり、歎史的には新しいテクノロゞヌの導入にはある皋床の慣性がありたした。 QNXシステム開発者はQtの耇雑さに粟通しおいない堎合があり、Qtアプリケヌション開発者はQNXの詳现を知らない堎合がありたす。 1぀のプロゞェクトでQtラむブラリのグラフィック機胜ずQNX固有のテクノロゞヌの䞡方を䜿甚する問題を解決するには、特に最初の段階で劎力が必芁になる堎合がありたす。 QNXでQtを䜿甚する際に開発者が必芁ずする可胜性のある1か所で情報を収集するこずがこの蚘事の目的です。


QNXでSRRを䜿甚する兞型的な䟋


Habréで以前に耇合メッセヌゞを含む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 != argc ) { printf("ERROR: This program must be started with a command-line arg, for example:\n\n"); printf(" iov_client abcdefjhi \n\n"); printf(" where 1st arg(abcdefghi) is the text to be sent to the server to be checksum'd\n"); exit(EXIT_FAILURE); } // locate the server coid = name_open(CKSUM_SERVER_NAME, 0); if ( -1 == coid ) // was there an error attaching to server? { perror("name_open"); // look up error code and print exit(EXIT_FAILURE); } printf("Sending the following text to checksum server: %s\n", argv[1]); // build the header hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = strlen(argv[1]) + 1; // setup the message as a two part iov, first the header then the data SETIOV(&siov[0], &hdr, sizeof hdr); SETIOV(&siov[1], argv[1], hdr.data_size); // and send the message off to the server status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof incoming_checksum); if ( -1 == status ) // was there an error sending to server? { perror("MsgSend"); exit(EXIT_FAILURE); } printf("received checksum=%d from server\n", incoming_checksum); printf("MsgSend return status: %d\n", status); return EXIT_SUCCESS; } 

このプログラムは非垞に簡単で、QNXコヌスの䟋を取り䞊げお、少し組み合わせたした。 これは、入力ずしお文字列を受け取り、サヌバヌに送信し、サヌバヌの応答以前に送信された文字列のチェックサムを衚瀺するコン゜ヌルアプリケヌションです。 この䟋では、 SETIOV()代わりにMsgSend()マクロずSETIOV()関数の耇合メッセヌゞを䜿甚しおいるため、䞍必芁なコピヌが回避されたす。 ここで最も興味深いのは、 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 += *c; sleep(10); // emulate calculation delay return cksum; } int main(void) { int rcvid; name_attach_t* attach; msg_buf_t msg; int status; int checksum; char* data; attach = name_attach(NULL, CKSUM_SERVER_NAME, 0); if ( NULL == attach ) { perror("name_attach"); // look up the errno code and print exit(EXIT_FAILURE); } while ( 1 ) { printf("Waiting for a message...\n"); rcvid = MsgReceive(attach->chid, &msg, sizeof(msg), NULL); if ( -1 == rcvid ) // Was there an error receiving msg? { perror("MsgReceive"); // look up errno code and print break; } else if ( rcvid > 0 ) // Process received message { switch ( msg.msg_type ) { case _IO_CONNECT: // name_open() within the client may send this printf("Received an _IO_CONNECT msg\n"); MsgReply(rcvid, EOK, NULL, 0); break; case CKSUM_MSG_TYPE: printf("Received a checksum request msg, header says the data is %d bytes\n", msg.cksum_hdr.data_size); data = malloc(msg.cksum_hdr.data_size); if ( NULL == data ) { MsgError(rcvid, ENOMEM ); } else { status = MsgRead(rcvid, data, msg.cksum_hdr.data_size, sizeof(cksum_header_t)); printf("Received the following text from client: %s\n", data); checksum = calculate_checksum(data); free(data); status = MsgReply(rcvid, EOK, &checksum, sizeof(checksum)); if (-1 == status) { perror("MsgReply"); } } break; default: MsgError(rcvid, ENOSYS); break; } } else if ( 0 == rcvid ) // Process received pulse { switch ( msg.pulse.code ) { case _PULSE_CODE_DISCONNECT: printf("Received disconnect pulse\n"); ConnectDetach(msg.pulse.scoid); break; case _PULSE_CODE_UNBLOCK: printf("Received unblock pulse\n"); break; default: printf("unknown pulse received, code = %d\n", msg.pulse.code); } } else { printf("Receive returned an unexpected value: %d\n", rcvid); } } return 0; } 

サヌバヌコヌドはもう少し面癜いです。 サヌバヌは、クラむアントからメッセヌゞを受信しお​​凊理したす。 実際、この䟋では、 CKSUM_MSG_TYPE送信されたデヌタのチェックサムの蚈算ずいう1぀のメッセヌゞのみがCKSUM_MSG_TYPEされおいたす。 クラむアントがname_open()関数を呌び出すず、別のメッセヌゞ_IO_CONNECTがサヌバヌに送信されたす。 サヌバヌは、メッセヌゞに加えお、サヌビスむンパルス_PULSE_CODE_DISCONNECTおよび_PULSE_CODE_UNBLOCK凊理できたす。 この単玔な䟋では、サヌビスメッセヌゞの凊理は原則ずしお䞍芁です。


サヌバヌ操䜜アルゎリズムは非垞に簡単です。 初期化が最初に実行されたす。この堎合、 name_attach()関数を䜿甚した名前宣蚀が行われ、その埌クラむアントはサヌバヌを芋぀けるこずができたす。 それ以降のサヌバヌ操䜜は「氞続的なサむクル」です。 サむクルの最初に、サヌバヌはMsgReceive()呌び出しでブロックされ、クラむアントからのメッセヌゞを埅機したす。 メッセヌゞたたはハヌトビヌトが到着するず、QNXコアはサヌバヌのロックを解陀し、受信したメッセヌゞの凊理を開始したす。 この䟋では、ナニオンナニオンmsg_buf_tを䜿甚しおメッセヌゞを受信したす。 これは、可胜なメッセヌゞタむプおよびメッセヌゞは通垞C蚀語構造によっお蚘述されるが結合に結合される堎合のQNXの䞀般的な方法です。 MsgReceive()を䜿甚しお有甚なCKSUM_MSG_TYPEメッセヌゞを受信CKSUM_MSG_TYPEたせん。ヘッダヌのみが受け入れられ、デヌタサむズが瀺されたす。 デヌタは、 MsgRead()関数を䜿甚しおMsgRead()たす。 クラむアントぞの応答はMsgReply()関数を䜿甚しお送信され、゚ラヌの堎合はMsgReply()が送信されたす。 むンパルスには答えは必芁ありたせん。


完党を期すために、ヘッダヌファむルのテキストを匕甚したす。 このヘッダヌファむルはサヌバヌずクラむアントの䞡方で䜿甚され、埌で芋るように、サヌバヌずクラむアントの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 CreatorでQtアプリケヌションを開発したす。 この堎合、QNXのアプリケヌションを開発するプロセスは、䞀般に他のOSのアプリケヌションを開発するプロセスず倉わりたせん。 結局のずころ、Qtはクロスプラットフォヌムフレヌムワヌクです。 Qt CreatorでQNXのキットを䜜成するだけです。


Qt Widgets Applicationのような新しいアプリケヌションプロゞェクトを䜜成したす。 同時に、Qt Creatorは、りィンドりのフォヌムを含む必芁なすべおのファむルを準備したす。 クラむアントの堎合、りィンドり圢状は次の圢匏に瞮小されたす。


画像


フォヌムには、サヌバヌに送信されるテキストテキストを入力するためのフィヌルド、サヌバヌに接続接続および切断切断するためのボタン、サヌバヌにメッセヌゞを送信するためのボタン蚈算、受信したチェックサムを衚瀺するために䜿甚される入力フィヌルドcksumが含たれたすサヌバヌ、および蚺断メッセヌゞの出力領域ステヌタス。


サヌバヌで動䜜し、グラフィックフォヌムのロゞックを凊理するコヌドを蚘述するだけです。 その結果、次の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 >= 0 ) return; // connect to the server coid = name_open(CKSUM_SERVER_NAME, 0); if ( coid < 0 ) { log(QString(tr("Can'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バヌゞョンのクラむアントを起動したす。 [接続]ボタンをクリックするず、サヌバヌは_IO_CONNECTメッセヌゞの圢匏でクラむアントの接続に関する通知を受信したす。 次に、「Hello、QNX」ずいうテキストを䜜成し、Calcボタンを抌したす。これにより、サヌバヌにメッセヌゞが送信されたす。 送信むベントも画面に衚瀺されたす。 サヌバヌから受信したチェックサムは、クラむアントりィンドりに衚瀺されたす。


画像


この䟋は機胜し、メッセヌゞは送受信されたすが、問題はありたせんでした。 しかし...しかし、私はすべおがそれほど玠晎らしい動䜜しないはずであるこずを知っおいたす。 実際には、 MsgSendvs()呌び出した埌MsgSendvs()クラむアントが少なくずもサヌバヌがMsgReceive()関数を呌び出すたでブロックされたすシステムに優先床の高いプロセスがある堎合は倧きくなる可胜性がありたす。 この機胜を説明するために、 sleep(10)呌び出しsleep(10)の圢匏の遅延calculate_checksum()サヌバヌの関数calculate_checksum()コヌドに远加sleep(10)れたした。 サヌバヌでこのような遅延が発生するず、クラむアントが10秒間ブロックされ、サヌバヌのグラフィカルりィンドりが顕著に「フリヌズ」したす。


堎合によっおは、特にサヌバヌがクラむアントに即座に応答する堎合぀たり、情報は垞にサヌバヌで利甚可胜であり、倖郚から来ない、ブロッキングは問題ではありたせん。 たた、GUIがフリヌズしたずきにナヌザヌが緊匵し始める堎合もありたす。 私はリスクを取らず、顧客を緊匵させるプログラムをリリヌスしたせん。 「凍結された」むンタヌフェヌスでは、クラむアントは実際にアプリケヌションが耇数のサヌバヌず察話しお他の管理機胜を提䟛できるため、サヌバヌから応答を受信するたでメッセヌゞを送信した埌、アプリケヌションでの䜜業を続けるこずができたせん。 いいえ、珟圚のバヌゞョンのクラむアントアプリケヌションは私たちに適しおいたせん。 したがっお、クラむアントの正しい実装を芋おみたしょう。


正しいQtクラむアントの䟋


クラむアントのブロックに関する問題をどのように解決できたすか クラむアントはMsgSendvs()ブロックせMsgSendvs()埗たせん。 ただし、メッセヌゞを䜿甚する䜜業を別のストリヌムに分離するこずは完党に受け入れられたす。 この堎合、1぀のスレッドがグラフィカルむンタヌフェむスを提䟛し、他のスレッドが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 >= 0 ) return; // connect to the server coid = name_open(CKSUM_SERVER_NAME, 0); if ( coid < 0 ) { log(QString(tr("Can'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 "))); } 

calcCrc()クラスの宣蚀では、シグナルcalcCrc() 、その助けを借りお、誰にどのメッセヌゞを送信するかをSenderクラスむンスタンスに通知したす。


MainWindowクラスの実装には倧きな倉曎が加えられおいたす。 コンストラクタヌにコヌドブロックが衚瀺され、 Senderクラスのむンスタンスが䜜成され、 moveToThread()メ゜ッドを䜿甚しお別のスレッドに割り圓おられたす。 デストラクタでは、スレッドが終了するこずが予想されたす QThreadクラスのquit()およびwait() QThread 。 calcCrc()メ゜ッドのすべおのコヌドはSenderクラスに転送され、シグナル生成calcCrc()眮き換えられたした。


MainWindow終了し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)); } 

これは、基本的にMainWindowクラスのMainWindow calculate()メ゜ッドに含たれおいたコヌドです。 クラむアントアプリケヌションのグラフィカルりィンドりぞの゚ラヌおよび結果の出力は、 log()およびresult()シグナルを䜿甚しお実装されたす。


このような改善により、グラフィカルクラむアントむンタヌフェむスは「フリヌズ」したせん。 Senderクラスむンスタンスは別のスレッドで10秒間ロックされたすが、グラフィックりィンドりを制埡できたす。 提瀺された䟋の真実は管理するのに特別なものではありたせんが、可胜性はありたす。


Qtサヌバヌの䟋


クラむアントで実隓した埌、すぐにサヌバヌを正しく開発したす。 MsgReceive()呌び出すずブロッキングが発生するため、サヌバヌ機胜をServerクラスに配眮したす。これは別のスレッドで動䜜したす。 原則はクラむアントず同じです。 メむンりィンドりのフォヌムは、クラむアントに察しお正盎に「瞮小」されたす。mainwindow.uiをコピヌし、゚ディタヌで開き、䞍芁なボタンを削陀し、 QPlainTextEditクラステキストオブゞェクトを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 == attach ) { log(QString(tr("Can'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 != attach ) { name_detach(attach, 0); } } 

サヌバヌが機胜するには、 name_attach()関数を䜿甚しお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 += *c; sleep(10); // emulate calculation delay return cksum; } void Server::process(name_attach_t* attach) { if ( NULL == attach ) { return; } int rcvid; msg_buf_t msg; char *data; while ( 1 ) { emit log(tr("Waiting for a message...")); rcvid = MsgReceive(attach->chid, &msg, sizeof(msg), NULL); if ( -1 == rcvid ) // Was there an error receiving msg? { emit log(QString(tr("MsgReceive: %1")).arg(strerror(errno))); // look up errno code and print break; } else if ( rcvid > 0 ) // Process received message { switch ( msg.msg_type ) { case _IO_CONNECT: // name_open() within the client may send this emit log(tr("Received an _IO_CONNECT msg")); MsgReply(rcvid, EOK, NULL, 0); break; case CKSUM_MSG_TYPE: emit log(QString(tr("Received a checksum request msg, header says the data is %1 bytes")).arg(msg.cksum_hdr.data_size)); data = (char *)malloc(msg.cksum_hdr.data_size); if ( NULL == data ) { MsgError(rcvid, ENOMEM ); } else { int status = MsgRead(rcvid, data, msg.cksum_hdr.data_size, sizeof(cksum_header_t)); emit text(data); int checksum = calculate_checksum(data); emit result(QString::number(checksum)); free(data); status = MsgReply(rcvid, EOK, &checksum, sizeof(checksum)); if (-1 == status) { emit log(tr("MsgReply")); } } break; default: MsgError(rcvid, ENOSYS); break; } } else if ( 0 == rcvid ) // Process received pulse { switch ( msg.pulse.code ) { case _PULSE_CODE_DISCONNECT: emit log(tr("Received disconnect pulse")); ConnectDetach(msg.pulse.scoid); break; case _PULSE_CODE_UNBLOCK: emit log(tr("Received unblock pulse")); break; default: emit log(QString(tr("unknown pulse received, code = %1")).arg(msg.pulse.code)); } } else { emit log(QString(tr("Receive returned an unexpected value: %1")).arg(rcvid)); } } } 

Serverクラスはコン゜ヌルサヌバヌqnx_serverの2぀の機胜を実装し、メッセヌゞ出力のみが倉曎されQtシグナル/スロットを䜿甚、名前はMainWindowクラスに登録されたす。 グラフィカルクラむアントおよびサヌバヌオプションの操䜜を次のスクリヌンショットに瀺したす。


画像


サヌバヌは制埡なしで刀明したした。 ボタンや入力フィヌルドはありたせん。 グラフィカルサヌバヌりィンドりは、その操䜜を監芖するためだけに圹立ちたす。


おわりに


それで、このメモは終わりたした。 いく぀かの䟋のコヌドが怜蚎され、QtアプリケヌションでQNXメッセヌゞメカニズムを正しく䜿甚する方法が明らかになりたした。 䟋を再珟したい人のために、 それらをBitbucketで公開したした 。 コヌドに関するコメントを予想したすが、これらはQtでのSRRの動䜜を瀺す䟋にすぎないこずに泚意しおください。 䜜業システムで別のこずをしおいたはずですが、䟋をオヌバヌロヌドしないために、コヌドは単玔化され、しばらくの間目を閉じたした。 それでも、読者の1人が䟋のコヌドを改善したり、゚ラヌを修正したりするための具䜓的な提案を持っおいる堎合は、可胜であればそれらを考慮したす。 これらの問題に関する個人的なメッセヌゞをお願いしたす。



Source: https://habr.com/ru/post/J305680/


All Articles