Showing
6 changed files
with
99 additions
and
43 deletions
... | ... | @@ -22,6 +22,7 @@ import javax.annotation.PreDestroy; |
22 | 22 | import java.util.concurrent.Executors; |
23 | 23 | |
24 | 24 | public abstract class JpaAbstractDaoListeningExecutorService { |
25 | + | |
25 | 26 | protected ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); |
26 | 27 | |
27 | 28 | @PreDestroy | ... | ... |
... | ... | @@ -17,9 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; |
17 | 17 | |
18 | 18 | import com.google.common.base.Function; |
19 | 19 | import com.google.common.collect.Lists; |
20 | -import com.google.common.util.concurrent.Futures; | |
21 | -import com.google.common.util.concurrent.ListenableFuture; | |
22 | -import com.google.common.util.concurrent.SettableFuture; | |
20 | +import com.google.common.util.concurrent.*; | |
23 | 21 | import lombok.extern.slf4j.Slf4j; |
24 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
25 | 23 | import org.springframework.data.domain.PageRequest; |
... | ... | @@ -36,10 +34,12 @@ import org.thingsboard.server.dao.timeseries.TimeseriesDao; |
36 | 34 | import org.thingsboard.server.dao.util.SqlDao; |
37 | 35 | |
38 | 36 | import javax.annotation.Nullable; |
37 | +import javax.annotation.PreDestroy; | |
39 | 38 | import java.util.ArrayList; |
40 | 39 | import java.util.List; |
41 | 40 | import java.util.Optional; |
42 | 41 | import java.util.concurrent.CompletableFuture; |
42 | +import java.util.concurrent.Executors; | |
43 | 43 | import java.util.stream.Collectors; |
44 | 44 | |
45 | 45 | import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; |
... | ... | @@ -50,6 +50,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; |
50 | 50 | @SqlDao |
51 | 51 | public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { |
52 | 52 | |
53 | + private ListeningExecutorService insertService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); | |
54 | + | |
53 | 55 | @Autowired |
54 | 56 | private TsKvRepository tsKvRepository; |
55 | 57 | |
... | ... | @@ -232,7 +234,8 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp |
232 | 234 | entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); |
233 | 235 | entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); |
234 | 236 | entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); |
235 | - return service.submit(() -> { | |
237 | + log.trace("Saving entity: " + entity); | |
238 | + return insertService.submit(() -> { | |
236 | 239 | tsKvRepository.save(entity); |
237 | 240 | return null; |
238 | 241 | }); |
... | ... | @@ -240,7 +243,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp |
240 | 243 | |
241 | 244 | @Override |
242 | 245 | public ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl) { |
243 | - return service.submit(() -> null); | |
246 | + return insertService.submit(() -> null); | |
244 | 247 | } |
245 | 248 | |
246 | 249 | @Override |
... | ... | @@ -254,10 +257,15 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp |
254 | 257 | latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); |
255 | 258 | latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null)); |
256 | 259 | latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); |
257 | - return service.submit(() -> { | |
260 | + return insertService.submit(() -> { | |
258 | 261 | tsKvLatestRepository.save(latestEntity); |
259 | 262 | return null; |
260 | 263 | }); |
261 | 264 | } |
262 | 265 | |
266 | + @PreDestroy | |
267 | + void onDestroy() { | |
268 | + insertService.shutdown(); | |
269 | + } | |
270 | + | |
263 | 271 | } | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.transport.mqtt.session; |
17 | 17 | |
18 | 18 | import com.google.gson.Gson; |
19 | +import com.google.gson.JsonArray; | |
19 | 20 | import com.google.gson.JsonElement; |
20 | 21 | import com.google.gson.JsonObject; |
21 | 22 | import io.netty.buffer.ByteBuf; |
... | ... | @@ -24,6 +25,7 @@ import io.netty.buffer.UnpooledByteBufAllocator; |
24 | 25 | import io.netty.handler.codec.mqtt.*; |
25 | 26 | import org.thingsboard.server.common.data.Device; |
26 | 27 | import org.thingsboard.server.common.data.id.SessionId; |
28 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
27 | 29 | import org.thingsboard.server.common.data.kv.KvEntry; |
28 | 30 | import org.thingsboard.server.common.msg.core.*; |
29 | 31 | import org.thingsboard.server.common.msg.kv.AttributesKVMsg; |
... | ... | @@ -35,6 +37,7 @@ import org.thingsboard.server.transport.mqtt.MqttTopics; |
35 | 37 | import org.thingsboard.server.transport.mqtt.MqttTransportHandler; |
36 | 38 | |
37 | 39 | import java.nio.charset.Charset; |
40 | +import java.util.List; | |
38 | 41 | import java.util.Optional; |
39 | 42 | import java.util.concurrent.atomic.AtomicInteger; |
40 | 43 | |
... | ... | @@ -83,7 +86,7 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { |
83 | 86 | if (responseMsg.isSuccess()) { |
84 | 87 | MsgType requestMsgType = responseMsg.getRequestMsgType(); |
85 | 88 | Integer requestId = responseMsg.getRequestId(); |
86 | - if (requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) { | |
89 | + if (requestId >= 0 && requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) { | |
87 | 90 | return Optional.of(MqttTransportHandler.createMqttPubAckMsg(requestId)); |
88 | 91 | } |
89 | 92 | } |
... | ... | @@ -135,40 +138,43 @@ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { |
135 | 138 | if (responseData.isPresent()) { |
136 | 139 | AttributesKVMsg msg = responseData.get(); |
137 | 140 | if (msg.getClientAttributes() != null) { |
138 | - msg.getClientAttributes().forEach(v -> addValueToJson(result, "value", v)); | |
141 | + addValues(result, msg.getClientAttributes()); | |
139 | 142 | } |
140 | 143 | if (msg.getSharedAttributes() != null) { |
141 | - msg.getSharedAttributes().forEach(v -> addValueToJson(result, "value", v)); | |
144 | + addValues(result, msg.getSharedAttributes()); | |
142 | 145 | } |
143 | 146 | } |
144 | 147 | return createMqttPublishMsg(topic, result); |
145 | 148 | } |
146 | 149 | |
150 | + private void addValues(JsonObject result, List<AttributeKvEntry> kvList) { | |
151 | + if (kvList.size() == 1) { | |
152 | + addValueToJson(result, "value", kvList.get(0)); | |
153 | + } else { | |
154 | + JsonObject values; | |
155 | + if (result.has("values")) { | |
156 | + values = result.get("values").getAsJsonObject(); | |
157 | + } else { | |
158 | + values = new JsonObject(); | |
159 | + result.add("values", values); | |
160 | + } | |
161 | + kvList.forEach(value -> addValueToJson(values, value.getKey(), value)); | |
162 | + } | |
163 | + } | |
164 | + | |
147 | 165 | private void addValueToJson(JsonObject json, String name, KvEntry entry) { |
148 | 166 | switch (entry.getDataType()) { |
149 | 167 | case BOOLEAN: |
150 | - Optional<Boolean> booleanValue = entry.getBooleanValue(); | |
151 | - if (booleanValue.isPresent()) { | |
152 | - json.addProperty(name, booleanValue.get()); | |
153 | - } | |
168 | + entry.getBooleanValue().ifPresent(aBoolean -> json.addProperty(name, aBoolean)); | |
154 | 169 | break; |
155 | 170 | case STRING: |
156 | - Optional<String> stringValue = entry.getStrValue(); | |
157 | - if (stringValue.isPresent()) { | |
158 | - json.addProperty(name, stringValue.get()); | |
159 | - } | |
171 | + entry.getStrValue().ifPresent(aString -> json.addProperty(name, aString)); | |
160 | 172 | break; |
161 | 173 | case DOUBLE: |
162 | - Optional<Double> doubleValue = entry.getDoubleValue(); | |
163 | - if (doubleValue.isPresent()) { | |
164 | - json.addProperty(name, doubleValue.get()); | |
165 | - } | |
174 | + entry.getDoubleValue().ifPresent(aDouble -> json.addProperty(name, aDouble)); | |
166 | 175 | break; |
167 | 176 | case LONG: |
168 | - Optional<Long> longValue = entry.getLongValue(); | |
169 | - if (longValue.isPresent()) { | |
170 | - json.addProperty(name, longValue.get()); | |
171 | - } | |
177 | + entry.getLongValue().ifPresent(aLong -> json.addProperty(name, aLong)); | |
172 | 178 | break; |
173 | 179 | } |
174 | 180 | } | ... | ... |
... | ... | @@ -41,10 +41,7 @@ import org.thingsboard.server.dao.relation.RelationService; |
41 | 41 | import org.thingsboard.server.transport.mqtt.MqttTransportHandler; |
42 | 42 | import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; |
43 | 43 | |
44 | -import java.util.Collections; | |
45 | -import java.util.HashMap; | |
46 | -import java.util.Map; | |
47 | -import java.util.Optional; | |
44 | +import java.util.*; | |
48 | 45 | import java.util.stream.Collectors; |
49 | 46 | |
50 | 47 | import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; |
... | ... | @@ -193,13 +190,22 @@ public class GatewaySessionCtx { |
193 | 190 | int requestId = jsonObj.get("id").getAsInt(); |
194 | 191 | String deviceName = jsonObj.get(DEVICE_PROPERTY).getAsString(); |
195 | 192 | boolean clientScope = jsonObj.get("client").getAsBoolean(); |
196 | - String key = jsonObj.get("key").getAsString(); | |
193 | + Set<String> keys; | |
194 | + if (jsonObj.has("key")) { | |
195 | + keys = Collections.singleton(jsonObj.get("key").getAsString()); | |
196 | + } else { | |
197 | + JsonArray keysArray = jsonObj.get("keys").getAsJsonArray(); | |
198 | + keys = new HashSet<>(); | |
199 | + for (JsonElement keyObj : keysArray) { | |
200 | + keys.add(keyObj.getAsString()); | |
201 | + } | |
202 | + } | |
197 | 203 | |
198 | 204 | BasicGetAttributesRequest request; |
199 | 205 | if (clientScope) { |
200 | - request = new BasicGetAttributesRequest(requestId, Collections.singleton(key), null); | |
206 | + request = new BasicGetAttributesRequest(requestId, keys, null); | |
201 | 207 | } else { |
202 | - request = new BasicGetAttributesRequest(requestId, null, Collections.singleton(key)); | |
208 | + request = new BasicGetAttributesRequest(requestId, null, keys); | |
203 | 209 | } |
204 | 210 | GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); |
205 | 211 | processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), |
... | ... | @@ -251,7 +257,7 @@ public class GatewaySessionCtx { |
251 | 257 | } |
252 | 258 | |
253 | 259 | private void ack(MqttPublishMessage msg) { |
254 | - if(msg.variableHeader().messageId() > 0) { | |
260 | + if (msg.variableHeader().messageId() > 0) { | |
255 | 261 | writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId())); |
256 | 262 | } |
257 | 263 | } | ... | ... |
... | ... | @@ -17,7 +17,42 @@ |
17 | 17 | import './home-links.scss'; |
18 | 18 | |
19 | 19 | /*@ngInject*/ |
20 | -export default function HomeLinksController($scope, menu) { | |
20 | +export default function HomeLinksController($scope, $mdMedia, menu) { | |
21 | + | |
21 | 22 | var vm = this; |
22 | - vm.model = menu.getHomeSections(); | |
23 | + | |
24 | + vm.sectionColspan = sectionColspan; | |
25 | + | |
26 | + $scope.$watch(function() { return $mdMedia('lg'); }, function() { | |
27 | + updateColumnCount(); | |
28 | + }); | |
29 | + | |
30 | + $scope.$watch(function() { return $mdMedia('gt-lg'); }, function() { | |
31 | + updateColumnCount(); | |
32 | + }); | |
33 | + | |
34 | + updateColumnCount(); | |
35 | + | |
36 | + menu.getHomeSections().then((homeSections) => { | |
37 | + vm.model = homeSections; | |
38 | + }); | |
39 | + | |
40 | + function updateColumnCount() { | |
41 | + vm.cols = 2; | |
42 | + if ($mdMedia('lg')) { | |
43 | + vm.cols = 3; | |
44 | + } | |
45 | + if ($mdMedia('gt-lg')) { | |
46 | + vm.cols = 4; | |
47 | + } | |
48 | + } | |
49 | + | |
50 | + function sectionColspan(section) { | |
51 | + var colspan = vm.cols; | |
52 | + if (section && section.places && section.places.length <= colspan) { | |
53 | + colspan = section.places.length; | |
54 | + } | |
55 | + return colspan; | |
56 | + } | |
57 | + | |
23 | 58 | } | ... | ... |
... | ... | @@ -15,8 +15,8 @@ |
15 | 15 | limitations under the License. |
16 | 16 | |
17 | 17 | --> |
18 | -<md-grid-list class="tb-home-links" md-cols="2" md-cols-lg="3" md-cols-gt-lg="4" md-row-height="280px"> | |
19 | - <md-grid-tile md-colspan="2" md-colspan-gt-sm="{{section.places.length}}" ng-repeat="section in vm.model"> | |
18 | +<md-grid-list class="tb-home-links" md-cols="{{vm.cols}}" md-row-height="280px"> | |
19 | + <md-grid-tile md-colspan="2" md-colspan-gt-sm="{{vm.sectionColspan(section)}}" ng-repeat="section in vm.model"> | |
20 | 20 | <md-card style='width: 100%;'> |
21 | 21 | <md-card-title> |
22 | 22 | <md-card-title-text> |
... | ... | @@ -25,12 +25,12 @@ |
25 | 25 | </md-card-title> |
26 | 26 | <md-card-content> |
27 | 27 | <md-grid-list md-row-height="170px" md-cols="{{section.places.length}}" md-cols-gt-md="{{section.places.length}}"> |
28 | - <md-grid-tile class="card-tile" ng-repeat="place in section.places"> | |
29 | - <md-button class="tb-card-button md-raised md-primary" layout="column" ui-sref="{{place.state}}"> | |
30 | - <md-icon class="material-icons tb-md-96" aria-label="{{place.icon}}">{{place.icon}}</md-icon> | |
31 | - <span translate>{{place.name}}</span> | |
32 | - </md-button> | |
33 | - </md-grid-tile> | |
28 | + <md-grid-tile class="card-tile" ng-repeat="place in section.places"> | |
29 | + <md-button class="tb-card-button md-raised md-primary" layout="column" ui-sref="{{place.state}}"> | |
30 | + <md-icon class="material-icons tb-md-96" aria-label="{{place.icon}}">{{place.icon}}</md-icon> | |
31 | + <span translate>{{place.name}}</span> | |
32 | + </md-button> | |
33 | + </md-grid-tile> | |
34 | 34 | </md-grid-list> |
35 | 35 | </md-card-content> |
36 | 36 | </md-card> | ... | ... |