thingsboard-aplcache

Details

diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRpcMsgHandler.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRpcMsgHandler.java
index ba5d610..874c480 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRpcMsgHandler.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryRpcMsgHandler.java
@@ -114,7 +114,7 @@ public class TelemetryRpcMsgHandler implements RpcMsgHandler {
         }
         Map<String, Long> statesMap = proto.getKeyStatesList().stream().collect(Collectors.toMap(SubscriptionKetStateProto::getKey, SubscriptionKetStateProto::getTs));
         Subscription subscription = new Subscription(
-                new SubscriptionState(proto.getSessionId(), proto.getSubscriptionId(), EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), SubscriptionType.valueOf(proto.getType()), proto.getAllKeys(), statesMap),
+                new SubscriptionState(proto.getSessionId(), proto.getSubscriptionId(), EntityIdFactory.getByTypeAndId(proto.getEntityType(), proto.getEntityId()), SubscriptionType.valueOf(proto.getType()), proto.getAllKeys(), statesMap, proto.getScope()),
                 false, msg.getServerAddress());
         subscriptionManager.addRemoteWsSubscription(ctx, msg.getServerAddress(), proto.getSessionId(), subscription);
     }
@@ -127,6 +127,7 @@ public class TelemetryRpcMsgHandler implements RpcMsgHandler {
         builder.setEntityId(cmd.getEntityId().getId().toString());
         builder.setType(cmd.getType().name());
         builder.setAllKeys(cmd.isAllKeys());
+        builder.setScope(cmd.getScope());
         cmd.getKeyStates().entrySet().forEach(e -> builder.addKeyStates(SubscriptionKetStateProto.newBuilder().setKey(e.getKey()).setTs(e.getValue()).build()));
         ctx.sendPluginRpcMsg(new RpcMsg(address, SUBSCRIPTION_CLAZZ, builder.build().toByteArray()));
     }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryWebsocketMsgHandler.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryWebsocketMsgHandler.java
index 7b0e6d8..6f02c9a 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryWebsocketMsgHandler.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/handlers/TelemetryWebsocketMsgHandler.java
@@ -131,7 +131,7 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
                 keys.forEach(key -> subState.put(key, 0L));
                 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
 
-                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.ATTRIBUTES, false, subState);
+                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.ATTRIBUTES, false, subState, cmd.getScope());
                 subscriptionManager.addLocalWsSubscription(ctx, sessionId, entityId, sub);
             }
 
@@ -168,7 +168,7 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
                 Map<String, Long> subState = new HashMap<>(attributesData.size());
                 attributesData.forEach(v -> subState.put(v.getKey(), v.getTs()));
 
-                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.ATTRIBUTES, true, subState);
+                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.ATTRIBUTES, true, subState, cmd.getScope());
                 subscriptionManager.addLocalWsSubscription(ctx, sessionId, entityId, sub);
             }
 
@@ -234,7 +234,7 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
                 sendWsMsg(ctx, sessionRef, new SubscriptionUpdate(cmd.getCmdId(), data));
                 Map<String, Long> subState = new HashMap<>(data.size());
                 data.forEach(v -> subState.put(v.getKey(), v.getTs()));
-                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.TIMESERIES, true, subState);
+                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.TIMESERIES, true, subState, cmd.getScope());
                 subscriptionManager.addLocalWsSubscription(ctx, sessionId, entityId, sub);
             }
 
@@ -262,7 +262,7 @@ public class TelemetryWebsocketMsgHandler extends DefaultWebsocketMsgHandler {
                 Map<String, Long> subState = new HashMap<>(keys.size());
                 keys.forEach(key -> subState.put(key, startTs));
                 data.forEach(v -> subState.put(v.getKey(), v.getTs()));
-                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.TIMESERIES, false, subState);
+                SubscriptionState sub = new SubscriptionState(sessionId, cmd.getCmdId(), entityId, SubscriptionType.TIMESERIES, false, subState, cmd.getScope());
                 subscriptionManager.addLocalWsSubscription(ctx, sessionId, entityId, sub);
             }
 
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java
index 1285cfa..360b018 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/Subscription.java
@@ -51,6 +51,10 @@ public class Subscription {
         return getSub().getType();
     }
 
+    public String getScope() {
+        return getSub().getScope();
+    }
+
     public boolean isAllKeys() {
         return getSub().isAllKeys();
     }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java
index 5e15fda..7e0a2ba 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/sub/SubscriptionState.java
@@ -33,6 +33,7 @@ public class SubscriptionState {
     @Getter private final SubscriptionType type;
     @Getter private final boolean allKeys;
     @Getter private final Map<String, Long> keyStates;
+    @Getter private final String scope;
 
     @Override
     public boolean equals(Object o) {
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/SubscriptionManager.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/SubscriptionManager.java
index d137e10..bad1678 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/SubscriptionManager.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/plugin/telemetry/SubscriptionManager.java
@@ -33,6 +33,7 @@ import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionU
 
 import java.util.*;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 /**
  * @author Andrew Shvayka
@@ -174,9 +175,13 @@ public class SubscriptionManager {
     }
 
     public void onLocalSubscriptionUpdate(PluginContext ctx, EntityId entityId, SubscriptionType type, Function<Subscription, List<TsKvEntry>> f) {
+        onLocalSubscriptionUpdate(ctx, entityId, s -> type == s.getType(), f);
+    }
+
+    public void onLocalSubscriptionUpdate(PluginContext ctx, EntityId entityId, Predicate<Subscription> filter, Function<Subscription, List<TsKvEntry>> f) {
         Set<Subscription> deviceSubscriptions = subscriptionsByEntityId.get(entityId);
         if (deviceSubscriptions != null) {
-            deviceSubscriptions.stream().filter(s -> type == s.getType()).forEach(s -> {
+            deviceSubscriptions.stream().filter(filter).forEach(s -> {
                 String sessionId = s.getWsSessionId();
                 List<TsKvEntry> subscriptionUpdate = f.apply(s);
                 if (!subscriptionUpdate.isEmpty()) {
@@ -206,7 +211,7 @@ public class SubscriptionManager {
     public void onAttributesUpdateFromServer(PluginContext ctx, EntityId entityId, String scope, List<AttributeKvEntry> attributes) {
         Optional<ServerAddress> serverAddress = ctx.resolve(entityId);
         if (!serverAddress.isPresent()) {
-            onLocalSubscriptionUpdate(ctx, entityId, SubscriptionType.ATTRIBUTES, s -> {
+            onLocalSubscriptionUpdate(ctx, entityId, s -> SubscriptionType.ATTRIBUTES == s.getType() && scope.equals(s.getScope()), s -> {
                 List<TsKvEntry> subscriptionUpdate = new ArrayList<TsKvEntry>();
                 for (AttributeKvEntry kv : attributes) {
                     if (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey())) {
diff --git a/extensions-core/src/main/proto/telemetry.proto b/extensions-core/src/main/proto/telemetry.proto
index 2bfef59..59c5c14 100644
--- a/extensions-core/src/main/proto/telemetry.proto
+++ b/extensions-core/src/main/proto/telemetry.proto
@@ -27,6 +27,7 @@ message SubscriptionProto {
   string type = 5;
   bool allKeys = 6;
   repeated SubscriptionKetStateProto keyStates = 7;
+  string scope = 8;
 }
 
 message SubscriptionUpdateProto {
diff --git a/ui/src/app/app.run.js b/ui/src/app/app.run.js
index 2ab5fe2..7d6c31f 100644
--- a/ui/src/app/app.run.js
+++ b/ui/src/app/app.run.js
@@ -20,7 +20,13 @@ import UrlHandler from './url.handler';
 export default function AppRun($rootScope, $window, $injector, $location, $log, $state, $mdDialog, $filter, loginService, userService, $translate) {
 
     $window.Flow = Flow;
-    var frame = $window.frameElement;
+    var frame = null;
+    try {
+        frame = $window.frameElement;
+    } catch(e) {
+        // ie11 fix
+    }
+
     var unauthorizedDialog = null;
     var forbiddenDialog = null;
 
diff --git a/ui/src/app/common/utils.service.js b/ui/src/app/common/utils.service.js
index 085a28b..54d85ec 100644
--- a/ui/src/app/common/utils.service.js
+++ b/ui/src/app/common/utils.service.js
@@ -553,7 +553,9 @@ function Utils($mdColorPalette, $rootScope, $window, $translate, $q, $timeout, t
             var aspect = imageAspectMap[urlHashCode];
             if (angular.isUndefined(aspect)) {
                 var testImage = document.createElement('img'); // eslint-disable-line
-                testImage.style.visibility = 'hidden';
+                testImage.style.position = 'absolute';
+                testImage.style.left = '-99999px';
+                testImage.style.top = '-99999px';
                 testImage.onload = function() {
                     aspect = testImage.width / testImage.height;
                     document.body.removeChild(testImage); //eslint-disable-line
diff --git a/ui/src/app/widget/lib/flot-widget.js b/ui/src/app/widget/lib/flot-widget.js
index 59787cf..282fd4f 100644
--- a/ui/src/app/widget/lib/flot-widget.js
+++ b/ui/src/app/widget/lib/flot-widget.js
@@ -238,6 +238,7 @@ export default class TbFlot {
                 if (this.ticksFormatterFunction) {
                     return this.ticksFormatterFunction(value);
                 }
+
                 var factor = this.tickDecimals ? Math.pow(10, this.tickDecimals) : 1,
                     formatted = "" + Math.round(value * factor) / factor;
                 if (this.tickDecimals != null) {
@@ -248,9 +249,12 @@ export default class TbFlot {
                         formatted = (precision ? formatted : formatted + ".") + ("" + factor).substr(1, this.tickDecimals - precision);
                     }
                 }
-                formatted += ' ' + this.tickUnits;
+                if (this.tickUnits) {
+                     formatted += ' ' + this.tickUnits;
+                }
+             
                 return formatted;
-            }
+            };
 
             this.yaxis.tickFormatter = ctx.yAxisTickFormatter;
 
@@ -262,6 +266,16 @@ export default class TbFlot {
                 this.yaxis.labelFont.color = this.yaxis.font.color;
                 this.yaxis.labelFont.size = this.yaxis.font.size+2;
                 this.yaxis.labelFont.weight = "bold";
+                if (angular.isNumber(settings.yaxis.tickSize)) {
+                    this.yaxis.tickSize = settings.yaxis.tickSize;
+                } else {
+                    this.yaxis.tickSize = null;
+                }
+                if (angular.isNumber(settings.yaxis.tickDecimals)) {
+                    this.yaxis.tickDecimals = settings.yaxis.tickDecimals
+                } else {
+                    this.yaxis.tickDecimals = null;
+                }
                 if (settings.yaxis.ticksFormatter && settings.yaxis.ticksFormatter.length) {
                     try {
                         this.yaxis.ticksFormatterFunction = new Function('value', settings.yaxis.ticksFormatter);
@@ -405,9 +419,13 @@ export default class TbFlot {
                 }
             }
             series.lines = {
-                fill: keySettings.fillLines === true,
-                show: this.chartType === 'line' ? keySettings.showLines !== false : keySettings.showLines === true
+                fill: keySettings.fillLines === true
             };
+            if (this.chartType === 'line' || this.chartType === 'state') {
+                series.lines.show = keySettings.showLines !== false
+            } else {
+                series.lines.show = keySettings.showLines === true;
+            }
 
             if (angular.isDefined(keySettings.lineWidth)) {
                 series.lines.lineWidth = keySettings.lineWidth;
@@ -487,9 +505,19 @@ export default class TbFlot {
 
     createYAxis(keySettings, units) {
         var yaxis = angular.copy(this.yaxis);
+        var tickDecimals, tickSize;
 
         var label = keySettings.axisTitle && keySettings.axisTitle.length ? keySettings.axisTitle : yaxis.label;
-        var tickDecimals = angular.isDefined(keySettings.axisTickDecimals) ? keySettings.axisTickDecimals : 0;
+        if (angular.isNumber(keySettings.axisTickDecimals)) {
+            tickDecimals = keySettings.axisTickDecimals;
+        } else {
+            tickDecimals = yaxis.tickDecimals;
+        }
+        if (angular.isNumber(keySettings.axisTickSize)) {
+            tickSize = keySettings.axisTickSize;
+        } else {
+            tickSize = yaxis.tickSize;
+        }
         var position = keySettings.axisPosition && keySettings.axisPosition.length ? keySettings.axisPosition : "left";
 
         var min = angular.isDefined(keySettings.axisMin) ? keySettings.axisMin : yaxis.min;
@@ -500,6 +528,7 @@ export default class TbFlot {
         yaxis.max = max;
         yaxis.tickUnits = units;
         yaxis.tickDecimals = tickDecimals;
+        yaxis.tickSize = tickSize;
         yaxis.alignTicksWithAxis = position == "right" ? 1 : null;
         yaxis.position = position;
 
@@ -545,7 +574,7 @@ export default class TbFlot {
                                     }
                                 }
                                 yaxis.hidden = hidden;
-                                var newIndex = -1;
+                                var newIndex = 1;
                                 if (!yaxis.hidden) {
                                     this.options.yaxes.push(yaxis);
                                     newIndex = this.options.yaxes.length;
@@ -928,6 +957,16 @@ export default class TbFlot {
                                 "title": "Ticks formatter function, f(value)",
                                 "type": "string",
                                 "default": ""
+                            },
+                            "tickDecimals": {
+                                "title": "The number of decimals to display",
+                                "type": "number",
+                                "default": 0
+                            },
+                            "tickSize": {
+                                "title": "Step size between ticks",
+                                "type": "number",
+                                "default": null
                             }
                         }
                     }
@@ -986,6 +1025,8 @@ export default class TbFlot {
                     "items": [
                         "yaxis.min",
                         "yaxis.max",
+                        "yaxis.tickDecimals",
+                        "yaxis.tickSize",
                         "yaxis.showLabels",
                         "yaxis.title",
                         "yaxis.titleAngle",
@@ -1010,24 +1051,24 @@ export default class TbFlot {
 
     static datakeySettingsSchema(defaultShowLines) {
         return {
-                "schema": {
+            "schema": {
                 "type": "object",
-                    "title": "DataKeySettings",
-                    "properties": {
+                "title": "DataKeySettings",
+                "properties": {
                     "showLines": {
                         "title": "Show lines",
-                            "type": "boolean",
-                            "default": defaultShowLines
+                        "type": "boolean",
+                        "default": defaultShowLines
                     },
                     "fillLines": {
                         "title": "Fill lines",
-                            "type": "boolean",
-                            "default": false
+                        "type": "boolean",
+                        "default": false
                     },
                     "showPoints": {
                         "title": "Show points",
-                            "type": "boolean",
-                            "default": false
+                        "type": "boolean",
+                        "default": false
                     },
                     "tooltipValueFormatter": {
                         "title": "Tooltip value format function, f(value)",
@@ -1059,6 +1100,11 @@ export default class TbFlot {
                         "type": "number",
                         "default": 0
                     },
+                    "axisTickSize": {
+                        "title": "Axis step size between ticks",
+                        "type": "number",
+                        "default": null
+                    },
                     "axisPosition": {
                         "title": "Axis position",
                         "type": "string",
@@ -1072,7 +1118,7 @@ export default class TbFlot {
                 },
                 "required": ["showLines", "fillLines", "showPoints"]
             },
-                "form": [
+            "form": [
                 "showLines",
                 "fillLines",
                 "showPoints",
@@ -1085,6 +1131,7 @@ export default class TbFlot {
                 "axisMax",
                 "axisTitle",
                 "axisTickDecimals",
+                "axisTickSize",
                 {
                     "key": "axisPosition",
                     "type": "rc-select",
@@ -1401,4 +1448,4 @@ export default class TbFlot {
     }
 }
 
-/* eslint-enable angular/angularelement */
\ No newline at end of file
+/* eslint-enable angular/angularelement */
diff --git a/ui/src/app/widget/lib/map-widget.js b/ui/src/app/widget/lib/map-widget.js
index ef86381..db04023 100644
--- a/ui/src/app/widget/lib/map-widget.js
+++ b/ui/src/app/widget/lib/map-widget.js
@@ -276,7 +276,7 @@ export default class TbMapWidget {
                     this.locationsSettings[i].useMarkerImage = true;
                     var url = this.ctx.settings.markerImage;
                     var size = this.ctx.settings.markerImageSize || 34;
-                    this.locationSettings.currentImage = {
+                    this.locationsSettings[i].currentImage = {
                         url: url,
                         size: size
                     };
diff --git a/ui/src/app/widget/lib/timeseries-table-widget.tpl.html b/ui/src/app/widget/lib/timeseries-table-widget.tpl.html
index 0913483..349edba 100644
--- a/ui/src/app/widget/lib/timeseries-table-widget.tpl.html
+++ b/ui/src/app/widget/lib/timeseries-table-widget.tpl.html
@@ -41,7 +41,7 @@
         <md-tabs flex md-selected="vm.sourceIndex" ng-class="{'tb-headless': vm.sources.length === 1}"
                  id="tabs" md-border-bottom flex>
             <md-tab ng-repeat="source in vm.sources" label="{{ source.datasource.name }}">
-                <md-table-container class="tb-absolute-fill layout-column">
+                <md-table-container class="flex">
                     <table md-table>
                         <thead md-head md-order="source.query.order" md-on-reorder="vm.onReorder(source)">
                             <tr md-row>