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人が例のコードを改善したり、エラーを修正したりするための具体的な提案を持っている場合は、可能であればそれらを考慮します。 これらの問題に関する個人的なメッセージをお願いします。








All Articles