Wiki

Clone wiki

mTango / mTangoUI

Welcome to mTangoUI page

On this page you will find mTangoUI user manual and its technical background. There is also an information about jsTangORB on top of which mTangoUI is implemented.

jsTangORB

jsTangORB is a javascript library that provides a high level API similar to TangORB.jar. It uses JsonP to access mTangoREST API + provides several optimizations and caches.

User guide

User guide is located on the mTango REST API page

jsTangORB API

jsTangORB defines the following javascript objects:

NOTE: here we only list important attributes of the entities. For more detailed information on the API please refer to jsTangORB API reference

/**
 * Model
 *
 *
 * @type {mtango.DeviceAttribute}
 */
mtango.DeviceAttribute = MVC.Model.extend('DeviceAttribute',
    /* @Static */
    {
        attributes: {
            name : "string",
            label: "string",
            description: "string",
            isReadOnly: "boolean",
            dataFormat: "string",
            dataType: "string",
            unit    : "string",
            displayUnit: "string",
            maxDimX: "int",
            maxDimY: "int",
            maxValue: "string",
            minValue: "string"
        },
        //...

More details on DeviceAttribute's interface can be found here

mtango.DeviceCommand = MVC.Model.extend('DeviceCommand',
    /* @Static */
    {
        attributes:{
            name: 'string',
            inputType   : 'string',
            inputDescription : 'string',
            outputType  : 'string',
            outputDescription : 'string'
        },
        //...
}

More details on DeviceCommand's interface can be found here

mtango.DeviceProxy = MVC.Model.extend('DeviceProxy',
    /* @Static */
    {
        //this constant defines a delay between request sent sequentially,
        // i.e. how may ms DeviceProxy will wait after getting a response for a request
        // before sending next request to the same address (this is used in polling to avoid data burst)
        kDeviceProxyDelay:200,//ms
        //how long a response remains valid. For instance we have two DeviceAttributes polling the same attribute
        // in the application. If 1st one is already got the value when 2nd requests within this period,
        // 2nd will immediately got the value from the 1st response
        kDeviceProxyRequestValidTime:200,//ms
        //how long wait for the server to response before throwing "No response from the server"
        error_timeout: 5000, // 5s
        //...
    }, {
        /**
         * Subscribes this device proxy to a specified event
         *
         * @param {string} attr -- Tango device attribute name
         * @param {mtango.EventTypeEnum} evt -- event type: [CHANGE, PERIODIC, USER, ARCHIVE]
         * @param {Function} handler -- onSuccess handler
         * @param {int} timeout -- this specifies how long DeviceProxy will wait for the server, otherwise it gets the old value
         *                                 and resend the request. See below in the Technical background section
         * @return {int} subscription's id
         * @asynchronous
         */
        subscribe:function(attr, evt, handler, timeout){
        //...
}

More details on DeviceProxy's interface can be found here

and one more important entity that is heavily used in the client code:

mtango.Response = MVC.Model.extend('Response',
    /* @Static@ */
    {
        attributes:{
            src:'URI',
            argout:'*',
            quality:'string',
            timestamp:'long'
        },
        //...

where quality is one of the enum values:

mtango.AttrQualityEnum = Object.freeze({
    VALID:'VALID',
    INVALID:'INVALID',
    ALARM:'ALARM',
    CHANGING:'CHANGING',
    WARNING:'WARNING'
});

All these entities extend MVC.Model from JavaScriptMVC-1.5.x. Please refer to its API reference for more information. But for the most cases this fact does not affect the client development in any way.

Technical background

jsTangORB communicates with the server via JsonP.

To reduce the load onto the server several optimizations and caches are implemented in jsTangORB. First of all all requests to the same attribute within certain period of time (DeviceProxy.kDeviceProxyRequestValidTime) are aggregated into a single network call. In the future releases writing attributes can also be aggregated like writing only the latest value. Plus there is a delay defined for the subsequent requests (DeviceProxy.kDeviceProxyDelay), i.e. single attribute won't be polled more frequently than this value.

Events are implemented following the Comet model. This means that when user subscribes to an event from the remote tango device mtangoREST.server holds this request until it gets a notification from the remote tango device. DeviceProxy.subscribe method defines a timeout for the server to wait for a notification. This means that if there is no notification has happened during this timeout mTangoREST.server releases the client request and returns the actual value. This behaviour is best illustrated by the sequence diagram in the figure below:

Event sequence diagram

Such strategy allows the application to stay responsive in the case when all available TCP connections are already utilized (but still this is very bad situation and developer must avoid this by reducing event subscriptions). In the future release this can be workarounded by multiplexing all event subscriptions into a single network call.

mTangoUI

mTangoUI is a JavaScriptMVC application with three major extensions:

  1. jsTangORB engine
  2. mTangoUI engine
  3. mtango command line tools

The first one is described above + there is a user guide for it here.

The following sections describe mTangoUI engine and mtango command line tools.

But before jumping to them please read this piece of information about JavaScriptMVC:

The JavaScriptMVC used in the mTangoSDK is a derived version of JavaScriptMVC-1.5.5. The main reason why we took the derived version is that it is written in pure JavaScript (controversially later JavaScriptMVC versions were implemented on top of jQuery). Including jQuery in every project is not very convenient. The other concern of ours is that JavaScriptMVC-1.5.5 is much more balanced in terms of implemented features and learning curves for our projects.

JavaScriptMVC-1.5.x has the following folder structure:

d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 apps
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 controllers
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 docs
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 engines
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 jmvc
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 libs
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 models
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 pages
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 resources
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 stylesheets
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 test
d---------+ 1 khokhria Domain Users     0 Mar 10 11:11 views
----------+ 1 khokhria Domain Users   749 Mar  9 16:32 changelog.txt
----------+ 1 khokhria Domain Users   863 Mar  9 16:32 js
----------+ 1 khokhria Domain Users  1101 Mar  9 16:32 js.bat
----------+ 1 khokhria Domain Users   163 Mar  9 16:32 readme.txt

Most of them are just to separate js files, i.e. controllers in controllers, models in models etc. This separation is supported by JavaScriptMVC kernel. The important one folder is jmvc it contains JavaScriptMVC kernel. There is also two cli utilities files (js.bat and js). This utility executes jmvc scripts, for instance js jmvc/generate/controller <name> <app_name> takes file controller which is js file even though the extension is ommited in jmvc/generate folder and executes it within Rhino environment. One can list all available commands by executing js -? on Windows or ./js -? on Linux/Mac (NOTE: ./). Please note that mtango commands are missing until #80.

So developing an application with mTangoUI is basically developing a JavaScriptMVC application (later jmvc app). But hopefully mTangoUI engine and mtango cli utils [jsTangORB used indirectly via mTangoUI engine] make this process much pleasant. Before diving into this amazing process please read the following sections - they describe mTangoUI engine and mtango cli utils.

mTangoUI engine

mTangoUI engine provides user with convenient abstractions to develop UI applications.

Page

There is a page abstraction so developer can easily add/remove pages via cli (see in the mtango cli utils section) to the application.

Every page consists of three views: header.ejs, content.ejs and footer.ejs placed into pages/{app_name}/{page_name} folder + page's model models/{app_name}/{page_name}_page.js.

Normally developer alters only content.ejs where page content is defined.

The only method developer should use of the Page is load(data) where data - is a javascript object developer wants to pass into page's ejs files.

There is a number of predefined UI components in mTangoUI to define page's content:

DeviceAttributeUI

DeviceAttributeUI can be created without any programmatic efforts by placing mtango:attr tag on the page.

This tag is from mtango namespace and is an UI component for tango device attributes. It has the following specification:

<mtango:attr src="aka sys/tg_test/1/Double_scalar|required"
             id="optional"
             view="[fld_text_ro,fld_text_rw,fld_list_ro,fld_list_rw,fld_empty, any custom ejs view specified by a full path aka views/{app_name}/{my_custom_fld}.ejs]|required"
             "[read,poll,change,periodic,user,archive]"="rate in ms aka 3000|required"
             enhance="true|false|optional">
</mtango:attr>

where [src] - is a full tango name of the attribute (but without tango-host); [id] - is an optional attribute which overrides default id; [view] - defines how this attribute will look like on the page; [read,poll,change,periodic,user,archive] - defines acquisition method, on the right hand side developer defines acquisition method update rate in ms, e.g. poll=3000 says "poll this attribute every 3s"; [enhance] - defines whether .enhanceWithin() will be called on this element after every update.

enhance=true considered to be a very bad practice because of its performance. Especially for frequently polled attributes. Below is the comparison between different methods of injecting new elements into HTML's DOM (that what is happened every update):

enhanceWithin performance

original

Here innerHTML with Enhanced is what happened when enhance=true and String -- otherwise.

A good practice is to pre-render jQuery mobile elements, see, for example, popup widget's Pre-rendered markup. All mTangoUI components are pre-rendered (except Page).

There are several predefined views, otherwise developer may provide its own view by specifying the full path to it, e.g. views/my_app/my_pretty_double.ejs.

Below is the list of all predefined in 1.0.1 views and their outlook.

fld_text_ro
<mtango:attr src="sys/tg_test/1/double_scalar"
     view="fld_text_ro"
     poll=3000>
</mtango:attr>

fld_text_ro

fld_text_rw
<mtango:attr src="sys/tg_test/1/double_scalar_w"
     view="fld_text_rw"
     read>
</mtango:attr>

fld_text_rw

fld_list_ro
<mtango:attr src="sys/tg_test/1/double_spectrum"
     view="fld_list_ro"
     read>
</mtango:attr>

fld_list_ro

fld_list_rw
<mtango:attr src="sys/tg_test/1/double_spectrum_w"
     view="fld_list_rw"
     read>
</mtango:attr>

fld_list_rw

fld_empty
<mtango:attr src="sys/tg_test/1/State"
     view="fld_empty"
     poll=1000>
</mtango:attr>

fld_empty

You can't see anything! It is empty!!! But it exists in the app and can be accessed programatically.

DeviceAttributeUI API

For the full reference please follow this link

In this section we just highlight the most important aspects of the DeviceAttributeUI API.

Each mtango:attr creates an instance of DeviceAttributeUI. So it can be access from within JavaScript using MVC.Model's static methods. Specifically find_one(id) where id is the value of [id] attribute specified in mtango:attr tag.

DeviceAttributeUI wraps mtango.DeviceAttribute an instance of the DeviceAttribute can be referred through wrapped field of the DeviceAttributeUI instance. For example, one can access latest value received from the server of an attribute specified like this:

<mtango:attr src="local/webcam/0/State"
             id="State"
             view="fld_empty"
             poll="3000">
</mtango:attr>

using this line of code: DeviceAttributeUI.find_one('State').wrapped.response.argout.

Each writable DeviceAttributeUI instance also has argin field. For scalar attributes it is a value, for spectrum -- index of the chosen value. Example:

  • Scalar:
<mtango:attr src="sys/tg_test/1/long_scalar_w"
             id="long_scalar_w"
             view="fld_text_rw"
             read>
</mtango:attr>

User enters 12345

DeviceAttributeUI.find_one('State').argin; // = 12345
  • Spectrum:
<mtango:attr src="sys/tg_test/1/double_spectrum"
             id="double_spectrum"
             view="fld_list_ro"
             read>
</mtango:attr>

User chooses 5th element from the list

DeviceAttributeUI.find_one('double_spectrum').argin; // = 4 -- 0-based index

There are two important methods must be mentioned explicitly: pause/resume. Use them to manipulate DeviceAttributeUI's activities, like polling.

Image

Image is a special case of DeviceAttributeUI:

<mtango:img src="aka sys/tg_test/1/Double_scalar|required"
            id="optional"
            "[read,poll,change,periodic,user,archive]"="rate in ms aka 3000|required"
            width="optional"
            height="optional">
</mtango:img>

Since #65 and #66 image and encoded attributes of the remote tango device are embedded into response by mTangoREST.server. Since browsers do not support float images only integral images will be displayed correctly:

Image

This is an example of sys/tg_test/1/ushort_image_ro

DeviceCommand

Again DeviceCommand is a special case of DeviceAttributeUI. Typical command definition looks like the following:

<mtango:cmd src="aka sys/tg_test/1/DevString"
            view="non-required, by default equals to fld_cmd"
            initial_value="*|optional">
</mtango:cmd>

[initial_value] defines a value that would be in the argin field of the DeviceCommand instance by default.

Predefined fld_cmd looks like this:

fld_cmd

Plot

Plot allows developer to easily add plots to the application, for instance:

<mtango:plot label="My awesome plot!"
             width="1024"
             height="480"
             refresh-rate="1000"
             range="100000">
    <mtango:plot-data src="sys/tg_test/1/double_scalar"
                      poll="1000">
    </mtango:plot-data>
    <mtango:plot-data src="sys/tg_test/1/long_scalar"
                      change="30000">
    </mtango:plot-data>
</mtango:plot>

here [label] -- is a label of the plot (rendered in the left upper corner of the plot, see picture below); [width|height] -- define dimension of the plot area on th page; [refresh-rate] -- defines how often the plot will be redrawn, ms; [range] -- defines how many values will be kept on the plot, ms.

As plot data is always time depended, i.e. there is time along X-axis, [range] defines for how long a particular value will be drawn on the plot. In other words if it was received at time X it will be displayed till X + value of range.

Plot must have data associated with it. This is done using mtango:plot-data tags. mtango:plot-data is much like mtango:attr but it has only [src] and [acquisition_method] attributes.

An example above is rendered to the following outlook on the page:

plot

Technical background

Since every engine in JavaScriptMVC is also a jmvc app, i.e. has its own controllers, models, views etc. mTangoUI defines all the mentioned models and views. Plus it has a special controller (page_controller) that listens for page load events and then parses the content of the page for the mtango tags. Once it finds any of them it creates an instance of the corresponding model.

mTangoUI implements also an important feature -- it automatically suspends all the activities on inactive pages (see #69). This is true only for the activities that are created by the page_controller. Developer must take care of any custom activities created by himself.

mtango cli utils

mtango cli uitls is a set of js scripts that can be executed from the command line interface. In this section available commands are listed and explained

NOTE: All examples below are for Windows. Do not forget to add ./ on Linux/Mac.

  • js jmvc/mtango/app {app_name}

Creates a new application stub.

  • js jmvc/mtango/proxy {fully qualified tango device name, i.e. tango://localhost:8080/sys/tg_test/1}

This creates (or overwrites if already exists) a new jsTango proxy class. It creates a new js file in models folder named after the Tango device class, i.e. TangoTest.js. This new model contains methods related to the Tango class's commands and attributes. To use this model developer must first add it in apps/{app_name}.js:

//apps/{app_name}.js

//...

include.models(
    "TangoTest"
)

and then create an instance of this model, for example in main_controller:

//controllers/{app_name}/main_controller.js

    load:function(){
        var tangoTest = new TangoTest("http://localhost:8080/mtango","sys/tg_test/1");
        //...

jsTango proxy model constructor expects exactly two arguments: an url to mTangoREST.server and a tango device name which will be proxied by newly created instance.

  • js jmvc/mtango/mTangoTest

Installs mTangoTest application from the repository.

  • js jmvc/mtango/page/add {page_name} {app_name}

Adds a new page to the specified application. This creates pages/{app_name}/{page_name} folder with three components of the page: header.ejs, content.ejs and footer.ejs. It also creates a new model in models/{app_name}/{page_name}_page.js and adds everything in the apps/{app_name}.js file. Developer only needs to add the following code lines to the main_controller:

//controllers/{app_name}/main_controller.js

    load:function(){
        var myNewPage = new NewPage();
        myNewPage.load();
        //...
  • js jmvc/mtango/page/remove {page_name} {app_name}

Reverts changes made by page/add.

Updated