ジャバーによる監視システム





多くの場合、特定のイベントについて即座に通知する機能が必要です。 システム管理者は、サービスとサーバーの不具合、工場の技術者-技術プロセスの不具合と逸脱、緊急対応サービス-インシデントについて、できるだけ早く発見する必要があります。 通知する最も明白な方法はSMSによるものです。 SMS経由の通知には、特定のグループにメッセージを送信する特別なインターネットサービスがあります。 GSMモデムを使用して、自分でメールを保存して実行できます。 ただし、この方法にはいくつかの欠点があります。シリアルポートを操作し、それを介してモデムでコマンドを順番に処理できる必要があります。 ロシア語でのメッセージ送信はそれほど簡単ではありません。 多数の受信者への送信速度が十分に速くない場合があります。 配送管理を確保するのが難しい。 迷惑メールを考慮した場合、モバイルオペレーターがSIMカードをブロックしないという保証はありません。 一般に、郵便サービスは少なくともいくつかの保証を提供しますが、いくらかお金がかかります。



インターネットを使用してメッセージを送信する場合、送信コストを大幅に節約できます。 メールメッセージは簡単に作成して送信できます。 ただし、電子メールは運用チャネルではありません。 すべての電子メールクライアントが新しいメッセージを受信したときに通知するわけではないため、受信者がメッセージを読むまでの時間はわかりません。 モバイルデバイスの電子メールクライアントはまったく設定されておらず、常に設定されているわけでもありません。



まったく異なる問題は、インスタントメッセージングシステム(ICQ、XMPP)です。 XMPPは開いているため優先されます。 また、これは本格的なネットワークプロトコルであるという事実により、次のオプションが「そのまま」利用可能です。



必要に応じて、リストを継続できます。



このアプローチの実装例として、技術機器のエラーを通知するプログラムが開発されました。 機器ラックは、メッセージ(およびエラーメッセージを含む)をデータベースに書き込む一種のプログラムです。 データベースの形式はParadoxであり、データエンコーディングはWin-1251です。 コンソールアプリケーションを優先してグラフィカルインターフェイスを放棄し、テキストファイルにパラメータを設定することが決定されました。 ソリューションツールはQTです。



実装された機能:多くの技術的なインストールからエラーを収集し、jabberを介してメッセージを送信し、jabberを介した一般的なチャット。



アーキテクチャと相互作用のメカニズム



最終的な機器に配置されるプログラムは、条件付きで「クライアント」と呼ばれます。 クライアントからデータを受信するプログラムは「サーバー」です。 データ交換は、ブロードキャストUDPを介して実行されます。 受け入れられたデータに従って、サーバーは連絡先リスト全体の情報をjabberアカウントに送信します。



お客様



プログラムパラメータ


パラメータはjsonに保存することにしました。 作業には、4番目のQTが使用されますが、jsonではまだ動作しないため、解析はQTのサードパーティライブラリ(qt-json)を使用して行われます。



Database.jsonクライアント設定ファイル

{ "LogFileName":"c://manlog.log", "ConnectionString":"Driver={Microsoft Paradox Driver (*.db )};DriverID=282;FIL=Paradox 4.x;DBQ=c:\\;ReadOnly=1", "CNCName":"CNC_01", "RefreshPeriod":"2000" }
      
      







パラメータの読み取り:

  QFile configFile("./config/database.json"); configFile.open(QIODevice::ReadOnly | QIODevice::Text); QTextStream dbFileIn(&configFile); QString jsonData = dbFileIn.readAll(); configFile.close(); bool ok = false; QVariantMap result = QtJson::parse(jsonData, ok).toMap(); if(ok) { foreach(QVariant key, result.keys()) { if(key.toString().toLower()=="connectionstring") connecionString = result.value(key.toString()).toString(); if(key.toString().toLower()=="logfilename") logFileName = result.value(key.toString()).toString(); if(key.toString().toLower()=="refreshperiod") refreshPeriod = result.value(key.toString()).toString().toInt(); if(key.toString().toLower()=="cncname") cncName = result.value(key.toString()).toString(); } }
      
      







PARADOXデータベースへの接続


QTからデータベースにアクセスするには、 QODBCがあります 。 PARADOXにアクセスするための接続文字列:

 "Driver={Microsoft Paradox Driver (*.db )};DriverID=282;FIL=Paradox 4.x;DBQ=c:\\;ReadOnly=1"
      
      





ここで、DBQはデータベースへのパスです。

重要な点:「* .db」の後の「)」の前の接続行にはスペースがあります!



システムソースを介したデータソースの設定は長くて不便なため、接続文字列をプログラムの隣のファイルに保存することにしました。 とにかく、すべての設定が1か所にあると便利です。



ラック上のプログラムは、ログをManLogテーブルに書き込みます。

フィールド名 種類 予定
データ 日付 ログのエントリの日付
時間 日時 ロギング時間
メス バルチャー メッセージ


列のデータ型では、間違える可能性があります。 また、QTも誤解されているため、SQLクエリをコンパイルするときに、時間を文字列に変換する必要がありました。toDateTime()のようなフィールドを取得できなかったためです。



 SELECT data, format(ManLog.Time,'hh:mm:ss'), mes from ManLog
      
      







データベースへの接続に関する問題はそこで終わりませんでした。 データベースファイルを表示すると、そこにあるデータはwin-1251でエンコードされています。 コーデック、エンコーディング変換、BDEでのエンコーディングの設定、レジストリ設定の操作の経験がありました。 これらはすべて、OSごとに異なる方法で機能します。 どのOSがラック上にあるかは不明であり、産業機器のOSに干渉する価値はありません。 したがって、Mesフィールドからデータをバイナリ形式で取得することが決定されました。

  QSqlQuery query; query.exec("SELECT data, format(ManLog.Time,'hh:mm:ss'), mes from ManLog"); ... QByteArray msg = query.value(2).toByteArray(); // !
      
      





データはデータベースから読み取られたため、エラーコードで検証する必要があります。 ただし、データベース内のエンコードでは、バイナリ形式のエラーコードも必要です。 エラーコードをjsonに登録できますが、jsonファイルを読み取ると、エンコードの問題が再び発生します。 したがって、 説明する時間はありません 。iniファイルを作成し、メモ帳でそこにWindowsエラーをドライブします。



エラーリストファイル


errorlist.ini-エラーのリストを含むファイル

     -  ..
      
      





バイナリ配列のリスト内のデータを読み取ります。

  QFile fileIni("./config/errorlist.ini"); fileIni.open(QFile::ReadOnly); QByteArray errorRaw = fileIni.readAll(); fileIni.close(); errorRaw = errorRaw.replace('\n', ""); QList<QByteArray> errorAllList = errorRaw.split('\r'); for(int i=0;i<errorAllList.count();i++) { if(errorAllList.at(i).count()>0) { errorList << errorAllList.at(i); } }
      
      







次に、ネットワークを介して再度送信されないように、すでに読み取られた行のリストを開始します。 refreshPeriod周期でタイマーを開始し、データの再要求を行います。 新しいデータがある場合-UDPを介してネットワーク経由で送信します。



ネットワーク経由でデータを送信する


QDataStreamを使用してデータをシリアル化することが決定されました。 マシン名、日付、時刻、符号が送信されます-行がエラーであるかどうか、およびメッセージ自体はバイナリ形式です。



行にエラーがあるかどうかを判別

  QString errText="ok"; foreach(QByteArray err, errorList) { if(msg.contains(err)) { errText = "ERROR"; } }
      
      







シリアル化して送信:

  QByteArray datagram; QDataStream out( &datagram, QIODevice::ReadWrite ); out.setVersion(QDataStream::Qt_4_0); out << cncName; out << query->value(0).toDate(); //date out << logTime;//time out << errText; out << msg;//bytearray udpSocket->writeDatagram(datagram, QHostAddress::Broadcast, 45000);
      
      







サーバー側





UDPメッセージの受信とxmppによる送信の2つの部分で構成されています。



UDPパケットを受信する


ポートを聞く:

  udpSocket = new QUdpSocket(this); connect(udpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); udpSocket->bind(45000);
      
      





受信したメッセージを解析し、レコードに「エラー」の兆候がある場合は信号を発信します。

 void CncReceiver::onReadyRead() { QByteArray buffer; ... buffer.resize(udpSocket->pendingDatagramSize()); udpSocket->readDatagram(buffer.data(), buffer.size()); QDataStream in( &buffer, QIODevice::ReadWrite ); in.setVersion(QDataStream::Qt_4_0); in >> cncName; in >> date; in >> time; in >> errText; in >> msg; ... if(errText!="ok") emit needSendToAll(msg, cncName, date, time); }
      
      





この信号は、メッセージを送信できるクラスによって受信されます。 私たちの場合、Jabberで。 類推により、メールまたはSMSで送信できます。



jabberにメッセージを送信する


メッセージを送信するために、 この素晴らしいライブラリ(qxmpp)からQXmppClientから派生したクラスを作成します。



使用するメーリングリストを保存するには

 QList<QString> sendList;
      
      





名簿ジャバーアカウントからのデータを入力します(アカウント名があります)。



そして、 needSendToAllシグナルをキャッチするスロットは、 sendListから全員にメッセージを送信します。

 void JabberClient::sendToAll(QByteArray msg, QString cncName, QDate errDate, QString errTime) { QString messageToUser; QTextCodec *codec = QTextCodec::codecForName("Windows-1251"); messageToUser = codec->toUnicode(msg); foreach(QString userName, sendList) { this->sendMessage(userName, "CNC_NAME: "+ cncName + "\n" + errDate.toString("yyyy-MM-dd ")+ errTime + "\nMesage:\n" + messageToUser); } }
      
      







Jabberチャットの実装


アカウントが受信したすべてのメッセージは、送信者の名前とともに連絡先リスト全体に送信されます。 したがって、一般的なチャットが取得されます。 ディスカッションに便利かもしれません。



非常に簡単に追加されます。 これを行うには、JabberClientコンストラクターで、別の信号(メッセージ受信信号)の処理を追加します。

  connect(this, SIGNAL(messageReceived(QXmppMessage)), SLOT(onMessageReceived(QXmppMessage)));
      
      





そして、メッセージ受信スロットで処理します:

 void JabberClient::onMessageReceived(QXmppMessage msg) { if(msg.body().length()>0) { qDebug() << nowStr() << "onMessageReceived" << msg.from() << msg.body(); foreach(QString userName, sendList) { this->sendMessage(userName, msg.from() + ">\n" + msg.body()); } } }
      
      





入力プロセス中に呼び出しが行われているが、メッセージの長さが0であるため、メッセージの長さを確認しています。



まとめ



このプロジェクトは、システムロケールを設定および設定せずに、特定のメッセージエンコーディングでQTからデータベースに接続する方法の例です。 また、QTでqxmppライブラリを使用して、jabberにメッセージを送信する方法も示します。



例のソースコード:

code.google.com/p/cnc-error-monitor



PS。 記事は補足および更新されます。

UPD:jsonからのパラメーターの解析を修正し、jabberを介してチャットを実装しました

UPD2:奇妙な発言:クライアントパーツをQT 4バージョンでアセンブルする場合、データベースのbyteArrayからのデータは、ベースエンコーディングで必要に応じて読み取られます。 また、QT5で収集すると、そこにあるデータが破損します(パラメーターが役に立たない)。



All Articles