📜 ⬆️ ⬇️

Real-time Web application for simple devices



Greetings Habr! Often it is necessary to engage in the development of software for control devices. As a rule, these are industrial computers with relatively low hardware and computing resources managed and monitored by client software. The client part in the form of a separate application has drawbacks: when updating the software of the device itself, it is necessary to update all clients, and the client must be cross-platform for good. There was an idea to make a client application in the form of a web, and preferably as quickly as possible and not resource-intensive. I hope these studies will help those who thought about this.

Formulation of the problem


And so, in the presence of a small computer in terms of resources, we will call it a computer (server) that controls the actuators, collects data, and solves the necessary and important tasks. And they can be several networked. The computer software is low-level and is written in C ++ and works under the operating system (in my case, Linux). And you need to manage and monitor all this from the outside through a browser (client).

And another important point - the server must be able to independently notify the client about events, and not just respond to requests.

Note: I do not aim to describe the features of the application and the capabilities of the products used - this is a separate topic. I want to tell what and what was used and what the result was
')

Start


There can be several calculators and they interact with each other over the network - here the framework for the remote call procedure of the Ice was found , namely its version for the Internet-things IceE. From the source code for the desired platform, we collect libraries, read the documentation and the network exchange at the function call level works! But as it turned out, IceE allows you to work with javascript clients and works through WebSocket. Well, the solution is found - it remains to try! And not only javascript, but also there is something else.

IceE at a glance


First you need to describe the interaction we want to get. For this we use the specialized language slice. Here is an example of what we will try:

#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); }; }; 

Based on this code, by means of Ice, C ++ classes for the server and javascript code for the web application are generated.

Server


The main thing is to implement the remoting class - we inherit it from the previously generated class.

 //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; }; 

It remains only to run it all. Below is a stream function that performs the necessary configuration and launch of the Ice system.

 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; } } } } 

That's the whole server. Easier to imagine.

Customer


To simplify the development of a web application, we use bootstrap - it contains predefined styles, components, linkers and a lot more. For data binding and the implementation of the MVC model, apply AngularJS . And I want to draw graphics for clarity of transferring arrays of data - flotr2 will help us . We skip the html text - besides the placement of the components and the data binding, there is no interesting information. Now the application's next javascript file:

 "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); }); }); }); 

Total


Now we launch the server application and open our html page with the browser and see:


Exchange is coming! Data is being transferred!

And so what was used:


As a result, using the specified set of components, it is possible to quickly implement a web application to monitor and control our server, without complicating the server software and performing interaction with the client directly from the code of the main application.

I also considered the option of using Wt . Also a very interesting thing. But it seems to me that in the solution considered in this article there is more flexibility in the implementation of the client software itself - we can use any means we need for web development. Yes, and Ice has already been used for network sharing - let it work here.

I hope these surveys will help you in solving the tasks!

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


All Articles