Commit 3e42f00cf789797737e21d9504d8fc14fd91e631

Authored by Volodymyr Babak
1 parent ab14bcb9

Ack cloud messages

... ... @@ -24,6 +24,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
24 24 import com.google.common.util.concurrent.FutureCallback;
25 25 import com.google.common.util.concurrent.Futures;
26 26 import com.google.common.util.concurrent.ListenableFuture;
  27 +import com.google.common.util.concurrent.MoreExecutors;
27 28 import com.google.gson.Gson;
28 29 import com.google.gson.JsonElement;
29 30 import com.google.gson.JsonObject;
... ... @@ -81,6 +82,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
81 82 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
82 83 import org.thingsboard.server.common.msg.session.SessionMsgType;
83 84 import org.thingsboard.server.common.transport.util.JsonUtils;
  85 +import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
84 86 import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
85 87 import org.thingsboard.server.gen.edge.AssetUpdateMsg;
86 88 import org.thingsboard.server.gen.edge.AttributesRequestMsg;
... ... @@ -93,11 +95,12 @@ import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
93 95 import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
94 96 import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
95 97 import org.thingsboard.server.gen.edge.DownlinkMsg;
  98 +import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
96 99 import org.thingsboard.server.gen.edge.EdgeConfiguration;
97 100 import org.thingsboard.server.gen.edge.EntityDataProto;
98   -import org.thingsboard.server.gen.edge.EntityUpdateMsg;
99 101 import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
100 102 import org.thingsboard.server.gen.edge.RelationRequestMsg;
  103 +import org.thingsboard.server.gen.edge.RelationUpdateMsg;
101 104 import org.thingsboard.server.gen.edge.RequestMsg;
102 105 import org.thingsboard.server.gen.edge.RequestMsgType;
103 106 import org.thingsboard.server.gen.edge.ResponseMsg;
... ... @@ -120,11 +123,14 @@ import org.thingsboard.server.service.edge.EdgeContextComponent;
120 123
121 124 import java.io.Closeable;
122 125 import java.io.IOException;
  126 +import java.util.ArrayList;
123 127 import java.util.Collections;
124 128 import java.util.List;
125 129 import java.util.Optional;
126 130 import java.util.UUID;
  131 +import java.util.concurrent.CountDownLatch;
127 132 import java.util.concurrent.ExecutionException;
  133 +import java.util.concurrent.TimeUnit;
128 134 import java.util.concurrent.locks.ReentrantLock;
129 135 import java.util.function.BiConsumer;
130 136 import java.util.function.Consumer;
... ... @@ -137,6 +143,8 @@ public final class EdgeGrpcSession implements Closeable {
137 143
138 144 private static final ReentrantLock deviceCreationLock = new ReentrantLock();
139 145
  146 + private static final ReentrantLock responseMsgLock = new ReentrantLock();
  147 +
140 148 private final Gson gson = new Gson();
141 149
142 150 private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs";
... ... @@ -152,6 +160,8 @@ public final class EdgeGrpcSession implements Closeable {
152 160 private StreamObserver<ResponseMsg> outputStream;
153 161 private boolean connected;
154 162
  163 + private CountDownLatch latch;
  164 +
155 165 private TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> ruleEngineMsgProducer;
156 166
157 167 EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver<ResponseMsg> outputStream, BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener,
... ... @@ -172,7 +182,7 @@ public final class EdgeGrpcSession implements Closeable {
172 182 public void onNext(RequestMsg requestMsg) {
173 183 if (!connected && requestMsg.getMsgType().equals(RequestMsgType.CONNECT_RPC_MESSAGE)) {
174 184 ConnectResponseMsg responseMsg = processConnect(requestMsg.getConnectRequestMsg());
175   - outputStream.onNext(ResponseMsg.newBuilder()
  185 + sendResponseMsg(ResponseMsg.newBuilder()
176 186 .setConnectResponseMsg(responseMsg)
177 187 .build());
178 188 if (ConnectResponseCode.ACCEPTED != responseMsg.getResponseCode()) {
... ... @@ -184,9 +194,10 @@ public final class EdgeGrpcSession implements Closeable {
184 194 }
185 195 if (connected) {
186 196 if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasUplinkMsg()) {
187   - outputStream.onNext(ResponseMsg.newBuilder()
188   - .setUplinkResponseMsg(processUplinkMsg(requestMsg.getUplinkMsg()))
189   - .build());
  197 + onUplinkMsg(requestMsg.getUplinkMsg());
  198 + }
  199 + if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasDownlinkResponseMsg()) {
  200 + onDownlinkResponse(requestMsg.getDownlinkResponseMsg());
190 201 }
191 202 }
192 203 }
... ... @@ -204,11 +215,54 @@ public final class EdgeGrpcSession implements Closeable {
204 215 };
205 216 }
206 217
  218 + private void onUplinkMsg(UplinkMsg uplinkMsg) {
  219 + ListenableFuture<List<Void>> future = processUplinkMsg(uplinkMsg);
  220 + Futures.addCallback(future, new FutureCallback<List<Void>>() {
  221 + @Override
  222 + public void onSuccess(@Nullable List<Void> result) {
  223 + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(true).build();
  224 + sendResponseMsg(ResponseMsg.newBuilder()
  225 + .setUplinkResponseMsg(uplinkResponseMsg)
  226 + .build());
  227 + }
  228 +
  229 + @Override
  230 + public void onFailure(Throwable t) {
  231 + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build();
  232 + sendResponseMsg(ResponseMsg.newBuilder()
  233 + .setUplinkResponseMsg(uplinkResponseMsg)
  234 + .build());
  235 + }
  236 + }, MoreExecutors.directExecutor());
  237 + }
  238 +
  239 + private void onDownlinkResponse(DownlinkResponseMsg msg) {
  240 + try {
  241 + if (msg.getSuccess()) {
  242 + log.debug("[{}] Msg has been processed successfully! {}", edge.getRoutingKey(), msg);
  243 + } else {
  244 + log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg());
  245 + }
  246 + latch.countDown();
  247 + } catch (Exception e) {
  248 + log.error("Can't process downlink response message [{}]", msg, e);
  249 + }
  250 + }
  251 +
  252 + private void sendResponseMsg(ResponseMsg responseMsg) {
  253 + try {
  254 + responseMsgLock.lock();
  255 + outputStream.onNext(responseMsg);
  256 + } finally {
  257 + responseMsgLock.unlock();
  258 + }
  259 + }
  260 +
207 261 void onConfigurationUpdate(Edge edge) {
208 262 try {
209 263 this.edge = edge;
210 264 // TODO: voba - push edge configuration update to edge
211   -// outputStream.onNext(org.thingsboard.server.gen.integration.ResponseMsg.newBuilder()
  265 +// sendResponseMsg(org.thingsboard.server.gen.integration.ResponseMsg.newBuilder()
212 266 // .setIntegrationUpdateMsg(IntegrationUpdateMsg.newBuilder()
213 267 // .setConfiguration(constructIntegrationConfigProto(configuration, defaultConverterProto, downLinkConverterProto))
214 268 // .build())
... ... @@ -223,48 +277,39 @@ public final class EdgeGrpcSession implements Closeable {
223 277 TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), queueStartTs, null, true);
224 278 TimePageData<EdgeEvent> pageData;
225 279 UUID ifOffset = null;
  280 + boolean success = true;
226 281 do {
227 282 pageData = ctx.getEdgeNotificationService().findEdgeEvents(edge.getTenantId(), edge.getId(), pageLink);
228 283 if (isConnected() && !pageData.getData().isEmpty()) {
229 284 log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());
230   - for (EdgeEvent edgeEvent : pageData.getData()) {
231   - log.trace("[{}] Processing edge event [{}]", this.sessionId, edgeEvent);
232   - try {
233   - ActionType edgeEventAction = ActionType.valueOf(edgeEvent.getEdgeEventAction());
234   - switch (edgeEventAction) {
235   - case UPDATED:
236   - case ADDED:
237   - case ASSIGNED_TO_EDGE:
238   - case DELETED:
239   - case UNASSIGNED_FROM_EDGE:
240   - case ALARM_ACK:
241   - case ALARM_CLEAR:
242   - case CREDENTIALS_UPDATED:
243   - case RELATION_ADD_OR_UPDATE:
244   - case RELATION_DELETED:
245   - processEntityMessage(edgeEvent, edgeEventAction);
246   - break;
247   - case ATTRIBUTES_UPDATED:
248   - case ATTRIBUTES_DELETED:
249   - case TIMESERIES_UPDATED:
250   - processTelemetryMessage(edgeEvent);
251   - break;
252   - }
253   - } catch (Exception e) {
254   - log.error("Exception during processing records from queue", e);
255   - }
256   - ifOffset = edgeEvent.getUuidId();
  285 + List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData());
  286 + log.trace("[{}] downlink msg(s) are going to be send.", downlinkMsgsPack.size());
  287 +
  288 + latch = new CountDownLatch(downlinkMsgsPack.size());
  289 + for (DownlinkMsg downlinkMsg : downlinkMsgsPack) {
  290 + sendResponseMsg(ResponseMsg.newBuilder()
  291 + .setDownlinkMsg(downlinkMsg)
  292 + .build());
  293 + }
  294 +
  295 + ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId();
  296 +
  297 + success = latch.await(10, TimeUnit.SECONDS);
  298 + if (!success) {
  299 + log.warn("Failed to deliver the batch: {}", downlinkMsgsPack);
257 300 }
258 301 }
259   - if (isConnected() && pageData.hasNext()) {
260   - pageLink = pageData.getNextPageLink();
  302 + if (isConnected() && (!success || pageData.hasNext())) {
261 303 try {
262 304 Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches());
263 305 } catch (InterruptedException e) {
264 306 log.error("Error during sleep between batches", e);
265 307 }
  308 + if (success) {
  309 + pageLink = pageData.getNextPageLink();
  310 + }
266 311 }
267   - } while (isConnected() && pageData.hasNext());
  312 + } while (isConnected() && (!success || pageData.hasNext()));
268 313
269 314 if (ifOffset != null) {
270 315 Long newStartTs = UUIDs.unixTimestamp(ifOffset);
... ... @@ -277,6 +322,42 @@ public final class EdgeGrpcSession implements Closeable {
277 322 }
278 323 }
279 324
  325 + private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {
  326 + List<DownlinkMsg> result = new ArrayList<>();
  327 + for (EdgeEvent edgeEvent : edgeEvents) {
  328 + log.trace("Processing edge event [{}]", edgeEvent);
  329 + try {
  330 + DownlinkMsg downlinkMsg = null;
  331 + ActionType edgeEventAction = ActionType.valueOf(edgeEvent.getEdgeEventAction());
  332 + switch (edgeEventAction) {
  333 + case UPDATED:
  334 + case ADDED:
  335 + case ASSIGNED_TO_EDGE:
  336 + case DELETED:
  337 + case UNASSIGNED_FROM_EDGE:
  338 + case ALARM_ACK:
  339 + case ALARM_CLEAR:
  340 + case CREDENTIALS_UPDATED:
  341 + case RELATION_ADD_OR_UPDATE:
  342 + case RELATION_DELETED:
  343 + downlinkMsg = processEntityMessage(edgeEvent, edgeEventAction);
  344 + break;
  345 + case ATTRIBUTES_UPDATED:
  346 + case ATTRIBUTES_DELETED:
  347 + case TIMESERIES_UPDATED:
  348 + downlinkMsg = processTelemetryMessage(edgeEvent);
  349 + break;
  350 + }
  351 + if (downlinkMsg != null) {
  352 + result.add(downlinkMsg);
  353 + }
  354 + } catch (Exception e) {
  355 + log.error("Exception during processing records from queue", e);
  356 + }
  357 + }
  358 + return result;
  359 + }
  360 +
280 361 private ListenableFuture<Long> getQueueStartTs() {
281 362 ListenableFuture<Optional<AttributeKvEntry>> future =
282 363 ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY);
... ... @@ -296,7 +377,7 @@ public final class EdgeGrpcSession implements Closeable {
296 377 ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);
297 378 }
298 379
299   - private void processTelemetryMessage(EdgeEvent edgeEvent) throws IOException {
  380 + private DownlinkMsg processTelemetryMessage(EdgeEvent edgeEvent) throws IOException {
300 381 log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent);
301 382 EntityId entityId = null;
302 383 switch (edgeEvent.getEdgeEventType()) {
... ... @@ -319,74 +400,61 @@ public final class EdgeGrpcSession implements Closeable {
319 400 entityId = new CustomerId(edgeEvent.getEntityId());
320 401 break;
321 402 }
  403 + DownlinkMsg downlinkMsg = null;
322 404 if (entityId != null) {
323 405 log.debug("Sending telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody());
324   - DownlinkMsg downlinkMsg;
325 406 try {
326 407 ActionType actionType = ActionType.valueOf(edgeEvent.getEdgeEventAction());
327 408 downlinkMsg = constructEntityDataProtoMsg(entityId, actionType, JsonUtils.parse(mapper.writeValueAsString(edgeEvent.getEntityBody())));
328   - outputStream.onNext(ResponseMsg.newBuilder()
329   - .setDownlinkMsg(downlinkMsg)
330   - .build());
331 409 } catch (Exception e) {
332 410 log.warn("Can't send telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody(), e);
333 411 }
334   -
335 412 }
  413 + return downlinkMsg;
336 414 }
337 415
338   - private void processEntityMessage(EdgeEvent edgeEvent, ActionType edgeEventAction) {
  416 + private DownlinkMsg processEntityMessage(EdgeEvent edgeEvent, ActionType edgeEventAction) {
339 417 UpdateMsgType msgType = getResponseMsgType(ActionType.valueOf(edgeEvent.getEdgeEventAction()));
340 418 log.trace("Executing processEntityMessage, edgeEvent [{}], edgeEventAction [{}], msgType [{}]", edgeEvent, edgeEventAction, msgType);
341 419 switch (edgeEvent.getEdgeEventType()) {
342 420 case EDGE:
343 421 // TODO: voba - add edge update logic
344   - break;
  422 + return null;
345 423 case DEVICE:
346   - processDevice(edgeEvent, msgType, edgeEventAction);
347   - break;
  424 + return processDevice(edgeEvent, msgType, edgeEventAction);
348 425 case ASSET:
349   - processAsset(edgeEvent, msgType, edgeEventAction);
350   - break;
  426 + return processAsset(edgeEvent, msgType, edgeEventAction);
351 427 case ENTITY_VIEW:
352   - processEntityView(edgeEvent, msgType, edgeEventAction);
353   - break;
  428 + return processEntityView(edgeEvent, msgType, edgeEventAction);
354 429 case DASHBOARD:
355   - processDashboard(edgeEvent, msgType, edgeEventAction);
356   - break;
  430 + return processDashboard(edgeEvent, msgType, edgeEventAction);
357 431 case CUSTOMER:
358   - processCustomer(edgeEvent, msgType, edgeEventAction);
359   - break;
  432 + return processCustomer(edgeEvent, msgType, edgeEventAction);
360 433 case RULE_CHAIN:
361   - processRuleChain(edgeEvent, msgType, edgeEventAction);
362   - break;
  434 + return processRuleChain(edgeEvent, msgType, edgeEventAction);
363 435 case RULE_CHAIN_METADATA:
364   - processRuleChainMetadata(edgeEvent, msgType);
365   - break;
  436 + return processRuleChainMetadata(edgeEvent, msgType);
366 437 case ALARM:
367   - processAlarm(edgeEvent, msgType);
368   - break;
  438 + return processAlarm(edgeEvent, msgType);
369 439 case USER:
370   - processUser(edgeEvent, msgType, edgeEventAction);
371   - break;
  440 + return processUser(edgeEvent, msgType, edgeEventAction);
372 441 case RELATION:
373   - processRelation(edgeEvent, msgType);
374   - break;
  442 + return processRelation(edgeEvent, msgType);
375 443 case WIDGETS_BUNDLE:
376   - processWidgetsBundle(edgeEvent, msgType, edgeEventAction);
377   - break;
  444 + return processWidgetsBundle(edgeEvent, msgType, edgeEventAction);
378 445 case WIDGET_TYPE:
379   - processWidgetType(edgeEvent, msgType, edgeEventAction);
380   - break;
  446 + return processWidgetType(edgeEvent, msgType, edgeEventAction);
381 447 case ADMIN_SETTINGS:
382   - processAdminSettings(edgeEvent);
383   - break;
  448 + return processAdminSettings(edgeEvent);
  449 + default:
  450 + log.warn("Unsupported edge event type [{}]", edgeEvent);
  451 + return null;
384 452 }
385 453 }
386 454
387   - private void processDevice(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
  455 + private DownlinkMsg processDevice(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
388 456 DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());
389   - EntityUpdateMsg entityUpdateMsg = null;
  457 + DownlinkMsg downlinkMsg = null;
390 458 switch (edgeActionType) {
391 459 case ADDED:
392 460 case UPDATED:
... ... @@ -395,8 +463,8 @@ public final class EdgeGrpcSession implements Closeable {
395 463 if (device != null) {
396 464 DeviceUpdateMsg deviceUpdateMsg =
397 465 ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device);
398   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
399   - .setDeviceUpdateMsg(deviceUpdateMsg)
  466 + downlinkMsg = DownlinkMsg.newBuilder()
  467 + .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))
400 468 .build();
401 469 }
402 470 break;
... ... @@ -404,8 +472,8 @@ public final class EdgeGrpcSession implements Closeable {
404 472 case UNASSIGNED_FROM_EDGE:
405 473 DeviceUpdateMsg deviceUpdateMsg =
406 474 ctx.getDeviceUpdateMsgConstructor().constructDeviceDeleteMsg(deviceId);
407   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
408   - .setDeviceUpdateMsg(deviceUpdateMsg)
  475 + downlinkMsg = DownlinkMsg.newBuilder()
  476 + .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))
409 477 .build();
410 478 break;
411 479 case CREDENTIALS_UPDATED:
... ... @@ -413,22 +481,18 @@ public final class EdgeGrpcSession implements Closeable {
413 481 if (deviceCredentials != null) {
414 482 DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg =
415 483 ctx.getDeviceUpdateMsgConstructor().constructDeviceCredentialsUpdatedMsg(deviceCredentials);
416   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
417   - .setDeviceCredentialsUpdateMsg(deviceCredentialsUpdateMsg)
  484 + downlinkMsg = DownlinkMsg.newBuilder()
  485 + .addAllDeviceCredentialsUpdateMsg(Collections.singletonList(deviceCredentialsUpdateMsg))
418 486 .build();
419 487 }
420 488 break;
421 489 }
422   - if (entityUpdateMsg != null) {
423   - outputStream.onNext(ResponseMsg.newBuilder()
424   - .setEntityUpdateMsg(entityUpdateMsg)
425   - .build());
426   - }
  490 + return downlinkMsg;
427 491 }
428 492
429   - private void processAsset(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
  493 + private DownlinkMsg processAsset(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
430 494 AssetId assetId = new AssetId(edgeEvent.getEntityId());
431   - EntityUpdateMsg entityUpdateMsg = null;
  495 + DownlinkMsg downlinkMsg = null;
432 496 switch (edgeEventAction) {
433 497 case ADDED:
434 498 case UPDATED:
... ... @@ -437,8 +501,8 @@ public final class EdgeGrpcSession implements Closeable {
437 501 if (asset != null) {
438 502 AssetUpdateMsg assetUpdateMsg =
439 503 ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset);
440   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
441   - .setAssetUpdateMsg(assetUpdateMsg)
  504 + downlinkMsg = DownlinkMsg.newBuilder()
  505 + .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg))
442 506 .build();
443 507 }
444 508 break;
... ... @@ -446,21 +510,17 @@ public final class EdgeGrpcSession implements Closeable {
446 510 case UNASSIGNED_FROM_EDGE:
447 511 AssetUpdateMsg assetUpdateMsg =
448 512 ctx.getAssetUpdateMsgConstructor().constructAssetDeleteMsg(assetId);
449   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
450   - .setAssetUpdateMsg(assetUpdateMsg)
  513 + downlinkMsg = DownlinkMsg.newBuilder()
  514 + .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg))
451 515 .build();
452 516 break;
453 517 }
454   - if (entityUpdateMsg != null) {
455   - outputStream.onNext(ResponseMsg.newBuilder()
456   - .setEntityUpdateMsg(entityUpdateMsg)
457   - .build());
458   - }
  518 + return downlinkMsg;
459 519 }
460 520
461   - private void processEntityView(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
  521 + private DownlinkMsg processEntityView(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
462 522 EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId());
463   - EntityUpdateMsg entityUpdateMsg = null;
  523 + DownlinkMsg downlinkMsg = null;
464 524 switch (edgeEventAction) {
465 525 case ADDED:
466 526 case UPDATED:
... ... @@ -469,8 +529,8 @@ public final class EdgeGrpcSession implements Closeable {
469 529 if (entityView != null) {
470 530 EntityViewUpdateMsg entityViewUpdateMsg =
471 531 ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView);
472   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
473   - .setEntityViewUpdateMsg(entityViewUpdateMsg)
  532 + downlinkMsg = DownlinkMsg.newBuilder()
  533 + .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg))
474 534 .build();
475 535 }
476 536 break;
... ... @@ -478,21 +538,17 @@ public final class EdgeGrpcSession implements Closeable {
478 538 case UNASSIGNED_FROM_EDGE:
479 539 EntityViewUpdateMsg entityViewUpdateMsg =
480 540 ctx.getEntityViewUpdateMsgConstructor().constructEntityViewDeleteMsg(entityViewId);
481   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
482   - .setEntityViewUpdateMsg(entityViewUpdateMsg)
  541 + downlinkMsg = DownlinkMsg.newBuilder()
  542 + .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg))
483 543 .build();
484 544 break;
485 545 }
486   - if (entityUpdateMsg != null) {
487   - outputStream.onNext(ResponseMsg.newBuilder()
488   - .setEntityUpdateMsg(entityUpdateMsg)
489   - .build());
490   - }
  546 + return downlinkMsg;
491 547 }
492 548
493   - private void processDashboard(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
  549 + private DownlinkMsg processDashboard(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
494 550 DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId());
495   - EntityUpdateMsg entityUpdateMsg = null;
  551 + DownlinkMsg downlinkMsg = null;
496 552 switch (edgeEventAction) {
497 553 case ADDED:
498 554 case UPDATED:
... ... @@ -501,8 +557,8 @@ public final class EdgeGrpcSession implements Closeable {
501 557 if (dashboard != null) {
502 558 DashboardUpdateMsg dashboardUpdateMsg =
503 559 ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard);
504   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
505   - .setDashboardUpdateMsg(dashboardUpdateMsg)
  560 + downlinkMsg = DownlinkMsg.newBuilder()
  561 + .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg))
506 562 .build();
507 563 }
508 564 break;
... ... @@ -510,21 +566,17 @@ public final class EdgeGrpcSession implements Closeable {
510 566 case UNASSIGNED_FROM_EDGE:
511 567 DashboardUpdateMsg dashboardUpdateMsg =
512 568 ctx.getDashboardUpdateMsgConstructor().constructDashboardDeleteMsg(dashboardId);
513   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
514   - .setDashboardUpdateMsg(dashboardUpdateMsg)
  569 + downlinkMsg = DownlinkMsg.newBuilder()
  570 + .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg))
515 571 .build();
516 572 break;
517 573 }
518   - if (entityUpdateMsg != null) {
519   - outputStream.onNext(ResponseMsg.newBuilder()
520   - .setEntityUpdateMsg(entityUpdateMsg)
521   - .build());
522   - }
  574 + return downlinkMsg;
523 575 }
524 576
525   - private void processCustomer(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
  577 + private DownlinkMsg processCustomer(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
526 578 CustomerId customerId = new CustomerId(edgeEvent.getEntityId());
527   - EntityUpdateMsg entityUpdateMsg = null;
  579 + DownlinkMsg downlinkMsg = null;
528 580 switch (edgeEventAction) {
529 581 case ADDED:
530 582 case UPDATED:
... ... @@ -532,29 +584,25 @@ public final class EdgeGrpcSession implements Closeable {
532 584 if (customer != null) {
533 585 CustomerUpdateMsg customerUpdateMsg =
534 586 ctx.getCustomerUpdateMsgConstructor().constructCustomerUpdatedMsg(msgType, customer);
535   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
536   - .setCustomerUpdateMsg(customerUpdateMsg)
  587 + downlinkMsg = DownlinkMsg.newBuilder()
  588 + .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg))
537 589 .build();
538 590 }
539 591 break;
540 592 case DELETED:
541 593 CustomerUpdateMsg customerUpdateMsg =
542 594 ctx.getCustomerUpdateMsgConstructor().constructCustomerDeleteMsg(customerId);
543   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
544   - .setCustomerUpdateMsg(customerUpdateMsg)
  595 + downlinkMsg = DownlinkMsg.newBuilder()
  596 + .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg))
545 597 .build();
546 598 break;
547 599 }
548   - if (entityUpdateMsg != null) {
549   - outputStream.onNext(ResponseMsg.newBuilder()
550   - .setEntityUpdateMsg(entityUpdateMsg)
551   - .build());
552   - }
  600 + return downlinkMsg;
553 601 }
554 602
555   - private void processRuleChain(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
  603 + private DownlinkMsg processRuleChain(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeEventAction) {
556 604 RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());
557   - EntityUpdateMsg entityUpdateMsg = null;
  605 + DownlinkMsg downlinkMsg = null;
558 606 switch (edgeEventAction) {
559 607 case ADDED:
560 608 case UPDATED:
... ... @@ -563,46 +611,41 @@ public final class EdgeGrpcSession implements Closeable {
563 611 if (ruleChain != null) {
564 612 RuleChainUpdateMsg ruleChainUpdateMsg =
565 613 ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain);
566   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
567   - .setRuleChainUpdateMsg(ruleChainUpdateMsg)
  614 + downlinkMsg = DownlinkMsg.newBuilder()
  615 + .addAllRuleChainUpdateMsg(Collections.singletonList(ruleChainUpdateMsg))
568 616 .build();
569 617 }
570 618 break;
571 619 case DELETED:
572 620 case UNASSIGNED_FROM_EDGE:
573   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
574   - .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainDeleteMsg(ruleChainId))
  621 + downlinkMsg = DownlinkMsg.newBuilder()
  622 + .addAllRuleChainUpdateMsg(Collections.singletonList(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainDeleteMsg(ruleChainId)))
575 623 .build();
576 624 break;
577 625 }
578   - if (entityUpdateMsg != null) {
579   - outputStream.onNext(ResponseMsg.newBuilder()
580   - .setEntityUpdateMsg(entityUpdateMsg)
581   - .build());
582   - }
  626 + return downlinkMsg;
583 627 }
584 628
585   - private void processRuleChainMetadata(EdgeEvent edgeEvent, UpdateMsgType msgType) {
  629 + private DownlinkMsg processRuleChainMetadata(EdgeEvent edgeEvent, UpdateMsgType msgType) {
586 630 RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());
587 631 RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId);
  632 + DownlinkMsg downlinkMsg = null;
588 633 if (ruleChain != null) {
589 634 RuleChainMetaData ruleChainMetaData = ctx.getRuleChainService().loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId);
590 635 RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =
591 636 ctx.getRuleChainUpdateMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData);
592 637 if (ruleChainMetadataUpdateMsg != null) {
593   - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
594   - .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg)
  638 + downlinkMsg = DownlinkMsg.newBuilder()
  639 + .addAllRuleChainMetadataUpdateMsg(Collections.singletonList(ruleChainMetadataUpdateMsg))
595 640 .build();
596   - outputStream.onNext(ResponseMsg.newBuilder()
597   - .setEntityUpdateMsg(entityUpdateMsg)
598   - .build());
599 641 }
600 642 }
  643 + return downlinkMsg;
601 644 }
602 645
603   - private void processUser(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
  646 + private DownlinkMsg processUser(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
604 647 UserId userId = new UserId(edgeEvent.getEntityId());
605   - EntityUpdateMsg entityUpdateMsg = null;
  648 + DownlinkMsg downlinkMsg = null;
606 649 switch (edgeActionType) {
607 650 case ADDED:
608 651 case UPDATED:
... ... @@ -612,15 +655,15 @@ public final class EdgeGrpcSession implements Closeable {
612 655 boolean fullAccess = Authority.TENANT_ADMIN.equals(user.getAuthority());
613 656 setFullAccess(user, fullAccess);
614 657
615   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
616   - .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user))
  658 + downlinkMsg = DownlinkMsg.newBuilder()
  659 + .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)))
617 660 .build();
618 661 }
619 662 break;
620 663 case DELETED:
621 664 case UNASSIGNED_FROM_EDGE:
622   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
623   - .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserDeleteMsg(userId))
  665 + downlinkMsg = DownlinkMsg.newBuilder()
  666 + .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserUpdateMsgConstructor().constructUserDeleteMsg(userId)))
624 667 .build();
625 668 break;
626 669 case CREDENTIALS_UPDATED:
... ... @@ -628,16 +671,12 @@ public final class EdgeGrpcSession implements Closeable {
628 671 if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {
629 672 UserCredentialsUpdateMsg userCredentialsUpdateMsg =
630 673 ctx.getUserUpdateMsgConstructor().constructUserCredentialsUpdatedMsg(userCredentialsByUserId);
631   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
632   - .setUserCredentialsUpdateMsg(userCredentialsUpdateMsg)
  674 + downlinkMsg = DownlinkMsg.newBuilder()
  675 + .addAllUserCredentialsUpdateMsg(Collections.singletonList(userCredentialsUpdateMsg))
633 676 .build();
634 677 }
635 678 }
636   - if (entityUpdateMsg != null) {
637   - outputStream.onNext(ResponseMsg.newBuilder()
638   - .setEntityUpdateMsg(entityUpdateMsg)
639   - .build());
640   - }
  679 + return downlinkMsg;
641 680 }
642 681
643 682 private void setFullAccess(User user, boolean isFullAccess) {
... ... @@ -649,36 +688,33 @@ public final class EdgeGrpcSession implements Closeable {
649 688 user.setAdditionalInfo(additionalInfo);
650 689 }
651 690
652   - private void processRelation(EdgeEvent edgeEvent, UpdateMsgType msgType) {
  691 + private DownlinkMsg processRelation(EdgeEvent edgeEvent, UpdateMsgType msgType) {
653 692 EntityRelation entityRelation = mapper.convertValue(edgeEvent.getEntityBody(), EntityRelation.class);
654   - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
655   - .setRelationUpdateMsg(ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation))
  693 + RelationUpdateMsg r = ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation);
  694 + return DownlinkMsg.newBuilder()
  695 + .addAllRelationUpdateMsg(Collections.singletonList(r))
656 696 .build();
657   - outputStream.onNext(ResponseMsg.newBuilder()
658   - .setEntityUpdateMsg(entityUpdateMsg)
659   - .build());
660 697 }
661 698
662   - private void processAlarm(EdgeEvent edgeEvent, UpdateMsgType msgType) {
  699 + private DownlinkMsg processAlarm(EdgeEvent edgeEvent, UpdateMsgType msgType) {
  700 + DownlinkMsg downlinkMsg = null;
663 701 try {
664 702 AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
665 703 Alarm alarm = ctx.getAlarmService().findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
666 704 if (alarm != null) {
667   - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
668   - .setAlarmUpdateMsg(ctx.getAlarmUpdateMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))
  705 + downlinkMsg = DownlinkMsg.newBuilder()
  706 + .addAllAlarmUpdateMsg(Collections.singletonList(ctx.getAlarmUpdateMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)))
669 707 .build();
670   - outputStream.onNext(ResponseMsg.newBuilder()
671   - .setEntityUpdateMsg(entityUpdateMsg)
672   - .build());
673 708 }
674 709 } catch (Exception e) {
675 710 log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
676 711 }
  712 + return downlinkMsg;
677 713 }
678 714
679   - private void processWidgetsBundle(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
  715 + private DownlinkMsg processWidgetsBundle(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
680 716 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId());
681   - EntityUpdateMsg entityUpdateMsg = null;
  717 + DownlinkMsg downlinkMsg = null;
682 718 switch (edgeActionType) {
683 719 case ADDED:
684 720 case UPDATED:
... ... @@ -686,29 +722,25 @@ public final class EdgeGrpcSession implements Closeable {
686 722 if (widgetsBundle != null) {
687 723 WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
688 724 ctx.getWidgetsBundleUpdateMsgConstructor().constructWidgetsBundleUpdateMsg(msgType, widgetsBundle);
689   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
690   - .setWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg)
  725 + downlinkMsg = DownlinkMsg.newBuilder()
  726 + .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg))
691 727 .build();
692 728 }
693 729 break;
694 730 case DELETED:
695 731 WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =
696 732 ctx.getWidgetsBundleUpdateMsgConstructor().constructWidgetsBundleDeleteMsg(widgetsBundleId);
697   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
698   - .setWidgetsBundleUpdateMsg(widgetsBundleUpdateMsg)
  733 + downlinkMsg = DownlinkMsg.newBuilder()
  734 + .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg))
699 735 .build();
700 736 break;
701 737 }
702   - if (entityUpdateMsg != null) {
703   - outputStream.onNext(ResponseMsg.newBuilder()
704   - .setEntityUpdateMsg(entityUpdateMsg)
705   - .build());
706   - }
  738 + return downlinkMsg;
707 739 }
708 740
709   - private void processWidgetType(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
  741 + private DownlinkMsg processWidgetType(EdgeEvent edgeEvent, UpdateMsgType msgType, ActionType edgeActionType) {
710 742 WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId());
711   - EntityUpdateMsg entityUpdateMsg = null;
  743 + DownlinkMsg downlinkMsg = null;
712 744 switch (edgeActionType) {
713 745 case ADDED:
714 746 case UPDATED:
... ... @@ -716,34 +748,28 @@ public final class EdgeGrpcSession implements Closeable {
716 748 if (widgetType != null) {
717 749 WidgetTypeUpdateMsg widgetTypeUpdateMsg =
718 750 ctx.getWidgetTypeUpdateMsgConstructor().constructWidgetTypeUpdateMsg(msgType, widgetType);
719   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
720   - .setWidgetTypeUpdateMsg(widgetTypeUpdateMsg)
  751 + downlinkMsg = DownlinkMsg.newBuilder()
  752 + .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg))
721 753 .build();
722 754 }
723 755 break;
724 756 case DELETED:
725 757 WidgetTypeUpdateMsg widgetTypeUpdateMsg =
726 758 ctx.getWidgetTypeUpdateMsgConstructor().constructWidgetTypeDeleteMsg(widgetTypeId);
727   - entityUpdateMsg = EntityUpdateMsg.newBuilder()
728   - .setWidgetTypeUpdateMsg(widgetTypeUpdateMsg)
  759 + downlinkMsg = DownlinkMsg.newBuilder()
  760 + .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg))
729 761 .build();
730 762 break;
731 763 }
732   - if (entityUpdateMsg != null) {
733   - outputStream.onNext(ResponseMsg.newBuilder()
734   - .setEntityUpdateMsg(entityUpdateMsg)
735   - .build());
736   - }
  764 + return downlinkMsg;
737 765 }
738 766
739   - private void processAdminSettings(EdgeEvent edgeEvent) {
  767 + private DownlinkMsg processAdminSettings(EdgeEvent edgeEvent) {
740 768 AdminSettings adminSettings = mapper.convertValue(edgeEvent.getEntityBody(), AdminSettings.class);
741   - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
742   - .setAdminSettingsUpdateMsg(ctx.getAdminSettingsUpdateMsgConstructor().constructAdminSettingsUpdateMsg(adminSettings))
  769 + AdminSettingsUpdateMsg t = ctx.getAdminSettingsUpdateMsgConstructor().constructAdminSettingsUpdateMsg(adminSettings);
  770 + return DownlinkMsg.newBuilder()
  771 + .addAllAdminSettingsUpdateMsg(Collections.singletonList(t))
743 772 .build();
744   - outputStream.onNext(ResponseMsg.newBuilder()
745   - .setEntityUpdateMsg(entityUpdateMsg)
746   - .build());
747 773 }
748 774
749 775 private UpdateMsgType getResponseMsgType(ActionType actionType) {
... ... @@ -775,112 +801,101 @@ public final class EdgeGrpcSession implements Closeable {
775 801 return builder.build();
776 802 }
777 803
778   - private UplinkResponseMsg processUplinkMsg(UplinkMsg uplinkMsg) {
  804 + private ListenableFuture<List<Void>> processUplinkMsg(UplinkMsg uplinkMsg) {
  805 + List<ListenableFuture<Void>> result = new ArrayList<>();
779 806 try {
780 807 if (uplinkMsg.getEntityDataList() != null && !uplinkMsg.getEntityDataList().isEmpty()) {
781 808 for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) {
782 809 EntityId entityId = constructEntityId(entityData);
783 810 if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg()) && entityId != null) {
784   - ListenableFuture<TbMsgMetaData> metaDataFuture = constructBaseMsgMetadata(entityId);
785   - Futures.transform(metaDataFuture, metaData -> {
786   - if (metaData != null) {
787   - metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE);
788   - if (entityData.hasPostAttributesMsg()) {
789   - processPostAttributes(entityId, entityData.getPostAttributesMsg(), metaData);
790   - }
791   - if (entityData.hasPostTelemetryMsg()) {
792   - processPostTelemetry(entityId, entityData.getPostTelemetryMsg(), metaData);
793   - }
794   - }
795   - return null;
796   - }, ctx.getDbCallbackExecutor());
  811 + TbMsgMetaData metaData = constructBaseMsgMetadata(entityId);
  812 + metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE);
  813 + if (entityData.hasPostAttributesMsg()) {
  814 + metaData.putValue("scope", entityData.getPostAttributeScope());
  815 + result.add(processPostAttributes(entityId, entityData.getPostAttributesMsg(), metaData));
  816 + }
  817 + if (entityData.hasPostTelemetryMsg()) {
  818 + result.add(processPostTelemetry(entityId, entityData.getPostTelemetryMsg(), metaData));
  819 + }
797 820 }
798 821 }
799 822 }
800 823
801 824 if (uplinkMsg.getDeviceUpdateMsgList() != null && !uplinkMsg.getDeviceUpdateMsgList().isEmpty()) {
802 825 for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) {
803   - onDeviceUpdate(deviceUpdateMsg);
  826 + result.add(onDeviceUpdate(deviceUpdateMsg));
804 827 }
805 828 }
806 829 if (uplinkMsg.getDeviceCredentialsUpdateMsgList() != null && !uplinkMsg.getDeviceCredentialsUpdateMsgList().isEmpty()) {
807 830 for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) {
808   - onDeviceCredentialsUpdate(deviceCredentialsUpdateMsg);
  831 + result.add(onDeviceCredentialsUpdate(deviceCredentialsUpdateMsg));
809 832 }
810 833 }
811 834 if (uplinkMsg.getAlarmUpdateMsgList() != null && !uplinkMsg.getAlarmUpdateMsgList().isEmpty()) {
812 835 for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) {
813   - onAlarmUpdate(alarmUpdateMsg);
  836 + result.add(onAlarmUpdate(alarmUpdateMsg));
814 837 }
815 838 }
816 839 if (uplinkMsg.getRuleChainMetadataRequestMsgList() != null && !uplinkMsg.getRuleChainMetadataRequestMsgList().isEmpty()) {
817 840 for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) {
818   - ctx.getSyncEdgeService().processRuleChainMetadataRequestMsg(edge, ruleChainMetadataRequestMsg);
  841 + result.add(ctx.getSyncEdgeService().processRuleChainMetadataRequestMsg(edge, ruleChainMetadataRequestMsg));
819 842 }
820 843 }
821 844 if (uplinkMsg.getAttributesRequestMsgList() != null && !uplinkMsg.getAttributesRequestMsgList().isEmpty()) {
822 845 for (AttributesRequestMsg attributesRequestMsg : uplinkMsg.getAttributesRequestMsgList()) {
823   - ctx.getSyncEdgeService().processAttributesRequestMsg(edge, attributesRequestMsg);
  846 + result.add(ctx.getSyncEdgeService().processAttributesRequestMsg(edge, attributesRequestMsg));
824 847 }
825 848 }
826 849 if (uplinkMsg.getRelationRequestMsgList() != null && !uplinkMsg.getRelationRequestMsgList().isEmpty()) {
827 850 for (RelationRequestMsg relationRequestMsg : uplinkMsg.getRelationRequestMsgList()) {
828   - ctx.getSyncEdgeService().processRelationRequestMsg(edge, relationRequestMsg);
  851 + result.add(ctx.getSyncEdgeService().processRelationRequestMsg(edge, relationRequestMsg));
829 852 }
830 853 }
831 854 if (uplinkMsg.getUserCredentialsRequestMsgList() != null && !uplinkMsg.getUserCredentialsRequestMsgList().isEmpty()) {
832 855 for (UserCredentialsRequestMsg userCredentialsRequestMsg : uplinkMsg.getUserCredentialsRequestMsgList()) {
833   - ctx.getSyncEdgeService().processUserCredentialsRequestMsg(edge, userCredentialsRequestMsg);
  856 + result.add(ctx.getSyncEdgeService().processUserCredentialsRequestMsg(edge, userCredentialsRequestMsg));
834 857 }
835 858 }
836 859 if (uplinkMsg.getDeviceCredentialsRequestMsgList() != null && !uplinkMsg.getDeviceCredentialsRequestMsgList().isEmpty()) {
837 860 for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg : uplinkMsg.getDeviceCredentialsRequestMsgList()) {
838   - ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge, deviceCredentialsRequestMsg);
  861 + result.add(ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge, deviceCredentialsRequestMsg));
839 862 }
840 863 }
841 864 } catch (Exception e) {
842   - return UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(e.getMessage()).build();
  865 + log.error("Can't process uplink msg [{}]", uplinkMsg, e);
843 866 }
844   -
845   - return UplinkResponseMsg.newBuilder().setSuccess(true).build();
  867 + return Futures.allAsList(result);
846 868 }
847 869
848   - private ListenableFuture<TbMsgMetaData> constructBaseMsgMetadata(EntityId entityId) {
  870 + private TbMsgMetaData constructBaseMsgMetadata(EntityId entityId) {
  871 + TbMsgMetaData metaData = new TbMsgMetaData();
849 872 switch (entityId.getEntityType()) {
850 873 case DEVICE:
851   - ListenableFuture<Device> deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edge.getTenantId(), new DeviceId(entityId.getId()));
852   - return Futures.transform(deviceFuture, device -> {
853   - TbMsgMetaData metaData = new TbMsgMetaData();
854   - if (device != null) {
855   - metaData.putValue("deviceName", device.getName());
856   - metaData.putValue("deviceType", device.getType());
857   - }
858   - return metaData;
859   - }, ctx.getDbCallbackExecutor());
  874 + Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), new DeviceId(entityId.getId()));
  875 + if (device != null) {
  876 + metaData.putValue("deviceName", device.getName());
  877 + metaData.putValue("deviceType", device.getType());
  878 + }
  879 + break;
860 880 case ASSET:
861   - ListenableFuture<Asset> assetFuture = ctx.getAssetService().findAssetByIdAsync(edge.getTenantId(), new AssetId(entityId.getId()));
862   - return Futures.transform(assetFuture, asset -> {
863   - TbMsgMetaData metaData = new TbMsgMetaData();
864   - if (asset != null) {
865   - metaData.putValue("assetName", asset.getName());
866   - metaData.putValue("assetType", asset.getType());
867   - }
868   - return metaData;
869   - }, ctx.getDbCallbackExecutor());
  881 + Asset asset = ctx.getAssetService().findAssetById(edge.getTenantId(), new AssetId(entityId.getId()));
  882 + if (asset != null) {
  883 + metaData.putValue("assetName", asset.getName());
  884 + metaData.putValue("assetType", asset.getType());
  885 + }
  886 + break;
870 887 case ENTITY_VIEW:
871   - ListenableFuture<EntityView> entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edge.getTenantId(), new EntityViewId(entityId.getId()));
872   - return Futures.transform(entityViewFuture, entityView -> {
873   - TbMsgMetaData metaData = new TbMsgMetaData();
874   - if (entityView != null) {
875   - metaData.putValue("entityViewName", entityView.getName());
876   - metaData.putValue("entityViewType", entityView.getType());
877   - }
878   - return metaData;
879   - }, ctx.getDbCallbackExecutor());
  888 + EntityView entityView = ctx.getEntityViewService().findEntityViewById(edge.getTenantId(), new EntityViewId(entityId.getId()));
  889 + if (entityView != null) {
  890 + metaData.putValue("entityViewName", entityView.getName());
  891 + metaData.putValue("entityViewType", entityView.getType());
  892 + }
  893 + break;
880 894 default:
881   - log.debug("Constructing empty metadata for entityId [{}]", entityId);
882   - return Futures.immediateFuture(new TbMsgMetaData());
  895 + log.debug("Using empty metadata for entityId [{}]", entityId);
  896 + break;
883 897 }
  898 + return metaData;
884 899 }
885 900
886 901 private EntityId constructEntityId(EntityDataProto entityData) {
... ... @@ -904,7 +919,7 @@ public final class EdgeGrpcSession implements Closeable {
904 919 }
905 920 }
906 921
907   - private void processPostTelemetry(EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) {
  922 + private ListenableFuture<Void> processPostTelemetry(EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) {
908 923 for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
909 924 JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
910 925 metaData.putValue("ts", tsKv.getTs() + "");
... ... @@ -912,17 +927,18 @@ public final class EdgeGrpcSession implements Closeable {
912 927 // TODO: voba - verify that null callback is OK
913 928 ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null);
914 929 }
  930 + return Futures.immediateFuture(null);
915 931 }
916 932
917   - private void processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
  933 + private ListenableFuture<Void> processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
918 934 JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
919 935 TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json));
920 936 // TODO: voba - verify that null callback is OK
921 937 ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null);
  938 + return Futures.immediateFuture(null);
922 939 }
923 940
924   - private void onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) {
925   - log.info("onDeviceUpdate {}", deviceUpdateMsg);
  941 + private ListenableFuture<Void> onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) {
926 942 DeviceId edgeDeviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
927 943 switch (deviceUpdateMsg.getMsgType()) {
928 944 case ENTITY_CREATED_RPC_MESSAGE:
... ... @@ -931,11 +947,12 @@ public final class EdgeGrpcSession implements Closeable {
931 947 if (device != null) {
932 948 // device with this name already exists on the cloud - update ID on the edge
933 949 if (!device.getId().equals(edgeDeviceId)) {
934   - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
935   - .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device))
  950 + DeviceUpdateMsg d = ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device);
  951 + DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()
  952 + .addAllDeviceUpdateMsg(Collections.singletonList(d))
936 953 .build();
937   - outputStream.onNext(ResponseMsg.newBuilder()
938   - .setEntityUpdateMsg(entityUpdateMsg)
  954 + sendResponseMsg(ResponseMsg.newBuilder()
  955 + .setDownlinkMsg(downlinkMsg)
939 956 .build());
940 957 }
941 958 } else {
... ... @@ -943,11 +960,12 @@ public final class EdgeGrpcSession implements Closeable {
943 960 if (deviceById != null) {
944 961 // this ID already used by other device - create new device and update ID on the edge
945 962 device = createDevice(deviceUpdateMsg);
946   - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
947   - .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device))
  963 + DeviceUpdateMsg d = ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device);
  964 + DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()
  965 + .addAllDeviceUpdateMsg(Collections.singletonList(d))
948 966 .build();
949   - outputStream.onNext(ResponseMsg.newBuilder()
950   - .setEntityUpdateMsg(entityUpdateMsg)
  967 + sendResponseMsg(ResponseMsg.newBuilder()
  968 + .setDownlinkMsg(downlinkMsg)
951 969 .build());
952 970 } else {
953 971 device = createDevice(deviceUpdateMsg);
... ... @@ -966,8 +984,10 @@ public final class EdgeGrpcSession implements Closeable {
966 984 }
967 985 break;
968 986 case UNRECOGNIZED:
969   - log.error("Unsupported msg type");
  987 + log.error("Unsupported msg type {}", deviceUpdateMsg.getMsgType());
  988 + return Futures.immediateFailedFuture(new RuntimeException("Unsupported msg type " + deviceUpdateMsg.getMsgType()));
970 989 }
  990 + return Futures.immediateFuture(null);
971 991 }
972 992
973 993 private void updateDevice(DeviceUpdateMsg deviceUpdateMsg) {
... ... @@ -981,33 +1001,26 @@ public final class EdgeGrpcSession implements Closeable {
981 1001 requestDeviceCredentialsFromEdge(device);
982 1002 }
983 1003
984   - private void onDeviceCredentialsUpdate(DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
  1004 + private ListenableFuture<Void> onDeviceCredentialsUpdate(DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
985 1005 log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg);
986 1006 DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()));
987 1007 ListenableFuture<Device> deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edge.getTenantId(), deviceId);
988   -
989   - Futures.addCallback(deviceFuture, new FutureCallback<Device>() {
990   - @Override
991   - public void onSuccess(@Nullable Device device) {
992   - if (device != null) {
993   - log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]",
994   - device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue());
995   - try {
996   - DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), device.getId());
997   - deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType()));
998   - deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId());
999   - deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue());
1000   - ctx.getDeviceCredentialsService().updateDeviceCredentials(edge.getTenantId(), deviceCredentials);
1001   - } catch (Exception e) {
1002   - log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e);
1003   - }
  1008 + return Futures.transform(deviceFuture, device -> {
  1009 + if (device != null) {
  1010 + log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]",
  1011 + device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue());
  1012 + try {
  1013 + DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), device.getId());
  1014 + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType()));
  1015 + deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId());
  1016 + deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue());
  1017 + ctx.getDeviceCredentialsService().updateDeviceCredentials(edge.getTenantId(), deviceCredentials);
  1018 + } catch (Exception e) {
  1019 + log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e);
  1020 + throw new RuntimeException(e);
1004 1021 }
1005 1022 }
1006   -
1007   - @Override
1008   - public void onFailure(Throwable t) {
1009   - log.error("Can't update device credentials for deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg, t);
1010   - }
  1023 + return null;
1011 1024 }, ctx.getDbCallbackExecutor());
1012 1025 }
1013 1026
... ... @@ -1015,7 +1028,7 @@ public final class EdgeGrpcSession implements Closeable {
1015 1028 log.debug("Executing requestDeviceCredentialsFromEdge device [{}]", device);
1016 1029
1017 1030 DownlinkMsg downlinkMsg = constructDeviceCredentialsRequestMsg(device.getId());
1018   - outputStream.onNext(ResponseMsg.newBuilder()
  1031 + sendResponseMsg(ResponseMsg.newBuilder()
1019 1032 .setDownlinkMsg(downlinkMsg)
1020 1033 .build());
1021 1034 }
... ... @@ -1118,49 +1131,52 @@ public final class EdgeGrpcSession implements Closeable {
1118 1131 }
1119 1132 }
1120 1133
1121   - private void onAlarmUpdate(AlarmUpdateMsg alarmUpdateMsg) {
  1134 + private ListenableFuture<Void> onAlarmUpdate(AlarmUpdateMsg alarmUpdateMsg) {
1122 1135 EntityId originatorId = getAlarmOriginator(alarmUpdateMsg.getOriginatorName(), org.thingsboard.server.common.data.EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
1123   - if (originatorId != null) {
1124   - try {
1125   - Alarm existentAlarm = ctx.getAlarmService().findLatestByOriginatorAndType(edge.getTenantId(), originatorId, alarmUpdateMsg.getType()).get();
1126   - switch (alarmUpdateMsg.getMsgType()) {
1127   - case ENTITY_CREATED_RPC_MESSAGE:
1128   - case ENTITY_UPDATED_RPC_MESSAGE:
1129   - if (existentAlarm == null || existentAlarm.getStatus().isCleared()) {
1130   - existentAlarm = new Alarm();
1131   - existentAlarm.setTenantId(edge.getTenantId());
1132   - existentAlarm.setType(alarmUpdateMsg.getName());
1133   - existentAlarm.setOriginator(originatorId);
1134   - existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
1135   - existentAlarm.setStartTs(alarmUpdateMsg.getStartTs());
1136   - existentAlarm.setClearTs(alarmUpdateMsg.getClearTs());
1137   - existentAlarm.setPropagate(alarmUpdateMsg.getPropagate());
1138   - }
1139   - existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus()));
1140   - existentAlarm.setAckTs(alarmUpdateMsg.getAckTs());
1141   - existentAlarm.setEndTs(alarmUpdateMsg.getEndTs());
1142   - existentAlarm.setDetails(mapper.readTree(alarmUpdateMsg.getDetails()));
1143   - ctx.getAlarmService().createOrUpdateAlarm(existentAlarm);
1144   - break;
1145   - case ALARM_ACK_RPC_MESSAGE:
1146   - if (existentAlarm != null) {
1147   - ctx.getAlarmService().ackAlarm(edge.getTenantId(), existentAlarm.getId(), alarmUpdateMsg.getAckTs());
1148   - }
1149   - break;
1150   - case ALARM_CLEAR_RPC_MESSAGE:
1151   - if (existentAlarm != null) {
1152   - ctx.getAlarmService().clearAlarm(edge.getTenantId(), existentAlarm.getId(), mapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs());
1153   - }
1154   - break;
1155   - case ENTITY_DELETED_RPC_MESSAGE:
1156   - if (existentAlarm != null) {
1157   - ctx.getAlarmService().deleteAlarm(edge.getTenantId(), existentAlarm.getId());
1158   - }
1159   - break;
1160   - }
1161   - } catch (Exception e) {
1162   - log.error("Error during finding existent alarm", e);
  1136 + if (originatorId == null) {
  1137 + return Futures.immediateFuture(null);
  1138 + }
  1139 + try {
  1140 + Alarm existentAlarm = ctx.getAlarmService().findLatestByOriginatorAndType(edge.getTenantId(), originatorId, alarmUpdateMsg.getType()).get();
  1141 + switch (alarmUpdateMsg.getMsgType()) {
  1142 + case ENTITY_CREATED_RPC_MESSAGE:
  1143 + case ENTITY_UPDATED_RPC_MESSAGE:
  1144 + if (existentAlarm == null || existentAlarm.getStatus().isCleared()) {
  1145 + existentAlarm = new Alarm();
  1146 + existentAlarm.setTenantId(edge.getTenantId());
  1147 + existentAlarm.setType(alarmUpdateMsg.getName());
  1148 + existentAlarm.setOriginator(originatorId);
  1149 + existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
  1150 + existentAlarm.setStartTs(alarmUpdateMsg.getStartTs());
  1151 + existentAlarm.setClearTs(alarmUpdateMsg.getClearTs());
  1152 + existentAlarm.setPropagate(alarmUpdateMsg.getPropagate());
  1153 + }
  1154 + existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus()));
  1155 + existentAlarm.setAckTs(alarmUpdateMsg.getAckTs());
  1156 + existentAlarm.setEndTs(alarmUpdateMsg.getEndTs());
  1157 + existentAlarm.setDetails(mapper.readTree(alarmUpdateMsg.getDetails()));
  1158 + ctx.getAlarmService().createOrUpdateAlarm(existentAlarm);
  1159 + break;
  1160 + case ALARM_ACK_RPC_MESSAGE:
  1161 + if (existentAlarm != null) {
  1162 + ctx.getAlarmService().ackAlarm(edge.getTenantId(), existentAlarm.getId(), alarmUpdateMsg.getAckTs());
  1163 + }
  1164 + break;
  1165 + case ALARM_CLEAR_RPC_MESSAGE:
  1166 + if (existentAlarm != null) {
  1167 + ctx.getAlarmService().clearAlarm(edge.getTenantId(), existentAlarm.getId(), mapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs());
  1168 + }
  1169 + break;
  1170 + case ENTITY_DELETED_RPC_MESSAGE:
  1171 + if (existentAlarm != null) {
  1172 + ctx.getAlarmService().deleteAlarm(edge.getTenantId(), existentAlarm.getId());
  1173 + }
  1174 + break;
1163 1175 }
  1176 + return Futures.immediateFuture(null);
  1177 + } catch (Exception e) {
  1178 + log.error("Error during finding existent alarm", e);
  1179 + return Futures.immediateFailedFuture(new RuntimeException("Error during finding existent alarm", e));
1164 1180 }
1165 1181 }
1166 1182
... ...
... ... @@ -316,64 +316,62 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
316 316 }
317 317
318 318 @Override
319   - public void processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
  319 + public ListenableFuture<Void> processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
320 320 if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
321 321 RuleChainId ruleChainId = new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
322   - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, ActionType.ADDED, ruleChainId, null);
  322 + ListenableFuture<EdgeEvent> future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, ActionType.ADDED, ruleChainId, null);
  323 + return Futures.transform(future, edgeEvent -> null, dbCallbackExecutorService);
323 324 }
  325 + return Futures.immediateFuture(null);
324 326 }
325 327
326 328 @Override
327   - public void processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg) {
  329 + public ListenableFuture<Void> processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg) {
328 330 EntityId entityId = EntityIdFactory.getByTypeAndUuid(
329 331 EntityType.valueOf(attributesRequestMsg.getEntityType()),
330 332 new UUID(attributesRequestMsg.getEntityIdMSB(), attributesRequestMsg.getEntityIdLSB()));
331 333 final EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(entityId.getEntityType());
332 334 if (edgeEventType != null) {
333 335 ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE);
334   - Futures.addCallback(ssAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
335   - @Override
336   - public void onSuccess(@Nullable List<AttributeKvEntry> ssAttributes) {
337   - if (ssAttributes != null && !ssAttributes.isEmpty()) {
338   - try {
339   - Map<String, Object> entityData = new HashMap<>();
340   - ObjectNode attributes = mapper.createObjectNode();
341   - for (AttributeKvEntry attr : ssAttributes) {
342   - if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
343   - attributes.put(attr.getKey(), attr.getBooleanValue().get());
344   - } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
345   - attributes.put(attr.getKey(), attr.getDoubleValue().get());
346   - } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
347   - attributes.put(attr.getKey(), attr.getLongValue().get());
348   - } else {
349   - attributes.put(attr.getKey(), attr.getValueAsString());
350   - }
  336 + return Futures.transform(ssAttrFuture, ssAttributes -> {
  337 + if (ssAttributes != null && !ssAttributes.isEmpty()) {
  338 + try {
  339 + Map<String, Object> entityData = new HashMap<>();
  340 + ObjectNode attributes = mapper.createObjectNode();
  341 + for (AttributeKvEntry attr : ssAttributes) {
  342 + if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
  343 + attributes.put(attr.getKey(), attr.getBooleanValue().get());
  344 + } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
  345 + attributes.put(attr.getKey(), attr.getDoubleValue().get());
  346 + } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
  347 + attributes.put(attr.getKey(), attr.getLongValue().get());
  348 + } else {
  349 + attributes.put(attr.getKey(), attr.getValueAsString());
351 350 }
352   - entityData.put("kv", attributes);
353   - entityData.put("scope", DataConstants.SERVER_SCOPE);
354   - JsonNode entityBody = mapper.valueToTree(entityData);
355   - log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, entityBody);
356   - saveEdgeEvent(edge.getTenantId(),
357   - edge.getId(),
358   - edgeEventType,
359   - ActionType.ATTRIBUTES_UPDATED,
360   - entityId,
361   - entityBody);
362   - } catch (Exception e) {
363   - log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
364 351 }
  352 + entityData.put("kv", attributes);
  353 + entityData.put("scope", DataConstants.SERVER_SCOPE);
  354 + JsonNode entityBody = mapper.valueToTree(entityData);
  355 + log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, entityBody);
  356 + saveEdgeEvent(edge.getTenantId(),
  357 + edge.getId(),
  358 + edgeEventType,
  359 + ActionType.ATTRIBUTES_UPDATED,
  360 + entityId,
  361 + entityBody);
  362 + } catch (Exception e) {
  363 + log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
  364 + throw new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e);
365 365 }
366 366 }
367   -
368   - @Override
369   - public void onFailure(Throwable t) {
370   -
371   - }
  367 + return null;
372 368 }, dbCallbackExecutorService);
373 369
374 370 // TODO: voba - push shared attributes to edge?
375   - ListenableFuture<List<AttributeKvEntry>> shAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE);
376   - ListenableFuture<List<AttributeKvEntry>> clAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE);
  371 + // ListenableFuture<List<AttributeKvEntry>> shAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE);
  372 + // ListenableFuture<List<AttributeKvEntry>> clAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE);
  373 + } else {
  374 + return Futures.immediateFuture(null);
377 375 }
378 376 }
379 377
... ... @@ -391,7 +389,7 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
391 389 }
392 390
393 391 @Override
394   - public void processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg) {
  392 + public ListenableFuture<Void> processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg) {
395 393 EntityId entityId = EntityIdFactory.getByTypeAndUuid(
396 394 EntityType.valueOf(relationRequestMsg.getEntityType()),
397 395 new UUID(relationRequestMsg.getEntityIdMSB(), relationRequestMsg.getEntityIdLSB()));
... ... @@ -400,39 +398,33 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
400 398 futures.add(findRelationByQuery(edge, entityId, EntitySearchDirection.FROM));
401 399 futures.add(findRelationByQuery(edge, entityId, EntitySearchDirection.TO));
402 400 ListenableFuture<List<List<EntityRelation>>> relationsListFuture = Futures.allAsList(futures);
403   - Futures.addCallback(relationsListFuture, new FutureCallback<List<List<EntityRelation>>>() {
404   - @Override
405   - public void onSuccess(@Nullable List<List<EntityRelation>> relationsList) {
406   - try {
407   - if (!relationsList.isEmpty()) {
408   - for (List<EntityRelation> entityRelations : relationsList) {
409   - log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size());
410   - for (EntityRelation relation : entityRelations) {
411   - try {
412   - if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
413   - !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
414   - saveEdgeEvent(edge.getTenantId(),
415   - edge.getId(),
416   - EdgeEventType.RELATION,
417   - ActionType.ADDED,
418   - null,
419   - mapper.valueToTree(relation));
420   - }
421   - } catch (Exception e) {
422   - log.error("Exception during loading relation [{}] to edge on sync!", relation, e);
  401 + return Futures.transform(relationsListFuture, relationsList -> {
  402 + try {
  403 + if (relationsList != null && !relationsList.isEmpty()) {
  404 + for (List<EntityRelation> entityRelations : relationsList) {
  405 + log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size());
  406 + for (EntityRelation relation : entityRelations) {
  407 + try {
  408 + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
  409 + !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
  410 + saveEdgeEvent(edge.getTenantId(),
  411 + edge.getId(),
  412 + EdgeEventType.RELATION,
  413 + ActionType.ADDED,
  414 + null,
  415 + mapper.valueToTree(relation));
423 416 }
  417 + } catch (Exception e) {
  418 + log.error("Exception during loading relation [{}] to edge on sync!", relation, e);
424 419 }
425 420 }
426 421 }
427   - } catch (Exception e) {
428   - log.error("Exception during loading relation(s) to edge on sync!", e);
429 422 }
  423 + } catch (Exception e) {
  424 + log.error("Exception during loading relation(s) to edge on sync!", e);
  425 + throw new RuntimeException("Exception during loading relation(s) to edge on sync!", e);
430 426 }
431   -
432   - @Override
433   - public void onFailure(Throwable t) {
434   - log.error("Exception during loading relation(s) to edge on sync!", t);
435   - }
  427 + return null;
436 428 }, dbCallbackExecutorService);
437 429 }
438 430
... ... @@ -443,22 +435,26 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
443 435 }
444 436
445 437 @Override
446   - public void processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) {
  438 + public ListenableFuture<Void> processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) {
447 439 if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) {
448 440 DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
449   - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED, deviceId, null);
  441 + ListenableFuture<EdgeEvent> future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED, deviceId, null);
  442 + return Futures.transform(future, edgeEvent -> null, dbCallbackExecutorService);
450 443 }
  444 + return Futures.immediateFuture(null);
451 445 }
452 446
453 447 @Override
454   - public void processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) {
  448 + public ListenableFuture<Void> processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) {
455 449 if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) {
456 450 UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
457   - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.CREDENTIALS_UPDATED, userId, null);
  451 + ListenableFuture<EdgeEvent> future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.CREDENTIALS_UPDATED, userId, null);
  452 + return Futures.transform(future, edgeEvent -> null, dbCallbackExecutorService);
458 453 }
  454 + return Futures.immediateFuture(null);
459 455 }
460 456
461   - private void saveEdgeEvent(TenantId tenantId,
  457 + private ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
462 458 EdgeId edgeId,
463 459 EdgeEventType edgeEventType,
464 460 ActionType edgeEventAction,
... ... @@ -476,6 +472,6 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
476 472 edgeEvent.setEntityId(entityId.getId());
477 473 }
478 474 edgeEvent.setEntityBody(entityBody);
479   - edgeEventService.saveAsync(edgeEvent);
  475 + return edgeEventService.saveAsync(edgeEvent);
480 476 }
481 477 }
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.edge.rpc.init;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 19 import org.thingsboard.server.common.data.edge.Edge;
19 20 import org.thingsboard.server.gen.edge.AttributesRequestMsg;
20 21 import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
... ... @@ -26,13 +27,13 @@ public interface SyncEdgeService {
26 27
27 28 void sync(Edge edge);
28 29
29   - void processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg);
  30 + ListenableFuture<Void> processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg);
30 31
31   - void processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg);
  32 + ListenableFuture<Void> processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg);
32 33
33   - void processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg);
  34 + ListenableFuture<Void> processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg);
34 35
35   - void processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg);
  36 + ListenableFuture<Void> processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg);
36 37
37   - void processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg);
  38 + ListenableFuture<Void> processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg);
38 39 }
... ...
... ... @@ -28,9 +28,9 @@ import org.thingsboard.server.gen.edge.ConnectRequestMsg;
28 28 import org.thingsboard.server.gen.edge.ConnectResponseCode;
29 29 import org.thingsboard.server.gen.edge.ConnectResponseMsg;
30 30 import org.thingsboard.server.gen.edge.DownlinkMsg;
  31 +import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
31 32 import org.thingsboard.server.gen.edge.EdgeConfiguration;
32 33 import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
33   -import org.thingsboard.server.gen.edge.EntityUpdateMsg;
34 34 import org.thingsboard.server.gen.edge.RequestMsg;
35 35 import org.thingsboard.server.gen.edge.RequestMsgType;
36 36 import org.thingsboard.server.gen.edge.ResponseMsg;
... ... @@ -41,6 +41,7 @@ import javax.net.ssl.SSLException;
41 41 import java.io.File;
42 42 import java.net.URISyntaxException;
43 43 import java.util.concurrent.TimeUnit;
  44 +import java.util.concurrent.locks.ReentrantLock;
44 45 import java.util.function.Consumer;
45 46
46 47 @Service
... ... @@ -62,12 +63,13 @@ public class EdgeGrpcClient implements EdgeRpcClient {
62 63
63 64 private StreamObserver<RequestMsg> inputStream;
64 65
  66 + private static final ReentrantLock uplinkMsgLock = new ReentrantLock();
  67 +
65 68 @Override
66 69 public void connect(String edgeKey,
67 70 String edgeSecret,
68 71 Consumer<UplinkResponseMsg> onUplinkResponse,
69 72 Consumer<EdgeConfiguration> onEdgeUpdate,
70   - Consumer<EntityUpdateMsg> onEntityUpdate,
71 73 Consumer<DownlinkMsg> onDownlink,
72 74 Consumer<Exception> onError) {
73 75 NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort).usePlaintext();
... ... @@ -83,7 +85,7 @@ public class EdgeGrpcClient implements EdgeRpcClient {
83 85 channel = builder.build();
84 86 EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel);
85 87 log.info("[{}] Sending a connect request to the TB!", edgeKey);
86   - this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onEntityUpdate, onDownlink, onError));
  88 + this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onDownlink, onError));
87 89 this.inputStream.onNext(RequestMsg.newBuilder()
88 90 .setMsgType(RequestMsgType.CONNECT_RPC_MESSAGE)
89 91 .setConnectRequestMsg(ConnectRequestMsg.newBuilder().setEdgeRoutingKey(edgeKey).setEdgeSecret(edgeSecret).build())
... ... @@ -110,16 +112,33 @@ public class EdgeGrpcClient implements EdgeRpcClient {
110 112
111 113 @Override
112 114 public void sendUplinkMsg(UplinkMsg msg) {
113   - this.inputStream.onNext(RequestMsg.newBuilder()
114   - .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
115   - .setUplinkMsg(msg)
116   - .build());
  115 + try {
  116 + uplinkMsgLock.lock();
  117 + this.inputStream.onNext(RequestMsg.newBuilder()
  118 + .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
  119 + .setUplinkMsg(msg)
  120 + .build());
  121 + } finally {
  122 + uplinkMsgLock.unlock();
  123 + }
  124 + }
  125 +
  126 + @Override
  127 + public void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg) {
  128 + try {
  129 + uplinkMsgLock.lock();
  130 + this.inputStream.onNext(RequestMsg.newBuilder()
  131 + .setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
  132 + .setDownlinkResponseMsg(downlinkResponseMsg)
  133 + .build());
  134 + } finally {
  135 + uplinkMsgLock.unlock();
  136 + }
117 137 }
118 138
119 139 private StreamObserver<ResponseMsg> initOutputStream(String edgeKey,
120 140 Consumer<UplinkResponseMsg> onUplinkResponse,
121 141 Consumer<EdgeConfiguration> onEdgeUpdate,
122   - Consumer<EntityUpdateMsg> onEntityUpdate,
123 142 Consumer<DownlinkMsg> onDownlink,
124 143 Consumer<Exception> onError) {
125 144 return new StreamObserver<ResponseMsg>() {
... ... @@ -137,9 +156,6 @@ public class EdgeGrpcClient implements EdgeRpcClient {
137 156 } else if (responseMsg.hasUplinkResponseMsg()) {
138 157 log.debug("[{}] Uplink response message received {}", edgeKey, responseMsg.getUplinkResponseMsg());
139 158 onUplinkResponse.accept(responseMsg.getUplinkResponseMsg());
140   - } else if (responseMsg.hasEntityUpdateMsg()) {
141   - log.debug("[{}] Entity update message received {}", edgeKey, responseMsg.getEntityUpdateMsg());
142   - onEntityUpdate.accept(responseMsg.getEntityUpdateMsg());
143 159 } else if (responseMsg.hasDownlinkMsg()) {
144 160 log.debug("[{}] Downlink message received {}", edgeKey, responseMsg.getDownlinkMsg());
145 161 onDownlink.accept(responseMsg.getDownlinkMsg());
... ...
... ... @@ -16,8 +16,8 @@
16 16 package org.thingsboard.edge.rpc;
17 17
18 18 import org.thingsboard.server.gen.edge.DownlinkMsg;
  19 +import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
19 20 import org.thingsboard.server.gen.edge.EdgeConfiguration;
20   -import org.thingsboard.server.gen.edge.EntityUpdateMsg;
21 21 import org.thingsboard.server.gen.edge.UplinkMsg;
22 22 import org.thingsboard.server.gen.edge.UplinkResponseMsg;
23 23
... ... @@ -29,11 +29,12 @@ public interface EdgeRpcClient {
29 29 String integrationSecret,
30 30 Consumer<UplinkResponseMsg> onUplinkResponse,
31 31 Consumer<EdgeConfiguration> onEdgeUpdate,
32   - Consumer<EntityUpdateMsg> onEntityUpdate,
33 32 Consumer<DownlinkMsg> onDownlink,
34 33 Consumer<Exception> onError);
35 34
36 35 void disconnect() throws InterruptedException;
37 36
38   - void sendUplinkMsg(UplinkMsg uplinkMsg) throws InterruptedException;
  37 + void sendUplinkMsg(UplinkMsg uplinkMsg);
  38 +
  39 + void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg);
39 40 }
... ...
... ... @@ -37,31 +37,13 @@ message RequestMsg {
37 37 RequestMsgType msgType = 1;
38 38 ConnectRequestMsg connectRequestMsg = 2;
39 39 UplinkMsg uplinkMsg = 3;
  40 + DownlinkResponseMsg downlinkResponseMsg = 4;
40 41 }
41 42
42 43 message ResponseMsg {
43 44 ConnectResponseMsg connectResponseMsg = 1;
44 45 UplinkResponseMsg uplinkResponseMsg = 2;
45   - EntityUpdateMsg entityUpdateMsg = 3;
46   - DownlinkMsg downlinkMsg = 4;
47   -}
48   -
49   -message EntityUpdateMsg {
50   - DeviceUpdateMsg deviceUpdateMsg = 1;
51   - DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 2;
52   - RuleChainUpdateMsg ruleChainUpdateMsg = 3;
53   - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 4;
54   - DashboardUpdateMsg dashboardUpdateMsg = 5;
55   - AssetUpdateMsg assetUpdateMsg = 6;
56   - EntityViewUpdateMsg entityViewUpdateMsg = 7;
57   - AlarmUpdateMsg alarmUpdateMsg = 8;
58   - UserUpdateMsg userUpdateMsg = 9;
59   - UserCredentialsUpdateMsg userCredentialsUpdateMsg = 10;
60   - CustomerUpdateMsg customerUpdateMsg = 11;
61   - RelationUpdateMsg relationUpdateMsg = 12;
62   - WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 13;
63   - WidgetTypeUpdateMsg widgetTypeUpdateMsg = 14;
64   - AdminSettingsUpdateMsg adminSettingsUpdateMsg = 15;
  46 + DownlinkMsg downlinkMsg = 3;
65 47 }
66 48
67 49 enum RequestMsgType {
... ... @@ -360,9 +342,30 @@ message UplinkResponseMsg {
360 342 string errorMsg = 2;
361 343 }
362 344
  345 +message DownlinkResponseMsg {
  346 + bool success = 1;
  347 + string errorMsg = 2;
  348 +}
  349 +
363 350 message DownlinkMsg {
364 351 int32 downlinkMsgId = 1;
365 352 repeated EntityDataProto entityData = 2;
366 353 repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 3;
  354 + repeated DeviceUpdateMsg deviceUpdateMsg = 4;
  355 + repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 5;
  356 + repeated RuleChainUpdateMsg ruleChainUpdateMsg = 6;
  357 + repeated RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 7;
  358 + repeated DashboardUpdateMsg dashboardUpdateMsg = 8;
  359 + repeated AssetUpdateMsg assetUpdateMsg = 9;
  360 + repeated EntityViewUpdateMsg entityViewUpdateMsg = 10;
  361 + repeated AlarmUpdateMsg alarmUpdateMsg = 11;
  362 + repeated UserUpdateMsg userUpdateMsg = 12;
  363 + repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 13;
  364 + repeated CustomerUpdateMsg customerUpdateMsg = 14;
  365 + repeated RelationUpdateMsg relationUpdateMsg = 15;
  366 + repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 16;
  367 + repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 17;
  368 + repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 18;
  369 +
367 370 }
368 371
... ...