From f87bc9b5b098420414147c78e516ba18df5f4278 Mon Sep 17 00:00:00 2001 From: mp-loki Date: Tue, 28 Nov 2017 00:02:53 -0500 Subject: [PATCH 1/5] Fixed java.lang.IllegalArgumentException when QoS = 0 --- .../server/transport/mqtt/session/GatewayDeviceSessionCtx.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 7b527dff83..7646131c42 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -83,7 +83,7 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { if (responseMsg.isSuccess()) { MsgType requestMsgType = responseMsg.getRequestMsgType(); Integer requestId = responseMsg.getRequestId(); - if (requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) { + if (requestId >= 0 && requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) { return Optional.of(MqttTransportHandler.createMqttPubAckMsg(requestId)); } } From 5251782c97afedff832179a85a76869c132aab68 Mon Sep 17 00:00:00 2001 From: mp-loki Date: Fri, 24 Nov 2017 22:14:38 -0500 Subject: [PATCH 2/5] TB-75 unique constraint violation fix --- .../JpaAbstractDaoListeningExecutorService.java | 1 + .../dao/sql/timeseries/JpaTimeseriesDao.java | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java index 7c34aa1d86..78282674ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDaoListeningExecutorService.java @@ -22,6 +22,7 @@ import javax.annotation.PreDestroy; import java.util.concurrent.Executors; public abstract class JpaAbstractDaoListeningExecutorService { + protected ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java index 7fddfaeebf..0a4e685007 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java @@ -17,9 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; import com.google.common.base.Function; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; +import com.google.common.util.concurrent.*; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -40,6 +38,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @@ -50,6 +49,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; @SqlDao public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { + private ListeningExecutorService insertService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + @Autowired private TsKvRepository tsKvRepository; @@ -232,7 +233,8 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); - return service.submit(() -> { + log.trace("Saving entity: " + entity); + return insertService.submit(() -> { tsKvRepository.save(entity); return null; }); @@ -240,7 +242,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp @Override public ListenableFuture savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) { - return service.submit(() -> null); + return insertService.submit(() -> null); } @Override @@ -254,7 +256,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null)); latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); - return service.submit(() -> { + return insertService.submit(() -> { tsKvLatestRepository.save(latestEntity); return null; }); From f7b0ec98b606006048df5c4c7bc9f15a88edf60c Mon Sep 17 00:00:00 2001 From: mp-loki Date: Mon, 27 Nov 2017 07:31:41 -0500 Subject: [PATCH 3/5] TB-75 Service shutdown added --- .../server/dao/sql/timeseries/JpaTimeseriesDao.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java index 0a4e685007..170f9a6c2c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/timeseries/JpaTimeseriesDao.java @@ -34,6 +34,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesDao; import org.thingsboard.server.dao.util.SqlDao; import javax.annotation.Nullable; +import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -262,4 +263,9 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp }); } + @PreDestroy + void onDestroy() { + insertService.shutdown(); + } + } From 336f0dc07952845e814fdcd45f4073129e4c4105 Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Wed, 29 Nov 2017 10:51:57 +0200 Subject: [PATCH 4/5] Ability to fetch multiple device attributes using Gateway MQTT API --- .../mqtt/session/GatewayDeviceSessionCtx.java | 42 +++++++++++-------- .../mqtt/session/GatewaySessionCtx.java | 22 ++++++---- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java index 7646131c42..7bed03ac48 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewayDeviceSessionCtx.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.mqtt.session; import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.netty.buffer.ByteBuf; @@ -24,6 +25,7 @@ import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.codec.mqtt.*; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.SessionId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; @@ -35,6 +37,7 @@ import org.thingsboard.server.transport.mqtt.MqttTopics; import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import java.nio.charset.Charset; +import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; @@ -135,40 +138,43 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { if (responseData.isPresent()) { AttributesKVMsg msg = responseData.get(); if (msg.getClientAttributes() != null) { - msg.getClientAttributes().forEach(v -> addValueToJson(result, "value", v)); + addValues(result, msg.getClientAttributes()); } if (msg.getSharedAttributes() != null) { - msg.getSharedAttributes().forEach(v -> addValueToJson(result, "value", v)); + addValues(result, msg.getSharedAttributes()); } } return createMqttPublishMsg(topic, result); } + private void addValues(JsonObject result, List kvList) { + if (kvList.size() == 1) { + addValueToJson(result, "value", kvList.get(0)); + } else { + JsonObject values; + if (result.has("values")) { + values = result.get("values").getAsJsonObject(); + } else { + values = new JsonObject(); + result.add("values", values); + } + kvList.forEach(value -> addValueToJson(values, value.getKey(), value)); + } + } + private void addValueToJson(JsonObject json, String name, KvEntry entry) { switch (entry.getDataType()) { case BOOLEAN: - Optional booleanValue = entry.getBooleanValue(); - if (booleanValue.isPresent()) { - json.addProperty(name, booleanValue.get()); - } + entry.getBooleanValue().ifPresent(aBoolean -> json.addProperty(name, aBoolean)); break; case STRING: - Optional stringValue = entry.getStrValue(); - if (stringValue.isPresent()) { - json.addProperty(name, stringValue.get()); - } + entry.getStrValue().ifPresent(aString -> json.addProperty(name, aString)); break; case DOUBLE: - Optional doubleValue = entry.getDoubleValue(); - if (doubleValue.isPresent()) { - json.addProperty(name, doubleValue.get()); - } + entry.getDoubleValue().ifPresent(aDouble -> json.addProperty(name, aDouble)); break; case LONG: - Optional longValue = entry.getLongValue(); - if (longValue.isPresent()) { - json.addProperty(name, longValue.get()); - } + entry.getLongValue().ifPresent(aLong -> json.addProperty(name, aLong)); break; } } diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java index 00d1f0c6d7..5bccf49663 100644 --- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java +++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java @@ -41,10 +41,7 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; @@ -193,13 +190,22 @@ public class GatewaySessionCtx { int requestId = jsonObj.get("id").getAsInt(); String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); boolean clientScope = jsonObj.get("client").getAsBoolean(); - String key = jsonObj.get("key").getAsString(); + Set keys; + if (jsonObj.has("key")) { + keys = Collections.singleton(jsonObj.get("key").getAsString()); + } else { + JsonArray keysArray = jsonObj.get("keys").getAsJsonArray(); + keys = new HashSet<>(); + for (JsonElement keyObj : keysArray) { + keys.add(keyObj.getAsString()); + } + } BasicGetAttributesRequest request; if (clientScope) { - request = new BasicGetAttributesRequest(requestId, Collections.singleton(key), null); + request = new BasicGetAttributesRequest(requestId, keys, null); } else { - request = new BasicGetAttributesRequest(requestId, null, Collections.singleton(key)); + request = new BasicGetAttributesRequest(requestId, null, keys); } GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), @@ -251,7 +257,7 @@ public class GatewaySessionCtx { } private void ack(MqttPublishMessage msg) { - if(msg.variableHeader().messageId() > 0) { + if (msg.variableHeader().messageId() > 0) { writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId())); } } From fbc83a726d9d0149815f3a39f6ef7899bec79173 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 4 Dec 2017 16:43:13 +0200 Subject: [PATCH 5/5] UI: Fix home page layout. --- ui/src/app/home/home-links.controller.js | 39 ++++++++++++++++++++++-- ui/src/app/home/home-links.tpl.html | 16 +++++----- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/ui/src/app/home/home-links.controller.js b/ui/src/app/home/home-links.controller.js index 799a0bc0b2..32ae0919a3 100644 --- a/ui/src/app/home/home-links.controller.js +++ b/ui/src/app/home/home-links.controller.js @@ -17,7 +17,42 @@ import './home-links.scss'; /*@ngInject*/ -export default function HomeLinksController($scope, menu) { +export default function HomeLinksController($scope, $mdMedia, menu) { + var vm = this; - vm.model = menu.getHomeSections(); + + vm.sectionColspan = sectionColspan; + + $scope.$watch(function() { return $mdMedia('lg'); }, function() { + updateColumnCount(); + }); + + $scope.$watch(function() { return $mdMedia('gt-lg'); }, function() { + updateColumnCount(); + }); + + updateColumnCount(); + + menu.getHomeSections().then((homeSections) => { + vm.model = homeSections; + }); + + function updateColumnCount() { + vm.cols = 2; + if ($mdMedia('lg')) { + vm.cols = 3; + } + if ($mdMedia('gt-lg')) { + vm.cols = 4; + } + } + + function sectionColspan(section) { + var colspan = vm.cols; + if (section && section.places && section.places.length <= colspan) { + colspan = section.places.length; + } + return colspan; + } + } diff --git a/ui/src/app/home/home-links.tpl.html b/ui/src/app/home/home-links.tpl.html index e27f20a820..f0e558257f 100644 --- a/ui/src/app/home/home-links.tpl.html +++ b/ui/src/app/home/home-links.tpl.html @@ -15,8 +15,8 @@ limitations under the License. --> - - + + @@ -25,12 +25,12 @@ - - - {{place.icon}} - {{place.name}} - - + + + {{place.icon}} + {{place.name}} + +