SenchaCon 2016: A Data-Driven Application for the Embedded World - Jean-Philippe Ehret

Preview:

Citation preview

A Data-Driven Application for the Embedded World

Jean-Philippe EHRETLas Vegas, November 6th 2016

A History of User Interfaces...

Embedded Systems’ Humble Beginnings

A simple message, and only this one : “It’s WORKING! Or is it?!”

3

The blinking LED

Embedded Systems’ Evolved

BETTER! But user-friendly?

4

Switches and lights

Expectations Nowadays

5

Interactive data-driven dashboards

Expectations Nowadays

6

Interactive data-driven dashboards

Real Time

Expectations Nowadays

7

Interactive data-driven dashboards

Real Time

Interactive

Expectations Nowadays

8

Interactive data-driven dashboards

Real Time

Interactive

Data Driven

Expectations Nowadays

9

Interactive data-driven dashboards

Real Time

Interactive

Data Driven

Reliable

Expectations Nowadays

10

Interactive data-driven dashboards

Real Time

Interactive

Data Driven

Reliable

And nice looking!

Expectations Nowadays

11

Interactive data-driven dashboards

Real Time

Interactive

Data Driven

Reliable

And nice looking!

NOT ONLYon powerful machines!

Expectations Nowadays

12

Interactive data-driven dashboards

Real Time

Interactive

Data Driven

Reliable

And nice looking!

Everywhere

Hardware Constraints

Embedded Generally Means

You don’t have that…

14

Non-extensible computing power…

Embedded Generally Means

But more something like that!

15

Already limited to start with…

Embedded Generally Means

16

For a slight difference!

EnterpriseServer

EmbeddedEquipment

RAM 32 GB+ 1 MB to 1 GBCPU Core i7, Octo Xeon, etc. ARM

Hard Drive RAID hard drives NopeLocal Storage Several dozens GB Up to a few GB

Embedded Generally Means

17

You don’t have this

Embedded Generally Means

18

You have that

Consequences for Developers

You Have to Be Really Smart!

• Because people want real-time, rich and reactive interfaces- And of course adaptive and working on many kind of devices : tablets, laptops, desktops...

But more something like that

20

You Have to Be Really Smart!

BUT!

21

You Have to Be Really Smart!

• But YOU HAVE GOT very limited resources

22

You Have to Be Really Smart!

• But YOU HAVE GOT very limited resources- The only resources you have are in a few components

But more something like that

23

You Have to Be Really Smart!

AND

24

You Have to Be Really Smart!

• You cannot develop on your computer leaving the question of performance for later:

25

You Have to Be Really Smart!

• You cannot develop on your computer leaving the question of performance for later:

26

Patching an embedded device remotely may just NOT BE POSSIBLE!

Real Life Application

HAGER in a Few WordsBasic stats

12,000 people

€1.6bn ($1.77bn) turnover

HQ in Germany, present in 23 countries

Clients in over 80 countries

Building EfficiencyA large step towards intelligent buildings

Agardio system contributes to the management of low voltage (LV) electrical system:

• Optimizing the building’s energy consumption:- Saving money and preserving the environment

• Maintaining service continuity:- Preventing operational losses

- Providing stable conditions for occupants

Building EfficiencyHager’s Agardio™ system

Building EfficiencyHager’s Agardio™ system

Technical details

Building EfficiencyHager’s Agardio™ system

Target Architecture

How to Include Ext JS in an Embedded Project?

How to Include Ext JS in an Embedded Project?Modularity

• Multilingual and modular across Python backend and Ext JS frontend

How to Include Ext JS in an Embedded Project?No configuration

• Must be available through tablets without any configuration

• Directly served by the equipment

What does it mean in Ext JS?

What does it mean in Ext JS?Sencha CMD

Small Memory FootprintYou definitely want to use Sencha CMD• Tablets will download the application from the equipment

• Heavy app is not such a problem in terms of memory, but the power of the CPU used to deliver the app is a different story

What does it mean in Ext JS?Dynamic Loading

Dynamic LoadingLoad a resource only when neededHager’s product is fully modular:

• Depending on the physical modules installed (light sensor, temperature sensor…), backend and frontend will have different features.

Additionally:

• The new modules can be plugged without any reboot

• Configuration files can be uploaded on-the-fly

• A new piece of UI can serve many purposes across multiple modules

-

Dynamic LoadingLoad a resource only when needed

The magic is that all of this is loaded dynamically like a sort of “plugin” :

• The Ext JS app CANNOT be deployed as a single JS as we are used to.

Dynamic LoadingLoad a resource only when needed

The magic is that all of this is loaded dynamically like a sort of “plugin” :

• The Ext JS app CANNOT be deployed as a single JS as we are used to.

• Each physical module brings its piece of extra Ext JS and resources within a dedicated Ext JS package

Dynamic LoadingLoad a resource only when needed

The magic is that all of this is loaded dynamically like a sort of “plugin” :

• The Ext JS app CANNOT be deployed as a single JS as we are used to.

• Each physical module brings its piece of extra Ext JS and resources within a dedicated Ext JS package

• Sencha CMD is to produce MULTIPLE JavaScript files, one per package / module

What does it mean in Ext JS?Data Driven Dynamic Charts

Data RepresentationMassive usage of d3.js visualizations Why d3.js?

• Hager needed custom visualizations and behaviors

• D3.js was the best pick due to that (SVG low-level building)

Data RepresentationMassive usage of d3.js visualizations Why not Sencha’s implementation?

• The project started before Sencha’s announcement of d3.js native support

Data RepresentationMassive usage of d3.js visualizations What’s next?

• Many issues with browser support due to very different behaviors of the SVG manipulation

• Long-term approach : switch to Sencha’s official implementation

Data RepresentationExt.define('Hes.common.d3.Chart', { extend: 'Ext.Component', xtype: 'd3-chart', fieldMappings: {}, autoEl: { tag: 'svg' }, … onBoxReady: function(…) {…}, createChart:function(w, he) { … this.setChart(this.getChartGenerator(w, he));

… this.getChart().updateChart( Hes.util.Format.storeToD3Data(chartDataStore, this.fieldMappings)); } },…});

Data RepresentationExt.define('Hes.common.d3.Chart', { extend: 'Ext.Component', xtype: 'd3-chart', fieldMappings: {}, autoEl: { tag: 'svg' }, … onBoxReady: function(…) {…}, …

Data DrivenMust deal with various software- modules-specific data

this.setChart( this.getChartGenerator(w, he));…this.getChart().updateChart( Hes.util.Format.storeToD3Data( chartDataStore, this.fieldMappings));

Transform Data to D3 Data

storeToD3Data: function(chartData, fieldMappings) { var data = [];

fieldMappings = fieldMappings || {}; if (chartData && Ext.isFunction(chartData.each)) { chartData.each(function(datum) { var dataObj = datum.data; var seriesData = {}; var dataKeys = Ext.Object.getKeys(dataObj); dataKeys.forEach(function(key) { seriesData[fieldMappings[key] || key] = datum.get(key); }); data.push(seriesData); });} return data;}

Transform Data to D3 Data

storeToD3Data: function(chartData, fieldMappings) { var data = [];

fieldMappings = fieldMappings || {}; if (chartData && Ext.isFunction(chartData.each)) { chartData.each(function(datum) { var dataObj = datum.data; var seriesData = {}; var dataKeys = Ext.Object.getKeys(dataObj); dataKeys.forEach(function(key) { pieData[fieldMappings[key] || key] = datum.get(key); }); data.push(pieData); });} return data;}

To find axis and all important D3 data types

var dataKeys = Ext.Object.getKeys(dataObj); dataKeys.forEach(function(key) { seriesData[fieldMappings[key] || key] =

datum.get(key); });

A Generator

Ext.define('Hes.common.d3.line.LineGenerator', { extend: 'Ext.Base', mixins: ['Ext.mixin.Observable'],…updateChart: function(data) { var seriesFields = this.chartOwner.getSeriesFields() || ['value']; this.data = data; this.seriesData = seriesFields.map(function(fieldName) { return { id: fieldName, values: data.map(function(d) { return { date: d.date, value: d[fieldName] };})};});

… this.svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + this.height + ")") .call(this.xAxis) .append("text") .style("font-size", "1.6em") .attr("y", -this.height) .attr("x", 15) .attr("fill", "#000") .text(this.chartOwner.getXTitle()); this.svg.append("g") .attr("class", "y axis") …

A Generator

Ext.define('Hes.common.d3.line.LineGenerator', { extend: 'Ext.Base', mixins: ['Ext.mixin.Observable'],…updateChart: function(data) { var seriesFields = this.chartOwner.getSeriesFields() || ['value']; this.data = data; this.seriesData = seriesFields.map(function(fieldName) { return { id: fieldName, values: data.map(function(d) { return { date: d.date, value: d[fieldName] };})};});

… this.svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + this.height + ")") .call(this.xAxis) .append("text") .style("font-size", "1.6em") .attr("y", -this.height) .attr("x", 15) .attr("fill", "#000") .text(this.chartOwner.getXTitle()); this.svg.append("g") .attr("class", "y axis") …

updateChartOverride for specific drawing

updateChart:function(data) {…

Data-drivenGeneric whatever is the property

return { date: d.date, value: d[fieldName]}

What does it involve in ExtJS?Dynamic Internationalization

InternationalizationOne app to rule them allWe use an approach with elements on top of Sencha’s official guidelines:

• One multilingual build instead of one build per language

• Dynamic loading to switch language as required

• Easily extendable for multiple language support

• Contents can be translated by non developers through specific products

• No redeployment needed

For more information about internationalization, please read our blogpost on sencha.com

Internationalization

Json result is language specific{"button": "Mon Bouton",

"title": "Mon titre"}

Ext.define('Jnesis.Labels', { singleton: true, button: 'My english button', title: 'My english title'});…Ext.define ('Jnesis.Application', { launch: function () { Ext.Ajax.request({ url: 'get-localization', params:{locale:'fr'}, callback: function (options, success, response) { var data = Ext.decode(response.responseText, true); Ext.override(Jnesis.Labels, data); Ext.create('Jnesis.view.main.Main'); } }); }});

InternationalizationExt.define('Jnesis.Labels', { singleton: true, button: 'My english button', title: 'My english title'});…Ext.define ('Jnesis.Application', { launch: function () { Ext.Ajax.request({ url: 'get-localization', params:{locale:'fr'}, callback: function (options, success, response) { var data = Ext.decode(response.responseText, true); Ext.override(Jnesis.Labels, data); Ext.create('Jnesis.view.main.Main'); } }); }});

Override with selected languageThe singleton gets new values

url: 'get-localization', params:{locale:'fr'},…Ext.override(Jnesis.Labels, data);

InternationalizationExt.define('overrides.localized.Component', { override: 'Ext.Component', initComponent: function() { var me = this, localized = me.localized, value; if (Ext.isObject(localized)) { for (var prop in localized) { value = localized[prop]; if (value) { me[prop] = eval(value); }}} me.callParent(arguments);});

Ext.define('Jnesis.view.main.Main', { extend: 'Ext.panel.Panel', localized: { title: 'Jnesis.Labels.title' }, buttons: [{ localized: { text: 'Jnesis.Labels.button' }, handler: 'onClickButton' }]});

Ext.define('overrides.localized.Component', { override: 'Ext.Component', initComponent: function() { var me = this, localized = me.localized, value; if (Ext.isObject(localized)) { for (var prop in localized) { value = localized[prop]; if (value) { me[prop] = eval(value); }}} me.callParent(arguments);});

Ext.define('Jnesis.view.main.Main', { extend: 'Ext.panel.Panel', localized: { title: 'Jnesis.Labels.title' }, buttons: [{ localized: { text: 'Jnesis.Labels.button' }, handler: 'onClickButton' }]});

Localized property“Bind like” property

localized: { title: 'Jnesis.Labels.title'},

Override Ext Components Ext.define('overrides.localized.Component', { override: 'Ext.Component',

What does it involves in ExtJS?Various Performance Optimizations

Application OptimizationSome best practices for embedding • Don’t blame the backend guy! Every REST call can harm the resources!

• Store locally every time it is possible (inside the end-user device):

Application OptimizationSome best practices for embedding • Don’t blame the backend guy! Every REST call can harm the resources!

• Store locally every time it is possible (inside the end-user device):

duplicate data = need for optimization

Application OptimizationSome best practices for embedding • You have to be reasonable in the use of websocket / SSE data exchanges

Application OptimizationSome best practices for embedding • Don’t forget to ask for compression in the backend!

Application OptimizationSome best practices for embedding • What can be performed on the frontend must be done on the frontend (e.g. CSV export,

data sorting, …)

Is ExtJS Fit for Embedded?

YES! But developers MUST understand what‘s happening behind the scene, although Sencha did a good job sparing this to developers in most other cases.

To Summarize

• Keep in mind that resources are scarce

To Summarize

• Keep in mind that resources are scarce

• Be smart not just comprehensive:

To Summarize

• Keep in mind that resources are scarce

• Be smart not just comprehensive:

- Design modular!

To Summarize

• Keep in mind that resources are scarce

• Be smart not just comprehensive- Design modular!

• Test, re-test, re-re-test before releasing, you work in an embedded system

To Conclude

Making Ext JS work with « Things » is nothing much than legitimate expectation.

To Conclude

Making Ext JS work with « Things » is nothing much than legitimate expectation.

Jnesis is always looking for solutions to use Ext JS wherever it can be in a beneficial way for its clients, check us out at the sponsor zone!