diff --git a/ui/src/app/api/data-aggregator.js b/ui/src/app/api/data-aggregator.js index 76d3b1cccc..68b0d91782 100644 --- a/ui/src/app/api/data-aggregator.js +++ b/ui/src/app/api/data-aggregator.js @@ -19,6 +19,10 @@ export default class DataAggregator { constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, types, $timeout, $filter) { this.onDataCb = onDataCb; this.tsKeyNames = tsKeyNames; + this.dataBuffer = {}; + for (var k in tsKeyNames) { + this.dataBuffer[tsKeyNames[k]] = []; + } this.startTs = startTs; this.aggregationType = aggregationType; this.types = types; @@ -120,11 +124,11 @@ export default class DataAggregator { if (delta || !this.data) { this.startTs += delta * this.interval; this.endTs += delta * this.interval; - this.data = toData(this.tsKeyNames, this.aggregationMap, this.startTs, this.endTs, this.$filter, this.limit); + this.data = this.updateData(); this.elapsed = this.elapsed - delta * this.interval; } } else { - this.data = toData(this.tsKeyNames, this.aggregationMap, this.startTs, this.endTs, this.$filter, this.limit); + this.data = this.updateData(); } if (this.onDataCb) { this.onDataCb(this.data, this.startTs, this.endTs, apply); @@ -138,6 +142,31 @@ export default class DataAggregator { } } + updateData() { + for (var k in this.tsKeyNames) { + this.dataBuffer[this.tsKeyNames[k]] = []; + } + for (var key in this.aggregationMap) { + var aggKeyData = this.aggregationMap[key]; + var keyData = this.dataBuffer[key]; + for (var aggTimestamp in aggKeyData) { + if (aggTimestamp <= this.startTs) { + delete aggKeyData[aggTimestamp]; + } else if (aggTimestamp <= this.endTs) { + var aggData = aggKeyData[aggTimestamp]; + var kvPair = [Number(aggTimestamp), aggData.aggValue]; + keyData.push(kvPair); + } + } + keyData = this.$filter('orderBy')(keyData, '+this[0]'); + if (keyData.length > this.limit) { + keyData = keyData.slice(keyData.length - this.limit); + } + this.dataBuffer[key] = keyData; + } + return this.dataBuffer; + } + destroy() { if (this.intervalTimeoutHandle) { this.$timeout.cancel(this.intervalTimeoutHandle); @@ -208,32 +237,6 @@ function updateAggregatedData(aggregationMap, isCount, noAggregation, aggFunctio } } -function toData(tsKeyNames, aggregationMap, startTs, endTs, $filter, limit) { - var data = {}; - for (var k in tsKeyNames) { - data[tsKeyNames[k]] = []; - } - for (var key in aggregationMap) { - var aggKeyData = aggregationMap[key]; - var keyData = data[key]; - for (var aggTimestamp in aggKeyData) { - if (aggTimestamp <= startTs) { - delete aggKeyData[aggTimestamp]; - } else if (aggTimestamp <= endTs) { - var aggData = aggKeyData[aggTimestamp]; - var kvPair = [Number(aggTimestamp), aggData.aggValue]; - keyData.push(kvPair); - } - } - keyData = $filter('orderBy')(keyData, '+this[0]'); - if (keyData.length > limit) { - keyData = keyData.slice(keyData.length - limit); - } - data[key] = keyData; - } - return data; -} - function convertValue(value, noAggregation) { if (!noAggregation || value && isNumeric(value)) { return Number(value); diff --git a/ui/src/app/api/datasource.service.js b/ui/src/app/api/datasource.service.js index 8b49cf44bc..7a2486d95e 100644 --- a/ui/src/app/api/datasource.service.js +++ b/ui/src/app/api/datasource.service.js @@ -110,6 +110,8 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic datasourceSubscription.subscriptionTimewindow.realtimeWindowMs; var timer; var frequency; + var tickElapsed = 0; + var tickScheduledTime = 0; var dataAggregator; var subscription = { @@ -353,11 +355,14 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic } dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.function); } + tickScheduledTime = currentTime(); if (history) { onTick(false); } else { timer = $timeout( - function() {onTick(true)}, + function() { + onTick(true) + }, 0, false ); @@ -393,6 +398,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic function unsubscribe() { if (timer) { $timeout.cancel(timer); + timer = null; } if (datasourceType === types.datasourceType.device) { for (var cmdId in subscribers) { @@ -456,15 +462,39 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic } } + /* eslint-disable */ + function currentTime() { + return window.performance && window.performance.now ? + window.performance.now() : Date.now(); + } + /* eslint-enable */ + + function onTick(apply) { + + var now = currentTime(); + tickElapsed += now - tickScheduledTime; + tickScheduledTime = now; + + if (timer) { + $timeout.cancel(timer); + timer = null; + } + var key; if (datasourceSubscription.type === types.widgetType.timeseries.value) { var startTime; var endTime; + var delta; var generatedData = { data: { } }; + if (!history) { + delta = Math.floor(tickElapsed / frequency); + } + var deltaElapsed = history ? frequency : delta * frequency; + tickElapsed = tickElapsed - deltaElapsed; for (key in dataKeys) { var dataKeyList = dataKeys[key]; for (var index = 0; index < dataKeyList.length; index ++) { @@ -472,11 +502,12 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic if (!startTime) { if (realtime) { if (dataKey.lastUpdateTime) { - startTime = dataKey.lastUpdateTime + frequency + startTime = dataKey.lastUpdateTime + frequency; + endTime = dataKey.lastUpdateTime + deltaElapsed; } else { startTime = datasourceSubscription.subscriptionTimewindow.startTs; + endTime = startTime + datasourceSubscription.subscriptionTimewindow.realtimeWindowMs + frequency; } - endTime = startTime + datasourceSubscription.subscriptionTimewindow.realtimeWindowMs; } else { startTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.startTimeMs; endTime = datasourceSubscription.subscriptionTimewindow.fixedWindow.endTimeMs; @@ -494,7 +525,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic } if (!history) { - timer = $timeout(function() {onTick(true)}, frequency / 2, false); + timer = $timeout(function() {onTick(true)}, frequency, false); } } diff --git a/ui/src/app/widget/lib/google-map.js b/ui/src/app/widget/lib/google-map.js index 027b145c0c..100f1b3f61 100644 --- a/ui/src/app/widget/lib/google-map.js +++ b/ui/src/app/widget/lib/google-map.js @@ -246,26 +246,31 @@ export default class TbGoogleMap { } /* eslint-enable no-undef */ + /* eslint-disable no-undef */ fitBounds(bounds) { - var tbMap = this; - google.maps.event.addListenerOnce(this.map, 'bounds_changed', function() { // eslint-disable-line no-undef - var newZoomLevel = tbMap.map.getZoom(); - if (tbMap.dontFitMapBounds && tbMap.defaultZoomLevel) { - newZoomLevel = tbMap.defaultZoomLevel; - } - tbMap.map.setZoom(newZoomLevel); - - if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { - tbMap.map.setZoom(tbMap.minZoomLevel); - } - }); - this.map.fitBounds(bounds); + if (this.dontFitMapBounds && this.defaultZoomLevel) { + this.map.setZoom(this.defaultZoomLevel); + this.map.setCenter(bounds.getCenter()); + } else { + var tbMap = this; + google.maps.event.addListenerOnce(this.map, 'bounds_changed', function() { // eslint-disable-line no-undef + if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { + tbMap.map.setZoom(tbMap.minZoomLevel); + } + }); + this.map.fitBounds(bounds); + } } + /* eslint-enable no-undef */ createLatLng(lat, lng) { return new google.maps.LatLng(lat, lng); // eslint-disable-line no-undef } + extendBoundsWithMarker(bounds, marker) { + bounds.extend(marker.getPosition()); + } + getMarkerPosition(marker) { return marker.getPosition(); } diff --git a/ui/src/app/widget/lib/map-widget.js b/ui/src/app/widget/lib/map-widget.js index 52079f38ec..7402ee67ac 100644 --- a/ui/src/app/widget/lib/map-widget.js +++ b/ui/src/app/widget/lib/map-widget.js @@ -386,7 +386,7 @@ export default class TbMapWidget { if (location.polyline) { tbMap.map.extendBounds(bounds, location.polyline); } else if (location.marker) { - bounds.extend(tbMap.map.getMarkerPosition(location.marker)); + tbMap.map.extendBoundsWithMarker(bounds, location.marker); } } } @@ -403,10 +403,10 @@ export default class TbMapWidget { if (location.polyline) { tbMap.map.extendBounds(bounds, location.polyline); } else if (location.marker) { - bounds.extend(tbMap.map.getMarkerPosition(location.marker)); + tbMap.map.extendBoundsWithMarker(bounds, location.marker); } } - if (!tbMap.dontFitMapBounds && locationsChanged) { + if (locationsChanged) { tbMap.map.fitBounds(bounds); } } @@ -448,10 +448,10 @@ export default class TbMapWidget { resize() { if (this.map && this.map.inited()) { this.map.invalidateSize(); - if (!this.dontFitMapBounds && this.locations && this.locations.size > 0) { + if (this.locations && this.locations.size > 0) { var bounds = this.map.createBounds(); for (var m in this.markers) { - bounds.extend(this.map.getMarkerPosition(this.markers[m])); + this.map.extendBoundsWithMarker(bounds, this.markers[m]); } if (this.polylines) { for (var p in this.polylines) { diff --git a/ui/src/app/widget/lib/openstreet-map.js b/ui/src/app/widget/lib/openstreet-map.js index 4ebcf94142..74438a4f77 100644 --- a/ui/src/app/widget/lib/openstreet-map.js +++ b/ui/src/app/widget/lib/openstreet-map.js @@ -146,25 +146,30 @@ export default class TbOpenStreetMap { } fitBounds(bounds) { - var tbMap = this; - this.map.once('zoomend', function() { - var newZoomLevel = tbMap.map.getZoom(); - if (tbMap.dontFitMapBounds && tbMap.defaultZoomLevel) { - newZoomLevel = tbMap.defaultZoomLevel; - } - tbMap.map.setZoom(newZoomLevel, {animate: false}); - - if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { - tbMap.map.setZoom(tbMap.minZoomLevel, {animate: false}); + if (bounds.isValid()) { + if (this.dontFitMapBounds && this.defaultZoomLevel) { + this.map.setZoom(this.defaultZoomLevel, {animate: false}); + this.map.panTo(bounds.getCenter(), {animate: false}); + } else { + var tbMap = this; + this.map.once('zoomend', function() { + if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { + tbMap.map.setZoom(tbMap.minZoomLevel, {animate: false}); + } + }); + this.map.fitBounds(bounds, {padding: [50, 50], animate: false}); } - }); - this.map.fitBounds(bounds, {padding: [50, 50], animate: false}); + } } createLatLng(lat, lng) { return L.latLng(lat, lng); } + extendBoundsWithMarker(bounds, marker) { + bounds.extend(marker.getLatLng()); + } + getMarkerPosition(marker) { return marker.getLatLng(); }