Showing
7 changed files
with
87 additions
and
10 deletions
... | ... | @@ -16,7 +16,8 @@ |
16 | 16 | |
17 | 17 | export default class DataAggregator { |
18 | 18 | |
19 | - constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, types, $timeout, $filter) { | |
19 | + constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, | |
20 | + steppedChart, types, $timeout, $filter) { | |
20 | 21 | this.onDataCb = onDataCb; |
21 | 22 | this.tsKeyNames = tsKeyNames; |
22 | 23 | this.dataBuffer = {}; |
... | ... | @@ -34,6 +35,8 @@ export default class DataAggregator { |
34 | 35 | this.limit = limit; |
35 | 36 | this.timeWindow = timeWindow; |
36 | 37 | this.interval = interval; |
38 | + this.steppedChart = steppedChart; | |
39 | + this.firstStepDataReceived = !this.steppedChart; | |
37 | 40 | this.aggregationTimeout = Math.max(this.interval, 1000); |
38 | 41 | switch (aggregationType) { |
39 | 42 | case types.aggregation.min.value: |
... | ... | @@ -78,6 +81,10 @@ export default class DataAggregator { |
78 | 81 | }, this.aggregationTimeout, false); |
79 | 82 | } |
80 | 83 | |
84 | + onFirstStepData(data) { | |
85 | + this.firstStepData = data; | |
86 | + } | |
87 | + | |
81 | 88 | onData(data, update, history, apply) { |
82 | 89 | if (!this.dataReceived || this.resetPending) { |
83 | 90 | var updateIntervalScheduledTime = true; | ... | ... |
... | ... | @@ -23,6 +23,8 @@ export default angular.module('thingsboard.api.datasource', [thingsboardApiDevic |
23 | 23 | .factory('datasourceService', DatasourceService) |
24 | 24 | .name; |
25 | 25 | |
26 | +const YEAR = 1000 * 60 * 60 * 24 * 365; | |
27 | + | |
26 | 28 | /*@ngInject*/ |
27 | 29 | function DatasourceService($timeout, $filter, $log, telemetryWebsocketService, types, utils) { |
28 | 30 | |
... | ... | @@ -280,6 +282,10 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic |
280 | 282 | telemetryWebsocketService.subscribe(subscriber); |
281 | 283 | subscribers[subscriber.historyCommand.cmdId] = subscriber; |
282 | 284 | |
285 | + if (subsTw.aggregation.steppedChart) { | |
286 | + createFirstStepSubscription(subsTw, tsKeys); | |
287 | + } | |
288 | + | |
283 | 289 | } else { |
284 | 290 | |
285 | 291 | subscriptionCommand = { |
... | ... | @@ -312,6 +318,11 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic |
312 | 318 | updateRealtimeSubscriptionCommand(this.subscriptionCommand, newSubsTw); |
313 | 319 | dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); |
314 | 320 | } |
321 | + | |
322 | + if (subsTw.aggregation.steppedChart) { | |
323 | + createFirstStepSubscription(subsTw, tsKeys); | |
324 | + } | |
325 | + | |
315 | 326 | } else { |
316 | 327 | subscriber.onReconnected = function() {} |
317 | 328 | subscriber.onData = function(data) { |
... | ... | @@ -377,6 +388,37 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic |
377 | 388 | } |
378 | 389 | } |
379 | 390 | |
391 | + function createFirstStepSubscription(subsTw, tsKeys) { | |
392 | + var startStepCommand = { | |
393 | + entityType: datasourceSubscription.entityType, | |
394 | + entityId: datasourceSubscription.entityId, | |
395 | + keys: tsKeys, | |
396 | + startTs: subsTw.fixedWindow.startTimeMs - YEAR, | |
397 | + endTs: subsTw.fixedWindow.startTimeMs, | |
398 | + interval: subsTw.aggregation.interval, | |
399 | + limit: 1, | |
400 | + agg: subsTw.aggregation.type | |
401 | + }; | |
402 | + var subscriber = { | |
403 | + historyCommand: startStepCommand, | |
404 | + type: types.dataKeyType.timeseries, | |
405 | + onData: function (data) { | |
406 | + if (data.data) { | |
407 | + for (var key in data.data) { | |
408 | + var keyData = data.data[key]; | |
409 | + data.data[key] = $filter('orderBy')(keyData, '+this[0]'); | |
410 | + } | |
411 | + //onData(data.data, types.dataKeyType.timeseries, true); | |
412 | + //TODO: onStartStepData | |
413 | + } | |
414 | + }, | |
415 | + onReconnected: function() {} | |
416 | + }; | |
417 | + | |
418 | + telemetryWebsocketService.subscribe(subscriber); | |
419 | + subscribers[subscriber.historyCommand.cmdId] = subscriber; | |
420 | + } | |
421 | + | |
380 | 422 | function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) { |
381 | 423 | return new DataAggregator( |
382 | 424 | function(data, apply) { |
... | ... | @@ -388,6 +430,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic |
388 | 430 | subsTw.aggregation.type, |
389 | 431 | subsTw.aggregation.timeWindow, |
390 | 432 | subsTw.aggregation.interval, |
433 | + subsTw.aggregation.steppedChart, | |
391 | 434 | types, |
392 | 435 | $timeout, |
393 | 436 | $filter | ... | ... |
... | ... | @@ -128,7 +128,7 @@ export default class Subscription { |
128 | 128 | stDiff: this.ctx.stDiff |
129 | 129 | } |
130 | 130 | this.useDashboardTimewindow = options.useDashboardTimewindow; |
131 | - | |
131 | + this.steppedChart = options.steppedChart; | |
132 | 132 | if (this.useDashboardTimewindow) { |
133 | 133 | this.timeWindowConfig = angular.copy(options.dashboardTimewindow); |
134 | 134 | } else { |
... | ... | @@ -612,7 +612,7 @@ export default class Subscription { |
612 | 612 | this.subscriptionTimewindow = |
613 | 613 | this.ctx.timeService.createSubscriptionTimewindow( |
614 | 614 | this.timeWindowConfig, |
615 | - this.timeWindow.stDiff); | |
615 | + this.timeWindow.stDiff, this.steppedChart); | |
616 | 616 | } |
617 | 617 | this.updateTimewindow(); |
618 | 618 | return this.subscriptionTimewindow; | ... | ... |
... | ... | @@ -261,7 +261,7 @@ function TimeService($translate, types) { |
261 | 261 | return historyTimewindow; |
262 | 262 | } |
263 | 263 | |
264 | - function createSubscriptionTimewindow(timewindow, stDiff) { | |
264 | + function createSubscriptionTimewindow(timewindow, stDiff, steppedChart) { | |
265 | 265 | |
266 | 266 | var subscriptionTimewindow = { |
267 | 267 | fixedWindow: null, |
... | ... | @@ -273,8 +273,22 @@ function TimeService($translate, types) { |
273 | 273 | } |
274 | 274 | }; |
275 | 275 | var aggTimewindow = 0; |
276 | + if (steppedChart) { | |
277 | + subscriptionTimewindow.aggregation = { | |
278 | + interval: SECOND, | |
279 | + limit: MAX_LIMIT, | |
280 | + type: types.aggregation.none.value, | |
281 | + steppedChart: true | |
282 | + }; | |
283 | + } else { | |
284 | + subscriptionTimewindow.aggregation = { | |
285 | + interval: SECOND, | |
286 | + limit: AVG_LIMIT, | |
287 | + type: types.aggregation.avg.value | |
288 | + }; | |
289 | + } | |
276 | 290 | |
277 | - if (angular.isDefined(timewindow.aggregation)) { | |
291 | + if (angular.isDefined(timewindow.aggregation) && !steppedChart) { | |
278 | 292 | subscriptionTimewindow.aggregation = { |
279 | 293 | type: timewindow.aggregation.type || types.aggregation.avg.value, |
280 | 294 | limit: timewindow.aggregation.limit || AVG_LIMIT | ... | ... |
... | ... | @@ -560,7 +560,8 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr |
560 | 560 | useCustomDatasources: false, |
561 | 561 | maxDatasources: -1, //unlimited |
562 | 562 | maxDataKeys: -1, //unlimited |
563 | - dataKeysOptional: false | |
563 | + dataKeysOptional: false, | |
564 | + steppedChart: false | |
564 | 565 | }; |
565 | 566 | ' }\n\n' + |
566 | 567 | |
... | ... | @@ -631,6 +632,9 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr |
631 | 632 | if (angular.isUndefined(result.typeParameters.dataKeysOptional)) { |
632 | 633 | result.typeParameters.dataKeysOptional = false; |
633 | 634 | } |
635 | + if (angular.isUndefined(result.typeParameters.steppedChart)) { | |
636 | + result.typeParameters.steppedChart = false; | |
637 | + } | |
634 | 638 | if (angular.isFunction(widgetTypeInstance.actionSources)) { |
635 | 639 | result.actionSources = widgetTypeInstance.actionSources(); |
636 | 640 | } else { | ... | ... |
... | ... | @@ -339,7 +339,8 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele |
339 | 339 | var deferred = $q.defer(); |
340 | 340 | if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { |
341 | 341 | options = { |
342 | - type: widget.type | |
342 | + type: widget.type, | |
343 | + steppedChart: vm.typeParameters.steppedChart | |
343 | 344 | } |
344 | 345 | if (widget.type == types.widgetType.alarm.value) { |
345 | 346 | options.alarmSource = angular.copy(widget.config.alarmSource); | ... | ... |
... | ... | @@ -168,7 +168,7 @@ export default class TbFlot { |
168 | 168 | } |
169 | 169 | }; |
170 | 170 | |
171 | - if (this.chartType === 'line' || this.chartType === 'bar') { | |
171 | + if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') { | |
172 | 172 | options.xaxis = { |
173 | 173 | mode: 'time', |
174 | 174 | timezone: 'browser', |
... | ... | @@ -270,6 +270,14 @@ export default class TbFlot { |
270 | 270 | fill: 0.9 |
271 | 271 | } |
272 | 272 | } |
273 | + | |
274 | + if (this.chartType === 'stepped') { | |
275 | + options.series.lines = { | |
276 | + steps: true, | |
277 | + show: true | |
278 | + } | |
279 | + } | |
280 | + | |
273 | 281 | } else if (this.chartType === 'pie') { |
274 | 282 | options.series = { |
275 | 283 | pie: { |
... | ... | @@ -381,7 +389,7 @@ export default class TbFlot { |
381 | 389 | |
382 | 390 | this.options.colors = colors; |
383 | 391 | this.options.yaxes = angular.copy(this.yaxes); |
384 | - if (this.chartType === 'line' || this.chartType === 'bar') { | |
392 | + if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') { | |
385 | 393 | if (this.chartType === 'bar') { |
386 | 394 | this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; |
387 | 395 | } |
... | ... | @@ -434,7 +442,7 @@ export default class TbFlot { |
434 | 442 | } |
435 | 443 | if (this.subscription) { |
436 | 444 | if (!this.isMouseInteraction && this.ctx.plot) { |
437 | - if (this.chartType === 'line' || this.chartType === 'bar') { | |
445 | + if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') { | |
438 | 446 | |
439 | 447 | var axisVisibilityChanged = false; |
440 | 448 | if (this.yaxis) { | ... | ... |