シンプルなデバイス用のリアルタイムWebアプリケーション





こんにちは、Habr! 多くの場合、制御および管理デバイス用のソフトウェアを開発する必要があります。 原則として、これらは比較的低いハードウェアおよびコンピューティングリソースを備えた産業用コンピューターであり、クライアントソフトウェアによって管理および監視されます。 別個のアプリケーションの形式のクライアント部分には欠点があります。デバイス自体のソフトウェアを更新する場合、すべてのクライアントを更新する必要があり、クライアントはクロスプラットフォームでなければなりません。 クライアントアプリケーションをWebの形式で作成するというアイデアがありました。できればできるだけ早く、リソースを集中的に使用しないでください。 この研究が、これについて考えている人々の助けになることを願っています。



問題の声明



そのため、リソースの観点から小さなコンピューターが存在する場合、アクチュエーターを制御し、データを収集し、必要かつ重要なタスクを解決するコンピューター(サーバー)と呼びます。 そして、それらは複数のネットワークに接続できます。 計算機ソフトウェアは低レベルで、C ++で記述されており、OS(私の場合はLinux)で動作します。 そして、ブラウザ(クライアント)を介して外部からこれらすべてを管理および監視する必要があります。







もう1つの重要なポイント-サーバーは、リクエストに応答するだけでなく、イベントについてクライアントに独立して通知できる必要があります。



注:アプリケーションの機能と使用する製品の機能については説明しません。これは別のトピックです。 何が何のために適用されたのか、結果は何だったのかを伝えたい



開始する



複数の計算機が存在し、ネットワークを介して相互にやり取りすることができます。ここでは、 Iceプロシージャを呼び出すためのリモートフレームワークのアプリケーション、つまりIceEインターネット関連のバージョンを見つけました。 目的のプラットフォームのソースコードからライブラリを収集し、ドキュメントを読むと、関数呼び出しレベルでのネットワーク交換が機能するようになりました。 しかし、判明したように、IceEではjavascriptクライアントを操作でき、WebSocketを介して操作できます。 さて、解決策が見つかりました-試してみてください! そして、javascriptだけでなく、まだ何かがあります。







IceEについて簡単に



まず、取得したい相互作用を説明する必要があります。 このために、特殊な言語スライスを使用します。 ここに私たちが試みることの例があります:



#pragma once #include <Ice/Identity.ice> //  ++  namespace module Remote { //    -  ++   vector<double> sequence<double> Measurement; // interface -       -    () interface CallbackReceiver { //       -   progress-bar void Callback(int num); //       -    void SendData(Measurement m); }; //        interface CallbackSender { //        void AddClient(Ice::Identity ident); }; };
      
      





このコードに基づいて、Iceツールはサーバー用のC ++クラスとWebアプリケーション用のjavascriptコードを生成します。



サーバー



主なことは、リモートインタラクションのクラスを実装することです。以前に生成されたクラスから継承します。



 //Remote::CallbackSender  Ice class ImplCallback: public Remote::CallbackSender { public: ImplCallback(const Ice::CommunicatorPtr& c) : communicator { c } { /*    */ th = std::thread([this]() { int count =0; constexpr int sizeMeasurement=30; /*typedef ::std::vector< ::Ice::Double> Measurement; -   */ Measurement measurement(sizeMeasurement); std::random_device r; std::default_random_engine e1(r()); std::uniform_real_distribution<double> uniform_dist(-10, 10); while(true) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lk(mut); auto it = clients.begin(); auto itend=clients.end(); for(;it!=itend;) { try { /*  -    progress-bar*/ (*it)->Callback(++count); for(auto& m:measurement) m=uniform_dist(e1); /*  -   */ (*it)->SendData(measurement); ++it; } catch(const std::exception& ex) { /*  - !*/ clients.erase(it++); } } } }); th.detach(); } /*     */ virtual void AddClient(const Ice::Identity& ident, const Ice::Current& current = ::Ice::Current()) override { cout << "adding client `" << communicator->identityToString(ident) << "'" << endl; std::lock_guard<std::mutex> lk(mut); /*        .   */ CallbackReceiverPrx c = CallbackReceiverPrx::uncheckedCast(current.con->createProxy(ident)); clients.insert(c); } private: /*    */ std::set<Remote::CallbackReceiverPrx> clients; Ice::CommunicatorPtr communicator; std::mutex mut; std::thread th; };
      
      





すべてを実行するだけです。 以下は、必要な設定を実行してIceシステムを起動するストリームの機能です。



 void ServerFun() { Ice::CommunicatorPtr ic; try { /* Ice*/ ic = Ice::initialize(); /*  WebSocket   20002*/ /*      -    */ Ice::ObjectAdapterPtr adapter2 = ic->createObjectAdapterWithEndpoints("Callback.Server", "ws -p 20002"); /*    ImplCallback     sender*/ adapter2->add(new ImplCallback(ic), ic->stringToIdentity("sender")); /*    - !*/ adapter2->activate(); while (true) { std::this_thread::sleep_for(std::chrono::seconds(1)); } ic->shutdown(); ic->destroy(); } catch (const std::exception& ex) { cout << ex.what() << endl; if (ic) { try { ic->destroy(); } catch (const Ice::Exception& ex2) { cout << ex2 << endl; } } } }
      
      





これがサーバー全体です。 想像しやすいです。



お客様



Webアプリケーションの開発を簡素化するために、 ブートストラップを使用します。これには、定義済みのスタイル、コンポーネント、リンカーなどが含まれています。 MVCモデルのデータバインディングと実装には、 AngularJSが適用可能です。 そして、データ配列の転送を説明するためにグラフィックスを描きたいです-flotr2が助けになります。 HTMLテキストはスキップします-コンポーネントの配置とデータバインディングに加えて、そこには興味深い情報はありません。次に、JavaScriptアプリケーションファイルが次の行にあります。



 "use strict" var app = angular.module('webApp', []); // angular    app.controller('webController', function myController($scope) { //   1- 2- 3- $scope.mode = 1; //progress-bar  0  100 $scope.valuenow = 0; //    -  radio html  $scope.mode1 = function() { $scope.mode = 1; } var communicator = Ice.initialize(); //      var CallbackReceiverI = Ice.Class(Remote.CallbackReceiver, { //  progress-bar Callback : function(num, current) { $scope.valuenow = num % 100; $scope.$apply(); }, //     SendData: function(measurement){ var data, graph; var container = document.getElementById('container'); data = []; for (var i = 0; i <measurement.length; ++i) { data.push([ i, measurement[i] ]); } //     flotr2   . if ($scope.mode == 1) { graph = Flotr.draw(container, [ data ], { colors : [ '#C0D800' ], yaxis : { max : 12, min : -12 } }); } //else    ... } }); var proxy2 = communicator.stringToProxy("sender:ws -h localhost -p 20002"); //        AddClient Remote.CallbackSenderPrx.checkedCast(proxy2).then(function(pr2) { communicator.createObjectAdapter("").then(function(adapter) { var r = adapter.addWithUUID(new CallbackReceiverI()); proxy2.ice_getCachedConnection().setAdapter(adapter); pr2.AddClient(r.ice_getIdentity()); //     Heartbeat proxy2.ice_getCachedConnection().setACM(undefined, undefined, Ice.ACMHeartbeat.HeartbeatAlways); }); }); });
      
      





まとめ



サーバーアプリケーションを起動し、ブラウザでhtmlページを開いて、以下を確認します。









交換が来ています! データが送信されています!



そして、使用されたもの:





その結果、指定されたコンポーネントのセットを使用して、サーバーソフトウェアを特に複雑にし、メインアプリケーションコードから直接クライアントと対話することなく、サーバーを制御および管理するWebアプリケーションをすばやく実装できます。



また、 Wtの適用も検討しました。 また、非常に興味深いことです。 しかし、この記事では、ソリューションはクライアントソフトウェア自体の実装においてより柔軟であるように思われます。Web開発に必要な任意のツールを使用できます。 はい。Iceはすでにネットワーク交換に使用されています-ここでも機能します。



この研究があなたの課題解決に役立つことを願っています!



All Articles