thingsboard-aplcache

Stepped chart

9/8/2017 1:50:58 PM

Details

diff --git a/ui/src/app/api/data-aggregator.js b/ui/src/app/api/data-aggregator.js
index ec41536..43b2e93 100644
--- a/ui/src/app/api/data-aggregator.js
+++ b/ui/src/app/api/data-aggregator.js
@@ -16,7 +16,8 @@
 
 export default class DataAggregator {
 
-    constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, types, $timeout, $filter) {
+    constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval,
+                steppedChart, types, $timeout, $filter) {
         this.onDataCb = onDataCb;
         this.tsKeyNames = tsKeyNames;
         this.dataBuffer = {};
@@ -34,6 +35,8 @@ export default class DataAggregator {
         this.limit = limit;
         this.timeWindow = timeWindow;
         this.interval = interval;
+        this.steppedChart = steppedChart;
+        this.firstStepDataReceived = !this.steppedChart;
         this.aggregationTimeout = Math.max(this.interval, 1000);
         switch (aggregationType) {
             case types.aggregation.min.value:
@@ -78,6 +81,10 @@ export default class DataAggregator {
         }, this.aggregationTimeout, false);
     }
 
+    onFirstStepData(data) {
+        this.firstStepData = data;
+    }
+
     onData(data, update, history, apply) {
         if (!this.dataReceived || this.resetPending) {
             var updateIntervalScheduledTime = true;
diff --git a/ui/src/app/api/datasource.service.js b/ui/src/app/api/datasource.service.js
index 1f265bb..a68e14c 100644
--- a/ui/src/app/api/datasource.service.js
+++ b/ui/src/app/api/datasource.service.js
@@ -23,6 +23,8 @@ export default angular.module('thingsboard.api.datasource', [thingsboardApiDevic
     .factory('datasourceService', DatasourceService)
     .name;
 
+const YEAR = 1000 * 60 * 60 * 24 * 365;
+
 /*@ngInject*/
 function DatasourceService($timeout, $filter, $log, telemetryWebsocketService, types, utils) {
 
@@ -280,6 +282,10 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
                     telemetryWebsocketService.subscribe(subscriber);
                     subscribers[subscriber.historyCommand.cmdId] = subscriber;
 
+                    if (subsTw.aggregation.steppedChart) {
+                        createFirstStepSubscription(subsTw, tsKeys);
+                    }
+
                 } else {
 
                     subscriptionCommand = {
@@ -312,6 +318,11 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
                             updateRealtimeSubscriptionCommand(this.subscriptionCommand, newSubsTw);
                             dataAggregator.reset(newSubsTw.startTs,  newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval);
                         }
+
+                        if (subsTw.aggregation.steppedChart) {
+                            createFirstStepSubscription(subsTw, tsKeys);
+                        }
+
                     } else {
                         subscriber.onReconnected = function() {}
                         subscriber.onData = function(data) {
@@ -377,6 +388,37 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
         }
     }
 
+    function createFirstStepSubscription(subsTw, tsKeys) {
+        var startStepCommand = {
+            entityType: datasourceSubscription.entityType,
+            entityId: datasourceSubscription.entityId,
+            keys: tsKeys,
+            startTs: subsTw.fixedWindow.startTimeMs - YEAR,
+            endTs: subsTw.fixedWindow.startTimeMs,
+            interval: subsTw.aggregation.interval,
+            limit: 1,
+            agg: subsTw.aggregation.type
+        };
+        var subscriber = {
+            historyCommand: startStepCommand,
+            type: types.dataKeyType.timeseries,
+            onData: function (data) {
+                if (data.data) {
+                    for (var key in data.data) {
+                        var keyData = data.data[key];
+                        data.data[key] = $filter('orderBy')(keyData, '+this[0]');
+                    }
+                    //onData(data.data, types.dataKeyType.timeseries, true);
+                    //TODO: onStartStepData
+                }
+            },
+            onReconnected: function() {}
+        };
+
+        telemetryWebsocketService.subscribe(subscriber);
+        subscribers[subscriber.historyCommand.cmdId] = subscriber;
+    }
+
     function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) {
         return new DataAggregator(
             function(data, apply) {
@@ -388,6 +430,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
             subsTw.aggregation.type,
             subsTw.aggregation.timeWindow,
             subsTw.aggregation.interval,
+            subsTw.aggregation.steppedChart,
             types,
             $timeout,
             $filter
diff --git a/ui/src/app/api/subscription.js b/ui/src/app/api/subscription.js
index fd26629..a464617 100644
--- a/ui/src/app/api/subscription.js
+++ b/ui/src/app/api/subscription.js
@@ -128,7 +128,7 @@ export default class Subscription {
                 stDiff: this.ctx.stDiff
             }
             this.useDashboardTimewindow = options.useDashboardTimewindow;
-
+            this.steppedChart = options.steppedChart;
             if (this.useDashboardTimewindow) {
                 this.timeWindowConfig = angular.copy(options.dashboardTimewindow);
             } else {
@@ -612,7 +612,7 @@ export default class Subscription {
             this.subscriptionTimewindow =
                 this.ctx.timeService.createSubscriptionTimewindow(
                     this.timeWindowConfig,
-                    this.timeWindow.stDiff);
+                    this.timeWindow.stDiff, this.steppedChart);
         }
         this.updateTimewindow();
         return this.subscriptionTimewindow;
diff --git a/ui/src/app/api/time.service.js b/ui/src/app/api/time.service.js
index 1f42290..d34c77e 100644
--- a/ui/src/app/api/time.service.js
+++ b/ui/src/app/api/time.service.js
@@ -261,7 +261,7 @@ function TimeService($translate, types) {
         return historyTimewindow;
     }
 
-    function createSubscriptionTimewindow(timewindow, stDiff) {
+    function createSubscriptionTimewindow(timewindow, stDiff, steppedChart) {
 
         var subscriptionTimewindow = {
             fixedWindow: null,
@@ -273,8 +273,22 @@ function TimeService($translate, types) {
             }
         };
         var aggTimewindow = 0;
+        if (steppedChart) {
+            subscriptionTimewindow.aggregation = {
+                interval: SECOND,
+                limit: MAX_LIMIT,
+                type: types.aggregation.none.value,
+                steppedChart: true
+            };
+        } else {
+            subscriptionTimewindow.aggregation = {
+                interval: SECOND,
+                limit: AVG_LIMIT,
+                type: types.aggregation.avg.value
+            };
+        }
 
-        if (angular.isDefined(timewindow.aggregation)) {
+        if (angular.isDefined(timewindow.aggregation) && !steppedChart) {
             subscriptionTimewindow.aggregation = {
                 type: timewindow.aggregation.type || types.aggregation.avg.value,
                 limit: timewindow.aggregation.limit || AVG_LIMIT
diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js
index 842b976..9fbb819 100644
--- a/ui/src/app/api/widget.service.js
+++ b/ui/src/app/api/widget.service.js
@@ -560,7 +560,8 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
                                 useCustomDatasources: false,
                                 maxDatasources: -1, //unlimited
                                 maxDataKeys: -1, //unlimited
-                                dataKeysOptional: false
+                                dataKeysOptional: false,
+                                steppedChart: false
                            };
          '    }\n\n' +
 
@@ -631,6 +632,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr
             if (angular.isUndefined(result.typeParameters.dataKeysOptional)) {
                 result.typeParameters.dataKeysOptional = false;
             }
+            if (angular.isUndefined(result.typeParameters.steppedChart)) {
+                result.typeParameters.steppedChart = false;
+            }
             if (angular.isFunction(widgetTypeInstance.actionSources)) {
                 result.actionSources = widgetTypeInstance.actionSources();
             } else {
diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js
index 8dde1fa..34a48f7 100644
--- a/ui/src/app/components/widget/widget.controller.js
+++ b/ui/src/app/components/widget/widget.controller.js
@@ -339,7 +339,8 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
         var deferred = $q.defer();
         if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) {
             options = {
-                type: widget.type
+                type: widget.type,
+                steppedChart: vm.typeParameters.steppedChart
             }
             if (widget.type == types.widgetType.alarm.value) {
                 options.alarmSource = angular.copy(widget.config.alarmSource);
diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js
index ff60bde..bf2b77e 100644
--- a/ui/src/app/widget/lib/flot-widget.js
+++ b/ui/src/app/widget/lib/flot-widget.js
@@ -168,7 +168,7 @@ export default class TbFlot {
             }
         };
 
-        if (this.chartType === 'line' || this.chartType === 'bar') {
+        if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') {
             options.xaxis = {
                 mode: 'time',
                 timezone: 'browser',
@@ -270,6 +270,14 @@ export default class TbFlot {
                         fill: 0.9
                 }
             }
+
+            if (this.chartType === 'stepped') {
+                options.series.lines = {
+                    steps: true,
+                    show: true
+                }
+            }
+
         } else if (this.chartType === 'pie') {
             options.series = {
                 pie: {
@@ -381,7 +389,7 @@ export default class TbFlot {
 
         this.options.colors = colors;
         this.options.yaxes = angular.copy(this.yaxes);
-        if (this.chartType === 'line' || this.chartType === 'bar') {
+        if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') {
             if (this.chartType === 'bar') {
                 this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6;
             }
@@ -434,7 +442,7 @@ export default class TbFlot {
         }
         if (this.subscription) {
             if (!this.isMouseInteraction && this.ctx.plot) {
-                if (this.chartType === 'line' || this.chartType === 'bar') {
+                if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') {
 
                     var axisVisibilityChanged = false;
                     if (this.yaxis) {