Browse Source

merge with develop/3.5.2

pull/8825/head
YevhenBondarenko 3 years ago
parent
commit
ad9fedaded
  1. 14
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  2. 50
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  3. 87
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  4. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  5. 43
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java
  6. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java
  7. 11
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java
  8. 1
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java
  9. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java
  10. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java
  11. 62
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DefaultProfilesEdgeEventFetcher.java
  12. 30
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  13. 37
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java
  14. 29
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java
  15. 60
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java
  16. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java
  17. 36
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java
  18. 11
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java
  19. 37
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java
  20. 19
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java
  21. 41
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java
  22. 33
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java
  23. 61
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java
  24. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/edge/EdgeProcessor.java
  25. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java
  26. 33
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java
  27. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java
  28. 25
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java
  29. 3
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java
  30. 14
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  31. 156
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
  32. 5
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
  33. 15
      application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java
  34. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java
  35. 104
      application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java
  36. 79
      application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java
  37. 6
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  38. 13
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  39. 54
      application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java
  40. 10
      application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java
  41. 57
      application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java
  42. 2
      application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java
  43. 67
      application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java
  44. 2
      application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java
  45. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  46. 29
      common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java
  47. 30
      common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
  48. 21
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java
  49. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java
  50. 18
      common/edge-api/src/main/proto/edge.proto
  51. 227
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java
  52. 16
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java
  53. 312
      common/transport/coap/src/main/proto/efento/proto_config.proto
  54. 197
      common/transport/coap/src/main/proto/efento/proto_device_info.proto
  55. 0
      common/transport/coap/src/main/proto/efento/proto_measurement_types.proto
  56. 2
      common/transport/coap/src/main/proto/efento/proto_measurements.proto
  57. 267
      common/transport/coap/src/main/proto/efento/proto_rule.proto
  58. 5
      dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java
  59. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  60. 8
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  61. 3
      dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java
  62. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java
  63. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  64. 14
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  65. 45
      dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java
  66. 12
      dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java
  67. 2
      ui-ngx/src/app/core/http/widget.service.ts
  68. 20
      ui-ngx/src/app/core/services/dashboard-utils.service.ts
  69. 3
      ui-ngx/src/app/modules/dashboard/dashboard-pages.routing.module.ts
  70. 1
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
  71. 3
      ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts
  72. 8
      ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html
  73. 2
      ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.html
  74. 13
      ui-ngx/src/app/shared/models/widget.models.ts

14
application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java

@ -155,9 +155,9 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Override @Override
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
log.debug("Pushing notification to edge {}", edgeNotificationMsg); TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
log.debug("[{}] Pushing notification to edge {}", tenantId, edgeNotificationMsg);
try { try {
TenantId tenantId = TenantId.fromUUID(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
ListenableFuture<Void> future; ListenableFuture<Void> future;
switch (type) { switch (type) {
@ -216,7 +216,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
future = tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); future = tenantProfileEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
break; break;
default: default:
log.warn("Edge event type [{}] is not designed to be pushed to edge", type); log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type);
future = Futures.immediateFuture(null); future = Futures.immediateFuture(null);
} }
Futures.addCallback(future, new FutureCallback<>() { Futures.addCallback(future, new FutureCallback<>() {
@ -227,16 +227,16 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Override @Override
public void onFailure(Throwable throwable) { public void onFailure(Throwable throwable) {
callBackFailure(edgeNotificationMsg, callback, throwable); callBackFailure(tenantId, edgeNotificationMsg, callback, throwable);
} }
}, dbCallBackExecutor); }, dbCallBackExecutor);
} catch (Exception e) { } catch (Exception e) {
callBackFailure(edgeNotificationMsg, callback, e); callBackFailure(tenantId, edgeNotificationMsg, callback, e);
} }
} }
private void callBackFailure(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) { private void callBackFailure(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback, Throwable throwable) {
log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, throwable); log.error("[{}] Can't push to edge updates, edgeNotificationMsg [{}]", tenantId, edgeNotificationMsg, throwable);
callback.onFailure(throwable); callback.onFailure(throwable);
} }

50
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -195,17 +195,17 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
switch (msg.getMsgType()) { switch (msg.getMsgType()) {
case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG: case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
EdgeEventUpdateMsg edgeEventUpdateMsg = (EdgeEventUpdateMsg) msg; EdgeEventUpdateMsg edgeEventUpdateMsg = (EdgeEventUpdateMsg) msg;
log.trace("[{}] onToEdgeSessionMsg [{}]", edgeEventUpdateMsg.getTenantId(), msg); log.trace("[{}] onToEdgeSessionMsg [{}]", tenantId, msg);
onEdgeEvent(tenantId, edgeEventUpdateMsg.getEdgeId()); onEdgeEvent(tenantId, edgeEventUpdateMsg.getEdgeId());
break; break;
case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG: case EDGE_SYNC_REQUEST_TO_EDGE_SESSION_MSG:
ToEdgeSyncRequest toEdgeSyncRequest = (ToEdgeSyncRequest) msg; ToEdgeSyncRequest toEdgeSyncRequest = (ToEdgeSyncRequest) msg;
log.trace("[{}] toEdgeSyncRequest [{}]", toEdgeSyncRequest.getTenantId(), msg); log.trace("[{}] toEdgeSyncRequest [{}]", tenantId, msg);
startSyncProcess(tenantId, toEdgeSyncRequest.getEdgeId(), toEdgeSyncRequest.getId()); startSyncProcess(tenantId, toEdgeSyncRequest.getEdgeId(), toEdgeSyncRequest.getId());
break; break;
case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG: case EDGE_SYNC_RESPONSE_FROM_EDGE_SESSION_MSG:
FromEdgeSyncResponse fromEdgeSyncResponse = (FromEdgeSyncResponse) msg; FromEdgeSyncResponse fromEdgeSyncResponse = (FromEdgeSyncResponse) msg;
log.trace("[{}] fromEdgeSyncResponse [{}]", fromEdgeSyncResponse.getTenantId(), msg); log.trace("[{}] fromEdgeSyncResponse [{}]", tenantId, msg);
processSyncResponse(fromEdgeSyncResponse); processSyncResponse(fromEdgeSyncResponse);
break; break;
} }
@ -263,7 +263,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
} }
private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) {
log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId); TenantId tenantId = edgeGrpcSession.getEdge().getTenantId();
log.info("[{}][{}] edge [{}] connected successfully.", tenantId, edgeGrpcSession.getSessionId(), edgeId);
sessions.put(edgeId, edgeGrpcSession); sessions.put(edgeId, edgeGrpcSession);
final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock()); final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
newEventLock.lock(); newEventLock.lock();
@ -272,10 +273,10 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
} finally { } finally {
newEventLock.unlock(); newEventLock.unlock();
} }
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
long lastConnectTs = System.currentTimeMillis(); long lastConnectTs = System.currentTimeMillis();
save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs); save(tenantId, edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, lastConnectTs);
pushRuleEngineMessage(edgeGrpcSession.getEdge().getTenantId(), edgeId, lastConnectTs, TbMsgType.CONNECT_EVENT); pushRuleEngineMessage(tenantId, edgeId, lastConnectTs, TbMsgType.CONNECT_EVENT);
cancelScheduleEdgeEventsCheck(edgeId); cancelScheduleEdgeEventsCheck(edgeId);
scheduleEdgeEventsCheck(edgeGrpcSession); scheduleEdgeEventsCheck(edgeGrpcSession);
} }
@ -334,7 +335,7 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
newEventLock.lock(); newEventLock.lock();
try { try {
if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) { if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
log.trace("[{}] Set session new events flag to false", edgeId.getId()); log.trace("[{}][{}] Set session new events flag to false", tenantId, edgeId.getId());
sessionNewEvents.put(edgeId, false); sessionNewEvents.put(edgeId, false);
Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() { Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
@Override @Override
@ -392,9 +393,10 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
} finally { } finally {
newEventLock.unlock(); newEventLock.unlock();
} }
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); TenantId tenantId = toRemove.getEdge().getTenantId();
save(tenantId, edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
long lastDisconnectTs = System.currentTimeMillis(); long lastDisconnectTs = System.currentTimeMillis();
save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs); save(tenantId, edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, lastDisconnectTs);
pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edgeId, lastDisconnectTs, TbMsgType.DISCONNECT_EVENT); pushRuleEngineMessage(toRemove.getEdge().getTenantId(), edgeId, lastDisconnectTs, TbMsgType.DISCONNECT_EVENT);
cancelScheduleEdgeEventsCheck(edgeId); cancelScheduleEdgeEventsCheck(edgeId);
} else { } else {
@ -402,36 +404,38 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
} }
} }
private void save(EdgeId edgeId, String key, long value) { private void save(TenantId tenantId, EdgeId edgeId, String key, long value) {
log.debug("[{}] Updating long edge telemetry [{}] [{}]", edgeId, key, value); log.debug("[{}][{}] Updating long edge telemetry [{}] [{}]", tenantId, edgeId, key, value);
if (persistToTelemetry) { if (persistToTelemetry) {
tsSubService.saveAndNotify( tsSubService.saveAndNotify(
TenantId.SYS_TENANT_ID, edgeId, tenantId, edgeId,
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))),
new AttributeSaveCallback(edgeId, key, value)); new AttributeSaveCallback(tenantId, edgeId, key, value));
} else { } else {
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); tsSubService.saveAttrAndNotify(tenantId, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value));
} }
} }
private void save(EdgeId edgeId, String key, boolean value) { private void save(TenantId tenantId, EdgeId edgeId, String key, boolean value) {
log.debug("[{}] Updating boolean edge telemetry [{}] [{}]", edgeId, key, value); log.debug("[{}][{}] Updating boolean edge telemetry [{}] [{}]", tenantId, edgeId, key, value);
if (persistToTelemetry) { if (persistToTelemetry) {
tsSubService.saveAndNotify( tsSubService.saveAndNotify(
TenantId.SYS_TENANT_ID, edgeId, tenantId, edgeId,
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))),
new AttributeSaveCallback(edgeId, key, value)); new AttributeSaveCallback(tenantId, edgeId, key, value));
} else { } else {
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); tsSubService.saveAttrAndNotify(tenantId, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(tenantId, edgeId, key, value));
} }
} }
private static class AttributeSaveCallback implements FutureCallback<Void> { private static class AttributeSaveCallback implements FutureCallback<Void> {
private final TenantId tenantId;
private final EdgeId edgeId; private final EdgeId edgeId;
private final String key; private final String key;
private final Object value; private final Object value;
AttributeSaveCallback(EdgeId edgeId, String key, Object value) { AttributeSaveCallback(TenantId tenantId, EdgeId edgeId, String key, Object value) {
this.tenantId = tenantId;
this.edgeId = edgeId; this.edgeId = edgeId;
this.key = key; this.key = key;
this.value = value; this.value = value;
@ -439,12 +443,12 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
@Override @Override
public void onSuccess(@Nullable Void result) { public void onSuccess(@Nullable Void result) {
log.trace("[{}] Successfully updated attribute [{}] with value [{}]", edgeId, key, value); log.trace("[{}][{}] Successfully updated attribute [{}] with value [{}]", tenantId, edgeId, key, value);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.warn("[{}] Failed to update attribute [{}] with value [{}]", edgeId, key, value, t); log.warn("[{}][{}] Failed to update attribute [{}] with value [{}]", tenantId, edgeId, key, value, t);
} }
} }

87
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -105,6 +105,7 @@ public final class EdgeGrpcSession implements Closeable {
private EdgeContextComponent ctx; private EdgeContextComponent ctx;
private Edge edge; private Edge edge;
private TenantId tenantId;
private StreamObserver<RequestMsg> inputStream; private StreamObserver<RequestMsg> inputStream;
private StreamObserver<ResponseMsg> outputStream; private StreamObserver<ResponseMsg> outputStream;
private boolean connected; private boolean connected;
@ -148,7 +149,7 @@ public final class EdgeGrpcSession implements Closeable {
outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); outputStream.onError(new RuntimeException(responseMsg.getErrorMsg()));
} else { } else {
if (requestMsg.getConnectRequestMsg().hasMaxInboundMessageSize()) { if (requestMsg.getConnectRequestMsg().hasMaxInboundMessageSize()) {
log.debug("[{}] Client max inbound message size: {}", sessionId, requestMsg.getConnectRequestMsg().getMaxInboundMessageSize()); log.debug("[{}][{}] Client max inbound message size: {}", tenantId, sessionId, requestMsg.getConnectRequestMsg().getMaxInboundMessageSize());
clientMaxInboundMessageSize = requestMsg.getConnectRequestMsg().getMaxInboundMessageSize(); clientMaxInboundMessageSize = requestMsg.getConnectRequestMsg().getMaxInboundMessageSize();
} }
connected = true; connected = true;
@ -179,13 +180,13 @@ public final class EdgeGrpcSession implements Closeable {
@Override @Override
public void onError(Throwable t) { public void onError(Throwable t) {
log.error("[{}] Stream was terminated due to error:", sessionId, t); log.error("[{}][{}] Stream was terminated due to error:", tenantId, sessionId, t);
closeSession(); closeSession();
} }
@Override @Override
public void onCompleted() { public void onCompleted() {
log.info("[{}] Stream was closed and completed successfully!", sessionId); log.info("[{}][{}] Stream was closed and completed successfully!", tenantId, sessionId);
closeSession(); closeSession();
} }
@ -206,7 +207,7 @@ public final class EdgeGrpcSession implements Closeable {
} }
public void startSyncProcess(boolean fullSync) { public void startSyncProcess(boolean fullSync) {
log.trace("[{}][{}][{}] Staring edge sync process", edge.getTenantId(), edge.getId(), this.sessionId); log.trace("[{}][{}][{}] Staring edge sync process", this.tenantId, edge.getId(), this.sessionId);
syncCompleted = false; syncCompleted = false;
interruptGeneralProcessingOnSync(); interruptGeneralProcessingOnSync();
doSync(new EdgeSyncCursor(ctx, edge, fullSync)); doSync(new EdgeSyncCursor(ctx, edge, fullSync));
@ -216,7 +217,7 @@ public final class EdgeGrpcSession implements Closeable {
if (cursor.hasNext()) { if (cursor.hasNext()) {
EdgeEventFetcher next = cursor.getNext(); EdgeEventFetcher next = cursor.getNext();
log.info("[{}][{}] starting sync process, cursor current idx = {}, class = {}", log.info("[{}][{}] starting sync process, cursor current idx = {}, class = {}",
edge.getTenantId(), edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName()); this.tenantId, edge.getId(), cursor.getCurrentIdx(), next.getClass().getSimpleName());
ListenableFuture<Pair<Long, Long>> future = startProcessingEdgeEvents(next); ListenableFuture<Pair<Long, Long>> future = startProcessingEdgeEvents(next);
Futures.addCallback(future, new FutureCallback<>() { Futures.addCallback(future, new FutureCallback<>() {
@Override @Override
@ -226,7 +227,7 @@ public final class EdgeGrpcSession implements Closeable {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("[{}][{}] Exception during sync process", edge.getTenantId(), edge.getId(), t); log.error("[{}][{}] Exception during sync process", tenantId, edge.getId(), t);
} }
}, ctx.getGrpcCallbackExecutorService()); }, ctx.getGrpcCallbackExecutorService());
} else { } else {
@ -243,7 +244,7 @@ public final class EdgeGrpcSession implements Closeable {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("[{}][{}] Exception during sending sync complete", edge.getTenantId(), edge.getId(), t); log.error("[{}][{}] Exception during sending sync complete", tenantId, edge.getId(), t);
} }
}, ctx.getGrpcCallbackExecutorService()); }, ctx.getGrpcCallbackExecutorService());
} }
@ -279,39 +280,40 @@ public final class EdgeGrpcSession implements Closeable {
try { try {
if (msg.getSuccess()) { if (msg.getSuccess()) {
sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId()); sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId());
log.debug("[{}] Msg has been processed successfully!Msd Id: [{}], Msg: {}", edge.getRoutingKey(), msg.getDownlinkMsgId(), msg); log.debug("[{}][{}] Msg has been processed successfully!Msd Id: [{}], Msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg);
} else { } else {
log.error("[{}] Msg processing failed! Msd Id: [{}], Error msg: {}", edge.getRoutingKey(), msg.getDownlinkMsgId(), msg.getErrorMsg()); log.error("[{}][{}] Msg processing failed! Msd Id: [{}], Error msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg.getErrorMsg());
} }
if (sessionState.getPendingMsgsMap().isEmpty()) { if (sessionState.getPendingMsgsMap().isEmpty()) {
log.debug("[{}] Pending msgs map is empty. Stopping current iteration", edge.getRoutingKey()); log.debug("[{}][{}] Pending msgs map is empty. Stopping current iteration", this.tenantId, edge.getRoutingKey());
stopCurrentSendDownlinkMsgsTask(false); stopCurrentSendDownlinkMsgsTask(false);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Can't process downlink response message [{}]", this.sessionId, msg, e); log.error("[{}][{}] Can't process downlink response message [{}]", this.tenantId, this.sessionId, msg, e);
} }
} }
private void sendDownlinkMsg(ResponseMsg downlinkMsg) { private void sendDownlinkMsg(ResponseMsg downlinkMsg) {
log.trace("[{}] Sending downlink msg [{}]", this.sessionId, downlinkMsg); log.trace("[{}][{}] Sending downlink msg [{}]", this.tenantId, this.sessionId, downlinkMsg);
if (isConnected()) { if (isConnected()) {
downlinkMsgLock.lock(); downlinkMsgLock.lock();
try { try {
outputStream.onNext(downlinkMsg); outputStream.onNext(downlinkMsg);
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Failed to send downlink message [{}]", this.sessionId, downlinkMsg, e); log.error("[{}][{}] Failed to send downlink message [{}]", this.tenantId, this.sessionId, downlinkMsg, e);
connected = false; connected = false;
sessionCloseListener.accept(edge.getId(), sessionId); sessionCloseListener.accept(edge.getId(), sessionId);
} finally { } finally {
downlinkMsgLock.unlock(); downlinkMsgLock.unlock();
} }
log.trace("[{}] Response msg successfully sent [{}]", this.sessionId, downlinkMsg); log.trace("[{}][{}] Response msg successfully sent [{}]", this.tenantId, this.sessionId, downlinkMsg);
} }
} }
void onConfigurationUpdate(Edge edge) { void onConfigurationUpdate(Edge edge) {
log.debug("[{}] onConfigurationUpdate [{}]", this.sessionId, edge); log.debug("[{}] onConfigurationUpdate [{}]", this.sessionId, edge);
this.edge = edge; this.edge = edge;
this.tenantId = edge.getTenantId();
EdgeUpdateMsg edgeConfig = EdgeUpdateMsg.newBuilder() EdgeUpdateMsg edgeConfig = EdgeUpdateMsg.newBuilder()
.setConfiguration(ctx.getEdgeMsgConstructor().constructEdgeConfiguration(edge)).build(); .setConfiguration(ctx.getEdgeMsgConstructor().constructEdgeConfiguration(edge)).build();
ResponseMsg edgeConfigMsg = ResponseMsg.newBuilder() ResponseMsg edgeConfigMsg = ResponseMsg.newBuilder()
@ -322,7 +324,7 @@ public final class EdgeGrpcSession implements Closeable {
ListenableFuture<Boolean> processEdgeEvents() throws Exception { ListenableFuture<Boolean> processEdgeEvents() throws Exception {
SettableFuture<Boolean> result = SettableFuture.create(); SettableFuture<Boolean> result = SettableFuture.create();
log.trace("[{}] starting processing edge events", this.sessionId); log.trace("[{}][{}] starting processing edge events", this.tenantId, this.sessionId);
if (isConnected() && isSyncCompleted()) { if (isConnected() && isSyncCompleted()) {
Pair<Long, Long> startTsAndSeqId = getQueueStartTsAndSeqId().get(); Pair<Long, Long> startTsAndSeqId = getQueueStartTsAndSeqId().get();
this.previousStartTs = startTsAndSeqId.getFirst(); this.previousStartTs = startTsAndSeqId.getFirst();
@ -342,7 +344,7 @@ public final class EdgeGrpcSession implements Closeable {
Futures.addCallback(updateFuture, new FutureCallback<>() { Futures.addCallback(updateFuture, new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable List<String> list) { public void onSuccess(@Nullable List<String> list) {
log.debug("[{}] queue offset was updated [{}]", sessionId, newStartTsAndSeqId); log.debug("[{}][{}] queue offset was updated [{}]", tenantId, sessionId, newStartTsAndSeqId);
if (fetcher.isSeqIdNewCycleStarted()) { if (fetcher.isSeqIdNewCycleStarted()) {
seqIdEnd = fetcher.getSeqIdEnd(); seqIdEnd = fetcher.getSeqIdEnd();
boolean newEventsAvailable = isNewEdgeEventsAvailable(); boolean newEventsAvailable = isNewEdgeEventsAvailable();
@ -359,24 +361,24 @@ public final class EdgeGrpcSession implements Closeable {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("[{}] Failed to update queue offset [{}]", sessionId, newStartTsAndSeqId, t); log.error("[{}][{}] Failed to update queue offset [{}]", tenantId, sessionId, newStartTsAndSeqId, t);
result.setException(t); result.setException(t);
} }
}, ctx.getGrpcCallbackExecutorService()); }, ctx.getGrpcCallbackExecutorService());
} else { } else {
log.trace("[{}] newStartTsAndSeqId is null. Skipping iteration without db update", sessionId); log.trace("[{}][{}] newStartTsAndSeqId is null. Skipping iteration without db update", tenantId, sessionId);
result.set(null); result.set(null);
} }
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("[{}] Failed to process events", sessionId, t); log.error("[{}][{}] Failed to process events", tenantId, sessionId, t);
result.setException(t); result.setException(t);
} }
}, ctx.getGrpcCallbackExecutorService()); }, ctx.getGrpcCallbackExecutorService());
} else { } else {
log.trace("[{}] edge is not connected or sync is not completed. Skipping iteration", sessionId); log.trace("[{}][{}] edge is not connected or sync is not completed. Skipping iteration", tenantId, sessionId);
result.set(null); result.set(null);
} }
return result; return result;
@ -393,13 +395,13 @@ public final class EdgeGrpcSession implements Closeable {
try { try {
PageData<EdgeEvent> pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink); PageData<EdgeEvent> pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink);
if (isConnected() && !pageData.getData().isEmpty()) { if (isConnected() && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); log.trace("[{}][{}][{}] event(s) are going to be processed.", this.tenantId, this.sessionId, pageData.getData().size());
List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData()); List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData());
Futures.addCallback(sendDownlinkMsgsPack(downlinkMsgsPack), new FutureCallback<>() { Futures.addCallback(sendDownlinkMsgsPack(downlinkMsgsPack), new FutureCallback<>() {
@Override @Override
public void onSuccess(@Nullable Boolean isInterrupted) { public void onSuccess(@Nullable Boolean isInterrupted) {
if (Boolean.TRUE.equals(isInterrupted)) { if (Boolean.TRUE.equals(isInterrupted)) {
log.debug("[{}][{}][{}] Send downlink messages task was interrupted", edge.getTenantId(), edge.getId(), sessionId); log.debug("[{}][{}][{}] Send downlink messages task was interrupted", tenantId, edge.getId(), sessionId);
result.set(null); result.set(null);
} else { } else {
if (isConnected() && pageData.hasNext()) { if (isConnected() && pageData.hasNext()) {
@ -452,14 +454,14 @@ public final class EdgeGrpcSession implements Closeable {
if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) { if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) {
List<DownlinkMsg> copy = new ArrayList<>(sessionState.getPendingMsgsMap().values()); List<DownlinkMsg> copy = new ArrayList<>(sessionState.getPendingMsgsMap().values());
if (attempt > 1) { if (attempt > 1) {
log.warn("[{}] Failed to deliver the batch: {}, attempt: {}", this.sessionId, copy, attempt); log.warn("[{}][{}] Failed to deliver the batch: {}, attempt: {}", this.tenantId, this.sessionId, copy, attempt);
} }
log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, copy.size()); log.trace("[{}][{}][{}] downlink msg(s) are going to be send.", this.tenantId, this.sessionId, copy.size());
for (DownlinkMsg downlinkMsg : copy) { for (DownlinkMsg downlinkMsg : copy) {
if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) { if (this.clientMaxInboundMessageSize != 0 && downlinkMsg.getSerializedSize() > this.clientMaxInboundMessageSize) {
log.error("[{}][{}][{}] Downlink msg size [{}] exceeds client max inbound message size [{}]. Skipping this message. " + log.error("[{}][{}][{}] Downlink msg size [{}] exceeds client max inbound message size [{}]. Skipping this message. " +
"Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it." + "Please increase value of CLOUD_RPC_MAX_INBOUND_MESSAGE_SIZE env variable on the edge and restart it." +
"Message {}", edge.getTenantId(), edge.getId(), this.sessionId, downlinkMsg.getSerializedSize(), "Message {}", this.tenantId, edge.getId(), this.sessionId, downlinkMsg.getSerializedSize(),
this.clientMaxInboundMessageSize, downlinkMsg); this.clientMaxInboundMessageSize, downlinkMsg);
sessionState.getPendingMsgsMap().remove(downlinkMsg.getDownlinkMsgId()); sessionState.getPendingMsgsMap().remove(downlinkMsg.getDownlinkMsgId());
} else { } else {
@ -471,15 +473,15 @@ public final class EdgeGrpcSession implements Closeable {
if (attempt < MAX_DOWNLINK_ATTEMPTS) { if (attempt < MAX_DOWNLINK_ATTEMPTS) {
scheduleDownlinkMsgsPackSend(attempt + 1); scheduleDownlinkMsgsPackSend(attempt + 1);
} else { } else {
log.warn("[{}] Failed to deliver the batch after {} attempts. Next messages are going to be discarded {}", log.warn("[{}][{}] Failed to deliver the batch after {} attempts. Next messages are going to be discarded {}",
this.sessionId, MAX_DOWNLINK_ATTEMPTS, copy); this.tenantId, this.sessionId, MAX_DOWNLINK_ATTEMPTS, copy);
stopCurrentSendDownlinkMsgsTask(false); stopCurrentSendDownlinkMsgsTask(false);
} }
} else { } else {
stopCurrentSendDownlinkMsgsTask(false); stopCurrentSendDownlinkMsgsTask(false);
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Failed to send downlink msgs. Error msg {}", this.sessionId, e.getMessage(), e); log.warn("[{}][{}] Failed to send downlink msgs. Error msg {}", this.tenantId, this.sessionId, e.getMessage(), e);
stopCurrentSendDownlinkMsgsTask(true); stopCurrentSendDownlinkMsgsTask(true);
} }
}; };
@ -499,7 +501,7 @@ public final class EdgeGrpcSession implements Closeable {
private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) { private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {
List<DownlinkMsg> result = new ArrayList<>(); List<DownlinkMsg> result = new ArrayList<>();
for (EdgeEvent edgeEvent : edgeEvents) { for (EdgeEvent edgeEvent : edgeEvents) {
log.trace("[{}][{}] converting edge event to downlink msg [{}]", edge.getTenantId(), this.sessionId, edgeEvent); log.trace("[{}][{}] converting edge event to downlink msg [{}]", this.tenantId, this.sessionId, edgeEvent);
DownlinkMsg downlinkMsg = null; DownlinkMsg downlinkMsg = null;
try { try {
switch (edgeEvent.getAction()) { switch (edgeEvent.getAction()) {
@ -518,7 +520,7 @@ public final class EdgeGrpcSession implements Closeable {
case ASSIGNED_TO_CUSTOMER: case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER: case UNASSIGNED_FROM_CUSTOMER:
downlinkMsg = convertEntityEventToDownlink(edgeEvent); downlinkMsg = convertEntityEventToDownlink(edgeEvent);
log.trace("[{}][{}] entity message processed [{}]", edgeEvent.getTenantId(), this.sessionId, downlinkMsg); log.trace("[{}][{}] entity message processed [{}]", this.tenantId, this.sessionId, downlinkMsg);
break; break;
case ATTRIBUTES_UPDATED: case ATTRIBUTES_UPDATED:
case POST_ATTRIBUTES: case POST_ATTRIBUTES:
@ -527,10 +529,10 @@ public final class EdgeGrpcSession implements Closeable {
downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent); downlinkMsg = ctx.getTelemetryProcessor().convertTelemetryEventToDownlink(edgeEvent);
break; break;
default: default:
log.warn("[{}][{}] Unsupported action type [{}]", edge.getTenantId(), this.sessionId, edgeEvent.getAction()); log.warn("[{}][{}] Unsupported action type [{}]", this.tenantId, this.sessionId, edgeEvent.getAction());
} }
} catch (Exception e) { } catch (Exception e) {
log.error("[{}][{}] Exception during converting edge event to downlink msg", edge.getTenantId(), this.sessionId, e); log.error("[{}][{}] Exception during converting edge event to downlink msg", this.tenantId, this.sessionId, e);
} }
if (downlinkMsg != null) { if (downlinkMsg != null) {
result.add(downlinkMsg); result.add(downlinkMsg);
@ -566,7 +568,7 @@ public final class EdgeGrpcSession implements Closeable {
PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), 0L, this.previousStartSeqId == 0 ? null : this.previousStartSeqId - 1, pageLink); PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), 0L, this.previousStartSeqId == 0 ? null : this.previousStartSeqId - 1, pageLink);
return !edgeEvents.getData().isEmpty(); return !edgeEvents.getData().isEmpty();
} catch (Exception e) { } catch (Exception e) {
log.error("[{}][{}][{}] Failed to execute isSeqIdStartedNewCycle", edge.getTenantId(), edge.getId(), sessionId, e); log.error("[{}][{}][{}] Failed to execute isSeqIdStartedNewCycle", this.tenantId, edge.getId(), sessionId, e);
} }
return false; return false;
} }
@ -577,7 +579,7 @@ public final class EdgeGrpcSession implements Closeable {
PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), this.newStartSeqId, null, pageLink); PageData<EdgeEvent> edgeEvents = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), this.newStartSeqId, null, pageLink);
return !edgeEvents.getData().isEmpty(); return !edgeEvents.getData().isEmpty();
} catch (Exception e) { } catch (Exception e) {
log.error("[{}][{}][{}] Failed to execute isNewEdgeEventsAvailable", edge.getTenantId(), edge.getId(), sessionId, e); log.error("[{}][{}][{}] Failed to execute isNewEdgeEventsAvailable", this.tenantId, edge.getId(), sessionId, e);
} }
return false; return false;
} }
@ -591,7 +593,7 @@ public final class EdgeGrpcSession implements Closeable {
startSeqId = edgeEvents.getData().get(0).getSeqId() - 1; startSeqId = edgeEvents.getData().get(0).getSeqId() - 1;
} }
} catch (Exception e) { } catch (Exception e) {
log.error("[{}][{}][{}] Failed to execute findStartSeqIdFromOldestEventIfAny", edge.getTenantId(), edge.getId(), sessionId, e); log.error("[{}][{}][{}] Failed to execute findStartSeqIdFromOldestEventIfAny", this.tenantId, edge.getId(), sessionId, e);
} }
return startSeqId; return startSeqId;
} }
@ -607,7 +609,7 @@ public final class EdgeGrpcSession implements Closeable {
} }
private DownlinkMsg convertEntityEventToDownlink(EdgeEvent edgeEvent) { private DownlinkMsg convertEntityEventToDownlink(EdgeEvent edgeEvent) {
log.trace("Executing convertEntityEventToDownlink, edgeEvent [{}], action [{}]", edgeEvent, edgeEvent.getAction()); log.trace("[{}] Executing convertEntityEventToDownlink, edgeEvent [{}], action [{}]", this.tenantId, edgeEvent, edgeEvent.getAction());
switch (edgeEvent.getType()) { switch (edgeEvent.getType()) {
case EDGE: case EDGE:
return ctx.getEdgeProcessor().convertEdgeEventToDownlink(edgeEvent); return ctx.getEdgeProcessor().convertEdgeEventToDownlink(edgeEvent);
@ -650,7 +652,7 @@ public final class EdgeGrpcSession implements Closeable {
case TENANT_PROFILE: case TENANT_PROFILE:
return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent); return ctx.getTenantProfileEdgeProcessor().convertTenantProfileEventToDownlink(edgeEvent);
default: default:
log.warn("Unsupported edge event type [{}]", edgeEvent); log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent);
return null; return null;
} }
} }
@ -749,7 +751,7 @@ public final class EdgeGrpcSession implements Closeable {
} }
} }
} catch (Exception e) { } catch (Exception e) {
log.error("[{}] Can't process uplink msg [{}]", this.sessionId, uplinkMsg, e); log.error("[{}][{}] Can't process uplink msg [{}]", this.tenantId, this.sessionId, uplinkMsg, e);
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} }
return Futures.allAsList(result); return Futures.allAsList(result);
@ -760,6 +762,7 @@ public final class EdgeGrpcSession implements Closeable {
Optional<Edge> optional = ctx.getEdgeService().findEdgeByRoutingKey(TenantId.SYS_TENANT_ID, request.getEdgeRoutingKey()); Optional<Edge> optional = ctx.getEdgeService().findEdgeByRoutingKey(TenantId.SYS_TENANT_ID, request.getEdgeRoutingKey());
if (optional.isPresent()) { if (optional.isPresent()) {
edge = optional.get(); edge = optional.get();
this.tenantId = edge.getTenantId();
try { try {
if (edge.getSecret().equals(request.getEdgeSecret())) { if (edge.getSecret().equals(request.getEdgeSecret())) {
sessionOpenListener.accept(edge.getId(), this); sessionOpenListener.accept(edge.getId(), this);
@ -791,25 +794,25 @@ public final class EdgeGrpcSession implements Closeable {
@Override @Override
public void close() { public void close() {
log.debug("[{}] Closing session", sessionId); log.debug("[{}][{}] Closing session", this.tenantId, sessionId);
connected = false; connected = false;
try { try {
outputStream.onCompleted(); outputStream.onCompleted();
} catch (Exception e) { } catch (Exception e) {
log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage()); log.debug("[{}][{}] Failed to close output stream: {}", this.tenantId, sessionId, e.getMessage());
} }
} }
private void interruptPreviousSendDownlinkMsgsTask() { private void interruptPreviousSendDownlinkMsgsTask() {
if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone() if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone()
|| sessionState.getScheduledSendDownlinkTask() != null && !sessionState.getScheduledSendDownlinkTask().isCancelled()) { || sessionState.getScheduledSendDownlinkTask() != null && !sessionState.getScheduledSendDownlinkTask().isCancelled()) {
log.debug("[{}][{}][{}] Previous send downlink future was not properly completed, stopping it now!", edge.getTenantId(), edge.getId(), this.sessionId); log.debug("[{}][{}][{}] Previous send downlink future was not properly completed, stopping it now!", this.tenantId, edge.getId(), this.sessionId);
stopCurrentSendDownlinkMsgsTask(true); stopCurrentSendDownlinkMsgsTask(true);
} }
} }
private void interruptGeneralProcessingOnSync() { private void interruptGeneralProcessingOnSync() {
log.debug("[{}][{}][{}] Sync process started. General processing interrupted!", edge.getTenantId(), edge.getId(), this.sessionId); log.debug("[{}][{}][{}] Sync process started. General processing interrupted!", this.tenantId, edge.getId(), this.sessionId);
stopCurrentSendDownlinkMsgsTask(true); stopCurrentSendDownlinkMsgsTask(true);
} }

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java

@ -25,6 +25,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.DefaultProfilesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.DevicesEdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.DevicesEdgeEventFetcher;
import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher; import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
@ -63,12 +64,13 @@ public class EdgeSyncCursor {
fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId())); fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
} }
} }
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
fetchers.add(new DefaultProfilesEdgeEventFetcher(ctx.getDeviceProfileService(), ctx.getAssetProfileService()));
fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService())); fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService()));
fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService())); fetchers.add(new AssetProfilesEdgeEventFetcher(ctx.getAssetProfileService()));
fetchers.add(new DevicesEdgeEventFetcher(ctx.getDeviceService())); fetchers.add(new DevicesEdgeEventFetcher(ctx.getDeviceService()));
fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService())); fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService()));
fetchers.add(new EntityViewsEdgeEventFetcher(ctx.getEntityViewService())); fetchers.add(new EntityViewsEdgeEventFetcher(ctx.getEntityViewService()));
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
if (fullSync) { if (fullSync) {
fetchers.add(new SystemWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService())); fetchers.add(new SystemWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService()));
fetchers.add(new TenantWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService())); fetchers.add(new TenantWidgetTypesEdgeEventFetcher(ctx.getWidgetTypeService()));

43
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java

@ -15,20 +15,9 @@
*/ */
package org.thingsboard.server.service.edge.rpc.constructor; package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -37,37 +26,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
@TbCoreComponent @TbCoreComponent
public class AlarmMsgConstructor { public class AlarmMsgConstructor {
@Autowired public AlarmUpdateMsg constructAlarmUpdatedMsg(UpdateMsgType msgType, Alarm alarm, String entityName) {
private DeviceService deviceService;
@Autowired
private AssetService assetService;
@Autowired
private EntityViewService entityViewService;
public AlarmUpdateMsg constructAlarmUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, Alarm alarm) {
String entityName = null;
switch (alarm.getOriginator().getEntityType()) {
case DEVICE:
Device deviceById = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId()));
if (deviceById != null) {
entityName = deviceById.getName();
}
break;
case ASSET:
Asset assetById = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId()));
if (assetById != null) {
entityName = assetById.getName();
}
break;
case ENTITY_VIEW:
EntityView entityViewById = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId()));
if (entityViewById != null) {
entityName = entityViewById.getName();
}
break;
}
AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder()
.setMsgType(msgType) .setMsgType(msgType)
.setIdMSB(alarm.getId().getId().getMostSignificantBits()) .setIdMSB(alarm.getId().getId().getMostSignificantBits())

9
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java

@ -33,10 +33,17 @@ public class DashboardMsgConstructor {
.setIdMSB(dashboard.getId().getId().getMostSignificantBits()) .setIdMSB(dashboard.getId().getId().getMostSignificantBits())
.setIdLSB(dashboard.getId().getId().getLeastSignificantBits()) .setIdLSB(dashboard.getId().getId().getLeastSignificantBits())
.setTitle(dashboard.getTitle()) .setTitle(dashboard.getTitle())
.setConfiguration(JacksonUtil.toString(dashboard.getConfiguration())); .setConfiguration(JacksonUtil.toString(dashboard.getConfiguration()))
.setMobileHide(dashboard.isMobileHide());
if (dashboard.getAssignedCustomers() != null) { if (dashboard.getAssignedCustomers() != null) {
builder.setAssignedCustomers(JacksonUtil.toString(dashboard.getAssignedCustomers())); builder.setAssignedCustomers(JacksonUtil.toString(dashboard.getAssignedCustomers()));
} }
if (dashboard.getImage() != null) {
builder.setImage(dashboard.getImage());
}
if (dashboard.getMobileOrder() != null) {
builder.setMobileOrder(dashboard.getMobileOrder());
}
return builder.build(); return builder.build();
} }

11
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg; import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.v1.EntityDataProto; import org.thingsboard.server.gen.edge.v1.EntityDataProto;
@ -40,7 +41,7 @@ import java.util.List;
@TbCoreComponent @TbCoreComponent
public class EntityDataMsgConstructor { public class EntityDataMsgConstructor {
public EntityDataProto constructEntityDataMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) { public EntityDataProto constructEntityDataMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) {
EntityDataProto.Builder builder = EntityDataProto.newBuilder() EntityDataProto.Builder builder = EntityDataProto.newBuilder()
.setEntityIdMSB(entityId.getId().getMostSignificantBits()) .setEntityIdMSB(entityId.getId().getMostSignificantBits())
.setEntityIdLSB(entityId.getId().getLeastSignificantBits()) .setEntityIdLSB(entityId.getId().getLeastSignificantBits())
@ -57,7 +58,7 @@ public class EntityDataMsgConstructor {
} }
builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts)); builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts));
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Can't convert to telemetry proto, entityData [{}]", entityId, entityData, e); log.warn("[{}][{}] Can't convert to telemetry proto, entityData [{}]", tenantId, entityId, entityData, e);
} }
break; break;
case ATTRIBUTES_UPDATED: case ATTRIBUTES_UPDATED:
@ -67,7 +68,7 @@ public class EntityDataMsgConstructor {
builder.setAttributesUpdatedMsg(attributesUpdatedMsg); builder.setAttributesUpdatedMsg(attributesUpdatedMsg);
builder.setPostAttributeScope(getScopeOfDefault(data)); builder.setPostAttributeScope(getScopeOfDefault(data));
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Can't convert to AttributesUpdatedMsg proto, entityData [{}]", entityId, entityData, e); log.warn("[{}][{}] Can't convert to AttributesUpdatedMsg proto, entityData [{}]", tenantId, entityId, entityData, e);
} }
break; break;
case POST_ATTRIBUTES: case POST_ATTRIBUTES:
@ -77,7 +78,7 @@ public class EntityDataMsgConstructor {
builder.setPostAttributesMsg(postAttributesMsg); builder.setPostAttributesMsg(postAttributesMsg);
builder.setPostAttributeScope(getScopeOfDefault(data)); builder.setPostAttributeScope(getScopeOfDefault(data));
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Can't convert to PostAttributesMsg, entityData [{}]", entityId, entityData, e); log.warn("[{}][{}] Can't convert to PostAttributesMsg, entityData [{}]", tenantId, entityId, entityData, e);
} }
break; break;
case ATTRIBUTES_DELETED: case ATTRIBUTES_DELETED:
@ -90,7 +91,7 @@ public class EntityDataMsgConstructor {
attributeDeleteMsg.build(); attributeDeleteMsg.build();
builder.setAttributeDeleteMsg(attributeDeleteMsg); builder.setAttributeDeleteMsg(attributeDeleteMsg);
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}] Can't convert to AttributeDeleteMsg proto, entityData [{}]", entityId, entityData, e); log.warn("[{}][{}] Can't convert to AttributeDeleteMsg proto, entityData [{}]", tenantId, entityId, entityData, e);
} }
break; break;
} }

1
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java

@ -51,6 +51,7 @@ public class WidgetTypeMsgConstructor {
if (widgetTypeDetails.getDescription() != null) { if (widgetTypeDetails.getDescription() != null) {
builder.setDescription(widgetTypeDetails.getDescription()); builder.setDescription(widgetTypeDetails.getDescription());
} }
builder.setDeprecated(widgetTypeDetails.isDeprecated());
return builder.build(); return builder.build();
} }

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/rule/AbstractRuleChainMetadataConstructor.java

@ -51,7 +51,7 @@ public abstract class AbstractRuleChainMetadataConstructor implements RuleChainM
builder.setMsgType(msgType); builder.setMsgType(msgType);
return builder.build(); return builder.build();
} catch (JsonProcessingException ex) { } catch (JsonProcessingException ex) {
log.error("Can't construct RuleChainMetadataUpdateMsg", ex); log.error("[{}] Can't construct RuleChainMetadataUpdateMsg", tenantId, ex);
} }
return null; return null;
} }

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AdminSettingsEdgeEventFetcher.java

@ -85,7 +85,7 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(tenantMailSettings))); EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(tenantMailSettings)));
AdminSettings systemMailTemplates = loadMailTemplates(); AdminSettings systemMailTemplates = loadMailTemplates(tenantId);
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(systemMailTemplates))); EdgeEventActionType.UPDATED, null, JacksonUtil.OBJECT_MAPPER.valueToTree(systemMailTemplates)));
@ -97,7 +97,7 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
return new PageData<>(result, 1, result.size(), false); return new PageData<>(result, 1, result.size(), false);
} }
private AdminSettings loadMailTemplates() throws Exception { private AdminSettings loadMailTemplates(TenantId tenantId) throws Exception {
Map<String, Object> mailTemplates = new HashMap<>(); Map<String, Object> mailTemplates = new HashMap<>();
for (String templatesName : templatesNames) { for (String templatesName : templatesNames) {
Template template = freemarkerConfig.getTemplate(templatesName); Template template = freemarkerConfig.getTemplate(templatesName);
@ -107,7 +107,7 @@ public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
if (mailTemplate != null) { if (mailTemplate != null) {
mailTemplates.put(name, mailTemplate); mailTemplates.put(name, mailTemplate);
} else { } else {
log.error("Can't load mail template from file {}", template.getName()); log.error("[{}] Can't load mail template from file {}", tenantId, template.getName());
} }
} }
} }

62
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/DefaultProfilesEdgeEventFetcher.java

@ -0,0 +1,62 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.fetch;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import java.util.ArrayList;
import java.util.List;
@AllArgsConstructor
@Slf4j
public class DefaultProfilesEdgeEventFetcher implements EdgeEventFetcher {
private final DeviceProfileService deviceProfileService;
private final AssetProfileService assetProfileService;
@Override
public PageLink getPageLink(int pageSize) {
return null;
}
@Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
List<EdgeEvent> result = new ArrayList<>();
DeviceProfile deviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId);
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE,
EdgeEventActionType.ADDED, deviceProfile.getId(), null));
AssetProfile assetProfile = assetProfileService.findDefaultAssetProfile(tenantId);
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET_PROFILE,
EdgeEventActionType.ADDED, assetProfile.getId(), null));
return new PageData<>(result, 1, result.size(), false);
}
}

30
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java

@ -46,12 +46,15 @@ import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetProfileService;
@ -78,6 +81,8 @@ import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor; import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
@ -566,18 +571,29 @@ public abstract class BaseEdgeProcessor {
relationService.saveRelation(tenantId, relation); relationService.saveRelation(tenantId, relation);
} }
protected TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) { protected TbMsgMetaData getEdgeActionTbMsgMetaData(Edge edge, CustomerId customerId) {
TbMsgMetaData metaData = getTbMsgMetaData(edge); TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("edgeId", edge.getId().toString());
metaData.putValue("edgeName", edge.getName());
if (customerId != null && !customerId.isNullUid()) { if (customerId != null && !customerId.isNullUid()) {
metaData.putValue("customerId", customerId.toString()); metaData.putValue("customerId", customerId.toString());
} }
return metaData; return metaData;
} }
protected TbMsgMetaData getTbMsgMetaData(Edge edge) { protected void pushEntityEventToRuleEngine(TenantId tenantId, EntityId entityId, CustomerId customerId,
TbMsgMetaData metaData = new TbMsgMetaData(); TbMsgType msgType, String msgData, TbMsgMetaData metaData) {
metaData.putValue("edgeId", edge.getId().toString()); TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, msgData);
metaData.putValue("edgeName", edge.getName()); tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, new TbQueueCallback() {
return metaData; @Override
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("[{}] Successfully send ENTITY_CREATED EVENT to rule engine [{}]", tenantId, msgData);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Failed to send ENTITY_CREATED EVENT to rule engine [{}]", tenantId, msgData, t);
}
});
} }
} }

37
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java

@ -20,15 +20,21 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
@ -45,7 +51,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB())); AlarmId alarmId = new AlarmId(new UUID(alarmUpdateMsg.getIdMSB(), alarmUpdateMsg.getIdLSB()));
if (originatorId == null) { if (originatorId == null) {
log.warn("Originator not found for the alarm msg {}", alarmUpdateMsg); log.warn("[{}] Originator not found for the alarm msg {}", tenantId, alarmUpdateMsg);
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
} }
try { try {
@ -129,13 +135,38 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
case ALARM_CLEAR: case ALARM_CLEAR:
Alarm alarm = alarmService.findAlarmById(tenantId, alarmId); Alarm alarm = alarmService.findAlarmById(tenantId, alarmId);
if (alarm != null) { if (alarm != null) {
return alarmMsgConstructor.constructAlarmUpdatedMsg(tenantId, msgType, alarm); return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, alarm, findOriginatorEntityName(tenantId, alarm));
} }
break; break;
case DELETED: case DELETED:
Alarm deletedAlarm = JacksonUtil.OBJECT_MAPPER.convertValue(body, Alarm.class); Alarm deletedAlarm = JacksonUtil.OBJECT_MAPPER.convertValue(body, Alarm.class);
return alarmMsgConstructor.constructAlarmUpdatedMsg(tenantId, msgType, deletedAlarm); return alarmMsgConstructor.constructAlarmUpdatedMsg(msgType, deletedAlarm, findOriginatorEntityName(tenantId, deletedAlarm));
} }
return null; return null;
} }
private String findOriginatorEntityName(TenantId tenantId, Alarm alarm) {
String entityName = null;
switch (alarm.getOriginator().getEntityType()) {
case DEVICE:
Device deviceById = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId()));
if (deviceById != null) {
entityName = deviceById.getName();
}
break;
case ASSET:
Asset assetById = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId()));
if (assetById != null) {
entityName = assetById.getName();
}
break;
case ENTITY_VIEW:
EntityView entityViewById = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId()));
if (entityViewById != null) {
entityName = entityViewById.getName();
}
break;
}
return entityName;
}
} }

29
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetEdgeProcessor.java

@ -15,15 +15,12 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.asset; package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
@ -35,15 +32,12 @@ import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.asset.BaseAssetService; import org.thingsboard.server.dao.asset.BaseAssetService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID; import java.util.UUID;
@ -104,22 +98,11 @@ public class AssetEdgeProcessor extends BaseAssetProcessor {
private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetId assetId) { private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetId assetId) {
try { try {
Asset asset = assetService.findAssetById(tenantId, assetId); Asset asset = assetService.findAssetById(tenantId, assetId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(asset); String assetAsString = JacksonUtil.toString(asset);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, assetId, asset.getCustomerId(), TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, asset.getCustomerId());
getActionTbMsgMetaData(edge, asset.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); pushEntityEventToRuleEngine(tenantId, assetId, asset.getCustomerId(), TbMsgType.ENTITY_CREATED, assetAsString, msgMetaData);
tbClusterService.pushMsgToRuleEngine(tenantId, assetId, tbMsg, new TbQueueCallback() { } catch (Exception e) {
@Override log.warn("[{}][{}] Failed to push asset action to rule engine: {}", tenantId, assetId, TbMsgType.ENTITY_CREATED.name(), e);
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", asset);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", asset, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push asset action to rule engine: {}", assetId, DataConstants.ENTITY_CREATED, e);
} }
} }

60
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/AssetProfileEdgeProcessor.java

@ -15,29 +15,28 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.asset; package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID; import java.util.UUID;
@ -64,7 +63,7 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor {
return handleUnsupportedMsgType(assetProfileUpdateMsg.getMsgType()); return handleUnsupportedMsgType(assetProfileUpdateMsg.getMsgType());
} }
} catch (DataValidationException e) { } catch (DataValidationException e) {
log.warn("Failed to process AssetProfileUpdateMsg from Edge [{}]", assetProfileUpdateMsg, e); log.warn("[{}] Failed to process AssetProfileUpdateMsg from Edge [{}]", tenantId, assetProfileUpdateMsg, e);
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} finally { } finally {
edgeSynchronizationManager.getSync().remove(); edgeSynchronizationManager.getSync().remove();
@ -72,32 +71,26 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor {
} }
private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge) { private void saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg, Edge edge) {
boolean created = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg); Pair<Boolean, Boolean> resultPair = super.saveOrUpdateAssetProfile(tenantId, assetProfileId, assetProfileUpdateMsg);
Boolean created = resultPair.getFirst();
if (created) { if (created) {
createRelationFromEdge(tenantId, edge.getId(), assetProfileId); createRelationFromEdge(tenantId, edge.getId(), assetProfileId);
pushAssetProfileCreatedEventToRuleEngine(tenantId, edge, assetProfileId); pushAssetProfileCreatedEventToRuleEngine(tenantId, edge, assetProfileId);
} }
Boolean assetProfileNameUpdated = resultPair.getSecond();
if (assetProfileNameUpdated) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET_PROFILE, EdgeEventActionType.UPDATED, assetProfileId, null);
}
} }
private void pushAssetProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetProfileId assetProfileId) { private void pushAssetProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, AssetProfileId assetProfileId) {
try { try {
AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId); AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(assetProfile); String assetProfileAsString = JacksonUtil.toString(assetProfile);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, assetProfileId, getTbMsgMetaData(edge), TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, null);
TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); pushEntityEventToRuleEngine(tenantId, assetProfileId, null, TbMsgType.ENTITY_CREATED, assetProfileAsString, msgMetaData);
tbClusterService.pushMsgToRuleEngine(tenantId, assetProfileId, tbMsg, new TbQueueCallback() { } catch (Exception e) {
@Override log.warn("[{}][{}] Failed to push asset profile action to rule engine: {}", tenantId, assetProfileId, TbMsgType.ENTITY_CREATED.name(), e);
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", assetProfile);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", assetProfile, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push asset profile action to rule engine: {}", assetProfileId, DataConstants.ENTITY_CREATED, e);
} }
} }
@ -129,4 +122,21 @@ public class AssetProfileEdgeProcessor extends BaseAssetProfileProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
@Override
protected void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) {
// do nothing on cloud
}
@Override
protected void setDefaultEdgeRuleChainId(TenantId tenantId,AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) {
UUID defaultEdgeRuleChainUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB());
assetProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null);
}
@Override
protected void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg) {
UUID defaultDashboardUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB());
assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null);
}
} }

7
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProcessor.java

@ -49,8 +49,8 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor {
Asset assetByName = assetService.findAssetByTenantIdAndName(tenantId, assetName); Asset assetByName = assetService.findAssetByTenantIdAndName(tenantId, assetName);
if (assetByName != null && !assetByName.getId().equals(assetId)) { if (assetByName != null && !assetByName.getId().equals(assetId)) {
assetName = assetName + "_" + StringUtils.randomAlphanumeric(15); assetName = assetName + "_" + StringUtils.randomAlphanumeric(15);
log.warn("Asset with name {} already exists. Renaming asset name to {}", log.warn("[{}] Asset with name {} already exists. Renaming asset name to {}",
assetUpdateMsg.getName(), assetName); tenantId, assetUpdateMsg.getName(), assetName);
assetNameUpdated = true; assetNameUpdated = true;
} }
asset.setName(assetName); asset.setName(assetName);
@ -69,6 +69,9 @@ public abstract class BaseAssetProcessor extends BaseEdgeProcessor {
asset.setId(assetId); asset.setId(assetId);
} }
assetService.saveAsset(asset, false); assetService.saveAsset(asset, false);
} catch (Exception e) {
log.error("[{}] Failed to process asset update msg [{}]", tenantId, assetUpdateMsg, e);
throw e;
} finally { } finally {
assetCreationLock.unlock(); assetCreationLock.unlock();
} }

36
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/asset/BaseAssetProfileProcessor.java

@ -17,22 +17,22 @@ package org.thingsboard.server.service.edge.rpc.processor.asset;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Slf4j @Slf4j
public class BaseAssetProfileProcessor extends BaseEdgeProcessor { public abstract class BaseAssetProfileProcessor extends BaseEdgeProcessor {
protected boolean saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) { protected Pair<Boolean, Boolean> saveOrUpdateAssetProfile(TenantId tenantId, AssetProfileId assetProfileId, AssetProfileUpdateMsg assetProfileUpdateMsg) {
boolean created = false; boolean created = false;
boolean assetProfileNameUpdated = false;
assetCreationLock.lock(); assetCreationLock.lock();
try { try {
AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId); AssetProfile assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId);
@ -43,6 +43,13 @@ public class BaseAssetProfileProcessor extends BaseEdgeProcessor {
assetProfile.setTenantId(tenantId); assetProfile.setTenantId(tenantId);
assetProfile.setCreatedTime(Uuids.unixTimestamp(assetProfileId.getId())); assetProfile.setCreatedTime(Uuids.unixTimestamp(assetProfileId.getId()));
} }
AssetProfile assetProfileByName = assetProfileService.findAssetProfileByName(tenantId, assetProfileName);
if (assetProfileByName != null && !assetProfileByName.getId().equals(assetProfileId)) {
assetProfileName = assetProfileName + "_" + StringUtils.randomAlphabetic(15);
log.warn("[{}] Asset profile with name {} already exists. Renaming asset profile name to {}",
tenantId, assetProfileUpdateMsg.getName(), assetProfileName);
assetProfileNameUpdated = true;
}
assetProfile.setName(assetProfileName); assetProfile.setName(assetProfileName);
assetProfile.setDefault(assetProfileUpdateMsg.getDefault()); assetProfile.setDefault(assetProfileUpdateMsg.getDefault());
assetProfile.setDefaultQueueName(assetProfileUpdateMsg.hasDefaultQueueName() ? assetProfileUpdateMsg.getDefaultQueueName() : null); assetProfile.setDefaultQueueName(assetProfileUpdateMsg.hasDefaultQueueName() ? assetProfileUpdateMsg.getDefaultQueueName() : null);
@ -50,20 +57,27 @@ public class BaseAssetProfileProcessor extends BaseEdgeProcessor {
assetProfile.setImage(assetProfileUpdateMsg.hasImage() assetProfile.setImage(assetProfileUpdateMsg.hasImage()
? new String(assetProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null); ? new String(assetProfileUpdateMsg.getImage().toByteArray(), StandardCharsets.UTF_8) : null);
UUID defaultRuleChainUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultRuleChainIdMSB(), assetProfileUpdateMsg.getDefaultRuleChainIdLSB()); setDefaultRuleChainId(tenantId, assetProfile, assetProfileUpdateMsg);
assetProfile.setDefaultRuleChainId(defaultRuleChainUUID != null ? new RuleChainId(defaultRuleChainUUID) : null); setDefaultEdgeRuleChainId(tenantId, assetProfile, assetProfileUpdateMsg);
setDefaultDashboardId(tenantId, assetProfile, assetProfileUpdateMsg);
UUID defaultDashboardUUID = safeGetUUID(assetProfileUpdateMsg.getDefaultDashboardIdMSB(), assetProfileUpdateMsg.getDefaultDashboardIdLSB());
assetProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null);
assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId);
if (created) { if (created) {
assetProfile.setId(assetProfileId); assetProfile.setId(assetProfileId);
} }
assetProfileService.saveAssetProfile(assetProfile, false); assetProfileService.saveAssetProfile(assetProfile, false);
} catch (Exception e) {
log.error("[{}] Failed to process asset profile update msg [{}]", tenantId, assetProfileUpdateMsg, e);
throw e;
} finally { } finally {
assetCreationLock.unlock(); assetCreationLock.unlock();
} }
return created; return Pair.of(created, assetProfileNameUpdated);
} }
protected abstract void setDefaultRuleChainId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg);
protected abstract void setDefaultEdgeRuleChainId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg);
protected abstract void setDefaultDashboardId(TenantId tenantId, AssetProfile assetProfile, AssetProfileUpdateMsg assetProfileUpdateMsg);
} }

11
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/BaseDashboardProcessor.java

@ -42,14 +42,19 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor {
dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId())); dashboard.setCreatedTime(Uuids.unixTimestamp(dashboardId.getId()));
} }
dashboard.setTitle(dashboardUpdateMsg.getTitle()); dashboard.setTitle(dashboardUpdateMsg.getTitle());
dashboard.setImage(dashboardUpdateMsg.hasImage() ? dashboardUpdateMsg.getImage() : null);
dashboard.setConfiguration(JacksonUtil.toJsonNode(dashboardUpdateMsg.getConfiguration())); dashboard.setConfiguration(JacksonUtil.toJsonNode(dashboardUpdateMsg.getConfiguration()));
Set<ShortCustomerInfo> assignedCustomers = null; Set<ShortCustomerInfo> assignedCustomers = null;
if (dashboardUpdateMsg.hasAssignedCustomers()) { if (dashboardUpdateMsg.hasAssignedCustomers()) {
assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() { assignedCustomers = JacksonUtil.fromString(dashboardUpdateMsg.getAssignedCustomers(), new TypeReference<>() {});
}); assignedCustomers = filterNonExistingCustomers(tenantId, assignedCustomers);
dashboard.setAssignedCustomers(assignedCustomers); dashboard.setAssignedCustomers(assignedCustomers);
} }
dashboard.setMobileOrder(dashboardUpdateMsg.hasMobileOrder() ? dashboardUpdateMsg.getMobileOrder() : null);
dashboard.setMobileHide(dashboardUpdateMsg.getMobileHide());
dashboardValidator.validate(dashboard, Dashboard::getTenantId); dashboardValidator.validate(dashboard, Dashboard::getTenantId);
if (created) { if (created) {
dashboard.setId(dashboardId); dashboard.setId(dashboardId);
@ -67,6 +72,8 @@ public abstract class BaseDashboardProcessor extends BaseEdgeProcessor {
return created; return created;
} }
protected abstract Set<ShortCustomerInfo> filterNonExistingCustomers(TenantId tenantId, Set<ShortCustomerInfo> assignedCustomers);
private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard) { private void unassignCustomersFromDashboard(TenantId tenantId, Dashboard dashboard) {
if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) { if (dashboard.getAssignedCustomers() != null && !dashboard.getAssignedCustomers().isEmpty()) {
for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) { for (ShortCustomerInfo assignedCustomer : dashboard.getAssignedCustomers()) {

37
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/dashboard/DashboardEdgeProcessor.java

@ -15,32 +15,28 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.dashboard; package org.thingsboard.server.service.edge.rpc.processor.dashboard;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
@Component @Component
@ -94,22 +90,11 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor {
private void pushDashboardCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DashboardId dashboardId) { private void pushDashboardCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DashboardId dashboardId) {
try { try {
Dashboard dashboard = dashboardService.findDashboardById(tenantId, dashboardId); Dashboard dashboard = dashboardService.findDashboardById(tenantId, dashboardId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(dashboard); String dashboardAsString = JacksonUtil.toString(dashboard);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, dashboardId, null, TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, null);
getActionTbMsgMetaData(edge, null), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); pushEntityEventToRuleEngine(tenantId, dashboardId, null, TbMsgType.ENTITY_CREATED, dashboardAsString, msgMetaData);
tbClusterService.pushMsgToRuleEngine(tenantId, dashboardId, tbMsg, new TbQueueCallback() { } catch (Exception e) {
@Override log.warn("[{}][{}] Failed to push dashboard action to rule engine: {}", tenantId, dashboardId, TbMsgType.ENTITY_CREATED.name(), e);
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", dashboard);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", dashboard, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push dashboard action to rule engine: {}", dashboardId, DataConstants.ENTITY_CREATED, e);
} }
} }
@ -145,4 +130,10 @@ public class DashboardEdgeProcessor extends BaseDashboardProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
@Override
protected Set<ShortCustomerInfo> filterNonExistingCustomers(TenantId tenantId, Set<ShortCustomerInfo> assignedCustomers) {
// do nothing on cloud
return assignedCustomers;
}
} }

19
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProcessor.java

@ -61,8 +61,8 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); Device deviceByName = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName);
if (deviceByName != null && !deviceByName.getId().equals(deviceId)) { if (deviceByName != null && !deviceByName.getId().equals(deviceId)) {
deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15); deviceName = deviceName + "_" + StringUtils.randomAlphabetic(15);
log.warn("Device with name {} already exists. Renaming device name to {}", log.warn("[{}] Device with name {} already exists. Renaming device name to {}",
deviceUpdateMsg.getName(), deviceName); tenantId, deviceUpdateMsg.getName(), deviceName);
deviceNameUpdated = true; deviceNameUpdated = true;
} }
device.setName(deviceName); device.setName(deviceName);
@ -98,7 +98,10 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials); deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
} }
tbClusterService.onDeviceUpdated(savedDevice, created ? null : device); tbClusterService.onDeviceUpdated(savedDevice, created ? null : device);
} finally { } catch (Exception e) {
log.error("[{}] Failed to process device update msg [{}]", tenantId, deviceUpdateMsg, e);
throw e;
} finally {
deviceCreationLock.unlock(); deviceCreationLock.unlock();
} }
return Pair.of(created, deviceNameUpdated); return Pair.of(created, deviceNameUpdated);
@ -110,8 +113,8 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
return dbCallbackExecutorService.submit(() -> { return dbCallbackExecutorService.submit(() -> {
Device device = deviceService.findDeviceById(tenantId, deviceId); Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device != null) { if (device != null) {
log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", log.debug("[{}] Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]",
device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue()); tenantId, device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue());
try { try {
edgeSynchronizationManager.getSync().set(true); edgeSynchronizationManager.getSync().set(true);
@ -122,14 +125,14 @@ public abstract class BaseDeviceProcessor extends BaseEdgeProcessor {
? deviceCredentialsUpdateMsg.getCredentialsValue() : null); ? deviceCredentialsUpdateMsg.getCredentialsValue() : null);
deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials); deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials);
} catch (Exception e) { } catch (Exception e) {
log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", log.error("[{}] Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]",
device.getName(), deviceCredentialsUpdateMsg, e); tenantId, device.getName(), deviceCredentialsUpdateMsg, e);
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
edgeSynchronizationManager.getSync().remove(); edgeSynchronizationManager.getSync().remove();
} }
} else { } else {
log.warn("Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", deviceId, deviceCredentialsUpdateMsg); log.warn("[{}] Can't find device by id [{}], deviceCredentialsUpdateMsg [{}]", tenantId, deviceId, deviceCredentialsUpdateMsg);
} }
return null; return null;
}); });

41
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/BaseDeviceProfileProcessor.java

@ -18,16 +18,15 @@ package org.thingsboard.server.service.edge.rpc.processor.device;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.queue.util.DataDecodingEncodingService; import org.thingsboard.server.queue.util.DataDecodingEncodingService;
@ -38,25 +37,33 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@Slf4j @Slf4j
public class BaseDeviceProfileProcessor extends BaseEdgeProcessor { public abstract class BaseDeviceProfileProcessor extends BaseEdgeProcessor {
@Autowired @Autowired
private DataDecodingEncodingService dataDecodingEncodingService; private DataDecodingEncodingService dataDecodingEncodingService;
protected boolean saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { protected Pair<Boolean, Boolean> saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
boolean created = false; boolean created = false;
boolean deviceProfileNameUpdated = false;
deviceCreationLock.lock(); deviceCreationLock.lock();
try { try {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
String deviceProfileName = deviceProfileUpdateMsg.getName();
if (deviceProfile == null) { if (deviceProfile == null) {
created = true; created = true;
deviceProfile = new DeviceProfile(); deviceProfile = new DeviceProfile();
deviceProfile.setTenantId(tenantId); deviceProfile.setTenantId(tenantId);
deviceProfile.setCreatedTime(Uuids.unixTimestamp(deviceProfileId.getId())); deviceProfile.setCreatedTime(Uuids.unixTimestamp(deviceProfileId.getId()));
} }
deviceProfile.setName(deviceProfileUpdateMsg.getName()); DeviceProfile deviceProfileByName = deviceProfileService.findDeviceProfileByName(tenantId, deviceProfileName);
if (deviceProfileByName != null && !deviceProfileByName.getId().equals(deviceProfileId)) {
deviceProfileName = deviceProfileName + "_" + StringUtils.randomAlphabetic(15);
log.warn("[{}] Device profile with name {} already exists. Renaming device profile name to {}",
tenantId, deviceProfileUpdateMsg.getName(), deviceProfileName);
deviceProfileNameUpdated = true;
}
deviceProfile.setName(deviceProfileName);
deviceProfile.setDescription(deviceProfileUpdateMsg.hasDescription() ? deviceProfileUpdateMsg.getDescription() : null); deviceProfile.setDescription(deviceProfileUpdateMsg.hasDescription() ? deviceProfileUpdateMsg.getDescription() : null);
deviceProfile.setDefault(deviceProfileUpdateMsg.getDefault());
deviceProfile.setType(DeviceProfileType.valueOf(deviceProfileUpdateMsg.getType())); deviceProfile.setType(DeviceProfileType.valueOf(deviceProfileUpdateMsg.getType()));
deviceProfile.setTransportType(deviceProfileUpdateMsg.hasTransportType() deviceProfile.setTransportType(deviceProfileUpdateMsg.hasTransportType()
? DeviceTransportType.valueOf(deviceProfileUpdateMsg.getTransportType()) : DeviceTransportType.DEFAULT); ? DeviceTransportType.valueOf(deviceProfileUpdateMsg.getTransportType()) : DeviceTransportType.DEFAULT);
@ -72,11 +79,9 @@ public class BaseDeviceProfileProcessor extends BaseEdgeProcessor {
dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray()); dataDecodingEncodingService.decode(deviceProfileUpdateMsg.getProfileDataBytes().toByteArray());
deviceProfile.setProfileData(profileDataOpt.orElse(null)); deviceProfile.setProfileData(profileDataOpt.orElse(null));
UUID defaultRuleChainUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB()); setDefaultRuleChainId(tenantId, deviceProfile, deviceProfileUpdateMsg);
deviceProfile.setDefaultRuleChainId(defaultRuleChainUUID != null ? new RuleChainId(defaultRuleChainUUID) : null); setDefaultEdgeRuleChainId(tenantId, deviceProfile, deviceProfileUpdateMsg);
setDefaultDashboardId(tenantId, deviceProfile, deviceProfileUpdateMsg);
UUID defaultDashboardUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB());
deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null);
String defaultQueueName = StringUtils.isNotBlank(deviceProfileUpdateMsg.getDefaultQueueName()) String defaultQueueName = StringUtils.isNotBlank(deviceProfileUpdateMsg.getDefaultQueueName())
? deviceProfileUpdateMsg.getDefaultQueueName() : null; ? deviceProfileUpdateMsg.getDefaultQueueName() : null;
@ -88,15 +93,23 @@ public class BaseDeviceProfileProcessor extends BaseEdgeProcessor {
UUID softwareUUID = safeGetUUID(deviceProfileUpdateMsg.getSoftwareIdMSB(), deviceProfileUpdateMsg.getSoftwareIdLSB()); UUID softwareUUID = safeGetUUID(deviceProfileUpdateMsg.getSoftwareIdMSB(), deviceProfileUpdateMsg.getSoftwareIdLSB());
deviceProfile.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null); deviceProfile.setSoftwareId(softwareUUID != null ? new OtaPackageId(softwareUUID) : null);
deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId); deviceProfileValidator.validate(deviceProfile, DeviceProfile::getTenantId);
if (created) { if (created) {
deviceProfile.setId(deviceProfileId); deviceProfile.setId(deviceProfileId);
} }
deviceProfileService.saveDeviceProfile(deviceProfile, false); deviceProfileService.saveDeviceProfile(deviceProfile, false);
} finally { } catch (Exception e) {
log.error("[{}] Failed to process device profile update msg [{}]", tenantId, deviceProfileUpdateMsg, e);
throw e;
} finally {
deviceCreationLock.unlock(); deviceCreationLock.unlock();
} }
return created; return Pair.of(created, deviceProfileNameUpdated);
} }
protected abstract void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg);
protected abstract void setDefaultEdgeRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg);
protected abstract void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg);
} }

33
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceEdgeProcessor.java

@ -113,22 +113,11 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceId deviceId) { private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceId deviceId) {
try { try {
Device device = deviceService.findDeviceById(tenantId, deviceId); Device device = deviceService.findDeviceById(tenantId, deviceId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(device); String deviceAsString = JacksonUtil.toString(device);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceId, device.getCustomerId(), TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, device.getCustomerId());
getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); pushEntityEventToRuleEngine(tenantId, deviceId, device.getCustomerId(), TbMsgType.ENTITY_CREATED, deviceAsString, msgMetaData);
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { } catch (Exception e) {
@Override log.warn("[{}][{}] Failed to push device action to rule engine: {}", tenantId, deviceId, TbMsgType.ENTITY_CREATED.name(), e);
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", device);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device action to rule engine: {}", deviceId, TbMsgType.ENTITY_CREATED.name(), e);
} }
} }
@ -161,7 +150,7 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Can't process push notification to core [{}]", deviceRpcCallMsg, t); log.error("[{}] Can't process push notification to core [{}]", tenantId, deviceRpcCallMsg, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}; };
@ -196,18 +185,18 @@ public class DeviceEdgeProcessor extends BaseDeviceProcessor {
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
@Override @Override
public void onSuccess(TbQueueMsgMetadata metadata) { public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}", log.debug("[{}] Successfully send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}",
device, deviceRpcCallMsg); tenantId, device, deviceRpcCallMsg);
} }
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.debug("Failed to send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}", log.debug("[{}] Failed to send TO_SERVER_RPC_REQUEST to rule engine [{}], deviceRpcCallMsg {}",
device, deviceRpcCallMsg, t); tenantId, device, deviceRpcCallMsg, t);
} }
}); });
} catch (JsonProcessingException | IllegalArgumentException e) { } catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push TO_SERVER_RPC_REQUEST to rule engine. deviceRpcCallMsg {}", deviceId, deviceRpcCallMsg, e); log.warn("[{}][{}] Failed to push TO_SERVER_RPC_REQUEST to rule engine. deviceRpcCallMsg {}", tenantId, deviceId, deviceRpcCallMsg, e);
} }
return Futures.immediateFuture(null); return Futures.immediateFuture(null);

61
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/device/DeviceProfileEdgeProcessor.java

@ -15,29 +15,28 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.device; package org.thingsboard.server.service.edge.rpc.processor.device;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg; import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID; import java.util.UUID;
@ -47,7 +46,6 @@ import java.util.UUID;
@TbCoreComponent @TbCoreComponent
public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor { public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor {
public ListenableFuture<Void> processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg) { public ListenableFuture<Void> processDeviceProfileMsgFromEdge(TenantId tenantId, Edge edge, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
log.trace("[{}] executing processDeviceProfileMsgFromEdge [{}] from edge [{}]", tenantId, deviceProfileUpdateMsg, edge.getName()); log.trace("[{}] executing processDeviceProfileMsgFromEdge [{}] from edge [{}]", tenantId, deviceProfileUpdateMsg, edge.getName());
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileUpdateMsg.getIdMSB(), deviceProfileUpdateMsg.getIdLSB())); DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileUpdateMsg.getIdMSB(), deviceProfileUpdateMsg.getIdLSB()));
@ -65,7 +63,7 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor {
return handleUnsupportedMsgType(deviceProfileUpdateMsg.getMsgType()); return handleUnsupportedMsgType(deviceProfileUpdateMsg.getMsgType());
} }
} catch (DataValidationException e) { } catch (DataValidationException e) {
log.warn("Failed to process DeviceProfileUpdateMsg from Edge [{}]", deviceProfileUpdateMsg, e); log.warn("[{}] Failed to process DeviceProfileUpdateMsg from Edge [{}]", tenantId, deviceProfileUpdateMsg, e);
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} finally { } finally {
edgeSynchronizationManager.getSync().remove(); edgeSynchronizationManager.getSync().remove();
@ -73,32 +71,26 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor {
} }
private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge) { private void saveOrUpdateDeviceProfile(TenantId tenantId, DeviceProfileId deviceProfileId, DeviceProfileUpdateMsg deviceProfileUpdateMsg, Edge edge) {
boolean created = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg); Pair<Boolean, Boolean> resultPair = super.saveOrUpdateDeviceProfile(tenantId, deviceProfileId, deviceProfileUpdateMsg);
Boolean created = resultPair.getFirst();
if (created) { if (created) {
createRelationFromEdge(tenantId, edge.getId(), deviceProfileId); createRelationFromEdge(tenantId, edge.getId(), deviceProfileId);
pushDeviceProfileCreatedEventToRuleEngine(tenantId, edge, deviceProfileId); pushDeviceProfileCreatedEventToRuleEngine(tenantId, edge, deviceProfileId);
} }
Boolean deviceProfileNameUpdated = resultPair.getSecond();
if (deviceProfileNameUpdated) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE, EdgeEventActionType.UPDATED, deviceProfileId, null);
}
} }
private void pushDeviceProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceProfileId deviceProfileId) { private void pushDeviceProfileCreatedEventToRuleEngine(TenantId tenantId, Edge edge, DeviceProfileId deviceProfileId) {
try { try {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(deviceProfile); String deviceProfileAsString = JacksonUtil.toString(deviceProfile);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, deviceProfileId, getTbMsgMetaData(edge), TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, null);
TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); pushEntityEventToRuleEngine(tenantId, deviceProfileId, null, TbMsgType.ENTITY_CREATED, deviceProfileAsString, msgMetaData);
tbClusterService.pushMsgToRuleEngine(tenantId, deviceProfileId, tbMsg, new TbQueueCallback() { } catch (Exception e) {
@Override log.warn("[{}][{}] Failed to push device profile action to rule engine: {}", tenantId, deviceProfileId, TbMsgType.ENTITY_CREATED.name(), e);
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", deviceProfile);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", deviceProfile, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device profile action to rule engine: {}", deviceProfileId, DataConstants.ENTITY_CREATED, e);
} }
} }
@ -130,4 +122,21 @@ public class DeviceProfileEdgeProcessor extends BaseDeviceProfileProcessor {
} }
return downlinkMsg; return downlinkMsg;
} }
@Override
protected void setDefaultRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
// do nothing on cloud
}
@Override
protected void setDefaultEdgeRuleChainId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
UUID defaultEdgeRuleChainUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultRuleChainIdMSB(), deviceProfileUpdateMsg.getDefaultRuleChainIdLSB());
deviceProfile.setDefaultEdgeRuleChainId(defaultEdgeRuleChainUUID != null ? new RuleChainId(defaultEdgeRuleChainUUID) : null);
}
@Override
protected void setDefaultDashboardId(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
UUID defaultDashboardUUID = safeGetUUID(deviceProfileUpdateMsg.getDefaultDashboardIdMSB(), deviceProfileUpdateMsg.getDefaultDashboardIdLSB());
deviceProfile.setDefaultDashboardId(defaultDashboardUUID != null ? new DashboardId(defaultDashboardUUID) : null);
}
} }

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/edge/EdgeProcessor.java

@ -85,7 +85,7 @@ public class EdgeProcessor extends BaseEdgeProcessor {
do { do {
pageData = userService.findCustomerUsers(tenantId, customerId, pageLink); pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size()); log.trace("[{}][{}][{}] user(s) are going to be added to edge.", tenantId, edge.getId(), pageData.getData().size());
for (User user : pageData.getData()) { for (User user : pageData.getData()) {
futures.add(saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null)); futures.add(saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null));
} }
@ -108,7 +108,7 @@ public class EdgeProcessor extends BaseEdgeProcessor {
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during processing edge event", e); log.error("[{}] Exception during processing edge event", tenantId, e);
return Futures.immediateFailedFuture(e); return Futures.immediateFailedFuture(e);
} }
} }

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/BaseEntityViewProcessor.java

@ -49,8 +49,8 @@ public abstract class BaseEntityViewProcessor extends BaseEdgeProcessor {
EntityView entityViewByName = entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName); EntityView entityViewByName = entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName);
if (entityViewByName != null && !entityViewByName.getId().equals(entityViewId)) { if (entityViewByName != null && !entityViewByName.getId().equals(entityViewId)) {
entityViewName = entityViewName + "_" + StringUtils.randomAlphanumeric(15); entityViewName = entityViewName + "_" + StringUtils.randomAlphanumeric(15);
log.warn("Entity view with name {} already exists. Renaming entity view name to {}", log.warn("[{}] Entity view with name {} already exists. Renaming entity view name to {}",
entityViewUpdateMsg.getName(), entityViewName); tenantId, entityViewUpdateMsg.getName(), entityViewName);
entityViewNameUpdated = true; entityViewNameUpdated = true;
} }
entityView.setName(entityViewName); entityView.setName(entityViewName);

33
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/entityview/EntityViewEdgeProcessor.java

@ -15,15 +15,12 @@
*/ */
package org.thingsboard.server.service.edge.rpc.processor.entityview; package org.thingsboard.server.service.edge.rpc.processor.entityview;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair; import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.Edge;
@ -34,14 +31,11 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg; import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID; import java.util.UUID;
@ -90,7 +84,7 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor {
Boolean created = resultPair.getFirst(); Boolean created = resultPair.getFirst();
if (created) { if (created) {
createRelationFromEdge(tenantId, edge.getId(), entityViewId); createRelationFromEdge(tenantId, edge.getId(), entityViewId);
pushAssetCreatedEventToRuleEngine(tenantId, edge, entityViewId); pushEntityViewCreatedEventToRuleEngine(tenantId, edge, entityViewId);
entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edge.getId()); entityViewService.assignEntityViewToEdge(tenantId, entityViewId, edge.getId());
} }
Boolean assetNameUpdated = resultPair.getSecond(); Boolean assetNameUpdated = resultPair.getSecond();
@ -99,25 +93,14 @@ public class EntityViewEdgeProcessor extends BaseEntityViewProcessor {
} }
} }
private void pushAssetCreatedEventToRuleEngine(TenantId tenantId, Edge edge, EntityViewId entityViewId) { private void pushEntityViewCreatedEventToRuleEngine(TenantId tenantId, Edge edge, EntityViewId entityViewId) {
try { try {
EntityView entityView = entityViewService.findEntityViewById(tenantId, entityViewId); EntityView entityView = entityViewService.findEntityViewById(tenantId, entityViewId);
ObjectNode entityNode = JacksonUtil.OBJECT_MAPPER.valueToTree(entityView); String entityViewAsString = JacksonUtil.toString(entityView);
TbMsg tbMsg = TbMsg.newMsg(TbMsgType.ENTITY_CREATED, entityViewId, entityView.getCustomerId(), TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, entityView.getCustomerId());
getActionTbMsgMetaData(edge, entityView.getCustomerId()), TbMsgDataType.JSON, JacksonUtil.OBJECT_MAPPER.writeValueAsString(entityNode)); pushEntityEventToRuleEngine(tenantId, entityViewId, entityView.getCustomerId(), TbMsgType.ENTITY_CREATED, entityViewAsString, msgMetaData);
tbClusterService.pushMsgToRuleEngine(tenantId, entityViewId, tbMsg, new TbQueueCallback() { } catch (Exception e) {
@Override log.warn("[{}][{}] Failed to push entity view action to rule engine: {}", tenantId, entityViewId, TbMsgType.ENTITY_CREATED.name(), e);
public void onSuccess(TbQueueMsgMetadata metadata) {
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", entityView);
}
@Override
public void onFailure(Throwable t) {
log.warn("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", entityView, t);
}
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push entity view action to rule engine: {}", entityViewId, DataConstants.ENTITY_CREATED, e);
} }
} }

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/relation/BaseRelationProcessor.java

@ -59,7 +59,7 @@ public abstract class BaseRelationProcessor extends BaseEdgeProcessor {
relationService.saveRelation(tenantId, entityRelation); relationService.saveRelation(tenantId, entityRelation);
break; break;
} else { } else {
log.warn("Skipping relating update msg because from/to entity doesn't exists on edge, {}", relationUpdateMsg); log.warn("[{}] Skipping relating update msg because from/to entity doesn't exists on edge, {}", tenantId, relationUpdateMsg);
break; break;
} }
case ENTITY_DELETED_RPC_MESSAGE: case ENTITY_DELETED_RPC_MESSAGE:

25
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/BaseTelemetryProcessor.java

@ -130,7 +130,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType())); result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
} }
} else { } else {
log.warn("Skipping telemetry update msg because entity doesn't exists on edge, {}", entityData); log.warn("[{}] Skipping telemetry update msg because entity doesn't exists on edge, {}", tenantId, entityData);
} }
return result; return result;
} }
@ -172,7 +172,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
} }
break; break;
default: default:
log.debug("Using empty metadata for entityId [{}]", entityId); log.debug("[{}] Using empty metadata for entityId [{}]", tenantId, entityId);
break; break;
} }
return new ImmutablePair<>(metaData, customerId != null ? customerId : new CustomerId(ModelConstants.NULL_UUID)); return new ImmutablePair<>(metaData, customerId != null ? customerId : new CustomerId(ModelConstants.NULL_UUID));
@ -193,7 +193,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Can't process post telemetry [{}]", msg, t); log.error("[{}] Can't process post telemetry [{}]", tenantId, msg, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}); });
@ -207,7 +207,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
if (EntityType.DEVICE.equals(entityId.getEntityType())) { if (EntityType.DEVICE.equals(entityId.getEntityType())) {
DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId()));
if (deviceProfile == null) { if (deviceProfile == null) {
log.warn("[{}] Device profile is null!", entityId); log.warn("[{}][{}] Device profile is null!", tenantId, entityId);
} else { } else {
ruleChainId = deviceProfile.getDefaultRuleChainId(); ruleChainId = deviceProfile.getDefaultRuleChainId();
queueName = deviceProfile.getDefaultQueueName(); queueName = deviceProfile.getDefaultQueueName();
@ -215,7 +215,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
} else if (EntityType.ASSET.equals(entityId.getEntityType())) { } else if (EntityType.ASSET.equals(entityId.getEntityType())) {
AssetProfile assetProfile = assetProfileCache.get(tenantId, new AssetId(entityId.getId())); AssetProfile assetProfile = assetProfileCache.get(tenantId, new AssetId(entityId.getId()));
if (assetProfile == null) { if (assetProfile == null) {
log.warn("[{}] Asset profile is null!", entityId); log.warn("[{}][{}] Asset profile is null!", tenantId, entityId);
} else { } else {
ruleChainId = assetProfile.getDefaultRuleChainId(); ruleChainId = assetProfile.getDefaultRuleChainId();
queueName = assetProfile.getDefaultQueueName(); queueName = assetProfile.getDefaultQueueName();
@ -237,7 +237,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Can't process post attributes [{}]", msg, t); log.error("[{}] Can't process post attributes [{}]", tenantId, msg, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}); });
@ -267,7 +267,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Can't process attributes update [{}]", msg, t); log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}); });
@ -275,7 +275,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Can't process attributes update [{}]", msg, t); log.error("[{}] Can't process attributes update [{}]", tenantId, msg, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}); });
@ -300,7 +300,7 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t); log.error("[{}] Can't process attribute delete msg [{}]", tenantId, attributeDeleteMsg, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}); });
@ -311,7 +311,8 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
}, dbCallbackExecutorService); }, dbCallbackExecutorService);
} }
public EntityDataProto convertTelemetryEventToEntityDataProto(EntityType entityType, public EntityDataProto convertTelemetryEventToEntityDataProto(TenantId tenantId,
EntityType entityType,
UUID entityUUID, UUID entityUUID,
EdgeEventActionType actionType, EdgeEventActionType actionType,
JsonNode body) throws JsonProcessingException { JsonNode body) throws JsonProcessingException {
@ -342,11 +343,11 @@ public abstract class BaseTelemetryProcessor extends BaseEdgeProcessor {
entityId = new EdgeId(entityUUID); entityId = new EdgeId(entityUUID);
break; break;
default: default:
log.warn("Unsupported edge event type [{}]", entityType); log.warn("[{}] Unsupported edge event type [{}]", tenantId, entityType);
return null; return null;
} }
JsonElement entityData = JsonParser.parseString(JacksonUtil.OBJECT_MAPPER.writeValueAsString(body)); JsonElement entityData = JsonParser.parseString(JacksonUtil.OBJECT_MAPPER.writeValueAsString(body));
return entityDataMsgConstructor.constructEntityDataMsg(entityId, actionType, entityData); return entityDataMsgConstructor.constructEntityDataMsg(tenantId, entityId, actionType, entityData);
} }
} }

3
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/telemetry/TelemetryEdgeProcessor.java

@ -38,7 +38,8 @@ public class TelemetryEdgeProcessor extends BaseTelemetryProcessor {
public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) throws JsonProcessingException { public DownlinkMsg convertTelemetryEventToDownlink(EdgeEvent edgeEvent) throws JsonProcessingException {
EntityType entityType = EntityType.valueOf(edgeEvent.getType().name()); EntityType entityType = EntityType.valueOf(edgeEvent.getType().name());
EntityDataProto entityDataProto = convertTelemetryEventToEntityDataProto(entityType, edgeEvent.getEntityId(), EntityDataProto entityDataProto = convertTelemetryEventToEntityDataProto(
edgeEvent.getTenantId(), entityType, edgeEvent.getEntityId(),
edgeEvent.getAction(), edgeEvent.getBody()); edgeEvent.getAction(), edgeEvent.getBody());
return DownlinkMsg.newBuilder() return DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .setDownlinkMsgId(EdgeUtils.nextPositiveInt())

14
application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java

@ -177,7 +177,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
entityData.put("kv", attributes); entityData.put("kv", attributes);
entityData.put("scope", scope); entityData.put("scope", scope);
JsonNode body = JacksonUtil.OBJECT_MAPPER.valueToTree(entityData); JsonNode body = JacksonUtil.OBJECT_MAPPER.valueToTree(entityData);
log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body); log.debug("[{}] Sending attributes data msg, entityId [{}], attributes [{}]", tenantId, entityId, body);
future = saveEdgeEvent(tenantId, edge.getId(), entityType, EdgeEventActionType.ATTRIBUTES_UPDATED, entityId, body); future = saveEdgeEvent(tenantId, edge.getId(), entityType, EdgeEventActionType.ATTRIBUTES_UPDATED, entityId, body);
} else { } else {
future = Futures.immediateFuture(null); future = Futures.immediateFuture(null);
@ -185,7 +185,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
} }
return Futures.transformAsync(future, v -> processLatestTimeseriesAndAddToEdgeQueue(tenantId, entityId, edge, entityType), dbCallbackExecutorService); return Futures.transformAsync(future, v -> processLatestTimeseriesAndAddToEdgeQueue(tenantId, entityId, edge, entityType), dbCallbackExecutorService);
} catch (Exception e) { } catch (Exception e) {
String errMsg = String.format("[%s] Failed to save attribute updates to the edge [%s]", edge.getId(), attributesRequestMsg); String errMsg = String.format("[%s][%s] Failed to save attribute updates to the edge [%s]", tenantId, edge.getId(), attributesRequestMsg);
log.error(errMsg, e); log.error(errMsg, e);
return Futures.immediateFailedFuture(new RuntimeException(errMsg, e)); return Futures.immediateFailedFuture(new RuntimeException(errMsg, e));
} }
@ -239,7 +239,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
if (relationsList != null && !relationsList.isEmpty()) { if (relationsList != null && !relationsList.isEmpty()) {
List<ListenableFuture<Void>> futures = new ArrayList<>(); List<ListenableFuture<Void>> futures = new ArrayList<>();
for (List<EntityRelation> entityRelations : relationsList) { for (List<EntityRelation> entityRelations : relationsList) {
log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size()); log.trace("[{}][{}][{}][{}] relation(s) are going to be pushed to edge.", tenantId, edge.getId(), entityId, entityRelations.size());
for (EntityRelation relation : entityRelations) { for (EntityRelation relation : entityRelations) {
try { try {
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
@ -252,7 +252,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
JacksonUtil.OBJECT_MAPPER.valueToTree(relation))); JacksonUtil.OBJECT_MAPPER.valueToTree(relation)));
} }
} catch (Exception e) { } catch (Exception e) {
String errMsg = String.format("[%s] Exception during loading relation [%s] to edge on sync!", edge.getId(), relation); String errMsg = String.format("[%s][%s] Exception during loading relation [%s] to edge on sync!", tenantId, edge.getId(), relation);
log.error(errMsg, e); log.error(errMsg, e);
futureToSet.setException(new RuntimeException(errMsg, e)); futureToSet.setException(new RuntimeException(errMsg, e));
return; return;
@ -267,7 +267,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override @Override
public void onFailure(Throwable throwable) { public void onFailure(Throwable throwable) {
String errMsg = String.format("[%s] Exception during saving edge events [%s]!", edge.getId(), relationRequestMsg); String errMsg = String.format("[%s][%s] Exception during saving edge events [%s]!", tenantId, edge.getId(), relationRequestMsg);
log.error(errMsg, throwable); log.error(errMsg, throwable);
futureToSet.setException(new RuntimeException(errMsg, throwable)); futureToSet.setException(new RuntimeException(errMsg, throwable));
} }
@ -276,7 +276,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
futureToSet.set(null); futureToSet.set(null);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e); log.error("[{}] Exception during loading relation(s) to edge on sync!", tenantId, e);
futureToSet.setException(e); futureToSet.setException(e);
} }
} }
@ -374,7 +374,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override @Override
public void onFailure(Throwable t) { public void onFailure(Throwable t) {
log.error("Exception during loading relation to edge on sync!", t); log.error("[{}] Exception during loading relation to edge on sync!", tenantId, t);
futureToSet.setException(t); futureToSet.setException(t);
} }
}, dbCallbackExecutorService); }, dbCallbackExecutorService);

156
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.service.entitiy.alarm; package org.thingsboard.server.service.entitiy.alarm;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -29,20 +30,20 @@ import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQueryV2;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Service @Service
@AllArgsConstructor @AllArgsConstructor
@ -104,19 +105,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} }
AlarmInfo alarmInfo = result.getAlarm(); AlarmInfo alarmInfo = result.getAlarm();
if (result.isModified()) { if (result.isModified()) {
AlarmComment alarmComment = AlarmComment.builder() String systemComment = String.format("Alarm was acknowledged by user %s", user.getTitle());
.alarmId(alarm.getId()) addSystemAlarmComment(alarmInfo, user, "ACK", systemComment);
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was acknowledged by user %s",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))
.put("userId", user.getId().toString())
.put("subtype", "ACK"))
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user); alarmInfo.getCustomerId(), ActionType.ALARM_ACK, user);
} else { } else {
@ -138,19 +128,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} }
AlarmInfo alarmInfo = result.getAlarm(); AlarmInfo alarmInfo = result.getAlarm();
if (result.isCleared()) { if (result.isCleared()) {
AlarmComment alarmComment = AlarmComment.builder() String systemComment = String.format("Alarm was cleared by user %s", user.getTitle());
.alarmId(alarm.getId()) addSystemAlarmComment(alarmInfo, user, "CLEAR", systemComment);
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was cleared by user %s",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))
.put("userId", user.getId().toString())
.put("subtype", "CLEAR"))
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user); alarmInfo.getCustomerId(), ActionType.ALARM_CLEAR, user);
} else { } else {
@ -168,21 +147,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
AlarmInfo alarmInfo = result.getAlarm(); AlarmInfo alarmInfo = result.getAlarm();
if (result.isModified()) { if (result.isModified()) {
AlarmAssignee assignee = alarmInfo.getAssignee(); AlarmAssignee assignee = alarmInfo.getAssignee();
AlarmComment alarmComment = AlarmComment.builder() String systemComment = String.format("Alarm was assigned by user %s to user %s", user.getTitle(), assignee.getTitle());
.alarmId(alarm.getId()) addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment, assignee.getId());
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was assigned by user %s to user %s",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName(),
(assignee.getFirstName() == null || assignee.getLastName() == null) ? assignee.getEmail() : assignee.getFirstName() + " " + assignee.getLastName()))
.put("userId", user.getId().toString())
.put("assigneeId", assignee.getId().toString())
.put("subtype", "ASSIGN"))
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user); alarmInfo.getCustomerId(), ActionType.ALARM_ASSIGNED, user);
} else { } else {
@ -199,19 +165,8 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} }
AlarmInfo alarmInfo = result.getAlarm(); AlarmInfo alarmInfo = result.getAlarm();
if (result.isModified()) { if (result.isModified()) {
AlarmComment alarmComment = AlarmComment.builder() String systemComment = String.format("Alarm was unassigned by user %s", user.getTitle());
.alarmId(alarm.getId()) addSystemAlarmComment(alarmInfo, user, "ASSIGN", systemComment);
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned by user %s",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))
.put("userId", user.getId().toString())
.put("subtype", "ASSIGN"))
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo, notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), alarmInfo,
alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user); alarmInfo.getCustomerId(), ActionType.ALARM_UNASSIGNED, user);
} else { } else {
@ -221,37 +176,20 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
} }
@Override @Override
public void unassignUserAlarms(TenantId tenantId, User user, long unassignTs) { public List<AlarmId> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) {
AlarmQueryV2 alarmQuery = AlarmQueryV2.builder().assigneeId(user.getId()).pageLink(new TimePageLink(Integer.MAX_VALUE)).build(); List<AlarmId> totalAlarmIds = new ArrayList<>();
try { PageLink pageLink = new PageLink(100, 0, null, new SortOrder("id", SortOrder.Direction.ASC));
List<AlarmInfo> alarms = alarmService.findAlarmsV2(tenantId, alarmQuery).get(30, TimeUnit.SECONDS).getData(); while (true) {
for (AlarmInfo alarm : alarms) { PageData<AlarmId> pageData = alarmService.findAlarmIdsByAssigneeId(user.getTenantId(), user.getId(), pageLink);
AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(tenantId, alarm.getId(), getOrDefault(unassignTs)); List<AlarmId> alarmIds = pageData.getData();
if (!result.isSuccessful()) { if (alarmIds.isEmpty()) {
continue; break;
}
if (result.isModified()) {
AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was unassigned because user %s - was deleted",
(user.getFirstName() == null || user.getLastName() == null) ? user.getName() : user.getFirstName() + " " + user.getLastName()))
.put("userId", user.getId().toString())
.put("subtype", "ASSIGN"))
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
notificationEntityService.logEntityAction(alarm.getTenantId(), alarm.getOriginator(), result.getAlarm(),
alarm.getCustomerId(), ActionType.ALARM_UNASSIGNED, user);
}
} }
processAlarmsUnassignment(tenantId, user, alarmIds, unassignTs);
} catch (InterruptedException | ExecutionException | TimeoutException e) { totalAlarmIds.addAll(alarmIds);
throw new RuntimeException(e); pageLink = pageLink.nextPageLink();
} }
return totalAlarmIds;
} }
@Override @Override
@ -265,4 +203,46 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
private static long getOrDefault(long ts) { private static long getOrDefault(long ts) {
return ts > 0 ? ts : System.currentTimeMillis(); return ts > 0 ? ts : System.currentTimeMillis();
} }
private void processAlarmsUnassignment(TenantId tenantId, User user, List<AlarmId> alarmIds, long unassignTs) {
for (AlarmId alarmId : alarmIds) {
log.trace("[{}] Unassigning alarm {} userId {}", tenantId, alarmId, user.getId());
AlarmApiCallResult result = alarmSubscriptionService.unassignAlarm(user.getTenantId(), alarmId, unassignTs);
if (!result.isSuccessful()) {
log.error("[{}] Cannot unassign alarm {} userId {}", tenantId, alarmId, user.getId());
continue;
}
if (result.isModified()) {
String comment = String.format("Alarm was unassigned because user %s - was deleted", user.getTitle());
addSystemAlarmComment(result.getAlarm(), null, "ASSIGN", comment);
notificationEntityService.logEntityAction(result.getAlarm().getTenantId(), result.getAlarm().getOriginator(), result.getAlarm(), result.getAlarm().getCustomerId(), ActionType.ALARM_UNASSIGNED, null);
}
}
}
private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText) {
addSystemAlarmComment(alarm, user, subType, commentText, null);
}
private void addSystemAlarmComment(Alarm alarm, User user, String subType, String commentText, UserId assigneeId) {
ObjectNode commentNode = JacksonUtil.newObjectNode();
commentNode.put("text", commentText)
.put("subtype", subType);
if (user != null) {
commentNode.put("userId", user.getId().getId().toString());
}
if (assigneeId != null) {
commentNode.put("assigneeId", assigneeId.getId().toString());
}
AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(commentNode)
.build();
try {
alarmCommentService.saveAlarmComment(alarm, alarmComment, user);
} catch (ThingsboardException e) {
log.error("Failed to save alarm comment", e);
}
}
} }

5
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java

@ -19,9 +19,12 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import java.util.List;
public interface TbAlarmService { public interface TbAlarmService {
Alarm save(Alarm entity, User user) throws ThingsboardException; Alarm save(Alarm entity, User user) throws ThingsboardException;
@ -38,7 +41,7 @@ public interface TbAlarmService {
AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException; AlarmInfo unassign(Alarm alarm, long unassignTs, User user) throws ThingsboardException;
void unassignUserAlarms(TenantId tenantId, User user, long unassignTs); List<AlarmId> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs);
Boolean delete(Alarm alarm, User user); Boolean delete(Alarm alarm, User user);
} }

15
application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java

@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmService;
import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.service.security.system.SystemSecurityService;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -44,7 +43,6 @@ import static org.thingsboard.server.controller.UserController.ACTIVATE_URL_PATT
public class DefaultUserService extends AbstractTbEntityService implements TbUserService { public class DefaultUserService extends AbstractTbEntityService implements TbUserService {
private final UserService userService; private final UserService userService;
private final TbAlarmService tbAlarmService;
private final MailService mailService; private final MailService mailService;
private final SystemSecurityService systemSecurityService; private final SystemSecurityService systemSecurityService;
@ -64,7 +62,7 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse
try { try {
mailService.sendActivationEmail(activateUrl, email); mailService.sendActivationEmail(activateUrl, email);
} catch (ThingsboardException e) { } catch (ThingsboardException e) {
userService.deleteUser(tenantId, savedUser.getId()); userService.deleteUser(tenantId, savedUser);
throw e; throw e;
} }
} }
@ -77,17 +75,16 @@ public class DefaultUserService extends AbstractTbEntityService implements TbUse
} }
@Override @Override
public void delete(TenantId tenantId, CustomerId customerId, User tbUser, User user) throws ThingsboardException { public void delete(TenantId tenantId, CustomerId customerId, User user, User responsibleUser) throws ThingsboardException {
ActionType actionType = ActionType.DELETED; ActionType actionType = ActionType.DELETED;
UserId userId = tbUser.getId(); UserId userId = user.getId();
try { try {
tbAlarmService.unassignUserAlarms(tbUser.getTenantId(), tbUser, System.currentTimeMillis()); userService.deleteUser(tenantId, user);
userService.deleteUser(tenantId, userId); notificationEntityService.logEntityAction(tenantId, userId, user, customerId, actionType, responsibleUser, customerId.toString());
notificationEntityService.logEntityAction(tenantId, userId, tbUser, customerId, actionType, user, customerId.toString());
} catch (Exception e) { } catch (Exception e) {
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER), notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.USER),
actionType, user, e, userId.toString()); actionType, responsibleUser, e, userId.toString());
throw e; throw e;
} }
} }

2
application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java

@ -25,5 +25,5 @@ import javax.servlet.http.HttpServletRequest;
public interface TbUserService { public interface TbUserService {
User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, User user) throws ThingsboardException; User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, User user) throws ThingsboardException;
void delete(TenantId tenantId, CustomerId customerId, User tbUser, User user) throws ThingsboardException; void delete(TenantId tenantId, CustomerId customerId, User user, User responsibleUser) throws ThingsboardException;
} }

104
application/src/main/java/org/thingsboard/server/service/housekeeper/InMemoryHouseKeeperServiceService.java

@ -0,0 +1,104 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.housekeeper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.housekeeper.HouseKeeperService;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@RequiredArgsConstructor
@Slf4j
public class InMemoryHouseKeeperServiceService implements HouseKeeperService {
final TbAlarmService alarmService;
ListeningExecutorService executor;
AtomicInteger queueSize = new AtomicInteger();
AtomicInteger totalProcessedCounter = new AtomicInteger();
@PostConstruct
public void init() {
log.debug("Starting HouseKeeper service");
executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper")));
}
@PreDestroy
public void destroy() {
if (executor != null) {
log.debug("Stopping HouseKeeper service");
executor.shutdown();
}
}
@TransactionalEventListener(fallbackExecution = true)
public void handleEvent(DeleteEntityEvent<?> event) {
log.trace("[{}] DeleteEntityEvent handler: {}", event.getTenantId(), event);
EntityId entityId = event.getEntityId();
if (EntityType.USER.equals(entityId.getEntityType())) {
unassignDeletedUserAlarms(event.getTenantId(), (User) event.getEntity(), event.getTs());
}
}
@Override
public ListenableFuture<List<AlarmId>> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs) {
log.debug("[{}][{}] unassignDeletedUserAlarms submitting, pending queue size: {} ", tenantId, user.getId().getId(), queueSize.get());
queueSize.incrementAndGet();
ListenableFuture<List<AlarmId>> future = executor.submit(() -> alarmService.unassignDeletedUserAlarms(tenantId, user, unassignTs));
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(List<AlarmId> alarmIds) {
queueSize.decrementAndGet();
totalProcessedCounter.incrementAndGet();
log.debug("[{}][{}] unassignDeletedUserAlarms finished, pending queue size: {}, total processed count: {} ",
tenantId, user.getId().getId(), queueSize.get(), totalProcessedCounter.get());
}
@Override
public void onFailure(@NotNull Throwable throwable) {
queueSize.decrementAndGet();
totalProcessedCounter.incrementAndGet();
log.error("[{}][{}] unassignDeletedUserAlarms failed, pending queue size: {}, total processed count: {}",
tenantId, user.getId().getId(), queueSize.get(), totalProcessedCounter.get(), throwable);
}
}, MoreExecutors.directExecutor());
return future;
}
}

79
application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java

@ -18,6 +18,7 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -50,6 +51,8 @@ import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -567,27 +570,28 @@ public class AlarmControllerTest extends AbstractControllerTest {
alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
Assert.assertNotNull(alarm); Assert.assertNotNull(alarm);
AlarmId alarmId = alarm.getId();
Mockito.reset(tbClusterService, auditLogService);
long beforeAssignmentTs = System.currentTimeMillis(); long beforeAssignmentTs = System.currentTimeMillis();
doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk());
AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Alarm foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm); Assert.assertNotNull(foundAlarm);
Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId());
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
beforeAssignmentTs = System.currentTimeMillis(); beforeAssignmentTs = System.currentTimeMillis();
Mockito.reset(tbClusterService, auditLogService);
loginSysAdmin(); loginSysAdmin();
doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk());
loginDifferentTenant(); loginDifferentTenant();
foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull);
Assert.assertNotNull(foundAlarm); Assert.assertNotNull(foundAlarm);
Assert.assertNull(foundAlarm.getAssigneeId()); Assert.assertNull(foundAlarm.getAssigneeId());
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
@ -613,15 +617,66 @@ public class AlarmControllerTest extends AbstractControllerTest {
.build(); .build();
alarm = doPost("/api/alarm", alarm, Alarm.class); alarm = doPost("/api/alarm", alarm, Alarm.class);
Assert.assertNotNull(alarm); Assert.assertNotNull(alarm);
AlarmId alarmId = alarm.getId();
alarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class);
Assert.assertNotNull(alarm);
long beforeAssignmentTs = System.currentTimeMillis();
alarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk());
AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm);
Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId());
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
beforeAssignmentTs = System.currentTimeMillis();
doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk());
foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull);
Assert.assertNotNull(foundAlarm);
Assert.assertNull(foundAlarm.getAssigneeId());
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
}
@Test
public void testUnassignAlarmOnCustomerRemoving() throws Exception {
createDifferentTenantCustomer();
loginDifferentTenant();
User user = new User();
user.setAuthority(Authority.CUSTOMER_USER);
user.setTenantId(tenantId);
user.setCustomerId(differentTenantCustomerId);
user.setEmail("customerForAssign@thingsboard.org");
User savedUser = createUser(user, "password");
Device device = createDevice("Different customer device", "default", "differentTenantTest");
Device assignedDevice = doPost("/api/customer/" + differentTenantCustomerId.getId()
+ "/device/" + device.getId().getId(), Device.class);
Assert.assertEquals(differentTenantCustomerId, assignedDevice.getCustomerId());
Alarm alarm = Alarm.builder()
.type(TEST_ALARM_TYPE)
.tenantId(savedDifferentTenant.getId())
.customerId(differentTenantCustomerId)
.originator(device.getId())
.severity(AlarmSeverity.MAJOR)
.build();
alarm = doPost("/api/alarm", alarm, Alarm.class);
Assert.assertNotNull(alarm);
AlarmId alarmId = alarm.getId();
alarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class);
Assert.assertNotNull(alarm); Assert.assertNotNull(alarm);
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
long beforeAssignmentTs = System.currentTimeMillis(); long beforeAssignmentTs = System.currentTimeMillis();
doPost("/api/alarm/" + alarm.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk()); doPost("/api/alarm/" + alarmId.getId() + "/assign/" + savedUser.getId().getId()).andExpect(status().isOk());
AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm); Assert.assertNotNull(foundAlarm);
Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId()); Assert.assertEquals(savedUser.getId(), foundAlarm.getAssigneeId());
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);
@ -630,9 +685,11 @@ public class AlarmControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/user/" + savedUser.getId().getId()).andExpect(status().isOk()); doDelete("/api/customer/" + differentTenantCustomerId.getId()).andExpect(status().isOk());
foundAlarm = Awaitility.await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> doGet("/api/alarm/info/" + alarmId.getId(), AlarmInfo.class), Objects::nonNull);
foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm); Assert.assertNotNull(foundAlarm);
Assert.assertNull(foundAlarm.getAssigneeId()); Assert.assertNull(foundAlarm.getAssigneeId());
Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs); Assert.assertTrue(foundAlarm.getAssignTs() >= beforeAssignmentTs);

6
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -860,7 +860,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret()); EdgeImitator edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class); edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.expectMessageAmount(23); edgeImitator.expectMessageAmount(25);
edgeImitator.connect(); edgeImitator.connect();
assertThat(edgeImitator.waitForMessages()).as("await for messages on first connect").isTrue(); assertThat(edgeImitator.waitForMessages()).as("await for messages on first connect").isTrue();
@ -873,7 +873,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty()); Assert.assertTrue(edgeImitator.getDownlinkMsgs().isEmpty());
edgeImitator.expectMessageAmount(18); edgeImitator.expectMessageAmount(20);
doPost("/api/edge/sync/" + edge.getId()); doPost("/api/edge/sync/" + edge.getId());
assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue(); assertThat(edgeImitator.waitForMessages()).as("await for messages after edge sync rest api call").isTrue();
@ -903,6 +903,8 @@ public class EdgeControllerTest extends AbstractControllerTest {
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mailTemplates", false)); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mailTemplates", false));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "test"));
Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));

13
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -129,7 +129,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
installation(); installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.expectMessageAmount(24); edgeImitator.expectMessageAmount(26);
edgeImitator.connect(); edgeImitator.connect();
requestEdgeRuleChainMetadata(); requestEdgeRuleChainMetadata();
@ -236,13 +236,15 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
// 4 messages - 4 messages from fetcher - 2 from system level ('mail', 'mailTemplates') and 2 from admin level ('mail', 'mailTemplates') // 4 messages - 4 messages from fetcher - 2 from system level ('mail', 'mailTemplates') and 2 from admin level ('mail', 'mailTemplates')
validateAdminSettings(); validateAdminSettings();
// 4 messages // 5 messages
// - 1 from default profile fetcher
// - 2 from device profile fetcher (default and thermostat) // - 2 from device profile fetcher (default and thermostat)
// - 1 from device fetcher // - 1 from device fetcher
// - 1 from device controller (thermostat) // - 1 from device controller (thermostat)
validateDeviceProfiles(); validateDeviceProfiles();
// 3 messages // 4 messages
// - 1 from default profile fetcher
// - 1 message from asset profile fetcher // - 1 message from asset profile fetcher
// - 1 message from asset fetcher // - 1 message from asset fetcher
// - 1 message from asset controller // - 1 message from asset controller
@ -301,11 +303,12 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
private void validateDeviceProfiles() throws Exception { private void validateDeviceProfiles() throws Exception {
List<DeviceProfileUpdateMsg> deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class); List<DeviceProfileUpdateMsg> deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class);
// default msg default device profile from fetcher
// default msg device profile from fetcher // default msg device profile from fetcher
// thermostat msg from device profile fetcher // thermostat msg from device profile fetcher
// thermostat msg from device fetcher // thermostat msg from device fetcher
// thermostat msg from creation of device // thermostat msg from creation of device
Assert.assertEquals(4, deviceProfileUpdateMsgList.size()); Assert.assertEquals(5, deviceProfileUpdateMsgList.size());
Optional<DeviceProfileUpdateMsg> thermostatProfileUpdateMsgOpt = Optional<DeviceProfileUpdateMsg> thermostatProfileUpdateMsgOpt =
deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny(); deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny();
Assert.assertTrue(thermostatProfileUpdateMsgOpt.isPresent()); Assert.assertTrue(thermostatProfileUpdateMsgOpt.isPresent());
@ -425,7 +428,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
private void validateAssetProfiles() throws Exception { private void validateAssetProfiles() throws Exception {
List<AssetProfileUpdateMsg> assetProfileUpdateMsgs = edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class); List<AssetProfileUpdateMsg> assetProfileUpdateMsgs = edgeImitator.findAllMessagesByType(AssetProfileUpdateMsg.class);
Assert.assertEquals(3, assetProfileUpdateMsgs.size()); Assert.assertEquals(4, assetProfileUpdateMsgs.size());
AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgs.get(0); AssetProfileUpdateMsg assetProfileUpdateMsg = assetProfileUpdateMsgs.get(0);
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType()); Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, assetProfileUpdateMsg.getMsgType());
UUID assetProfileUUID = new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB()); UUID assetProfileUUID = new UUID(assetProfileUpdateMsg.getIdMSB(), assetProfileUpdateMsg.getIdLSB());

54
application/src/test/java/org/thingsboard/server/edge/AssetProfileEdgeTest.java

@ -20,6 +20,7 @@ import com.google.protobuf.AbstractMessage;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
@ -30,6 +31,7 @@ import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg; import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -85,7 +87,7 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest {
@Test @Test
public void testSendAssetProfileToCloud() throws Exception { public void testSendAssetProfileToCloud() throws Exception {
RuleChainId ruleChainId = createEdgeRuleChainAndAssignToEdge("Asset Profile Rule Chain"); RuleChainId edgeRuleChainId = createEdgeRuleChainAndAssignToEdge("Asset Profile Rule Chain");
DashboardId dashboardId = createDashboardAndAssignToEdge("Asset Profile Dashboard"); DashboardId dashboardId = createDashboardAndAssignToEdge("Asset Profile Dashboard");
UUID uuid = Uuids.timeBased(); UUID uuid = Uuids.timeBased();
@ -96,8 +98,8 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest {
assetProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits()); assetProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
assetProfileUpdateMsgBuilder.setName("Asset Profile On Edge"); assetProfileUpdateMsgBuilder.setName("Asset Profile On Edge");
assetProfileUpdateMsgBuilder.setDefault(false); assetProfileUpdateMsgBuilder.setDefault(false);
assetProfileUpdateMsgBuilder.setDefaultRuleChainIdMSB(ruleChainId.getId().getMostSignificantBits()); assetProfileUpdateMsgBuilder.setDefaultRuleChainIdMSB(edgeRuleChainId.getId().getMostSignificantBits());
assetProfileUpdateMsgBuilder.setDefaultRuleChainIdLSB(ruleChainId.getId().getLeastSignificantBits()); assetProfileUpdateMsgBuilder.setDefaultRuleChainIdLSB(edgeRuleChainId.getId().getLeastSignificantBits());
assetProfileUpdateMsgBuilder.setDefaultDashboardIdMSB(dashboardId.getId().getMostSignificantBits()); assetProfileUpdateMsgBuilder.setDefaultDashboardIdMSB(dashboardId.getId().getMostSignificantBits());
assetProfileUpdateMsgBuilder.setDefaultDashboardIdLSB(dashboardId.getId().getLeastSignificantBits()); assetProfileUpdateMsgBuilder.setDefaultDashboardIdLSB(dashboardId.getId().getLeastSignificantBits());
assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE); assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
@ -117,6 +119,9 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest {
AssetProfile assetProfile = doGet("/api/assetProfile/" + uuid, AssetProfile.class); AssetProfile assetProfile = doGet("/api/assetProfile/" + uuid, AssetProfile.class);
Assert.assertNotNull(assetProfile); Assert.assertNotNull(assetProfile);
Assert.assertEquals("Asset Profile On Edge", assetProfile.getName()); Assert.assertEquals("Asset Profile On Edge", assetProfile.getName());
Assert.assertEquals(dashboardId, assetProfile.getDefaultDashboardId());
Assert.assertNull(assetProfile.getDefaultRuleChainId());
Assert.assertEquals(edgeRuleChainId, assetProfile.getDefaultEdgeRuleChainId());
// delete profile // delete profile
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
@ -132,6 +137,47 @@ public class AssetProfileEdgeTest extends AbstractEdgeTest {
// cleanup // cleanup
unAssignFromEdgeAndDeleteDashboard(dashboardId); unAssignFromEdgeAndDeleteDashboard(dashboardId);
unAssignFromEdgeAndDeleteRuleChain(ruleChainId); unAssignFromEdgeAndDeleteRuleChain(edgeRuleChainId);
}
@Test
public void testSendAssetProfileToCloudWithNameThatAlreadyExistsOnCloud() throws Exception {
String assetProfileOnCloudName = StringUtils.randomAlphanumeric(15);
edgeImitator.expectMessageAmount(1);
AssetProfile assetProfileOnCloud = this.createAssetProfile(assetProfileOnCloudName);
assetProfileOnCloud = doPost("/api/assetProfile", assetProfileOnCloud, AssetProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages());
UUID uuid = Uuids.timeBased();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
AssetProfileUpdateMsg.Builder assetProfileUpdateMsgBuilder = AssetProfileUpdateMsg.newBuilder();
assetProfileUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
assetProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
assetProfileUpdateMsgBuilder.setName(assetProfileOnCloudName);
assetProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
uplinkMsgBuilder.addAssetProfileUpdateMsg(assetProfileUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
Optional<AssetProfileUpdateMsg> assetProfileUpdateMsgOpt = edgeImitator.findMessageByType(AssetProfileUpdateMsg.class);
Assert.assertTrue(assetProfileUpdateMsgOpt.isPresent());
AssetProfileUpdateMsg latestAssetProfileUpdateMsg = assetProfileUpdateMsgOpt.get();
Assert.assertNotEquals(assetProfileOnCloudName, latestAssetProfileUpdateMsg.getName());
Assert.assertNotEquals(assetProfileOnCloud.getUuidId(), uuid);
AssetProfile assetProfile = doGet("/api/assetProfile/" + uuid, AssetProfile.class);
Assert.assertNotNull(assetProfile);
Assert.assertNotEquals(assetProfileOnCloudName, assetProfile.getName());
} }
} }

10
application/src/test/java/org/thingsboard/server/edge/DashboardEdgeTest.java

@ -33,7 +33,6 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg; import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UplinkMsg; import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -45,12 +44,18 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@DaoSqlTest @DaoSqlTest
public class DashboardEdgeTest extends AbstractEdgeTest { public class DashboardEdgeTest extends AbstractEdgeTest {
private static final int MOBILE_ORDER = 5;
private static final String IMAGE = "data:image/png;base64,iVBORw0KGgoA";
@Test @Test
public void testDashboards() throws Exception { public void testDashboards() throws Exception {
// create dashboard and assign to edge // create dashboard and assign to edge
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
Dashboard dashboard = new Dashboard(); Dashboard dashboard = new Dashboard();
dashboard.setTitle("Edge Test Dashboard"); dashboard.setTitle("Edge Test Dashboard");
dashboard.setMobileHide(true);
dashboard.setImage(IMAGE);
dashboard.setMobileOrder(MOBILE_ORDER);
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
doPost("/api/edge/" + edge.getUuidId() doPost("/api/edge/" + edge.getUuidId()
+ "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class); + "/dashboard/" + savedDashboard.getUuidId(), Dashboard.class);
@ -62,6 +67,9 @@ public class DashboardEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB()); Assert.assertEquals(savedDashboard.getUuidId().getMostSignificantBits(), dashboardUpdateMsg.getIdMSB());
Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB()); Assert.assertEquals(savedDashboard.getUuidId().getLeastSignificantBits(), dashboardUpdateMsg.getIdLSB());
Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle()); Assert.assertEquals(savedDashboard.getTitle(), dashboardUpdateMsg.getTitle());
Assert.assertTrue(dashboardUpdateMsg.getMobileHide());
Assert.assertEquals(IMAGE, dashboardUpdateMsg.getImage());
Assert.assertEquals(MOBILE_ORDER, dashboardUpdateMsg.getMobileOrder());
testAutoGeneratedCodeByProtobuf(dashboardUpdateMsg); testAutoGeneratedCodeByProtobuf(dashboardUpdateMsg);
// update dashboard // update dashboard

57
application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java

@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.device.data.PowerMode; import org.thingsboard.server.common.data.device.data.PowerMode;
import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration;
import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration;
@ -303,21 +303,21 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest {
UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg(); UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg();
Assert.assertTrue(latestResponseMsg.getSuccess()); Assert.assertTrue(latestResponseMsg.getSuccess());
AssetProfile assetProfile = doGet("/api/deviceProfile/" + uuid, AssetProfile.class); DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + uuid, DeviceProfile.class);
Assert.assertNotNull(assetProfile); Assert.assertNotNull(deviceProfile);
Assert.assertEquals("Device Profile On Edge", assetProfile.getName()); Assert.assertEquals("Device Profile On Edge", deviceProfile.getName());
// delete profile // delete profile
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
doDelete("/api/deviceProfile/" + assetProfile.getUuidId()) doDelete("/api/deviceProfile/" + deviceProfile.getUuidId())
.andExpect(status().isOk()); .andExpect(status().isOk());
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg); Assert.assertTrue(latestMessage instanceof DeviceProfileUpdateMsg);
DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage; DeviceProfileUpdateMsg deviceProfileUpdateMsg = (DeviceProfileUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType()); Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType());
Assert.assertEquals(assetProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB()); Assert.assertEquals(deviceProfile.getUuidId().getMostSignificantBits(), deviceProfileUpdateMsg.getIdMSB());
Assert.assertEquals(assetProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB()); Assert.assertEquals(deviceProfile.getUuidId().getLeastSignificantBits(), deviceProfileUpdateMsg.getIdLSB());
// cleanup // cleanup
unAssignFromEdgeAndDeleteDashboard(dashboardId); unAssignFromEdgeAndDeleteDashboard(dashboardId);
@ -412,4 +412,47 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest {
transportConfiguration.setCoapDeviceTypeConfiguration(coapDeviceTypeConfiguration); transportConfiguration.setCoapDeviceTypeConfiguration(coapDeviceTypeConfiguration);
return transportConfiguration; return transportConfiguration;
} }
@Test
public void testSendDeviceProfileToCloudWithNameThatAlreadyExistsOnCloud() throws Exception {
String deviceProfileOnCloudName = StringUtils.randomAlphanumeric(15);
edgeImitator.expectMessageAmount(1);
DeviceProfile deviceProfileOnCloud = this.createDeviceProfile(deviceProfileOnCloudName);
deviceProfileOnCloud = doPost("/api/deviceProfile", deviceProfileOnCloud, DeviceProfile.class);
Assert.assertTrue(edgeImitator.waitForMessages());
UUID uuid = Uuids.timeBased();
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
DeviceProfileUpdateMsg.Builder deviceProfileUpdateMsgBuilder = DeviceProfileUpdateMsg.newBuilder();
deviceProfileUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
deviceProfileUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
deviceProfileUpdateMsgBuilder.setName(deviceProfileOnCloudName);
deviceProfileUpdateMsgBuilder.setType(DeviceProfileType.DEFAULT.name());
deviceProfileUpdateMsgBuilder.setMsgType(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
deviceProfileUpdateMsgBuilder.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfileOnCloud.getProfileData())));
uplinkMsgBuilder.addDeviceProfileUpdateMsg(deviceProfileUpdateMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
Optional<DeviceProfileUpdateMsg> deviceProfileUpdateMsgOpt = edgeImitator.findMessageByType(DeviceProfileUpdateMsg.class);
Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent());
DeviceProfileUpdateMsg latestDeviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get();
Assert.assertNotEquals(deviceProfileOnCloudName, latestDeviceProfileUpdateMsg.getName());
Assert.assertNotEquals(deviceProfileOnCloud.getUuidId(), uuid);
DeviceProfile deviceProfile = doGet("/api/deviceProfile/" + uuid, DeviceProfile.class);
Assert.assertNotNull(deviceProfile);
Assert.assertNotEquals(deviceProfileOnCloudName, deviceProfile.getName());
}
} }

2
application/src/test/java/org/thingsboard/server/edge/WidgetEdgeTest.java

@ -57,6 +57,7 @@ public class WidgetEdgeTest extends AbstractEdgeTest {
ObjectNode descriptor = JacksonUtil.newObjectNode(); ObjectNode descriptor = JacksonUtil.newObjectNode();
descriptor.put("key", "value"); descriptor.put("key", "value");
widgetType.setDescriptor(descriptor); widgetType.setDescriptor(descriptor);
widgetType.setDeprecated(true);
WidgetType savedWidgetType = doPost("/api/widgetType", widgetType, WidgetType.class); WidgetType savedWidgetType = doPost("/api/widgetType", widgetType, WidgetType.class);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage(); latestMessage = edgeImitator.getLatestMessage();
@ -67,6 +68,7 @@ public class WidgetEdgeTest extends AbstractEdgeTest {
Assert.assertEquals(savedWidgetType.getUuidId().getLeastSignificantBits(), widgetTypeUpdateMsg.getIdLSB()); Assert.assertEquals(savedWidgetType.getUuidId().getLeastSignificantBits(), widgetTypeUpdateMsg.getIdLSB());
Assert.assertEquals(savedWidgetType.getFqn(), widgetTypeUpdateMsg.getFqn()); Assert.assertEquals(savedWidgetType.getFqn(), widgetTypeUpdateMsg.getFqn());
Assert.assertEquals(savedWidgetType.getName(), widgetTypeUpdateMsg.getName()); Assert.assertEquals(savedWidgetType.getName(), widgetTypeUpdateMsg.getName());
Assert.assertTrue(widgetTypeUpdateMsg.getDeprecated());
Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor()); Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor());
// update widget bundle // update widget bundle

67
application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.service.entitiy.alarm; package org.thingsboard.server.service.entitiy.alarm;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -23,14 +24,20 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.edge.EdgeService;
@ -39,6 +46,8 @@ import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import java.util.Collections;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@ -74,7 +83,6 @@ public class DefaultTbAlarmServiceTest {
protected TbClusterService tbClusterService; protected TbClusterService tbClusterService;
@MockBean @MockBean
private EntitiesVersionControlService vcService; private EntitiesVersionControlService vcService;
@SpyBean @SpyBean
DefaultTbAlarmService service; DefaultTbAlarmService service;
@ -124,4 +132,61 @@ public class DefaultTbAlarmServiceTest {
verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.DELETED), any()); verify(notificationEntityService, times(1)).logEntityAction(any(), any(), any(), any(), eq(ActionType.DELETED), any());
verify(alarmSubscriptionService, times(1)).deleteAlarm(any(), any()); verify(alarmSubscriptionService, times(1)).deleteAlarm(any(), any());
} }
@Test
public void testUnassignAlarm() throws ThingsboardException {
AlarmInfo alarm = new AlarmInfo();
alarm.setId(new AlarmId(UUID.randomUUID()));
when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong()))
.thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(alarm).build());
User user = new User();
user.setEmail("testEmail@gmail.com");
user.setId(new UserId(UUID.randomUUID()));
service.unassign(new Alarm(), 0L, user);
ObjectNode commentNode = JacksonUtil.newObjectNode();
commentNode.put("subtype", "ASSIGN");
commentNode.put("text", "Alarm was unassigned by user " + user.getTitle());
commentNode.put("userId", user.getId().getId().toString());
AlarmComment expectedAlarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(commentNode)
.build();
verify(alarmCommentService, times(1))
.saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(user));
}
@Test
public void testUnassignDeletedUserAlarms() throws ThingsboardException {
AlarmInfo alarm = new AlarmInfo();
alarm.setId(new AlarmId(UUID.randomUUID()));
when(alarmService.findAlarmIdsByAssigneeId(any(), any(), any()))
.thenReturn(new PageData<>(List.of(alarm.getId()), 0, 1, false))
.thenReturn(new PageData<>(Collections.EMPTY_LIST, 0, 0, false));
when(alarmSubscriptionService.unassignAlarm(any(), any(), anyLong()))
.thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).alarm(alarm).build());
User user = new User();
user.setEmail("testEmail@gmail.com");
user.setId(new UserId(UUID.randomUUID()));
service.unassignDeletedUserAlarms(new TenantId(UUID.randomUUID()), user, System.currentTimeMillis());
ObjectNode commentNode = JacksonUtil.newObjectNode();
commentNode.put("subtype", "ASSIGN");
commentNode.put("text", String.format("Alarm was unassigned because user %s - was deleted", user.getTitle()));
AlarmComment expectedAlarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(commentNode)
.build();
verify(alarmCommentService, times(1))
.saveAlarmComment(eq(alarm), eq(expectedAlarmComment), eq(null));
}
} }

2
application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java

@ -295,6 +295,7 @@ public class DefaultDeviceStateServiceTest {
Mockito.verify(telemetrySubscriptionService, Mockito.never()).saveAttrAndNotify(Mockito.any(), Mockito.eq(deviceId), Mockito.any(), Mockito.eq("active"), Mockito.any(), Mockito.any()); Mockito.verify(telemetrySubscriptionService, Mockito.never()).saveAttrAndNotify(Mockito.any(), Mockito.eq(deviceId), Mockito.any(), Mockito.eq("active"), Mockito.any(), Mockito.any());
long newTimeout = 1; long newTimeout = 1;
Thread.sleep(newTimeout);
service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout);
activityVerify(false); activityVerify(false);
@ -330,6 +331,7 @@ public class DefaultDeviceStateServiceTest {
Mockito.reset(telemetrySubscriptionService); Mockito.reset(telemetrySubscriptionService);
long newTimeout = 1; long newTimeout = 1;
Thread.sleep(newTimeout);
service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout); service.onDeviceInactivityTimeoutUpdate(tenantId, deviceId, newTimeout);
Mockito.verify(telemetrySubscriptionService, Mockito.never()).saveAttrAndNotify(Mockito.any(), Mockito.eq(deviceId), Mockito.any(), Mockito.eq("active"), Mockito.any(), Mockito.any()); Mockito.verify(telemetrySubscriptionService, Mockito.never()).saveAttrAndNotify(Mockito.any(), Mockito.eq(deviceId), Mockito.any(), Mockito.eq("active"), Mockito.any(), Mockito.any());

2
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -125,6 +125,8 @@ public interface AlarmService extends EntityDaoService {
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds); AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink);
void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId); void deleteEntityAlarmRelations(TenantId tenantId, EntityId entityId);
long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);

29
common/dao-api/src/main/java/org/thingsboard/server/dao/housekeeper/HouseKeeperService.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.housekeeper;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
public interface HouseKeeperService {
ListenableFuture<List<AlarmId>> unassignDeletedUserAlarms(TenantId tenantId, User user, long unassignTs);
}

30
common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java

@ -30,34 +30,34 @@ import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.List; import java.util.List;
public interface UserService extends EntityDaoService { public interface UserService extends EntityDaoService {
User findUserById(TenantId tenantId, UserId userId);
ListenableFuture<User> findUserByIdAsync(TenantId tenantId, UserId userId); User findUserById(TenantId tenantId, UserId userId);
User findUserByEmail(TenantId tenantId, String email); ListenableFuture<User> findUserByIdAsync(TenantId tenantId, UserId userId);
User findUserByEmail(TenantId tenantId, String email);
User findUserByTenantIdAndEmail(TenantId tenantId, String email); User findUserByTenantIdAndEmail(TenantId tenantId, String email);
User saveUser(TenantId tenantId, User user); User saveUser(TenantId tenantId, User user);
UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId);
UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken);
UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken);
UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId); UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials);
UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken);
UserCredentials findUserCredentialsByResetToken(TenantId tenantId, String resetToken); UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password);
UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials); UserCredentials requestPasswordReset(TenantId tenantId, String email);
UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password);
UserCredentials requestPasswordReset(TenantId tenantId, String email);
UserCredentials requestExpiredPasswordReset(TenantId tenantId, UserCredentialsId userCredentialsId); UserCredentials requestExpiredPasswordReset(TenantId tenantId, UserCredentialsId userCredentialsId);
UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials); UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials);
void deleteUser(TenantId tenantId, UserId userId); void deleteUser(TenantId tenantId, User user);
PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink); PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink);

21
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmAssignee.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data.alarm; package org.thingsboard.server.common.data.alarm;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
@ -22,6 +23,8 @@ import org.thingsboard.server.common.data.id.UserId;
import java.io.Serializable; import java.io.Serializable;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
@Builder @Builder
@AllArgsConstructor @AllArgsConstructor
@Data @Data
@ -34,4 +37,22 @@ public class AlarmAssignee implements Serializable {
private final String lastName; private final String lastName;
private final String email; private final String email;
@JsonIgnore
public String getTitle() {
String title = "";
if (isNotEmpty(firstName)) {
title += firstName;
}
if (isNotEmpty(lastName)) {
if (!title.isEmpty()) {
title += " ";
}
title += lastName;
}
if (title.isEmpty()) {
title = email;
}
return title;
}
} }

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java

@ -22,6 +22,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmCommentId;
@ -44,6 +45,7 @@ public class AlarmComment extends BaseData<AlarmCommentId> implements HasName {
@ApiModelProperty(position = 6, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode") @ApiModelProperty(position = 6, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode")
@NoXss @NoXss
@Length(fieldName = "comment", max = 10000) @Length(fieldName = "comment", max = 10000)
@EqualsAndHashCode.Include
private transient JsonNode comment; private transient JsonNode comment;
@ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " + @ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " +

18
common/edge-api/src/main/proto/edge.proto

@ -187,6 +187,9 @@ message DashboardUpdateMsg {
string title = 6; string title = 6;
string configuration = 7; string configuration = 7;
optional string assignedCustomers = 8; optional string assignedCustomers = 8;
optional string image = 9;
optional int32 mobileOrder = 10;
bool mobileHide = 11;
} }
message DeviceUpdateMsg { message DeviceUpdateMsg {
@ -362,12 +365,15 @@ message WidgetTypeUpdateMsg {
UpdateMsgType msgType = 1; UpdateMsgType msgType = 1;
int64 idMSB = 2; int64 idMSB = 2;
int64 idLSB = 3; int64 idLSB = 3;
optional string fqn = 4; optional string bundleAlias = 4; // deprecated
optional string name = 5; optional string alias = 5; // deprecated
optional string descriptorJson = 6; optional string name = 6;
bool isSystem = 7; optional string descriptorJson = 7;
optional string image = 8; bool isSystem = 8;
optional string description = 9; optional string image = 9;
optional string description = 10;
optional string fqn = 11;
bool deprecated = 12;
} }
message AdminSettingsUpdateMsg { message AdminSettingsUpdateMsg {

227
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java

@ -15,13 +15,15 @@
*/ */
package org.thingsboard.server.transport.coap.efento; package org.thingsboard.server.transport.coap.efento;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.TriConsumer;
import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.network.Exchange; import org.eclipse.californium.core.network.Exchange;
import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource; import org.eclipse.californium.core.server.resources.Resource;
@ -32,8 +34,11 @@ import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransp
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration; import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.ProtoConverter;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.coap.ConfigProtos;
import org.thingsboard.server.gen.transport.coap.DeviceInfoProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos; import org.thingsboard.server.gen.transport.coap.MeasurementTypeProtos;
import org.thingsboard.server.gen.transport.coap.MeasurementsProtos; import org.thingsboard.server.gen.transport.coap.MeasurementsProtos;
import org.thingsboard.server.transport.coap.AbstractCoapTransportResource; import org.thingsboard.server.transport.coap.AbstractCoapTransportResource;
@ -43,13 +48,17 @@ import org.thingsboard.server.transport.coap.callback.CoapEfentoCallback;
import org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils; import org.thingsboard.server.transport.coap.efento.utils.CoapEfentoUtils;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static com.google.gson.JsonParser.parseString;
import static org.thingsboard.server.transport.coap.CoapTransportService.CONFIGURATION; import static org.thingsboard.server.transport.coap.CoapTransportService.CONFIGURATION;
import static org.thingsboard.server.transport.coap.CoapTransportService.CURRENT_TIMESTAMP; import static org.thingsboard.server.transport.coap.CoapTransportService.CURRENT_TIMESTAMP;
import static org.thingsboard.server.transport.coap.CoapTransportService.DEVICE_INFO; import static org.thingsboard.server.transport.coap.CoapTransportService.DEVICE_INFO;
@ -94,14 +103,13 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
String requestType = uriPath.get(1); String requestType = uriPath.get(1);
switch (requestType) { switch (requestType) {
case MEASUREMENTS: case MEASUREMENTS:
processMeasurementsRequest(exchange, request); processMeasurementsRequest(exchange);
break; break;
case DEVICE_INFO: case DEVICE_INFO:
processDeviceInfoRequest(exchange);
break;
case CONFIGURATION: case CONFIGURATION:
//We respond only to confirmed requests in order to reduce battery consumption for Efento devices. processConfigurationRequest(exchange);
if (exchange.advanced().getRequest().isConfirmable()) {
exchange.respond(new Response(CoAP.ResponseCode.CREATED));
}
break; break;
default: default:
exchange.respond(CoAP.ResponseCode.BAD_REQUEST); exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
@ -109,34 +117,92 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
} }
} }
private void processMeasurementsRequest(CoapExchange exchange, Request request) { private void processMeasurementsRequest(CoapExchange exchange) {
byte[] bytes = request.getPayload(); byte[] bytes = exchange.advanced().getRequest().getPayload();
try { try {
MeasurementsProtos.ProtoMeasurements protoMeasurements = MeasurementsProtos.ProtoMeasurements.parseFrom(bytes); MeasurementsProtos.ProtoMeasurements protoMeasurements = MeasurementsProtos.ProtoMeasurements.parseFrom(bytes);
log.trace("Successfully parsed Efento ProtoMeasurements: [{}]", protoMeasurements.getCloudToken()); log.trace("Successfully parsed Efento ProtoMeasurements: [{}]", protoMeasurements.getCloudToken());
String token = protoMeasurements.getCloudToken(); validateAndProcessEffentoMessage(protoMeasurements.getCloudToken(), exchange, (deviceProfile, sessionInfo, sessionId) -> {
transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(), try {
new CoapDeviceAuthCallback(exchange, (msg, deviceProfile) -> { List<EfentoTelemetry> measurements = getEfentoMeasurements(protoMeasurements, sessionId);
TransportProtos.SessionInfoProto sessionInfo = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()); transportService.process(sessionInfo,
UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, measurements),
try { new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
validateEfentoTransportConfiguration(deviceProfile); } catch (AdaptorException e) {
List<EfentoMeasurements> efentoMeasurements = getEfentoMeasurements(protoMeasurements, sessionId); log.error("[{}] Failed to decode Efento ProtoMeasurements: ", sessionId, e);
transportService.process(sessionInfo, exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, efentoMeasurements), }
new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)); });
reportSubscriptionInfo(sessionInfo, false, false);
} catch (AdaptorException e) {
log.error("[{}] Failed to decode Efento ProtoMeasurements: ", sessionId, e);
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
}
}));
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to decode Efento ProtoMeasurements: ", e); log.error("Failed to decode Efento ProtoMeasurements: ", e);
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
} }
} }
private void processDeviceInfoRequest(CoapExchange exchange) {
byte[] bytes = exchange.advanced().getRequest().getPayload();
try {
DeviceInfoProtos.ProtoDeviceInfo protoDeviceInfo = DeviceInfoProtos.ProtoDeviceInfo.parseFrom(bytes);
String token = protoDeviceInfo.getCloudToken();
log.trace("Successfully parsed Efento ProtoDeviceInfo: [{}]", token);
validateAndProcessEffentoMessage(token, exchange, (deviceProfile, sessionInfo, sessionId) -> {
try {
EfentoTelemetry deviceInfo = getEfentoDeviceInfo(protoDeviceInfo);
transportService.process(sessionInfo,
transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, List.of(deviceInfo)),
new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
} catch (AdaptorException e) {
log.error("[{}] Failed to decode Efento ProtoDeviceInfo: ", sessionId, e);
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
}
});
} catch (Exception e) {
log.error("Failed to decode Efento ProtoDeviceInfo: ", e);
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
}
}
private void processConfigurationRequest(CoapExchange exchange) {
byte[] bytes = exchange.advanced().getRequest().getPayload();
try {
ConfigProtos.ProtoConfig protoConfig = ConfigProtos.ProtoConfig.parseFrom(bytes);
String token = protoConfig.getCloudToken();
log.trace("Successfully parsed Efento ProtoConfig: [{}]", token);
validateAndProcessEffentoMessage(token, exchange, (deviceProfile, sessionInfo, sessionId) -> {
try {
JsonElement configuration = getEfentoConfiguration(bytes);
transportService.process(sessionInfo,
transportContext.getEfentoCoapAdaptor().convertToPostAttributes(sessionId, configuration),
new CoapEfentoCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
} catch (AdaptorException e) {
log.error("[{}] Failed to decode Efento ProtoConfig: ", sessionId, e);
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException(e);
}
});
} catch (Exception e) {
log.error("Failed to decode Efento ProtoConfig: ", e);
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
}
}
private void validateAndProcessEffentoMessage(String token, CoapExchange exchange, TriConsumer<DeviceProfile, TransportProtos.SessionInfoProto, UUID> requestProcessor) {
transportService.process(DeviceTransportType.COAP, TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
new CoapDeviceAuthCallback(exchange, (msg, deviceProfile) -> {
TransportProtos.SessionInfoProto sessionInfo = SessionInfoCreator.create(msg, transportContext, UUID.randomUUID());
UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
try {
validateEfentoTransportConfiguration(deviceProfile);
requestProcessor.accept(deviceProfile, sessionInfo, sessionId);
reportSubscriptionInfo(sessionInfo, false, false);
} catch (AdaptorException e) {
log.error("[{}] Failed to decode Efento request: ", sessionId, e);
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
}
}));
}
@Override @Override
public Resource getChild(String name) { public Resource getChild(String name) {
return this; return this;
@ -155,7 +221,7 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
} }
} }
private List<EfentoMeasurements> getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) { private List<EfentoTelemetry> getEfentoMeasurements(MeasurementsProtos.ProtoMeasurements protoMeasurements, UUID sessionId) {
String serialNumber = CoapEfentoUtils.convertByteArrayToString(protoMeasurements.getSerialNum().toByteArray()); String serialNumber = CoapEfentoUtils.convertByteArrayToString(protoMeasurements.getSerialNum().toByteArray());
boolean batteryStatus = protoMeasurements.getBatteryStatus(); boolean batteryStatus = protoMeasurements.getBatteryStatus();
int measurementPeriodBase = protoMeasurements.getMeasurementPeriodBase(); int measurementPeriodBase = protoMeasurements.getMeasurementPeriodBase();
@ -243,6 +309,12 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k)); CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k));
values.addProperty("ok_alarm_" + channel, data); values.addProperty("ok_alarm_" + channel, data);
break; break;
case PULSE_CNT:
values = valuesMap.computeIfAbsent(startTimestampMillis, k ->
CoapEfentoUtils.setDefaultMeasurements(serialNumber, batteryStatus, measurementPeriod, nextTransmissionAtMillis, signal, k));
values.addProperty("pulse_cnt_" + channel, (double) (startPoint + sampleOffset));
startTimestampMillis = startTimestampMillis + measurementPeriodMillis;
break;
case NO_SENSOR: case NO_SENSOR:
case UNRECOGNIZED: case UNRECOGNIZED:
log.trace("[{}][{}] Sensor error value! Ignoring.", sessionId, measurementTypeName); log.trace("[{}][{}] Sensor error value! Ignoring.", sessionId, measurementTypeName);
@ -261,9 +333,9 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
throw new IllegalStateException("[" + sessionId + "]: Failed to get Efento measurements, reason: channels list is empty!"); throw new IllegalStateException("[" + sessionId + "]: Failed to get Efento measurements, reason: channels list is empty!");
} }
if (!CollectionUtils.isEmpty(valuesMap)) { if (!CollectionUtils.isEmpty(valuesMap)) {
List<EfentoMeasurements> efentoMeasurements = new ArrayList<>(); List<EfentoTelemetry> efentoMeasurements = new ArrayList<>();
for (Long ts : valuesMap.keySet()) { for (Long ts : valuesMap.keySet()) {
EfentoMeasurements measurement = new EfentoMeasurements(ts, valuesMap.get(ts)); EfentoTelemetry measurement = new EfentoTelemetry(ts, valuesMap.get(ts));
efentoMeasurements.add(measurement); efentoMeasurements.add(measurement);
} }
return efentoMeasurements; return efentoMeasurements;
@ -272,12 +344,107 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
} }
} }
private EfentoTelemetry getEfentoDeviceInfo(DeviceInfoProtos.ProtoDeviceInfo protoDeviceInfo) {
JsonObject values = new JsonObject();
values.addProperty("sw_version", protoDeviceInfo.getSwVersion());
//memory statistics
values.addProperty("nv_storage_status", protoDeviceInfo.getMemoryStatistics(0));
values.addProperty("timestamp_of_the_end_of_collecting_statistics", getDate(protoDeviceInfo.getMemoryStatistics(1)));
values.addProperty("capacity_of_memory_in_bytes", protoDeviceInfo.getMemoryStatistics(2));
values.addProperty("used_space_in_bytes", protoDeviceInfo.getMemoryStatistics(3));
values.addProperty("size_of_invalid_packets_in_bytes", protoDeviceInfo.getMemoryStatistics(4));
values.addProperty("size_of_corrupted_packets_in_bytes", protoDeviceInfo.getMemoryStatistics(5));
values.addProperty("number_of_valid_packets", protoDeviceInfo.getMemoryStatistics(6));
values.addProperty("number_of_invalid_packets", protoDeviceInfo.getMemoryStatistics(7));
values.addProperty("number_of_corrupted_packets", protoDeviceInfo.getMemoryStatistics(8));
values.addProperty("number_of_all_samples_for_channel_1", protoDeviceInfo.getMemoryStatistics(9));
values.addProperty("number_of_all_samples_for_channel_2", protoDeviceInfo.getMemoryStatistics(10));
values.addProperty("number_of_all_samples_for_channel_3", protoDeviceInfo.getMemoryStatistics(11));
values.addProperty("number_of_all_samples_for_channel_4", protoDeviceInfo.getMemoryStatistics(12));
values.addProperty("number_of_all_samples_for_channel_5", protoDeviceInfo.getMemoryStatistics(13));
values.addProperty("number_of_all_samples_for_channel_6", protoDeviceInfo.getMemoryStatistics(14));
values.addProperty("timestamp_of_the_first_binary_measurement", getDate(protoDeviceInfo.getMemoryStatistics(15)));
values.addProperty("timestamp_of_the_last_binary_measurement", getDate(protoDeviceInfo.getMemoryStatistics(16)));
values.addProperty("timestamp_of_the_first_binary_measurement_sent", getDate(protoDeviceInfo.getMemoryStatistics(17)));
values.addProperty("timestamp_of_the_first_continuous_measurement", getDate(protoDeviceInfo.getMemoryStatistics(18)));
values.addProperty("timestamp_of_the_last_continuous_measurement", getDate(protoDeviceInfo.getMemoryStatistics(19)));
values.addProperty("timestamp_of_the_last_continuous_measurement_sent", getDate(protoDeviceInfo.getMemoryStatistics(20)));
values.addProperty("nvm_write_counter", protoDeviceInfo.getMemoryStatistics(21));
//modem info
DeviceInfoProtos.ProtoModem modem = protoDeviceInfo.getModem();
values.addProperty("modem_types", modem.getType().toString());
values.addProperty("sc_EARNFCN_offset", modem.getParameters(0));
values.addProperty("sc_EARFCN", modem.getParameters(1));
values.addProperty("sc_PCI", modem.getParameters(2));
values.addProperty("sc_Cell_id", modem.getParameters(3));
values.addProperty("sc_RSRP", modem.getParameters(4));
values.addProperty("sc_RSRQ", modem.getParameters(5));
values.addProperty("sc_RSSI", modem.getParameters(6));
values.addProperty("sc_SINR", modem.getParameters(7));
values.addProperty("sc_Band", modem.getParameters(8));
values.addProperty("sc_TAC", modem.getParameters(9));
values.addProperty("sc_ECL", modem.getParameters(10));
values.addProperty("sc_TX_PWR", modem.getParameters(11));
values.addProperty("op_mode", modem.getParameters(12));
values.addProperty("nc_EARFCN", modem.getParameters(13));
values.addProperty("nc_EARNFCN_offset", modem.getParameters(14));
values.addProperty("nc_PCI", modem.getParameters(15));
values.addProperty("nc_RSRP", modem.getParameters(16));
values.addProperty("RLC_UL_BLER", modem.getParameters(17));
values.addProperty("RLC_DL_BLER", modem.getParameters(18));
values.addProperty("MAC_UL_BLER", modem.getParameters(19));
values.addProperty("MAC_DL_BLER", modem.getParameters(20));
values.addProperty("MAC_UL_TOTAL_BYTES", modem.getParameters(21));
values.addProperty("MAC_DL_TOTAL_BYTES", modem.getParameters(22));
values.addProperty("MAC_UL_total_HARQ_Tx", modem.getParameters(23));
values.addProperty("MAC_DL_total_HARQ_Tx", modem.getParameters(24));
values.addProperty("MAC_UL_HARQ_re_Tx", modem.getParameters(25));
values.addProperty("MAC_DL_HARQ_re_Tx", modem.getParameters(26));
values.addProperty("RLC_UL_tput", modem.getParameters(27));
values.addProperty("RLC_DL_tput", modem.getParameters(28));
values.addProperty("MAC_UL_tput", modem.getParameters(29));
values.addProperty("MAC_DL_tput", modem.getParameters(30));
values.addProperty("sleep_duration", modem.getParameters(31));
values.addProperty("rx_time", modem.getParameters(32));
values.addProperty("tx_time", modem.getParameters(33));
//Runtime info
DeviceInfoProtos.ProtoRuntime runtimeInfo = protoDeviceInfo.getRuntimeInfo();
values.addProperty("battery_reset_timestamp", getDate(runtimeInfo.getBatteryResetTimestamp()));
values.addProperty("max_mcu_temp", runtimeInfo.getMaxMcuTemperature());
values.addProperty("mcu_temp", runtimeInfo.getMcuTemperature());
values.addProperty("counter_of_confirmable_messages_attempts", runtimeInfo.getMessageCounters(0));
values.addProperty("counter_of_non_confirmable_messages_attempts", runtimeInfo.getMessageCounters(1));
values.addProperty("counter_of_succeeded_messages", runtimeInfo.getMessageCounters(2));
values.addProperty("min_battery_mcu_temp", runtimeInfo.getMinBatteryMcuTemperature());
values.addProperty("min_battery_voltage", runtimeInfo.getMinBatteryVoltage());
values.addProperty("min_mcu_temp", runtimeInfo.getMinMcuTemperature());
values.addProperty("runtime_errors", runtimeInfo.getRuntimeErrorsCount());
values.addProperty("up_time", runtimeInfo.getUpTime());
return new EfentoTelemetry(System.currentTimeMillis(), values);
}
private JsonElement getEfentoConfiguration(byte[] bytes) throws InvalidProtocolBufferException {
return parseString(ProtoConverter.dynamicMsgToJson(bytes, ConfigProtos.getDescriptor().getMessageTypes().get(2)));
}
private static String getDate(long seconds) {
if (seconds == -1L || seconds == 4294967295L) {
return "Undefined";
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMM yyyy HH:mm:ss Z");
return simpleDateFormat.format(new Date(TimeUnit.SECONDS.toMillis(seconds)));
}
@Data @Data
@AllArgsConstructor @AllArgsConstructor
public static class EfentoMeasurements { public static class EfentoTelemetry {
private long ts; private long ts;
private JsonObject values; private JsonElement values;
} }
} }

16
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/adaptor/EfentoCoapAdaptor.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.coap.efento.adaptor; package org.thingsboard.server.transport.coap.efento.adaptor;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonElement;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.AdaptorException;
@ -32,12 +33,23 @@ public class EfentoCoapAdaptor {
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, List<CoapEfentoTransportResource.EfentoMeasurements> measurements) throws AdaptorException { public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, List<CoapEfentoTransportResource.EfentoTelemetry> telemetryList) throws AdaptorException {
try { try {
return JsonConverter.convertToTelemetryProto(gson.toJsonTree(measurements)); return JsonConverter.convertToTelemetryProto(gson.toJsonTree(telemetryList));
} catch (Exception ex) { } catch (Exception ex) {
log.warn("[{}] Failed to convert EfentoMeasurements to PostTelemetry request!", sessionId); log.warn("[{}] Failed to convert EfentoMeasurements to PostTelemetry request!", sessionId);
throw new AdaptorException(ex); throw new AdaptorException(ex);
} }
} }
public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, JsonElement deviceInfo) throws AdaptorException {
try {
return JsonConverter.convertToAttributesProto(deviceInfo);
} catch (Exception ex) {
log.warn("[{}] Failed to convert JsonObject to PostTelemetry request!", sessionId);
throw new AdaptorException(ex);
}
}
} }

312
common/transport/coap/src/main/proto/efento/proto_config.proto

@ -0,0 +1,312 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto3";
import "efento/proto_measurement_types.proto";
import "efento/proto_rule.proto";
option java_package = "org.thingsboard.server.gen.transport.coap";
option java_outer_classname = "ConfigProtos";
/* Message containing optional channels control parameters */
message ProtoChannelControl {
/* Channel index */
uint32 channel_index = 1;
/* Control parameters. Maximal number equals 4. This field is channel specific: */
/* IO_control channel: */
/* - control_params[0]: */
/* - Byte 0: On state configuration */
/* 0x01 - Low */
/* 0x02 - High */
/* 0x03 - High-Z (disconnected) */
/* - Byte 1: Off state configuration */
/* 0x01 - Low */
/* 0x02 - High */
/* 0x03 - High-Z (disconnected) */
/* - Byte 2: Power on channel state */
/* 0x01 - On */
/* 0x02 - Off */
repeated uint32 control_params = 2;
}
/* Message containing request data for accesing calibration parameters */
message ProtoCalibrationParameters {
/* Request details. Bitmask: */
/* - calibration_request[0:2] - requested channel number. */
uint32 calibration_request = 1;
/* Assignment of a channel. */
uint32 channel_assignment = 2;
/* Table of calibration parameters. Max size = 8. */
repeated int32 parameters = 3;
}
/* Main message sent in the payload. Each field in this message is independent of the others - only parameters that should be */
/* changed need to be sent in the payload. */
/* If the value of a selected parameter shall not be changed, do not include it in the payload */
message ProtoConfig {
/* DEPRECATED - Used for backward compatibility with fw versions 5.x */
/* repeated Threshold thresholds = 1; */
/* 'Measurement_period_base' and 'measurement_period_factor' define how often the measurements are taken. */
/* Sensors of 'Continuous' type take measurement each Measurement_period_base * measurement_period_factor. */
/* Sensors of 'Binary' type take measurement each Measurement_period_base. */
/* For backward compatibility with versions 5.x in case of binary/mixed sensors, if the 'measurement_period_factor' is */
/* not sent (equal to 0), then the default value '14' shall be used for period calculation. */
/* For backward compatibility with versions 5.x in case of continues sensors, if the measurement_period_factor is */
/* not sent (equal to 0), then the default value '1' shall be used for period calculation. */
/* measurement period base in seconds */
/* Range [1:65535] - minimum value can vary depends on installed sensors */
uint32 measurement_period_base = 2;
/* Measurement period factor */
/* Range [1:65535] - minimum value can vary depends on installed sensors */
uint32 measurement_period_factor = 26;
/* Transmission interval in seconds. Range: [60:604800] */
uint32 transmission_interval = 3;
/* BLE turnoff time in seconds. Once receiving this setting, BLE will be switched off after the set number of seconds. */
/* If BLE is already switched off, it will switch on for the set number of seconds and switch off afterwards. */
/* Range [60:604800] and 0xFFFFFFFF */
/* 0xFFFFFFFF - always on */
uint32 ble_turnoff_time = 4;
/* ACK interval in seconds */
/* Range [180:2592000] and 0xFFFFFFFF */
/* 0xFFFFFFFF - always request ACK */
uint32 ack_interval = 5;
/* Specifies, if the additional device info is requested. If true, sensor will send a message to endpoint '/i' with the */
/* device info. This field is only sent by server */
bool request_device_info = 6;
/* Specifies, if software update is available. This field is only sent by server */
bool request_fw_update = 7;
/* Current time in seconds sine 1st of January 1970 (epoch time). */
uint32 current_time = 8;
/* NB-IoT transfer limit */
/* Range: [1:65535] */
/* 65535 - disable transfer limit function */
uint32 transfer_limit = 9;
/* NB-IoT transfer limit timer in seconds */
/* Range: [1:65535] */
/* 65535 - disable transfer limit function */
uint32 transfer_limit_timer = 10;
/* Data (measurements) server IP address */
/* IP of the data server as string in form x.x.x.x. For example: 18.184.24.239 */
string data_server_ip = 11;
/* Data (measurements) server port */
/* Range: [1:65535] */
uint32 data_server_port = 12;
/* Update server IP address */
/* IP of data server as string in form x.x.x.x. For example: 18.184.24.239 */
string update_server_ip = 13;
/* Update server port for UDP transmission */
/* Range: [1:65535] */
uint32 update_server_port_udp = 14;
/* Update server port for CoAP transmission */
/* Range: [1:65535] */
uint32 update_server_port_coap = 15;
/* APN as string. Max length 49 */
/* String with special character 0x7F (DEL) only indicates that automatic apn is turn on */
string apn = 16;
/* PLMN selection */
/* Range: [100:999999] */
/* 0xFFFFFFFF or 1000000 - automatic selection */
uint32 plmn_selection = 17;
/* Device will power off its cellular modem for requested number of seconds. Maximum number of seconds 604800 (7 days) */
/* This field is only sent by server */
uint32 disable_modem_request = 18;
/* If set, the device will send its configuration to the endpoint '/c' as a confirmable message */
/* This field is only sent by server */
bool request_configuration = 19;
/* Device's error codes. */
/* This field is only sent by device */
repeated uint32 errors = 20;
/* Identifier of current configuration - Every change of the configuration results in change of the value of this field */
/* This field is only sent by device */
uint32 hash = 21;
/* If true, the device will accept the configuration without functional testing (eg. network connection) */
bool accept_without_testing = 22;
/* Cloud token configuration: */
/* - 1: cloud token set to the value of cloud_token field */
/* - 2: cloud token set to IMEI of the cellular module */
/* - 255: do not send cloud_token field */
uint32 cloud_token_config = 23;
/* Cloud token that should be sent with each measurement frame */
string cloud_token = 24;
/* Serial number of the device */
/* This field is only sent by device */
bytes serial_number = 25;
/* Type of channel */
/* This field is only sent by device */
repeated MeasurementType channel_types = 27;
/* Edge logic rules set on the device. Up to 12 rules are supported */
repeated ProtoRule rules = 28;
/* Supervision period */
/* Range: [180:604800] */
/* 0xFFFFFFFF - Functionality disabled */
uint32 supervision_period = 29;
/* If true, sensor's measurement memory will be erased */
bool memory_reset_request = 30;
/* Bytes 0-4 - Band selection mask. Mask = 1 << position */
/* Band | 1 | 2 | 3 | 4 | 5 | 8 | 12 | 13 | 17 | 18 | 19 | 20 | 25 | 26 | 28 | 66 | 71 | 85 | */
/* Position: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | */
/* example: To enable band 3, 8 and 20 set to (1 << 2) + (1 << 5) + (1 << 11) = 2084 */
uint32 modem_bands_mask = 31;
/* Data endpoint (string - max length 16) */
string data_endpoint = 32;
/* Configuration endpoint (string - max length 16) */
string configuration_endpoint = 33;
/* Device info endpoint (string - max length 16) */
string device_info_endpoint = 34;
/* Time endpoint (string - max length 16) */
string time_endpoint = 35;
/* Bluetooth TX power level. Value is the index of the absolute value of TX power, that depends on the BLE module */
/* Range: [1:4] */
uint32 ble_tx_power_level = 36;
/* Deprecated field */
/* If true, the sensor's runtime errors will be cleared */
bool request_runtime_errors_clear = 37;
/* Timestamp when a new error code was reported */
uint32 error_timestamp = 38;
/* Timestamp when the new configuration was set */
uint32 hash_timestamp = 39;
/* Cloud token CoAP option ID: */
/* - [1:64999] - CoAP option ID containing cloud token */
/* - 65000 - cloud token sent in the payload */
uint32 cloud_token_coap_option = 40;
/* ECDSA payload signature CoAP option ID: */
/* - [1:64999] - CoAP option ID containing ECDSA payload signature */
/* - 65000 - no payload signature in CoAP option */
uint32 payload_signature_coap_option = 41;
/* DNS server IP address grouped in the array as four octets. Set 255.255.255.255 to use a network DNS server */
/* Note: when setting less than four octets the remaining will be filled with zeros. */
repeated uint32 dns_server_ip = 42;
/* DNS TTL configuration: */
/* - [1:864000] - custom TTL in seconds (additionally, the DNS request when communication has failed) */
/* - 864001 - accept TTL from the DNS server (additionally, the DNS request when communication has failed) */
/* - 864002 - DNS request is only after communication failed */
uint32 dns_ttl_config = 43;
/* Configuration payload split information. Information about dividing the payload into parts */
/* values < 0 - payload has been split, expect another part of the payload in the next message. */
/* The absolute value indicates an index of the current message. */
/* value = 0 - payload has not been splitted */
/* values > 0 - last part of the split payload, the value indicates the total number of the messages sent */
sint32 payload_split_info = 44;
/* Modem update request (string - max length 48) */
/* This field is only sent by server */
/* For BC66 module, this field is a DFOTA URL */
string modem_update_request = 45;
/* Cellular configuration parameters. */
/* 1st item - Number of used cellular parameters */
/* 2nd - 12th items - Cellular parameters */
repeated uint32 cellular_config_params = 46;
/* Calendar configuration. Up to 6 calendars are supported */
repeated ProtoCalendar calendars = 47;
/* Control parameters for channels. Maximal number of requests equals 6 */
/* This field is only sent by server */
repeated ProtoChannelControl channels_control_request = 48;
/* Set/get calibration parameters for single channel. */
ProtoCalibrationParameters calibration_parameters_request = 49;
/* LED behaviour configuration: */
/* Period of LEDs flashing (5-600 seconds in 5 seconds resolution): */
/* - led_config[0] - green LED */
/* - led_config[1] - red LED */
/* Time from entering the normal state, after which the LED indication is turned off */
/* (0-240 minutes in 1 minute resolution, or 255 for always turned on): */
/* - led_config[2] - flashing red led on communication problem */
/* - led_config[3] - flashing red led on a sensor problem */
/* - led_config[4] - flashing red led on a low power */
/* - led_config[5] - flashing green led on measurement */
/* - led_config[6] - flashing green led on transmission */
/* - led_config[7] - flashing green led to indicate sensor's proper operation */
/* - led_config[8] - Blink duration (20-1000ms in 5 ms resolution) */
repeated uint32 led_config = 50;
/* Network troubleshooting configuration, if bluetooth is turned off and communication with the server is faulty, */
/* bluetooth will be automatically turned on until the connection is stabilized */
/* - 1: network troubleshooting disabled */
/* - 2: network troubleshooting enabled */
uint32 network_troubleshooting = 51;
/* Reserved by gateway client */
reserved 52, 53;
/* Encryption key configuration. Sensor sends in this field two last bytes of SHA256 hash calculated from its current */
/* encryption_key configuration. */
/* Max length: 16 bytes. */
/* 0x7F - encryption key disabled. */
bytes encryption_key = 54;
/* User name as string. Max length 31 */
/* String with special character 0x7F (DEL) only indicates that automatic user name is turn on */
/* User name can only be set to custom value if apn has been configured (is not automatic) */
string apn_user_name = 55;
/* Password as string. Max length 31 */
/* String with special character 0x7F (DEL) only indicates that automatic password is turn on */
/* Password can only be set to custom value if apn_user_name has been configured (is not automatic) */
string apn_password = 56;
}

197
common/transport/coap/src/main/proto/efento/proto_device_info.proto

@ -0,0 +1,197 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto3";
option java_package = "org.thingsboard.server.gen.transport.coap";
option java_outer_classname = "DeviceInfoProtos";
enum ModemType
{
/* Invalid value */
MODEM_TYPE_UNSPECIFIED = 0;
/* Quectel BC66 modem */
MODEM_TYPE_BC66 = 1;
/* Quectel BC66-NA modem */
MODEM_TYPE_BC66NA = 2;
}
message ProtoRuntime
{
/* Up-time in seconds (since reset) */
uint32 up_time = 1;
/* Message counters (since reset). There are 3 counters: */
/* message_counters[0] - Counter of confirmable messages attempts */
/* message_counters[1] - Counter of non-confirmable messages attempts */
/* message_counters[2] - Counter of succeeded messages */
repeated uint32 message_counters = 2;
/* MCU temperature in Celsius */
sint32 mcu_temperature = 3;
/* Minimum battery voltage in mV */
uint32 min_battery_voltage = 4;
/* MCU temperature in Celsius, while the minimum battery voltage was reached */
sint32 min_battery_mcu_temperature = 5;
/* Battery reset timestamp (Unix timestamp) */
uint32 battery_reset_timestamp = 6;
/* Max MCU temperature in Celsius */
sint32 max_mcu_temperature = 7;
/* Min MCU temperature in Celsius */
sint32 min_mcu_temperature = 8;
/* Table of runtime errors. Max length: 20 */
repeated uint32 runtime_errors = 9;
}
message ProtoModem
{
ModemType type = 1;
/* Parameters for BC66 modem: */
/* parameters[0] - sc_EARFCN - Range: [0:262143]. Unknown value: -1 */
/* parameters[1] - sc_EARNFCN_offset - Range: [0:4] mapped to [-2, -1, -0.5, 0, 1]. Unknown value: -1 */
/* parameters[2] - sc_PCI - Range: [0:502]. Unknown value: -1 */
/* parameters[3] - sc_Cell id - Range: [1:268435456]. Unknown value: 0 */
/* parameters[4] - sc_RSRP - [dBm] - Range: [-140:-44]. Unknown value: 0 */
/* parameters[5] - sc_RSRQ - [dB] - Range: [-20:-3]. Unknown value: 0 */
/* parameters[6] - sc_RSSI - [dBm] - Range: [-110:-3] Unknown value: 0 */
/* parameters[7] - sc_SINR - [dB] - Range: [-10:30]. Unknown value: 31 */
/* parameters[8] - sc_Band - Range: [see module supported bands]. The current serving cell band. Unknown value: -1 */
/* parameters[9] - sc_TAC - Range: [0:65536]. Unknown value: -1 */
/* parameters[10] - sc_ECL - Range: [0:2]. Unknown value: -1 */
/* parameters[11] - sc_TX_PWR - [0.1cBm] - Range [-440:230]. Unknown value: -1000 */
/* parameters[12] - OP_MODE - Range: [0:3]. Unknown value: -1 */
/* parameters[13] - nc_EARFCN - Range: [0:262143]. Unknown value: -1 */
/* parameters[14] - nc_EARNFCN_offset - Range: [0:4] mapped to [-2, -1, -0.5, 0, 1]. Unknown value: -1 */
/* parameters[15] - nc_PCI - Range: [0:502]. Unknown value: -1 */
/* parameters[16] - nc_RSRP - [dBm] - Range: [-140:-44]. Unknown value: 0 */
/* parameters[17] - RLC_UL_BLER - Range: [0:100]. Unknown value: -1 */
/* parameters[18] - RLC_DL_BLER - Range: [0:100]. Unknown value: -1 */
/* parameters[19] - MAC_UL_BLER - Range: [0:100]. Unknown value: -1 */
/* parameters[20] - MAC_DL_BLER - Range: [0:100]. Unknown value: -1 */
/* parameters[21] - MAC_UL_TOTAL_BYTES - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[22] - MAC_DL_TOTAL_BYTES - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[23] - MAC_UL_total_HARQ_Tx - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[24] - MAC_DL_total_HARQ_Tx - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[25] - MAC_UL_HARQ_re_Tx - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[26] - MAC_DL_HARQ_re_Tx - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[27] - RLC_UL_tput - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[28] - RLC_DL_tput - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[29] - MAC_UL_tput - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[30] - MAC_DL_tput - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[31] - sleep_duration - [0.1s] - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[32] - Rx_time - [0.1s] - Range: [0:2147483647]. Unknown value: -1 */
/* parameters[33] - Tx_time - [0.1s] - Range: [0:2147483647]. Unknown value: -1 */
repeated sint32 parameters = 2;
}
message ProtoUpdateInfo
{
/* Timestamp of update (Unix timestamp) */
uint32 timestamp = 1;
/* Update status, possible values: */
/* - 0x1 - No update yet */
/* - 0x2 - No error */
/* - 0x3 - UDP socekt error */
/* - 0x4 - Hash error */
/* - 0x5 - Missing packet error */
/* - 0x6 - Invalid data error */
/* - 0x7 - Sending timeout error */
/* - 0x8 - No SW to update error */
/* - 0x9 - Sending unexpected error */
/* - 0x10 - Unexpected error */
uint32 status = 2;
}
message ProtoDeviceInfo
{
/* Serial number of device */
bytes serial_num = 1;
/* Deprecated field */
reserved 2;
/* Software version e.g ver 06.10 -> 0x060A -> 1546 */
uint32 sw_version = 3;
/* Deprecated fields */
reserved 4,5,6,7,8,9,10,11,12;
/* Structure with battery and temperature information */
ProtoRuntime runtime_info = 13;
/* Structure with modem specific runtime information */
ProtoModem modem = 14;
/* String up to 7 bytes long. Software commit id e.g. "e0e8556" */
/* From version 06.07 the first two characters indicate the LTS version. */
/* For example: the value "0bdd23f" means LTS version 11 and the beginning of the commit ID "dd23f" */
string commit_id = 15;
/* Optional string up to 36 bytes long. Can be set to any user define value or hold device's IMEI */
string cloud_token = 16;
/* Memory statistics: */
/* memory_statistics[0] - Status of Nv storage: */
/* - 0 - Nv storage hasn't errors */
/* - 1 - Nv storage has some corrupted packet. Memory is read-only */
/* - 2 - Nv storage is corrupted. Memory is unavailable */
/* memory_statistics[1] - Timestamp of the end of collecting statistics. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[2] - Capacity of memory in bytes */
/* memory_statistics[3] - Used space in bytes */
/* memory_statistics[4] - Size of invalid (outdated) packets in bytes */
/* memory_statistics[5] - Size of corrupted packets in bytes */
/* memory_statistics[6] - Number of valid packets */
/* memory_statistics[7] - Number of invalid (outdated) packets */
/* memory_statistics[8] - Number of corrupted packets */
/* memory_statistics[9] - Number of all samples for channel 1 (valid packets) */
/* memory_statistics[10] - Number of all samples for channel 2 (valid packets) */
/* memory_statistics[11] - Number of all samples for channel 3 (valid packets) */
/* memory_statistics[12] - Number of all samples for channel 4 (valid packets) */
/* memory_statistics[13] - Number of all samples for channel 5 (valid packets) */
/* memory_statistics[14] - Number of all samples for channel 6 (valid packets) */
/* memory_statistics[15] - Timestamp of the first binary measurement. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[16] - Timestamp of the last binary measurement. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[17] - Timestamp of the last binary measurement, that marked as sent. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[18] - Timestamp of the first continuous measurement. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[19] - Timestamp of the last continuous measurement. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[20] - Timestamp of the last continuous measurement, that marked as sent. */
/* Value in seconds since UNIX EPOCH 01-01-1970. Undefined value: 4294967295 */
/* memory_statistics[21] - NVM write counter */
repeated uint32 memory_statistics = 17;
/* Information about last sensor SW update */
ProtoUpdateInfo last_update_info = 18;
}

0
common/transport/coap/src/main/proto/proto_measurement_types.proto → common/transport/coap/src/main/proto/efento/proto_measurement_types.proto

2
common/transport/coap/src/main/proto/proto_measurements.proto → common/transport/coap/src/main/proto/efento/proto_measurements.proto

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
syntax = "proto3"; syntax = "proto3";
import "proto_measurement_types.proto"; import "efento/proto_measurement_types.proto";
option java_package = "org.thingsboard.server.gen.transport.coap"; option java_package = "org.thingsboard.server.gen.transport.coap";
option java_outer_classname = "MeasurementsProtos"; option java_outer_classname = "MeasurementsProtos";

267
common/transport/coap/src/main/proto/efento/proto_rule.proto

@ -0,0 +1,267 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto3";
option java_package = "org.thingsboard.server.gen.transport.coap";
option java_outer_classname = "ProtoRuleProtos";
/* Encoding A: used to set absolute values in the Rules (e.g. upper and lower threshold values) */
/* - TEMPERATURE - [°C] - Celsius degree. Resolution 0.1°C. Range [-273.2:4000.0]. */
/* - HUMIDITY - [% RH] - Relative humidity. Resolution 1%. Range [0:100]. */
/* - ATMOSPHERIC_PRESSURE - [hPa] - Hectopascal (1hPa = 100Pa). Resolution 0.1hPa. Range: [1.0:2000.0]. */
/* - DIFERENTIAL_PRESSURE - [Pa] - Pascal. Resolution 1Pa. Range [-10000:10000] */
/* - OK/ALARM - Not applicable */
/* - IAQ - [IAQ] - IAQ index. Resolution 1IAQ. Range [0:500]. */
/* - FLOODING - Not applicable */
/* - PULSE_CNT - [NB] Number of pulses. Resolution 1 pulse. Range [0:8000000]. */
/* - ELECTRICITY_METER - [W] - Watt; Resolution 1W. Range [0:8000000]. Average power consumption in period */
/* - WATER_METER [l/min] - Liter per minute. Resolution 1l/min. Range [0:8000000]. Average water flow in period. */
/* - SOIL_MOISTURE - [kPa] - Kilopascal (1kPa = 1000Pa); Resolution 1kPa. Range [-1000:0]. Soil moisture (tension). */
/* - CO_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Carbon monoxide concentration. */
/* - NO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Nitrogen dioxide concentration. */
/* - H2S_GAS - [ppm] - Parts per million. Resolution 0.01ppm. Range [0.00:80000.00]. Hydrogen sulfide concentration. */
/* - AMBIENT_LIGHT -[lx] - Lux. Resolution 0.1lx. Range [0.0:100000.0]. Illuminance. */
/* - PM_1_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0:1000]. */
/* - PM_2_5 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0:1000]. */
/* - PM_10_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [0:1000]. */
/* - NOISE_LEVEL - [dB] - Decibels. Resolution 0.1 dB. Range: [0.0:200.0]. Noise level. */
/* - NH3_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Ammonia concentration. */
/* - CH4_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Methane concentration. */
/* - HIGH_PRESSURE - [kPa] - Kilopascal (1kPa = 1000Pa, 100kPa = 1bar). Resolution 1kPa. Range [0:200000]. Pressure. */
/* - DISTANCE_MM - [mm] - Millimeter. Resolution 1mm. Range [0:100000]. Distance. */
/* - WATER_METER_ACC_MINOR - [l] - Liter. Resolution 1l. Range [0:1000000]. Accumulative water meter (minor). */
/* - WATER_METER_ACC_MAJOR - [hl] - Hectoliter. Resolution 1hl. Range [0:1000000]. Accumulative water meter (major). */
/* - CO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Carbon dioxide concentration. */
/* - HUMIDITY ACCURATE - [% RH] - Relative humidity. Resolution 0.1%. Range [0.0:100.0]. */
/* - STATIC_IAQ - [sIAQ] - Static IAQ index. Resolution 1sIAQ. Range [0:10000]. */
/* - CO2_EQUIVALENT - [ppm] - Parts per million. Resolution 1ppm. Range [0:1000000]. Carbon dioxide equivalent. */
/* - BREATH_VOC - [ppm] - Parts per million. Resolution 1ppm. Range [0:100000]. Breath VOC estimate. */
/* - PERCENTAGE - [%] - Percentage. Resolution 0.01%. Range [0.00:100.00]. */
/* - PULSE_CNT_ACC_MINOR - [NB] - Number of pulses. Resolution 1 pulse. Range [0:1000000]. Accumulative pulse counter (minor). */
/* - PULSE_CNT_ACC_MAJOR - [kNB] - Number of kilopulses. Resolution 1 kilopulse. Range [0:1000000]. */
/* Accumulative pulse counter (major). */
/* - ELEC_METER_ACC_MINOR - [Wh] - Watt-hour. Resolution 1Wh. Range [0:1000000]. Accumulative electricity meter (minor). */
/* - ELEC_METER_ACC_MAJOR - [kWh] - Kilowatt-hour. Resolution 1kWh. Range [0:1000000]. Accumulative electricity meter (major). */
/* Encoding R: used to set relative values in the Rules (e.g. differential threshold and hysteresis) */
/* - TEMPERATURE - [°C] - Celsius degree. Resolution 0.1°C. Range [0.1:4273.2]. */
/* - HUMIDITY - [% RH] - Relative humidity. Resolution 1%. Range [1:100]. */
/* - ATMOSPHERIC_PRESSURE - [hPa] - Hectopascal (1hPa = 100Pa). Resolution 0.1hPa. Range: [0.1:1999.0]. */
/* - DIFERENTIAL_PRESSURE - [Pa] - Pascal. Resolution 1Pa. Range [1:20000] */
/* - OK/ALARM - Not applicable */
/* - VOC - [IAQ] - Iaq index. Resolution 1IAQ. Range [1:500]. */
/* - FLOODING - Not applicable */
/* - PULSE_CNT - [NB] Number of pulses. Resolution 1 pulse. Range [1:8000000]. */
/* - ELECTRICITY_METER - [W] - Watt; Resolution 1W. Range [1:8000000]. Average power consumption in period */
/* - WATER_METER [l/min] - Liter per minute. Resolution 1l/min. Range [1:8000000]. Average water flow in period. */
/* - SOIL_MOISTURE - [kPa] - Kilopascal (1kPa = 1000Pa); Resolution 1kPa. Range [1:1000]. Soil moisture (tension). */
/* - CO_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Carbon monoxide concentration. */
/* - NO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Nitrogen dioxide concentration. */
/* - H2S_GAS - [ppm] - Parts per million. Resolution 0.01ppm. Range [0.01:80000.00]. Hydrogen sulfide concentration. */
/* - AMBIENT_LIGHT -[lx] - Lux. Resolution 0.1lx. Range [0.1:100000.0]. Illuminance. */
/* - PM_1_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [1:1000]. */
/* - PM_2_5 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [1:1000]. */
/* - PM_10_0 - [µg/m^3] - Micro gram per cubic meter. Resolution 1µg/m^3 Range [1:1000]. */
/* - NOISE_LEVEL - [dB] - Decibels. Resolution 0.1 dB. Range: [0.1:200.0]. Noise level. */
/* - NH3_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Ammonia concentration. */
/* - CH4_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Methane concentration. */
/* - HIGH_PRESSURE - [kPa] - Kilopascal (1kPa = 1000Pa, 100kPa = 1bar). Resolution 1kPa. Range [1:200000]. Pressure. */
/* - DISTANCE_MM - [mm] - Millimeter. Resolution 1mm. Range [1:100000]. Distance. */
/* - WATER_METER_ACC_MINOR - [l] - Liter. Resolution 1l. Range [1:1000000]. Accumulative water meter (minor). */
/* - WATER_METER_ACC_MAJOR - [hl] - Hectoliter. Resolution 1hl. Range [1:1000000]. Accumulative water meter (major). */
/* - CO2_GAS - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Carbon dioxide concentration. */
/* - HUMIDITY ACCURATE - [% RH] - Relative humidity. Resolution 0.1%. Range [0.1:100.0]. */
/* - STATIC_IAQ - [sIAQ] - Static IAQ index. Resolution 1sIAQ. Range [1:10000]. */
/* - CO2_EQUIVALENT - [ppm] - Parts per million. Resolution 1ppm. Range [1:1000000]. Carbon dioxide equivalent. */
/* - BREATH_VOC - [ppm] - Parts per million. Resolution 1ppm. Range [1:100000]. Breath VOC estimate. */
/* - PERCENTAGE - [%] - Percentage. Resolution 0.01%. Range [0.01:100.00]. */
/* - PULSE_CNT_ACC_MINOR - [NB] - Number of pulses. Resolution 1 pulse. Range [1:1000000]. Accumulative pulse counter (minor). */
/* - PULSE_CNT_ACC_MAJOR - [kNB] - Number of kilopulses. Resolution 1 kilopulse. Range [1:1000000]. */
/* Accumulative pulse counter (major). */
/* - ELEC_METER_ACC_MINOR - [Wh] - Watt-hour. Resolution 1Wh. Range [1:1000000]. Accumulative electricity meter (minor). */
/* - ELEC_METER_ACC_MAJOR - [kWh] - Kilowatt-hour. Resolution 1kWh. Range [1:1000000]. Accumulative electricity meter (major). */
/* Condition to be checked by the device. If the condition is true, an action is triggered */
enum Condition {
/* Invalid value */
CONDITION_UNSPECIFIED = 0;
/* Threshold function for given rule_id is disabled */
CONDITION_DISABLED = 1;
/* Upper threshold. Continuous sensors only. If the measurement (or average from a few measurements) is over the threshold, */
/* an action is triggered. */
/* parameter[0] - Threshold value in "Encoding A" format. Must match channel type */
/* parameter[1] - Hysteresis value in "Encoding R" format. Must much channel type. Set to "0" to disable */
/* parameter[2] - Triggering mode: */
/* - 1 - moving average (a1=(n1+n2+n3)/3, a2=(n2+n3+n4)/3, etc.) */
/* - 2 - window average (a1=(n1+n2+n3)/3, a2=(n4+n5+n6)/3, etc.) */
/* - 3 - consecutive samples (number of consecutive samples above threshold) */
/* parameter[3] - Number of measurements for trigger determination. E.g parameter[3] equals 3, average value from three */
/* samples will be calculated and compared to the threshold value in average mode or the third consecutive */
/* sample above threshold will trigger action in consecutive mode. Range: [1:10]. */
/* parameter[4] - Type of measurement (as described in MeasurementType). */
CONDITION_HIGH_THRESHOLD = 2;
/* Lower threshold. Continuous sensors only. If the measurement (or average from a few measurements) is below the threshold, */
/* an action is triggered. */
/* parameter[0] - Threshold value in "Encoding A" format. Must match channel type */
/* parameter[1] - Hysteresis value in "Encoding R" format. Must much channel type. Set to "0" to disable */
/* parameter[2] - Triggering mode: */
/* - 1 - moving average (a1=(n1+n2+n3)/3, a2=(n2+n3+n4)/3, etc.) */
/* - 2 - window average (a1=(n1+n2+n3)/3, a2=(n4+n5+n6)/3, etc.) */
/* - 3 - consecutive samples (number of consecutive samples above threshold) */
/* parameter[3] - Number of measurements for trigger determination. E.g parameter[3] equals 3, average value from three */
/* samples will be calculated and compared to the threshold value in average mode or the third consecutive */
/* sample below threshold will trigger action in consecutive mode. Range: [1:10]. */
/* parameter[4] - Type of measurement (as described in MeasurementType). */
CONDITION_LOW_THRESHOLD = 3;
/* Differential threshold. Continuous sensors only. If the absolute value of the difference between the last value sent to */
/* the server and the measurement value (or average from a few measurements) is greater or equal to the value of */
/* the threshold set, an action is triggered. */
/* parameter[0] - Threshold value in "Encoding R" format. Must match channel type */
/* parameter[1] - Triggering mode: */
/* - 1 - moving average (a1=(n1+n2+n3)/3, a2=(n2+n3+n4)/3, etc.) */
/* - 2 - window average (a1=(n1+n2+n3)/3, a2=(n4+n5+n6)/3, etc.) */
/* - 3 - consecutive samples (number of consecutive samples above threshold) */
/* parameter[2] - Number of measurements for trigger determination. E.g parameter[3] equals 3, average value from three */
/* samples will be calculated and compared to the threshold value in average mode or the third consecutive */
/* sample exceeding threshold will trigger action in consecutive mode. Range: [1:10]. */
/* parameter[3] - Type of measurement (as described in MeasurementType). */
CONDITION_DIFF_THRESHOLD = 4;
/* Change of binary sensor's state. Binary sensors only. Each change of the binary's sensor state will trigger an action. */
CONDITION_BINARY_CHANGE_STATE = 5;
/* Logic operator. Used for combining multiple rules into more complex conditions. If the logic condition specified by */
/* parameters (logic operator and selected rules) is met, an action is triggered. */
/* parameter[0] - Logic operator (as described in LogicOperation). */
/* parameter[1] - Rule selector (bit mask). Specifies which rules should be taken into account while determining */
/* rules outcome. */
/* parameter[2] - Rule negation (bit mask). Specifies which of chosen in parameter[1] rules should be negated */
/* before determining rules outcome. */
/* parameter[3] - Rule action delay [s]. Specifies time delay between the rule activation and rule action being triggered. */
/* Range: [0:864000]. */
/* parameter[4] - Rule return delay [s]. Specifies time delay between the rule deactivation and rule action being triggered. */
/* Range: [0:864001]. Max parameter value disables action triggering on rule deactivation. */
CONDITION_LOGIC_OPERATOR = 6;
/* On measurement. Continous sensors only. The basic function is to trigger communication after measurement if at least 60s */
/* have passed since the last one. Transmission may occur every x measurement. Optionally dependency on the other rule can */
/* be configured, then, when all conditions are met, transmission is triggered. */
/* parameter[0] - Send every n measurement. This parameter specifies every which measurement transmission will be triggered */
/* if all other conditions are met. Range: [1:500]. If parameter[0] equals 1, transmission will occur after */
/* every measurement. */
/* parameter[1] - Optional. Rule selector (bit mask). Specifies which rule should be taken into account while determining */
/* the measurement rule outcome. */
/* parameter[2] - Optional. Rule negation (bit mask). Specifies which of chosen in parameter[1] rule should be negated */
/* before determining the measurement rule outcome. */
CONDITION_ON_MEASUREMENT = 7;
}
/* Logic operators to be used for determining the outcome of rules with logic operator condition. */
enum LogicOperator {
/* Invalid use */
LOGIC_OPERATOR_UNSPECIFIED = 0;
/* Logic AND */
LOGIC_OPERATOR_AND = 1;
/* Logic OR */
LOGIC_OPERATOR_OR = 2;
}
/* Action to be triggered. Currently the only possible action is to trigger the transmission. */
/* Other actions will be available in next SW releases. */
enum Action {
/* Invalid value */
ACTION_UNSPECIFIED = 0;
/* To trigger the transmission */
ACTION_TRIGGER_TRANSMISSION = 1;
/* To take no action. Possible for logic operator components */
ACTION_NO_ACTION = 2;
/* To trigger the transmission with ACK */
ACTION_TRIGGER_TRANSMISSION_WITH_ACK = 3;
}
/* Type of a rule calendars. */
enum CalendarType {
/* Invalid value */
CALENDAR_TYPE_UNSPECIFIED = 0;
/* Type for inactive calendars */
CALENDAR_TYPE_DISABLED = 1;
/* Week type. Enables selcted rules on specified days of the week in specified time periods. */
/* parameter[0] - Week day mask. Bitmask of days when selected rules are enabled */
/* - Bit 0 - Sunday */
/* - Bit 1 - Monday */
/* ... */
/* - Bit 6 - Saturday */
/* parameter[1] - 'From time' - point in time from which selected rules will be enabled (in minutes from midnight). */
/* parameter[2] - 'To time' - point in time from which selected rules will be disabled (in minutes from midnight). */
/* Note: if 'From time' is bigger than 'To time' there are two periods when rules are enabled - from 00:00 to 'To time' */
/* and from 'From time' to 23:59. */
/* parameter[3] - Timezone - desired timezone for date comparison. Encoded as number (N) of 15 minutes offsets */
/* - example - if N = 4, then offset = 4 * 15min = 1h. I.e. timezone is UTC+1. */
CALENDAR_TYPE_WEEK = 2;
}
/* Rules calendars. Used for enabling/disabling rules based on date/time. */
/* It is possible to configure up to 6 calendars. Each of them can affect any number of rules. */
message ProtoCalendar {
/* Bit mask of selected rules. Mask on bits [0:11] */
/* - Bit 0 - Rule ID 0 */
/* - Bit 1 - Rule ID 1 */
/* ... */
/* - Bit 11 - Rule ID 11 */
uint32 rule_mask = 1;
/* Calendars's parameters. Described in Type. */
repeated sint32 parameters = 2;
/* Calendar's type. Described in Type. */
CalendarType type = 3;
}
/* Rules used to define edge logic on the device. Rules are defined by conditions and actions: */
/* If Condition is true, trigger Action. It is possible to configure up to 12 rules and assign them to different channels. */
/* One rule can be assigned to any number of channels. For instance rule "If temperature is over 10 C, trigger the transmission"*/
/* can be assigned to channels 1 and 2. No matter to how many channels a rule is assigned, it's still counted as one rule. */
message ProtoRule {
/* Channels to which the rule is assigned. One rule can be assigned to multiple channels as long as those are of the same type*/
/* Bit mask on bits [0:5]. E.g. To assign the rule for channel 1: "000001", to assign rule to channels 2 and 4: "001010" */
uint32 channel_mask = 1;
/* Rule's condition (as described in Condition). */
Condition condition = 2;
/* Condition's parameters (as described in Condition). For binary sensors there are no parameters */
repeated sint32 parameters = 3;
/* Action to be triggered. */
Action action = 4;
}

5
dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java

@ -37,6 +37,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
public abstract class DaoUtil { public abstract class DaoUtil {
@ -113,6 +114,10 @@ public abstract class DaoUtil {
return ids; return ids;
} }
public static <I> List<I> fromUUIDs(List<UUID> uuids, Function<UUID, I> mapper) {
return uuids.stream().map(mapper).collect(Collectors.toList());
}
public static <I> I toEntityId(UUID uuid, Function<UUID, I> creator) { public static <I> I toEntityId(UUID uuid, Function<UUID, I> creator) {
if (uuid != null) { if (uuid != null) {
return creator.apply(uuid); return creator.apply(uuid);

2
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java

@ -78,6 +78,8 @@ public interface AlarmDao extends Dao<Alarm> {
PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink);
PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink);
void createEntityAlarmRecord(EntityAlarm entityAlarm); void createEntityAlarmRecord(EntityAlarm entityAlarm);
List<EntityAlarm> findEntityAlarmRecords(TenantId tenantId, AlarmId id); List<EntityAlarm> findEntityAlarmRecords(TenantId tenantId, AlarmId id);

8
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -415,6 +415,13 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Page
return Futures.immediateFuture(alarmDao.findCustomerAlarmsV2(tenantId, customerId, query)); return Futures.immediateFuture(alarmDao.findCustomerAlarmsV2(tenantId, customerId, query));
} }
@Override
public PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UserId userId, PageLink pageLink) {
log.trace("[{}] Executing findAlarmIdsByAssigneeId [{}]", tenantId, userId);
validateId(userId, "Incorrect userId " + userId);
return alarmDao.findAlarmIdsByAssigneeId(tenantId, userId.getId(), pageLink);
}
@Override @Override
public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus, String assigneeId) { AlarmStatus alarmStatus, String assigneeId) {
@ -564,5 +571,4 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Page
request.setEndTs(request.getStartTs()); request.setEndTs(request.getStartTs());
} }
} }
} }

3
dao/src/main/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEvent.java

@ -28,4 +28,7 @@ public class DeleteEntityEvent<T> {
private final EntityId entityId; private final EntityId entityId;
private final EdgeId edgeId; private final EdgeId edgeId;
private final T entity; private final T entity;
@Builder.Default
private final long ts = System.currentTimeMillis();
} }

4
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java

@ -23,6 +23,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.sql.AlarmEntity; import org.thingsboard.server.dao.model.sql.AlarmEntity;
import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
@ -317,6 +318,9 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId") @Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId")
AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId); AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID alarmId);
@Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.assigneeId = :assigneeId")
Page<UUID> findAlarmIdsByAssigneeId(@Param("tenantId") UUID tenantId, @Param("assigneeId") UUID assigneeId, Pageable pageable);
@Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " + @Query(value = "SELECT create_or_update_active_alarm(:t_id, :c_id, :a_id, :a_created_ts, :a_o_id, :a_o_type, :a_type, :a_severity, " +
":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " + ":a_start_ts, :a_end_ts, :a_details, :a_propagate, :a_propagate_to_owner, " +
":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true) ":a_propagate_to_tenant, :a_propagation_types, :a_creation_enabled)", nativeQuery = true)

6
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -290,6 +290,12 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
.mapData(AlarmId::new); .mapData(AlarmId::new);
} }
@Override
public PageData<AlarmId> findAlarmIdsByAssigneeId(TenantId tenantId, UUID userId, PageLink pageLink) {
return DaoUtil.pageToPageData(alarmRepository.findAlarmIdsByAssigneeId(tenantId.getId(), userId, DaoUtil.toPageable(pageLink)))
.mapData(AlarmId::new);
}
@Override @Override
public void createEntityAlarmRecord(EntityAlarm entityAlarm) { public void createEntityAlarmRecord(EntityAlarm entityAlarm) {
log.debug("Saving entity {}", entityAlarm); log.debug("Saving entity {}", entityAlarm);

14
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -55,6 +55,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import static org.thingsboard.server.common.data.StringUtils.generateSafeToken; import static org.thingsboard.server.common.data.StringUtils.generateSafeToken;
@ -246,8 +247,10 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
@Override @Override
@Transactional @Transactional
public void deleteUser(TenantId tenantId, UserId userId) { public void deleteUser(TenantId tenantId, User user) {
log.trace("Executing deleteUser [{}]", userId); Objects.requireNonNull(user, "User is null");
UserId userId = user.getId();
log.trace("[{}] Executing deleteUser [{}]", tenantId, userId);
validateId(userId, INCORRECT_USER_ID + userId); validateId(userId, INCORRECT_USER_ID + userId);
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
userCredentialsDao.removeById(tenantId, userCredentials.getUuidId()); userCredentialsDao.removeById(tenantId, userCredentials.getUuidId());
@ -258,7 +261,8 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
countService.publishCountEntityEvictEvent(tenantId, EntityType.USER); countService.publishCountEntityEvictEvent(tenantId, EntityType.USER);
eventPublisher.publishEvent(DeleteEntityEvent.builder() eventPublisher.publishEvent(DeleteEntityEvent.builder()
.tenantId(tenantId) .tenantId(tenantId)
.entityId(userId).build()); .entityId(userId)
.entity(user).build());
} }
@Override @Override
@ -443,7 +447,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
@Override @Override
protected void removeEntity(TenantId tenantId, User entity) { protected void removeEntity(TenantId tenantId, User entity) {
deleteUser(tenantId, new UserId(entity.getUuidId())); deleteUser(tenantId, entity);
} }
}; };
@ -456,7 +460,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
@Override @Override
protected void removeEntity(TenantId tenantId, User entity) { protected void removeEntity(TenantId tenantId, User entity) {
deleteUser(tenantId, new UserId(entity.getUuidId())); deleteUser(tenantId, entity);
} }
}; };

45
dao/src/test/java/org/thingsboard/server/dao/eventsourcing/DeleteEntityEventTest.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.eventsourcing;
import org.junit.jupiter.api.Test;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.BDDAssertions.byLessThan;
class DeleteEntityEventTest {
@Test
void testBuilderDefaultTs() {
assertThat(DeleteEntityEvent.builder().build().getTs())
.isCloseTo(System.currentTimeMillis(), byLessThan(TimeUnit.MINUTES.toMillis(1)));
assertThat(DeleteEntityEvent.builder().ts(Long.MIN_VALUE).build().getTs())
.isEqualTo(Long.MIN_VALUE);
assertThat(DeleteEntityEvent.builder().ts(Long.MAX_VALUE).build().getTs())
.isEqualTo(Long.MAX_VALUE);
assertThat(DeleteEntityEvent.builder().ts(-1L).build().getTs())
.isEqualTo(-1L);
assertThat(DeleteEntityEvent.builder().ts(0L).build().getTs())
.isEqualTo(0L);
assertThat(DeleteEntityEvent.builder().ts(1692175215000L).build().getTs())
.isEqualTo(1692175215000L);
}
}

12
dao/src/test/java/org/thingsboard/server/dao/service/UserServiceTest.java

@ -135,7 +135,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertEquals("Joe", savedUser.getFirstName()); Assert.assertEquals("Joe", savedUser.getFirstName());
Assert.assertEquals("Downs", savedUser.getLastName()); Assert.assertEquals("Downs", savedUser.getLastName());
userService.deleteUser(tenantId, savedUser.getId()); userService.deleteUser(tenantId, savedUser);
} }
@Test @Test
@ -188,7 +188,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertNotNull(foundUser); Assert.assertNotNull(foundUser);
UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId()); UserCredentials userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId());
Assert.assertNotNull(userCredentials); Assert.assertNotNull(userCredentials);
userService.deleteUser(tenantId, foundUser.getId()); userService.deleteUser(tenantId, foundUser);
userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId()); userCredentials = userService.findUserCredentialsByUserId(tenantId, foundUser.getId());
foundUser = userService.findUserById(tenantId, foundUser.getId()); foundUser = userService.findUserById(tenantId, foundUser.getId());
Assert.assertNull(foundUser); Assert.assertNull(foundUser);
@ -301,7 +301,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2); Assert.assertEquals(tenantAdminsEmail2, loadedTenantAdminsEmail2);
for (User user : loadedTenantAdminsEmail1) { for (User user : loadedTenantAdminsEmail1) {
userService.deleteUser(tenantId, user.getId()); userService.deleteUser(tenantId, user);
} }
pageLink = new PageLink(4, 0, email1); pageLink = new PageLink(4, 0, email1);
@ -310,7 +310,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
for (User user : loadedTenantAdminsEmail2) { for (User user : loadedTenantAdminsEmail2) {
userService.deleteUser(tenantId, user.getId()); userService.deleteUser(tenantId, user);
} }
pageLink = new PageLink(4, 0, email2); pageLink = new PageLink(4, 0, email2);
@ -440,7 +440,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertEquals(customerUsersEmail2, loadedCustomerUsersEmail2); Assert.assertEquals(customerUsersEmail2, loadedCustomerUsersEmail2);
for (User user : loadedCustomerUsersEmail1) { for (User user : loadedCustomerUsersEmail1) {
userService.deleteUser(tenantId, user.getId()); userService.deleteUser(tenantId, user);
} }
pageLink = new PageLink(4, 0, email1); pageLink = new PageLink(4, 0, email1);
@ -449,7 +449,7 @@ public class UserServiceTest extends AbstractServiceTest {
Assert.assertEquals(0, pageData.getData().size()); Assert.assertEquals(0, pageData.getData().size());
for (User user : loadedCustomerUsersEmail2) { for (User user : loadedCustomerUsersEmail2) {
userService.deleteUser(tenantId, user.getId()); userService.deleteUser(tenantId, user);
} }
pageLink = new PageLink(4, 0, email2); pageLink = new PageLink(4, 0, email2);

2
ui-ngx/src/app/core/http/widget.service.ts

@ -207,7 +207,7 @@ export class WidgetService {
public saveWidgetType(widgetTypeDetails: WidgetTypeDetails, public saveWidgetType(widgetTypeDetails: WidgetTypeDetails,
config?: RequestConfig): Observable<WidgetTypeDetails> { config?: RequestConfig): Observable<WidgetTypeDetails> {
return this.http.post<WidgetTypeDetails>(`/api/widgetType`, return this.http.post<WidgetTypeDetails>(`/api/widgetType`, widgetTypeDetails,
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }

20
ui-ngx/src/app/core/services/dashboard-utils.service.ts

@ -223,6 +223,15 @@ export class DashboardUtilsService {
public validateAndUpdateWidget(widget: Widget): Widget { public validateAndUpdateWidget(widget: Widget): Widget {
widget.config = this.validateAndUpdateWidgetConfig(widget.config, widget.type); widget.config = this.validateAndUpdateWidgetConfig(widget.config, widget.type);
widget = this.validateAndUpdateWidgetTypeFqn(widget); widget = this.validateAndUpdateWidgetTypeFqn(widget);
if (isDefined((widget as any).title)) {
delete (widget as any).title;
}
if (isDefined((widget as any).image)) {
delete (widget as any).image;
}
if (isDefined((widget as any).description)) {
delete (widget as any).description;
}
// Temp workaround // Temp workaround
if (['system.charts.state_chart', if (['system.charts.state_chart',
'system.charts.basic_timeseries', 'system.charts.basic_timeseries',
@ -246,12 +255,21 @@ export class DashboardUtilsService {
} }
private validateAndUpdateWidgetTypeFqn(widget: Widget): Widget { private validateAndUpdateWidgetTypeFqn(widget: Widget): Widget {
const w = widget as any;
if (!isValidWidgetFullFqn(widget.typeFullFqn)) { if (!isValidWidgetFullFqn(widget.typeFullFqn)) {
const w = widget as any;
if (isDefinedAndNotNull(w.isSystemType) && isNotEmptyStr(w.bundleAlias) && isNotEmptyStr(w.typeAlias)) { if (isDefinedAndNotNull(w.isSystemType) && isNotEmptyStr(w.bundleAlias) && isNotEmptyStr(w.typeAlias)) {
widget.typeFullFqn = (w.isSystemType ? 'system' : 'tenant') + '.' + w.bundleAlias + '.' + w.typeAlias; widget.typeFullFqn = (w.isSystemType ? 'system' : 'tenant') + '.' + w.bundleAlias + '.' + w.typeAlias;
} }
} }
if (isDefined(w.isSystemType)) {
delete w.isSystemType;
}
if (isDefined(w.bundleAlias)) {
delete w.bundleAlias;
}
if (isDefined(w.typeAlias)) {
delete w.typeAlias;
}
// Temp workaround // Temp workaround
if (widget.typeFullFqn === 'system.charts.timeseries') { if (widget.typeFullFqn === 'system.charts.timeseries') {
widget.typeFullFqn = 'system.charts.basic_timeseries'; widget.typeFullFqn = 'system.charts.basic_timeseries';

3
ui-ngx/src/app/modules/dashboard/dashboard-pages.routing.module.ts

@ -40,9 +40,6 @@ export class WidgetEditorDashboardResolver implements Resolve<Dashboard> {
const widget: Widget = { const widget: Widget = {
typeFullFqn: 'system.customWidget', typeFullFqn: 'system.customWidget',
type: editWidgetInfo.type, type: editWidgetInfo.type,
title: 'My widget',
image: null,
description: null,
sizeX: editWidgetInfo.sizeX * 2, sizeX: editWidgetInfo.sizeX * 2,
sizeY: editWidgetInfo.sizeY * 2, sizeY: editWidgetInfo.sizeY * 2,
row: 2, row: 2,

1
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts

@ -585,7 +585,6 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
const widget: Widget = { const widget: Widget = {
typeFullFqn: widgetInfo.fullFqn, typeFullFqn: widgetInfo.fullFqn,
type: widgetInfo.type, type: widgetInfo.type,
title: widgetInfo.widgetName,
sizeX, sizeX,
sizeY, sizeY,
row: 0, row: 0,

3
ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts

@ -1162,9 +1162,6 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
let newWidget: Widget = { let newWidget: Widget = {
typeFullFqn: widgetTypeInfo.fullFqn, typeFullFqn: widgetTypeInfo.fullFqn,
type: widgetTypeInfo.type, type: widgetTypeInfo.type,
title: 'New widget',
image: null,
description: null,
sizeX: widgetTypeInfo.sizeX, sizeX: widgetTypeInfo.sizeX,
sizeY: widgetTypeInfo.sizeY, sizeY: widgetTypeInfo.sizeY,
config, config,

8
ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html

@ -16,14 +16,14 @@
--> -->
<div fxFlex fxLayout="column" class="tb-progress-cover" fxLayoutAlign="center center" <div fxFlex fxLayout="column" class="tb-progress-cover" fxLayoutAlign="center center"
[ngStyle]="dashboardStyle" [style]="dashboardStyle"
[style.backgroundImage]="backgroundImage" [style.backgroundImage]="backgroundImage"
[fxShow]="(isLoading$ | async) && !ignoreLoading && !isEdit"> [fxShow]="(isLoading$ | async) && !ignoreLoading && !isEdit">
<mat-spinner color="warn" mode="indeterminate" diameter="100"> <mat-spinner color="warn" mode="indeterminate" diameter="100">
</mat-spinner> </mat-spinner>
</div> </div>
<div id="gridster-parent" <div id="gridster-parent"
fxFlex class="tb-dashboard-content layout-wrap" [ngClass]="{'autofill-height': isAutofillHeight()}" fxFlex class="tb-dashboard-content layout-wrap" [class]="{'autofill-height': isAutofillHeight()}"
(contextmenu)="openDashboardContextMenu($event)"> (contextmenu)="openDashboardContextMenu($event)">
<div #dashboardMenuTrigger="matMenuTrigger" style="visibility: hidden; position: fixed" <div #dashboardMenuTrigger="matMenuTrigger" style="visibility: hidden; position: fixed"
[style.left]="dashboardMenuPosition.x" [style.left]="dashboardMenuPosition.x"
@ -61,9 +61,9 @@
</div> </div>
</ng-template> </ng-template>
</mat-menu> </mat-menu>
<div [ngClass]="dashboardClass" id="gridster-background" style="height: auto; min-height: 100%; display: inline;"> <div [class]="dashboardClass" id="gridster-background" style="height: 100%;">
<gridster #gridster id="gridster-child" [options]="gridsterOpts"> <gridster #gridster id="gridster-child" [options]="gridsterOpts">
<gridster-item #gridsterItem [item]="widget" [ngClass]="{'tb-noselect': isEdit}" *ngFor="let widget of dashboardWidgets"> <gridster-item #gridsterItem [item]="widget" [class]="{'tb-noselect': isEdit}" *ngFor="let widget of dashboardWidgets">
<tb-widget-container <tb-widget-container
[gridsterItem]="gridsterItem" [gridsterItem]="gridsterItem"
[widget]="widget" [widget]="widget"

2
ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.html

@ -38,7 +38,7 @@
<mat-option *ngIf="!(filteredRuleChains | async)?.length" [value]="null" class="tb-not-found"> <mat-option *ngIf="!(filteredRuleChains | async)?.length" [value]="null" class="tb-not-found">
<div class="tb-not-found-content" (click)="$event.stopPropagation()"> <div class="tb-not-found-content" (click)="$event.stopPropagation()">
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty"> <div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
<span translate>device-profile.no-device-profiles-found</span> <span translate>rulechain.no-rulechains-text</span>
</div> </div>
<ng-template #searchNotEmpty> <ng-template #searchNotEmpty>
<span> <span>

13
ui-ngx/src/app/shared/models/widget.models.ts

@ -681,7 +681,13 @@ export interface WidgetConfig {
[key: string]: any; [key: string]: any;
} }
export interface Widget extends WidgetInfo{ export interface BaseWidgetInfo {
id?: string;
typeFullFqn: string;
type: widgetType;
}
export interface Widget extends BaseWidgetInfo {
typeId?: WidgetTypeId; typeId?: WidgetTypeId;
sizeX: number; sizeX: number;
sizeY: number; sizeY: number;
@ -690,10 +696,7 @@ export interface Widget extends WidgetInfo{
config: WidgetConfig; config: WidgetConfig;
} }
export interface WidgetInfo { export interface WidgetInfo extends BaseWidgetInfo {
id?: string;
typeFullFqn: string;
type: widgetType;
title: string; title: string;
image?: string; image?: string;
description?: string; description?: string;

Loading…
Cancel
Save