Browse Source

Merge remote-tracking branch 'origin/master' into feature/bulk-import/device-credentials

# Conflicts:
#	application/src/main/java/org/thingsboard/server/controller/DeviceController.java
#	application/src/main/java/org/thingsboard/server/controller/EdgeController.java
#	common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
#	ui-ngx/src/app/core/http/device.service.ts
pull/5023/head
Viacheslav Klimov 5 years ago
parent
commit
e33a336ed9
  1. 9
      application/pom.xml
  2. 16
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  3. 1
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  4. 2
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  5. 5
      application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
  6. 4
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java
  7. 47
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  8. 6
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  9. 2
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  10. 1
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  11. 120
      application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
  12. 10
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  13. 8
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  14. 55
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  15. 2
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  16. 41
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  17. 4
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  18. 12
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  19. 15
      application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
  20. 55
      application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java
  21. 128
      application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
  22. 16
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  23. 3
      application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java
  24. 4
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  25. 3
      application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java
  26. 2
      application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
  27. 2
      application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
  28. 13
      application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
  29. 7
      application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java
  30. 26
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  31. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  32. 52
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeProtoUtils.java
  33. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java
  34. 11
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java
  35. 20
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java
  36. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java
  37. 19
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java
  38. 13
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java
  39. 9
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java
  40. 10
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java
  41. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java
  42. 18
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java
  43. 34
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java
  44. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java
  45. 109
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmEdgeProcessor.java
  46. 33
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  47. 56
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceEdgeProcessor.java
  48. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationEdgeProcessor.java
  49. 161
      application/src/main/java/org/thingsboard/server/service/edge/rpc/sync/DefaultEdgeRequestsService.java
  50. 2
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  51. 8
      application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java
  52. 122
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java
  53. 4
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  54. 4
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  55. 13
      application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java
  56. 7
      application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java
  57. 3
      application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponseActorMsg.java
  58. 44
      application/src/main/java/org/thingsboard/server/service/rpc/RemoveRpcActorMsg.java
  59. 3
      application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java
  60. 2
      application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java
  61. 1
      application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java
  62. 2
      application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java
  63. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  64. 11
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
  65. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
  66. 113
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  67. 15
      application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java
  68. 10
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java
  69. 3
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  70. 4
      application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java
  71. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
  72. 2
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  73. 10
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  74. 9
      application/src/main/resources/thingsboard.yml
  75. 33
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java
  76. 62
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  77. 2
      application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java
  78. 8
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcDefaultIntegrationTest.java
  79. 6
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java
  80. 3
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  81. 104
      application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java
  82. 8
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java
  83. 8
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
  84. 2
      application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java
  85. 144
      common/cluster-api/pom.xml
  86. 15
      common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java
  87. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java
  88. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueCallback.java
  89. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java
  90. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueHandler.java
  91. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsg.java
  92. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgDecoder.java
  93. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgHeaders.java
  94. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgMetadata.java
  95. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueProducer.java
  96. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java
  97. 0
      common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueResponseTemplate.java
  98. 0
      common/cluster-api/src/main/proto/jsinvoke.proto
  99. 0
      common/cluster-api/src/main/proto/queue.proto
  100. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

9
application/pom.xml

@ -65,6 +65,10 @@
<groupId>org.thingsboard.rule-engine</groupId>
<artifactId>rule-engine-api</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>cluster-api</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.rule-engine</groupId>
<artifactId>rule-engine-components</artifactId>
@ -289,6 +293,11 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

16
application/src/main/data/json/system/widget_bundles/control_widgets.json

@ -18,8 +18,8 @@
"resources": [],
"templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>",
"templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n",
"controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\"\n ]\n}",
"controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}
@ -55,7 +55,7 @@
"templateHtml": "<tb-knob [ctx]='ctx'></tb-knob>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\"\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"title\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\",\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
@ -73,7 +73,7 @@
"templateHtml": "<tb-switch [ctx]='ctx'></tb-switch>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"showOnOffLabels\": {\n \"title\": \"Show on/off labels\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n \"showOnOffLabels\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"showOnOffLabels\":true,\"title\":\"Switch control\"},\"title\":\"Switch Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
@ -91,7 +91,7 @@
"templateHtml": "<tb-round-switch [ctx]='ctx'></tb-round-switch>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"Switch title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve on/off value using method\",\n \"type\": \"string\",\n \"default\": \"rpc\"\n },\n \"valueKey\": {\n \"title\": \"Attribute/Timeseries value key (only when subscribe for attribute/timeseries method)\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"getValueMethod\": {\n \"title\": \"RPC get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"RPC set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"convertValueFunction\": {\n \"title\": \"Convert value function, f(value), returns payload used by RPC set value method\",\n \"type\": \"string\",\n \"default\": \"return value;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"none\",\n \"label\": \"Don't retrieve\"\n },\n {\n \"value\": \"rpc\",\n \"label\": \"Call RPC get value method\"\n },\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueKey\",\n \"getValueMethod\",\n \"setValueMethod\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"convertValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":false,\"getValueMethod\":\"getValue\",\"setValueMethod\":\"setValue\",\"title\":\"Round switch\",\"retrieveValueMethod\":\"rpc\",\"valueKey\":\"value\",\"parseValueFunction\":\"return data ? true : false;\",\"convertValueFunction\":\"return value;\"},\"title\":\"Round switch\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
@ -109,7 +109,7 @@
"templateHtml": "<tb-led-indicator [ctx]='ctx'></tb-led-indicator>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onResize = function() {\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"checkStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n }\n },\n \"required\": [\"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"checkStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true,\"checkStatusMethod\":\"checkStatus\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}"
}
@ -126,8 +126,8 @@
"resources": [],
"templateHtml": "<div class=\"tb-rpc-button\" fxLayout=\"column\">\n <div fxFlex=\"20\" class=\"title-container\" fxLayout=\"row\"\n fxLayoutAlign=\"center center\" [fxShow]=\"showTitle\">\n <span class=\"button-title\">{{title}}</span>\n </div>\n <div fxFlex=\"{{showTitle ? 80 : 100}}\" [ngStyle]=\"{paddingTop: showTitle ? '5px': '10px'}\"\n class=\"button-container\" fxLayout=\"column\" fxLayoutAlign=\"center center\">\n <div>\n <button mat-button (click)=\"sendCommand()\"\n [class.mat-raised-button]=\"styleButton?.isRaised\"\n [color]=\"styleButton?.isPrimary ? 'primary' : ''\"\n [ngStyle]=\"customStyle\">\n {{buttonLable}}\n </button>\n </div>\n </div>\n <div class=\"error-container\" [ngStyle]=\"{'background': error?.length ? 'rgba(255,255,255,0.25)' : 'none'}\"\n fxLayout=\"row\" fxLayoutAlign=\"center center\">\n <span class=\"button-error\">{{ error }}</span>\n </div>\n</div>",
"templateCss": ".tb-rpc-button {\n width: 100%;\n height: 100%;\n}\n\n.tb-rpc-button .title-container {\n font-weight: 500;\n white-space: nowrap;\n margin: 10px 0;\n}\n\n.tb-rpc-button .button-container div{\n min-width: 80%\n}\n\n.tb-rpc-button .button-container .mat-button{\n width: 100%;\n margin: 0;\n}\n\n.tb-rpc-button .error-container {\n position: absolute;\n top: 2%;\n right: 0;\n left: 0;\n z-index: 4;\n height: 14px;\n}\n\n.tb-rpc-button .error-container .button-error {\n color: #ff3315;\n white-space: nowrap;\n}",
"controllerScript": "self.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"title\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"buttonText\": {\n \"title\": \"Button label\",\n \"type\": \"string\",\n \"default\": \"Send RPC\"\n },\n \"oneWayElseTwoWay\": {\n \"title\": \"Is One Way Command\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showError\": {\n \"title\": \"Show RPC command execution error\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"methodName\": {\n \"title\": \"RPC method\",\n \"type\": \"string\",\n \"default\": \"rpcCommand\"\n },\n \"methodParams\": {\n \"title\": \"RPC method params\",\n \"type\": \"string\",\n \"default\": \"{}\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 5000\n },\n \"styleButton\": {\n \"type\": \"object\",\n \"title\": \"Button Style\",\n \"properties\": {\n \"isRaised\": {\n \"type\": \"boolean\",\n \"title\": \"Raised\",\n \"default\": true\n },\n \"isPrimary\": {\n \"type\": \"boolean\",\n \"title\": \"Primary color\",\n \"default\": false\n },\n \"bgColor\": {\n \"type\": \"string\",\n \"title\": \"Button background color\",\n \"default\": null\n },\n \"textColor\": {\n \"type\": \"string\",\n \"title\": \"Button text color\",\n \"default\": null\n }\n }\n },\n \"required\": []\n }\n },\n \"form\": [\n \"title\",\n \"buttonText\",\n \"oneWayElseTwoWay\",\n \"showError\",\n \"methodName\",\n {\n \"key\": \"methodParams\",\n \"type\": \"json\"\n },\n \"requestTimeout\",\n {\n \"key\": \"styleButton\",\n \"items\": [\n \"styleButton.isRaised\",\n \"styleButton.isPrimary\",\n {\n \"key\": \"styleButton.bgColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"styleButton.textColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n\n}",
"controllerScript": "var requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n \n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges();\n });\n};\n\nfunction init() {\n let rpcEnabled = self.ctx.defaultSubscription.rpcEnabled;\n\n self.ctx.$scope.buttonLable = self.ctx.settings.buttonText;\n self.ctx.$scope.showTitle = self.ctx.settings.title &&\n self.ctx.settings.title.length ? true : false;\n self.ctx.$scope.title = self.ctx.settings.title;\n self.ctx.$scope.styleButton = self.ctx.settings.styleButton;\n\n if (self.ctx.settings.styleButton.isPrimary ===\n false) {\n self.ctx.$scope.customStyle = {\n 'background-color': self.ctx.$scope.styleButton.bgColor,\n 'color': self.ctx.$scope.styleButton.textColor\n };\n }\n\n if (!rpcEnabled) {\n self.ctx.$scope.error =\n 'Target device is not set!';\n }\n\n self.ctx.$scope.sendCommand = function() {\n var rpcMethod = self.ctx.settings.methodName;\n var rpcParams = self.ctx.settings.methodParams;\n var timeout = self.ctx.settings.requestTimeout;\n var oneWayElseTwoWay = self.ctx.settings.oneWayElseTwoWay ?\n true : false;\n\n var commandPromise;\n if (oneWayElseTwoWay) {\n commandPromise = self.ctx.controlApi.sendOneWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n } else {\n commandPromise = self.ctx.controlApi.sendTwoWayCommand(\n rpcMethod, rpcParams, timeout, requestPersistent, persistentPollingInterval);\n }\n commandPromise.subscribe(\n function success() {\n self.ctx.$scope.error = \"\";\n self.ctx.detectChanges();\n },\n function fail(rejection) {\n if (self.ctx.settings.showError) {\n self.ctx.$scope.error =\n rejection.status + \": \" +\n rejection.statusText;\n self.ctx.detectChanges();\n }\n }\n );\n };\n}\n\nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"title\": {\n \"title\": \"Widget title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"buttonText\": {\n \"title\": \"Button label\",\n \"type\": \"string\",\n \"default\": \"Send RPC\"\n },\n \"oneWayElseTwoWay\": {\n \"title\": \"Is One Way Command\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showError\": {\n \"title\": \"Show RPC command execution error\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"methodName\": {\n \"title\": \"RPC method\",\n \"type\": \"string\",\n \"default\": \"rpcCommand\"\n },\n \"methodParams\": {\n \"title\": \"RPC method params\",\n \"type\": \"string\",\n \"default\": \"{}\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 5000\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"persistentPollingInterval\": {\n \"title\": \"Polling interval in milliseconds to get persistent RPC command response\",\n \"type\": \"number\",\n \"default\": 5000,\n \"minimum\": 1000\n },\n \"styleButton\": {\n \"type\": \"object\",\n \"title\": \"Button Style\",\n \"properties\": {\n \"isRaised\": {\n \"type\": \"boolean\",\n \"title\": \"Raised\",\n \"default\": true\n },\n \"isPrimary\": {\n \"type\": \"boolean\",\n \"title\": \"Primary color\",\n \"default\": false\n },\n \"bgColor\": {\n \"type\": \"string\",\n \"title\": \"Button background color\",\n \"default\": null\n },\n \"textColor\": {\n \"type\": \"string\",\n \"title\": \"Button text color\",\n \"default\": null\n }\n }\n },\n \"required\": []\n }\n },\n \"form\": [\n \"title\",\n \"buttonText\",\n \"oneWayElseTwoWay\",\n \"showError\",\n \"methodName\",\n {\n \"key\": \"methodParams\",\n \"type\": \"json\"\n },\n \"requestTimeout\",\n \"requestPersistent\",\n {\n \"key\": \"persistentPollingInterval\",\n \"condition\": \"model.requestPersistent === true\"\n },\n {\n \"key\": \"styleButton\",\n \"items\": [\n \"styleButton.isRaised\",\n \"styleButton.isPrimary\",\n {\n \"key\": \"styleButton.bgColor\",\n \"type\": \"color\"\n },\n {\n \"key\": \"styleButton.textColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#e6e7e8\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":5000,\"oneWayElseTwoWay\":true,\"buttonText\":\"Send RPC\",\"styleButton\":{\"isRaised\":true,\"isPrimary\":false},\"methodName\":\"rpcCommand\",\"methodParams\":\"{}\"},\"title\":\"RPC Button\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
}

1
application/src/main/data/upgrade/3.2.2/schema_update.sql

@ -209,6 +209,7 @@ CREATE TABLE IF NOT EXISTS rpc (
expiration_time bigint NOT NULL,
request varchar(10000000) NOT NULL,
response varchar(10000000),
additional_info varchar(10000000),
status varchar(255) NOT NULL
);

2
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -81,7 +81,7 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
import org.thingsboard.server.service.mail.MailExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.rpc.TbRpcService;
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;

5
application/src/main/java/org/thingsboard/server/actors/app/AppActor.java

@ -55,7 +55,7 @@ public class AppActor extends ContextAwareActor {
private final TbTenantProfileCache tenantProfileCache;
private final TenantService tenantService;
private final Set<TenantId> deletedTenants;
private boolean ruleChainsInitialized;
private volatile boolean ruleChainsInitialized;
private AppActor(ActorSystemContext systemContext) {
super(systemContext);
@ -69,7 +69,7 @@ public class AppActor extends ContextAwareActor {
if (!ruleChainsInitialized) {
initTenantActors();
ruleChainsInitialized = true;
if (msg.getMsgType() != MsgType.APP_INIT_MSG) {
if (msg.getMsgType() != MsgType.APP_INIT_MSG && msg.getMsgType() != MsgType.PARTITION_CHANGE_MSG) {
log.warn("Rule Chains initialized by unexpected message: {}", msg);
}
}
@ -95,6 +95,7 @@ public class AppActor extends ContextAwareActor {
case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
onToDeviceActorMsg((TenantAwareMsg) msg, true);
break;
case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:

4
application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
@ -84,6 +85,9 @@ public class DeviceActor extends ContextAwareActor {
case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
processor.processEdgeUpdate((DeviceEdgeUpdateMsg) msg);
break;
case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
processor.processRemoveRpc(ctx, (RemoveRpcActorMsg) msg);
break;
default:
return false;
}

47
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -26,7 +26,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.LinkedHashMapRemoveEldest;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
@ -86,8 +86,9 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
@ -97,11 +98,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
@ -236,6 +235,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
rpc.setExpirationTime(request.getExpirationTime());
rpc.setRequest(JacksonUtil.valueToTree(request));
rpc.setStatus(status);
rpc.setAdditionalInfo(JacksonUtil.valueToTree(request.getAdditionalInfo()));
return systemContext.getTbRpcService().save(tenantId, rpc);
}
@ -264,6 +264,21 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
}
}
void processRemoveRpc(TbActorCtx context, RemoveRpcActorMsg msg) {
log.debug("[{}] Processing remove rpc command", msg.getRequestId());
Integer requestId = null;
for (Map.Entry<Integer, ToDeviceRpcRequestMetadata> entry : toDeviceRpcPendingMap.entrySet()) {
if (entry.getValue().getMsg().getMsg().getId().equals(msg.getRequestId())) {
requestId = entry.getKey();
break;
}
}
if (requestId != null) {
toDeviceRpcPendingMap.remove(requestId);
}
}
private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
@ -383,11 +398,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
}
private void reportSessionOpen() {
systemContext.getDeviceStateService().onDeviceConnect(deviceId);
systemContext.getDeviceStateService().onDeviceConnect(tenantId, deviceId);
}
private void reportSessionClose() {
systemContext.getDeviceStateService().onDeviceDisconnect(deviceId);
systemContext.getDeviceStateService().onDeviceDisconnect(tenantId, deviceId);
}
private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
@ -586,13 +601,13 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
return;
}
log.info("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
log.debug("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
if (sessions.size() == 1) {
reportSessionOpen();
}
systemContext.getDeviceStateService().onDeviceActivity(deviceId, System.currentTimeMillis());
systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, System.currentTimeMillis());
dumpSessions();
} else if (msg.getEvent() == SessionEvent.CLOSED) {
log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
@ -622,7 +637,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
if (subscriptionInfo.getRpcSubscription()) {
rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
}
systemContext.getDeviceStateService().onDeviceActivity(deviceId, subscriptionInfo.getLastActivityTime());
systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, subscriptionInfo.getLastActivityTime());
dumpSessions();
}
@ -729,18 +744,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
edgeEvent.setBody(body);
edgeEvent.setEdgeId(edgeId);
ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(EdgeEvent result) {
systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
}
}, systemContext.getDbCallbackExecutor());
systemContext.getEdgeEventService().save(edgeEvent);
systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
}
private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {

6
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -33,6 +33,7 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorRef;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
@ -453,6 +454,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getDeviceService();
}
@Override
public TbClusterService getClusterService() {
return mainCtx.getClusterService();
}
@Override
public DashboardService getDashboardService() {
return mainCtx.getDashboardService();

2
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -49,7 +49,7 @@ import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper;
import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import java.util.ArrayList;
import java.util.Collections;

1
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -165,6 +165,7 @@ public class TenantActor extends RuleChainManagerActor {
case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
case REMOVE_RPC_TO_DEVICE_ACTOR_MSG:
onToDeviceActorMsg((DeviceAwareMsg) msg, true);
break;
case RULE_CHAIN_TO_RULE_CHAIN_MSG:

120
application/src/main/java/org/thingsboard/server/controller/RpcController.java → application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java

@ -16,41 +16,28 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RpcId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rpc.Rpc;
import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.LocalRequestMetaData;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.security.AccessValidator;
@ -59,97 +46,32 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.exception.ToErrorResponseEntity;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Optional;
import java.util.UUID;
/**
* Created by ashvayka on 22.03.18.
*/
@RestController
@TbCoreComponent
@RequestMapping(TbUrlConstants.RPC_URL_PREFIX)
@Slf4j
public class RpcController extends BaseController {
protected final ObjectMapper jsonMapper = new ObjectMapper();
public abstract class AbstractRpcController extends BaseController {
@Autowired
private TbCoreDeviceRpcService deviceRpcService;
protected TbCoreDeviceRpcService deviceRpcService;
@Autowired
private AccessValidator accessValidator;
@Value("${server.rest.server_side_rpc.min_timeout:5000}")
private long minTimeout;
protected long minTimeout;
@Value("${server.rest.server_side_rpc.default_timeout:10000}")
private long defaultTimeout;
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
@ResponseBody
public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
try {
RpcId rpcId = new RpcId(UUID.fromString(strRpc));
return checkRpcId(rpcId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
@ResponseBody
public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam RpcStatus rpcStatus,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("DeviceId", strDeviceId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
@ResponseBody
public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
try {
rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));
} catch (Exception e) {
throw handleException(e);
}
}
protected long defaultTimeout;
private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
protected DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody, HttpStatus timeoutStatus, HttpStatus noActiveConnectionStatus) throws ThingsboardException {
try {
JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(rpcRequestBody.get("method").asText(), jsonMapper.writeValueAsString(rpcRequestBody.get("params")));
JsonNode rpcRequestBody = JacksonUtil.toJsonNode(requestBody);
ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(rpcRequestBody.get("method").asText(), JacksonUtil.toString(rpcRequestBody.get("params")));
SecurityUser currentUser = getCurrentUser();
TenantId tenantId = currentUser.getTenantId();
final DeferredResult<ResponseEntity> response = new DeferredResult<>();
@ -157,7 +79,8 @@ public class RpcController extends BaseController {
long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID();
boolean persisted = rpcRequestBody.has(DataConstants.PERSISTENT) && rpcRequestBody.get(DataConstants.PERSISTENT).asBoolean();
accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
String additionalInfo = JacksonUtil.toString(rpcRequestBody.get(DataConstants.ADDITIONAL_INFO));
accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
ToDeviceRpcRequest rpcRequest = new ToDeviceRpcRequest(rpcRequestUUID,
@ -166,9 +89,10 @@ public class RpcController extends BaseController {
oneWay,
expTime,
body,
persisted
persisted,
additionalInfo
);
deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse, timeoutStatus, noActiveConnectionStatus), currentUser);
}
@Override
@ -184,12 +108,12 @@ public class RpcController extends BaseController {
}
}));
return response;
} catch (IOException ioe) {
} catch (IllegalArgumentException ioe) {
throw new ThingsboardException("Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
}
public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response) {
public void reply(LocalRequestMetaData rpcRequest, FromDeviceRpcResponse response, HttpStatus timeoutStatus, HttpStatus noActiveConnectionStatus) {
Optional<RpcError> rpcError = response.getError();
DeferredResult<ResponseEntity> responseWriter = rpcRequest.getResponseWriter();
if (rpcError.isPresent()) {
@ -197,13 +121,13 @@ public class RpcController extends BaseController {
RpcError error = rpcError.get();
switch (error) {
case TIMEOUT:
responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
break;
case NO_ACTIVE_CONNECTION:
responseWriter.setResult(new ResponseEntity<>(HttpStatus.CONFLICT));
responseWriter.setResult(new ResponseEntity<>(noActiveConnectionStatus));
break;
default:
responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
break;
}
} else {
@ -212,8 +136,8 @@ public class RpcController extends BaseController {
String data = responseData.get();
try {
logRpcCall(rpcRequest, rpcError, null);
responseWriter.setResult(new ResponseEntity<>(jsonMapper.readTree(data), HttpStatus.OK));
} catch (IOException e) {
responseWriter.setResult(new ResponseEntity<>(JacksonUtil.toJsonNode(data), HttpStatus.OK));
} catch (IllegalArgumentException e) {
log.debug("Failed to decode device response: {}", data, e);
logRpcCall(rpcRequest, rpcError, e);
responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE));

10
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -26,12 +26,12 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.UpdateMessage;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
@ -67,7 +67,7 @@ public class AdminController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key));
if (adminSettings.getKey().equals("mail")) {
((ObjectNode) adminSettings.getJsonValue()).put("password", "");
((ObjectNode) adminSettings.getJsonValue()).remove("password");
}
return adminSettings;
} catch (Exception e) {
@ -84,7 +84,7 @@ public class AdminController extends BaseController {
adminSettings = checkNotNull(adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings));
if (adminSettings.getKey().equals("mail")) {
mailService.updateMailConfiguration();
((ObjectNode) adminSettings.getJsonValue()).put("password", "");
((ObjectNode) adminSettings.getJsonValue()).remove("password");
} else if (adminSettings.getKey().equals("sms")) {
smsService.updateSmsConfiguration();
}
@ -126,6 +126,10 @@ public class AdminController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
adminSettings = checkNotNull(adminSettings);
if (adminSettings.getKey().equals("mail")) {
if(!adminSettings.getJsonValue().has("password")) {
AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"));
((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText());
}
String email = getCurrentUser().getEmail();
mailService.sendTestMail(adminSettings.getJsonValue(), email);
}

8
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.page.PageData;
@ -46,6 +47,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
@RestController
@TbCoreComponent
@RequestMapping("/api")
@ -112,10 +115,13 @@ public class AlarmController extends BaseController {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), alarm.getOriginator());
logEntityAction(alarm.getOriginator(), alarm,
getCurrentUser().getCustomerId(),
ActionType.ALARM_DELETE, null);
sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED);
sendAlarmDeleteNotificationMsg(getTenantId(), alarmId, relatedEdgeIds, alarm);
return alarmService.deleteAlarm(getTenantId(), alarmId);
} catch (Exception e) {

55
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -33,7 +33,6 @@ import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
@ -118,7 +117,6 @@ import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -129,7 +127,7 @@ import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.AccessControlService;
@ -846,13 +844,25 @@ public abstract class BaseController {
}
protected void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds) {
sendDeleteNotificationMsg(tenantId, entityId, edgeIds, null);
}
protected void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds, String body) {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, null, EdgeEventActionType.DELETED);
sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, body, null, EdgeEventActionType.DELETED);
}
}
}
protected void sendAlarmDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds, Alarm alarm) {
try {
sendDeleteNotificationMsg(tenantId, entityId, edgeIds, json.writeValueAsString(alarm));
} catch (Exception e) {
log.warn("Failed to push delete alarm msg to core: {}", alarm, e);
}
}
protected void sendEntityAssignToCustomerNotificationMsg(TenantId tenantId, EntityId entityId, CustomerId customerId, EdgeEventActionType action) {
try {
sendNotificationMsgToEdgeService(tenantId, null, entityId, json.writeValueAsString(customerId), null, action);
@ -870,42 +880,7 @@ public abstract class BaseController {
}
private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
if (!edgesEnabled) {
return;
}
if (type == null) {
if (entityId != null) {
type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
} else {
log.trace("[{}] entity id and type are null. Ignoring this notification", tenantId);
return;
}
if (type == null) {
log.trace("[{}] edge event type is null. Ignoring this notification [{}]", tenantId, entityId);
return;
}
}
TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setType(type.name());
builder.setAction(action.name());
if (entityId != null) {
builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
builder.setEntityType(entityId.getEntityType().name());
}
if (edgeId != null) {
builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
}
if (body != null) {
builder.setBody(body);
}
TransportProtos.EdgeNotificationMsgProto msg = builder.build();
log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId,
TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
tbClusterService.sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, body, type, action);
}
protected List<EdgeId> findRelatedEdgeIds(TenantId tenantId, EntityId entityId) {

2
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -149,7 +149,7 @@ public class CustomerController extends BaseController {
ActionType.DELETED, null, strCustomerId);
sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
tbClusterService.onEntityStateChange(getTenantId(), customerId, ComponentLifecycleEvent.DELETED);
tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), customerId, ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.CUSTOMER),

41
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -36,7 +36,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.data.ClaimRequest;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
@ -60,7 +59,6 @@ import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
@ -134,20 +132,16 @@ public class DeviceController extends BaseController {
try {
device.setTenantId(getCurrentUser().getTenantId());
checkEntity(device.getId(), device, Resource.DEVICE);
Device oldDevice;
Device oldDevice = null;
if (!created) {
oldDevice = deviceService.findDeviceById(getTenantId(), device.getId());
oldDevice = checkDeviceId(device.getId(), Operation.WRITE);
} else {
oldDevice = null;
checkEntity(null, device, Resource.DEVICE);
}
Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
onDeviceCreatedOrUpdated(savedDevice, !created);
otaPackageStateService.update(savedDevice, oldDevice);
onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created);
return savedDevice;
} catch (Exception e) {
@ -158,29 +152,16 @@ public class DeviceController extends BaseController {
}
private void onDeviceCreatedOrUpdated(Device device, boolean updated) {
tbClusterService.onDeviceChange(device, null);
tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(),
device.getId(), device.getName(), device.getType()), null);
tbClusterService.onEntityStateChange(device.getTenantId(), device.getId(), updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED);
if (updated) {
sendEntityNotificationMsg(device.getTenantId(), device.getId(), EdgeEventActionType.UPDATED);
}
private void onDeviceCreatedOrUpdated(Device savedDevice, Device oldDevice, boolean updated) {
tbClusterService.onDeviceUpdated(savedDevice, oldDevice);
try {
logEntityAction(device.getId(), device,
device.getCustomerId(),
logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(),
updated ? ActionType.UPDATED : ActionType.ADDED, null);
} catch (ThingsboardException e) {
log.error("Failed to log entity action", e);
}
if (updated) {
deviceStateService.onDeviceUpdated(device);
} else {
deviceStateService.onDeviceAdded(device);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@ -197,15 +178,12 @@ public class DeviceController extends BaseController {
deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
tbClusterService.onDeviceDeleted(device, null);
tbClusterService.onEntityStateChange(device.getTenantId(), deviceId, ComponentLifecycleEvent.DELETED);
logEntityAction(deviceId, device,
device.getCustomerId(),
ActionType.DELETED, null, strDeviceId);
sendDeleteNotificationMsg(getTenantId(), deviceId, relatedEdgeIds);
deviceStateService.onDeviceDeleted(device);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE),
null,
@ -819,8 +797,7 @@ public class DeviceController extends BaseController {
@PostMapping("/device/bulk_import")
public BulkImportResult<Device> processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception {
return deviceBulkImportService.processBulkImport(request, getCurrentUser(), importedDeviceInfo -> {
onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.isUpdated());
otaPackageStateService.update(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity());
onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated());
});
}

4
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -161,7 +161,7 @@ public class DeviceProfileController extends BaseController {
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile,
@ -191,7 +191,7 @@ public class DeviceProfileController extends BaseController {
deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId);
tbClusterService.onDeviceProfileDelete(deviceProfile, null);
tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
logEntityAction(deviceProfileId, deviceProfile,
null,

12
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -153,7 +153,7 @@ public class EdgeController extends BaseController {
edgeService.assignDefaultRuleChainsToEdge(tenantId, edge.getId());
}
tbClusterService.onEntityStateChange(edge.getTenantId(), edge.getId(),
tbClusterService.broadcastEntityStateChangeEvent(edge.getTenantId(), edge.getId(),
updated ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED);
logEntityAction(edge.getId(), edge, null, updated ? ActionType.UPDATED : ActionType.ADDED, null);
@ -169,7 +169,7 @@ public class EdgeController extends BaseController {
Edge edge = checkEdgeId(edgeId, Operation.DELETE);
edgeService.deleteEdge(getTenantId(), edgeId);
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
ComponentLifecycleEvent.DELETED);
logEntityAction(edgeId, edge,
@ -220,7 +220,7 @@ public class EdgeController extends BaseController {
Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId));
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
ComponentLifecycleEvent.UPDATED);
logEntityAction(edgeId, savedEdge,
@ -254,7 +254,7 @@ public class EdgeController extends BaseController {
Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId));
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
ComponentLifecycleEvent.UPDATED);
logEntityAction(edgeId, edge,
@ -284,7 +284,7 @@ public class EdgeController extends BaseController {
Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId());
Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, publicCustomer.getId()));
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
tbClusterService.broadcastEntityStateChangeEvent(getTenantId(), edgeId,
ComponentLifecycleEvent.UPDATED);
logEntityAction(edgeId, savedEdge,
@ -376,7 +376,7 @@ public class EdgeController extends BaseController {
Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId);
tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
tbClusterService.broadcastEntityStateChangeEvent(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);

15
application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java

@ -24,13 +24,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Resource;
@ -66,22 +64,11 @@ public class Lwm2mController extends BaseController {
checkEntity(device.getId(), device, Resource.DEVICE);
Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials);
checkNotNull(savedDevice);
tbClusterService.onDeviceChange(savedDevice, null);
tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),
device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
tbClusterService.onDeviceUpdated(savedDevice, device);
logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(),
device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (device.getId() == null) {
deviceStateService.onDeviceAdded(savedDevice);
} else {
deviceStateService.onDeviceUpdated(savedDevice);
}
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), device,

55
application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java

@ -0,0 +1,55 @@
/**
* Copyright © 2016-2021 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.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@RestController
@TbCoreComponent
@RequestMapping(TbUrlConstants.RPC_V1_URL_PREFIX)
@Slf4j
public class RpcV1Controller extends AbstractRpcController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
}
}

128
application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java

@ -0,0 +1,128 @@
/**
* Copyright © 2016-2021 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.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.RpcId;
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.common.data.rpc.Rpc;
import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
import org.thingsboard.server.service.security.permission.Operation;
import java.util.UUID;
import static org.thingsboard.server.common.data.DataConstants.RPC_DELETED;
@RestController
@TbCoreComponent
@RequestMapping(TbUrlConstants.RPC_V2_URL_PREFIX)
@Slf4j
public class RpcV2Controller extends AbstractRpcController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleOneWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<ResponseEntity> handleTwoWayDeviceRPCRequest(@PathVariable("deviceId") String deviceIdStr, @RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
@ResponseBody
public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
try {
RpcId rpcId = new RpcId(UUID.fromString(strRpc));
return checkRpcId(rpcId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
@ResponseBody
public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam RpcStatus rpcStatus,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("DeviceId", strDeviceId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
@ResponseBody
public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
try {
RpcId rpcId = new RpcId(UUID.fromString(strRpc));
Rpc rpc = checkRpcId(rpcId, Operation.DELETE);
if (rpc != null) {
if (rpc.getStatus().equals(RpcStatus.QUEUED)) {
RemoveRpcActorMsg removeMsg = new RemoveRpcActorMsg(getTenantId(), rpc.getDeviceId(), rpc.getUuidId());
log.trace("[{}] Forwarding msg {} to queue actor!", rpc.getDeviceId(), rpc);
tbClusterService.pushMsgToCore(removeMsg, null);
}
rpcService.deleteRpc(getTenantId(), rpcId);
TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc));
tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null);
}
} catch (Exception e) {
throw handleException(e);
}
}
}

16
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -149,7 +149,7 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), savedRuleChain.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
}
@ -183,7 +183,7 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName());
tbClusterService.onEntityStateChange(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null);
@ -210,7 +210,7 @@ public class RuleChainController extends BaseController {
if (previousRootRuleChain != null) {
previousRootRuleChain = ruleChainService.findRuleChainById(getTenantId(), previousRootRuleChain.getId());
tbClusterService.onEntityStateChange(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
tbClusterService.broadcastEntityStateChangeEvent(previousRootRuleChain.getTenantId(), previousRootRuleChain.getId(),
ComponentLifecycleEvent.UPDATED);
logEntityAction(previousRootRuleChain.getId(), previousRootRuleChain,
@ -218,7 +218,7 @@ public class RuleChainController extends BaseController {
}
ruleChain = ruleChainService.findRuleChainById(getTenantId(), ruleChainId);
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(),
tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(),
ComponentLifecycleEvent.UPDATED);
logEntityAction(ruleChain.getId(), ruleChain,
@ -254,7 +254,7 @@ public class RuleChainController extends BaseController {
RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()));
if (RuleChainType.CORE.equals(ruleChain.getType())) {
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
}
logEntityAction(ruleChain.getId(), ruleChain,
@ -323,9 +323,9 @@ public class RuleChainController extends BaseController {
if (RuleChainType.CORE.equals(ruleChain.getType())) {
referencingRuleChainIds.forEach(referencingRuleChainId ->
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
}
logEntityAction(ruleChainId, ruleChain,
@ -456,7 +456,7 @@ public class RuleChainController extends BaseController {
List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, RuleChainType.CORE, overwrite);
if (!CollectionUtils.isEmpty(importResults)) {
for (RuleChainImportResult importResult : importResults) {
tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
}
}
} catch (Exception e) {

3
application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java

@ -20,5 +20,6 @@ package org.thingsboard.server.controller;
*/
public class TbUrlConstants {
public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry";
public static final String RPC_URL_PREFIX = "/api/plugins/rpc";
public static final String RPC_V1_URL_PREFIX = "/api/plugins/rpc";
public static final String RPC_V2_URL_PREFIX = "/api/rpc";
}

4
application/src/main/java/org/thingsboard/server/controller/TenantController.java

@ -99,7 +99,7 @@ public class TenantController extends BaseController {
}
tenantProfileCache.evict(tenant.getId());
tbClusterService.onTenantChange(tenant, null);
tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
tbClusterService.broadcastEntityStateChangeEvent(tenant.getId(), tenant.getId(),
newTenant ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
return tenant;
} catch (Exception e) {
@ -118,7 +118,7 @@ public class TenantController extends BaseController {
tenantService.deleteTenant(tenantId);
tenantProfileCache.evict(tenantId);
tbClusterService.onTenantDelete(tenant, null);
tbClusterService.onEntityStateChange(tenantId, tenantId, ComponentLifecycleEvent.DELETED);
tbClusterService.broadcastEntityStateChangeEvent(tenantId, tenantId, ComponentLifecycleEvent.DELETED);
} catch (Exception e) {
throw handleException(e);
}

3
application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java

@ -34,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@ -98,7 +97,7 @@ public class TenantProfileController extends BaseController {
tenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(getTenantId(), tenantProfile));
tenantProfileCache.put(tenantProfile);
tbClusterService.onTenantProfileChange(tenantProfile, null);
tbClusterService.onEntityStateChange(TenantId.SYS_TENANT_ID, tenantProfile.getId(),
tbClusterService.broadcastEntityStateChangeEvent(TenantId.SYS_TENANT_ID, tenantProfile.getId(),
newTenantProfile ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
return tenantProfile;
} catch (Exception e) {

2
application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java

@ -41,7 +41,7 @@ import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import java.util.List;
import java.util.Map;

2
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java

@ -61,7 +61,7 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.telemetry.InternalTelemetryService;
import javax.annotation.PostConstruct;

13
application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java

@ -29,6 +29,7 @@ import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
@ -69,6 +70,8 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
private static final String CLAIM_DATA_ATTRIBUTE_NAME = "claimingData";
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private TbClusterService clusterService;
@Autowired
private DeviceService deviceService;
@Autowired
@ -155,6 +158,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
if (device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
device.setCustomerId(customerId);
Device savedDevice = deviceService.saveDevice(device);
clusterService.onDeviceUpdated(savedDevice, device);
return Futures.transform(removeClaimingSavedData(cache, claimData, device), result -> new ClaimResult(savedDevice, ClaimResponse.SUCCESS), MoreExecutors.directExecutor());
}
return Futures.transform(removeClaimingSavedData(cache, claimData, device), result -> new ClaimResult(null, ClaimResponse.CLAIMED), MoreExecutors.directExecutor());
@ -179,13 +183,14 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
cacheEviction(device.getId());
Customer unassignedCustomer = customerService.findCustomerById(tenantId, device.getCustomerId());
device.setCustomerId(null);
deviceService.saveDevice(device);
Device savedDevice = deviceService.saveDevice(device);
clusterService.onDeviceUpdated(savedDevice, device);
if (isAllowedClaimingByDefault) {
return Futures.immediateFuture(new ReclaimResult(unassignedCustomer));
}
SettableFuture<ReclaimResult> result = SettableFuture.create();
telemetryService.saveAndNotify(
tenantId, device.getId(), DataConstants.SERVER_SCOPE, Collections.singletonList(
tenantId, savedDevice.getId(), DataConstants.SERVER_SCOPE, Collections.singletonList(
new BaseAttributeKvEntry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true), System.currentTimeMillis())
),
new FutureCallback<>() {
@ -198,7 +203,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
public void onFailure(Throwable t) {
result.setException(t);
}
});
});
return result;
}
cacheEviction(device.getId());
@ -238,7 +243,7 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
public void onFailure(Throwable t) {
result.setException(t);
}
});
});
return result;
}

7
application/src/main/java/org/thingsboard/server/service/device/DeviceProvisionServiceImpl.java

@ -24,6 +24,7 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
@ -77,6 +78,9 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
private static final String DEVICE_PROVISION_STATE = "provisionState";
private static final String PROVISIONED_STATE = "provisioned";
@Autowired
TbClusterService clusterService;
@Autowired
DeviceDao deviceDao;
@ -190,8 +194,7 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService {
provisionRequest.setDeviceName(newDeviceName);
}
Device savedDevice = deviceService.saveDevice(provisionRequest, profile);
deviceStateService.onDeviceAdded(savedDevice);
clusterService.onDeviceUpdated(savedDevice, null);
saveProvisionStateAttribute(savedDevice).get();
pushDeviceCreatedEventToRuleEngine(savedDevice);
notify(savedDevice, provisionRequest, DataConstants.PROVISION_SUCCESS, true);

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

@ -16,11 +16,7 @@
package org.thingsboard.server.service.edge;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.edge.Edge;
@ -41,8 +37,7 @@ import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@ -65,9 +60,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Autowired
private TbClusterService clusterService;
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
@Autowired
private EdgeProcessor edgeProcessor;
@ -123,23 +115,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setBody(body);
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
clusterService.onEdgeEventUpdate(tenantId, edgeId);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
}
}, dbCallbackExecutorService);
edgeEventService.save(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edgeId);
}
@Override
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
log.trace("Pushing notification to edge {}", edgeNotificationMsg);
try {
TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());

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

@ -259,7 +259,7 @@ public final class EdgeGrpcSession implements Closeable {
log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg());
}
if (sessionState.getPendingMsgsMap().isEmpty()) {
log.debug("[{}] Pending msgs map is empty. Stopping current iteration {}", edge.getRoutingKey(), msg);
log.debug("[{}] Pending msgs map is empty. Stopping current iteration", edge.getRoutingKey());
if (sessionState.getScheduledSendDownlinkTask() != null) {
sessionState.getScheduledSendDownlinkTask().cancel(false);
}
@ -527,7 +527,7 @@ public final class EdgeGrpcSession implements Closeable {
case RULE_CHAIN_METADATA:
return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType);
case ALARM:
return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType);
return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType, action);
case USER:
return ctx.getUserProcessor().processUserToEdge(edge, edgeEvent, msgType, action);
case RELATION:

52
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeProtoUtils.java

@ -0,0 +1,52 @@
/**
* Copyright © 2016-2021 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;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import com.google.protobuf.BytesValue;
import com.google.protobuf.Int64Value;
import com.google.protobuf.StringValue;
public class EdgeProtoUtils {
private EdgeProtoUtils() {
}
public static BoolValue getBoolValue(Boolean value) {
BoolValue.Builder builder = BoolValue.newBuilder();
builder.setValue(value);
return builder.build();
}
public static StringValue getStringValue(String value) {
StringValue.Builder builder = StringValue.newBuilder();
builder.setValue(value);
return builder.build();
}
public static Int64Value getInt64Value(Long value) {
Int64Value.Builder builder = Int64Value.newBuilder();
builder.setValue(value);
return builder.build();
}
public static BytesValue getBytesValue(ByteString value) {
BytesValue.Builder builder = BytesValue.newBuilder();
builder.setValue(value);
return builder.build();
}
}

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

@ -58,6 +58,8 @@ public class AlarmMsgConstructor {
}
AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(alarm.getId().getId().getMostSignificantBits())
.setIdLSB(alarm.getId().getId().getLeastSignificantBits())
.setName(alarm.getName())
.setType(alarm.getType())
.setOriginatorName(entityName)

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

@ -24,6 +24,9 @@ import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class AssetMsgConstructor {
@ -36,14 +39,14 @@ public class AssetMsgConstructor {
.setName(asset.getName())
.setType(asset.getType());
if (asset.getLabel() != null) {
builder.setLabel(asset.getLabel());
builder.setLabel(getStringValue(asset.getLabel()));
}
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
}
if (asset.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo()));
builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(asset.getAdditionalInfo())));
}
return builder.build();
}

20
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java

@ -23,6 +23,8 @@ import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class CustomerMsgConstructor {
@ -34,31 +36,31 @@ public class CustomerMsgConstructor {
.setIdLSB(customer.getId().getId().getLeastSignificantBits())
.setTitle(customer.getTitle());
if (customer.getCountry() != null) {
builder.setCountry(customer.getCountry());
builder.setCountry(getStringValue(customer.getCountry()));
}
if (customer.getState() != null) {
builder.setState(customer.getState());
builder.setState(getStringValue(customer.getState()));
}
if (customer.getCity() != null) {
builder.setCity(customer.getCity());
builder.setCity(getStringValue(customer.getCity()));
}
if (customer.getAddress() != null) {
builder.setAddress(customer.getAddress());
builder.setAddress(getStringValue(customer.getAddress()));
}
if (customer.getAddress2() != null) {
builder.setAddress2(customer.getAddress2());
builder.setAddress2(getStringValue(customer.getAddress2()));
}
if (customer.getZip() != null) {
builder.setZip(customer.getZip());
builder.setZip(getStringValue(customer.getZip()));
}
if (customer.getPhone() != null) {
builder.setPhone(customer.getPhone());
builder.setPhone(getStringValue(customer.getPhone()));
}
if (customer.getEmail() != null) {
builder.setEmail(customer.getEmail());
builder.setEmail(getStringValue(customer.getEmail()));
}
if (customer.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo()));
builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(customer.getAdditionalInfo())));
}
return builder.build();
}

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

@ -24,6 +24,8 @@ import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
@Component
@TbCoreComponent
public class DashboardMsgConstructor {
@ -36,8 +38,8 @@ public class DashboardMsgConstructor {
.setTitle(dashboard.getTitle())
.setConfiguration(JacksonUtil.toString(dashboard.getConfiguration()));
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
}
return builder.build();
}

19
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java

@ -32,6 +32,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class DeviceMsgConstructor {
@ -46,21 +49,21 @@ public class DeviceMsgConstructor {
.setName(device.getName())
.setType(device.getType());
if (device.getLabel() != null) {
builder.setLabel(device.getLabel());
builder.setLabel(getStringValue(device.getLabel()));
}
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
}
if (device.getDeviceProfileId() != null) {
builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits());
builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits());
builder.setDeviceProfileIdMSB(getInt64Value(device.getDeviceProfileId().getId().getMostSignificantBits()));
builder.setDeviceProfileIdLSB(getInt64Value(device.getDeviceProfileId().getId().getLeastSignificantBits()));
}
if (device.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(device.getAdditionalInfo())));
}
if (conflictName != null) {
builder.setConflictName(conflictName);
builder.setConflictName(getStringValue(conflictName));
}
return builder.build();
}
@ -74,7 +77,7 @@ public class DeviceMsgConstructor {
.setCredentialsId(deviceCredentials.getCredentialsId());
}
if (deviceCredentials.getCredentialsValue() != null) {
builder.setCredentialsValue(deviceCredentials.getCredentialsValue());
builder.setCredentialsValue(getStringValue(deviceCredentials.getCredentialsValue()));
}
return builder.build();
}

13
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceProfileMsgConstructor.java

@ -27,6 +27,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import java.nio.charset.StandardCharsets;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getBytesValue;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class DeviceProfileMsgConstructor {
@ -52,19 +55,19 @@ public class DeviceProfileMsgConstructor {
// builder.setDefaultQueueName(deviceProfile.getDefaultQueueName());
// }
if (deviceProfile.getDescription() != null) {
builder.setDescription(deviceProfile.getDescription());
builder.setDescription(getStringValue(deviceProfile.getDescription()));
}
if (deviceProfile.getTransportType() != null) {
builder.setTransportType(deviceProfile.getTransportType().name());
builder.setTransportType(getStringValue(deviceProfile.getTransportType().name()));
}
if (deviceProfile.getProvisionType() != null) {
builder.setProvisionType(deviceProfile.getProvisionType().name());
builder.setProvisionType(getStringValue(deviceProfile.getProvisionType().name()));
}
if (deviceProfile.getProvisionDeviceKey() != null) {
builder.setProvisionDeviceKey(deviceProfile.getProvisionDeviceKey());
builder.setProvisionDeviceKey(getStringValue(deviceProfile.getProvisionDeviceKey()));
}
if (deviceProfile.getImage() != null) {
builder.setImage(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8)));
builder.setImage(getBytesValue(ByteString.copyFrom(deviceProfile.getImage().getBytes(StandardCharsets.UTF_8))));
}
return builder.build();
}

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

@ -25,6 +25,9 @@ import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class EntityViewMsgConstructor {
@ -51,11 +54,11 @@ public class EntityViewMsgConstructor {
.setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits())
.setEntityType(entityType);
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
}
if (entityView.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo()));
builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(entityView.getAdditionalInfo())));
}
return builder.build();
}

10
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java

@ -22,6 +22,8 @@ import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class RelationMsgConstructor {
@ -35,10 +37,12 @@ public class RelationMsgConstructor {
.setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits())
.setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits())
.setToEntityType(entityRelation.getTo().getEntityType().name())
.setType(entityRelation.getType())
.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo()));
.setType(entityRelation.getType());
if (entityRelation.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo()));
}
if (entityRelation.getTypeGroup() != null) {
builder.setTypeGroup(entityRelation.getTypeGroup().name());
builder.setTypeGroup(getStringValue(entityRelation.getTypeGroup().name()));
}
return builder.build();
}

6
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java

@ -37,6 +37,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.ArrayList;
import java.util.List;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
@Component
@Slf4j
@TbCoreComponent
@ -54,8 +56,8 @@ public class RuleChainMsgConstructor {
.setDebugMode(ruleChain.isDebugMode())
.setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration()));
if (ruleChain.getFirstRuleNodeId() != null) {
builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits())
.setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits());
builder.setFirstRuleNodeIdMSB(getInt64Value(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()))
.setFirstRuleNodeIdLSB(getInt64Value(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()));
}
return builder.build();
}

18
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java

@ -16,16 +16,19 @@
package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getInt64Value;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class UserMsgConstructor {
@ -38,20 +41,17 @@ public class UserMsgConstructor {
.setEmail(user.getEmail())
.setAuthority(user.getAuthority().name());
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
builder.setCustomerIdMSB(getInt64Value(customerId.getId().getMostSignificantBits()));
builder.setCustomerIdLSB(getInt64Value(customerId.getId().getLeastSignificantBits()));
}
if (user.getFirstName() != null) {
builder.setFirstName(user.getFirstName());
builder.setFirstName(getStringValue(user.getFirstName()));
}
if (user.getLastName() != null) {
builder.setLastName(user.getLastName());
}
if (user.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo()));
builder.setLastName(getStringValue(user.getLastName()));
}
if (user.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo()));
builder.setAdditionalInfo(getStringValue(JacksonUtil.toString(user.getAdditionalInfo())));
}
return builder.build();
}

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

@ -16,14 +16,16 @@
package org.thingsboard.server.service.edge.rpc.constructor;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class WidgetTypeMsgConstructor {
@ -33,21 +35,21 @@ public class WidgetTypeMsgConstructor {
.setMsgType(msgType)
.setIdMSB(widgetType.getId().getId().getMostSignificantBits())
.setIdLSB(widgetType.getId().getId().getLeastSignificantBits());
if (widgetType.getBundleAlias() != null) {
builder.setBundleAlias(widgetType.getBundleAlias());
}
if (widgetType.getAlias() != null) {
builder.setAlias(widgetType.getAlias());
}
if (widgetType.getName() != null) {
builder.setName(widgetType.getName());
}
if (widgetType.getDescriptor() != null) {
builder.setDescriptorJson(JacksonUtil.toString(widgetType.getDescriptor()));
}
if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true);
}
if (widgetType.getBundleAlias() != null) {
builder.setBundleAlias(getStringValue(widgetType.getBundleAlias()));
}
if (widgetType.getAlias() != null) {
builder.setAlias(getStringValue(widgetType.getAlias()));
}
if (widgetType.getName() != null) {
builder.setName(getStringValue(widgetType.getName()));
}
if (widgetType.getDescriptor() != null) {
builder.setDescriptorJson(getStringValue(JacksonUtil.toString(widgetType.getDescriptor())));
}
if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true);
}
return builder.build();
}

7
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java

@ -26,6 +26,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import java.nio.charset.StandardCharsets;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getBytesValue;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Component
@TbCoreComponent
public class WidgetsBundleMsgConstructor {
@ -38,10 +41,10 @@ public class WidgetsBundleMsgConstructor {
.setTitle(widgetsBundle.getTitle())
.setAlias(widgetsBundle.getAlias());
if (widgetsBundle.getImage() != null) {
builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8)));
builder.setImage(getBytesValue(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))));
}
if (widgetsBundle.getDescription() != null) {
builder.setDescription(widgetsBundle.getDescription());
builder.setDescription(getStringValue(widgetsBundle.getDescription()));
}
if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true);

109
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmEdgeProcessor.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -54,6 +55,7 @@ public class AlarmEdgeProcessor extends BaseEdgeProcessor {
EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(),
EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
if (originatorId == null) {
log.warn("Originator not found for the alarm msg {}", alarmUpdateMsg);
return Futures.immediateFuture(null);
}
try {
@ -113,59 +115,84 @@ public class AlarmEdgeProcessor extends BaseEdgeProcessor {
}
}
public DownlinkMsg processAlarmToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType) {
public DownlinkMsg processAlarmToEdge(Edge edge, EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
DownlinkMsg downlinkMsg = null;
try {
AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());
Alarm alarm = alarmService.findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
if (alarm != null) {
switch (action) {
case ADDED:
case UPDATED:
case ALARM_ACK:
case ALARM_CLEAR:
try {
Alarm alarm = alarmService.findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();
if (alarm != null) {
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addAlarmUpdateMsg(alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))
.build();
}
} catch (Exception e) {
log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
}
break;
case DELETED:
Alarm alarm = mapper.convertValue(edgeEvent.getBody(), Alarm.class);
AlarmUpdateMsg alarmUpdateMsg =
alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm);
downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addAlarmUpdateMsg(alarmMsgConstructor.constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm))
.addAlarmUpdateMsg(alarmUpdateMsg)
.build();
}
} catch (Exception e) {
log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);
break;
}
return downlinkMsg;
}
public void processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
public void processAlarmNotification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
@Override
public void onSuccess(@Nullable Alarm alarm) {
if (alarm != null) {
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
if (type != null) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId edgeId : pageData.getData()) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.ALARM,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
alarmId,
null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
switch (actionType) {
case DELETED:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
Alarm alarm = mapper.readValue(edgeNotificationMsg.getBody(), Alarm.class);
saveEdgeEvent(tenantId, edgeId, EdgeEventType.ALARM, actionType, alarmId, mapper.valueToTree(alarm));
break;
default:
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
@Override
public void onSuccess(@Nullable Alarm alarm) {
if (alarm != null) {
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
if (type != null) {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
PageData<EdgeId> pageData;
do {
pageData = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator(), pageLink);
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (EdgeId edgeId : pageData.getData()) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.ALARM,
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
alarmId,
null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
}
} while (pageData != null && pageData.hasNext());
}
} while (pageData != null && pageData.hasNext());
}
}
}
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find alarm by id [{}] {}", tenantId.getId(), alarmId.getId(), t);
}
}, dbCallbackExecutorService);
@Override
public void onFailure(Throwable t) {
log.warn("[{}] can't find alarm by id [{}] {}", tenantId.getId(), alarmId.getId(), t);
}
}, dbCallbackExecutorService);
}
}
}

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

@ -17,11 +17,7 @@ package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.edge.Edge;
@ -66,7 +62,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstruc
import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
@Slf4j
@ -178,12 +174,12 @@ public abstract class BaseEdgeProcessor {
@Autowired
protected DbCallbackExecutorService dbCallbackExecutorService;
protected ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
EntityId entityId,
JsonNode body) {
protected void saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
EntityId entityId,
JsonNode body) {
log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " +
"action [{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
@ -197,19 +193,8 @@ public abstract class BaseEdgeProcessor {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setBody(body);
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
}
}, dbCallbackExecutorService);
return future;
edgeEventService.save(edgeEvent);
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
}
protected CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity, Edge edge) {

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

@ -18,7 +18,6 @@ package org.thingsboard.server.service.edge.rpc.processor;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
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.SettableFuture;
@ -27,7 +26,7 @@ import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
@ -60,7 +59,7 @@ 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.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
import java.util.UUID;
@ -105,19 +104,8 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
Device newDevice = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName);
ObjectNode body = mapper.createObjectNode();
body.put("conflictName", deviceName);
ListenableFuture<EdgeEvent> future =
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(EdgeEvent edgeEvent) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null);
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to save ENTITY_MERGE_REQUEST edge event [{}][{}]", tenantId, deviceUpdateMsg, edge.getId(), t);
}
}, dbCallbackExecutorService);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, newDevice.getId(), body);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, newDevice.getId(), null);
}
} while (pageData != null && pageData.hasNext());
} else {
@ -155,7 +143,9 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType()));
deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId());
deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue());
if (deviceCredentialsUpdateMsg.hasCredentialsValue()) {
deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue().getValue());
}
deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials);
} catch (Exception e) {
log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e);
@ -173,14 +163,20 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
if (device != null) {
device.setName(deviceUpdateMsg.getName());
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
if (deviceUpdateMsg.getDeviceProfileIdMSB() != 0 && deviceUpdateMsg.getDeviceProfileIdLSB() != 0) {
if (deviceUpdateMsg.hasLabel()) {
device.setLabel(deviceUpdateMsg.getLabel().getValue());
}
if (deviceUpdateMsg.hasAdditionalInfo()) {
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo().getValue()));
}
if (deviceUpdateMsg.hasDeviceProfileIdMSB() && deviceUpdateMsg.hasDeviceProfileIdLSB()) {
DeviceProfileId deviceProfileId = new DeviceProfileId(
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()));
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB().getValue(),
deviceUpdateMsg.getDeviceProfileIdLSB().getValue()));
device.setDeviceProfileId(deviceProfileId);
}
deviceService.saveDevice(device);
Device savedDevice = deviceService.saveDevice(device);
tbClusterService.onDeviceUpdated(savedDevice, device);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null);
} else {
log.warn("[{}] can't find device [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId());
@ -206,22 +202,26 @@ public class DeviceEdgeProcessor extends BaseEdgeProcessor {
device.setCustomerId(getCustomerId(edge));
device.setName(deviceName);
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
if (deviceUpdateMsg.getDeviceProfileIdMSB() != 0 && deviceUpdateMsg.getDeviceProfileIdLSB() != 0) {
if (deviceUpdateMsg.hasLabel()) {
device.setLabel(deviceUpdateMsg.getLabel().getValue());
}
if (deviceUpdateMsg.hasAdditionalInfo()) {
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo().getValue()));
}
if (deviceUpdateMsg.hasDeviceProfileIdMSB() && deviceUpdateMsg.hasDeviceProfileIdLSB()) {
DeviceProfileId deviceProfileId = new DeviceProfileId(
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB()));
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB().getValue(),
deviceUpdateMsg.getDeviceProfileIdLSB().getValue()));
device.setDeviceProfileId(deviceProfileId);
}
Device savedDevice = deviceService.saveDevice(device, false);
tbClusterService.onDeviceUpdated(savedDevice, device);
if (created) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
deviceStateService.onDeviceAdded(savedDevice);
}
createRelationFromEdge(tenantId, edge.getId(), device.getId());
pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);

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

@ -72,7 +72,9 @@ public class RelationEdgeProcessor extends BaseEdgeProcessor {
entityRelation.setTo(toId);
entityRelation.setType(relationUpdateMsg.getType());
entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()));
if (relationUpdateMsg.hasTypeGroup()) {
entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup().getValue()));
}
entityRelation.setAdditionalInfo(mapper.readTree(relationUpdateMsg.getAdditionalInfo()));
switch (relationUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:

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

@ -73,7 +73,7 @@ import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import java.util.ArrayList;
import java.util.HashMap;
@ -122,26 +122,13 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override
public ListenableFuture<Void> processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
log.trace("[{}] processRuleChainMetadataRequestMsg [{}][{}]", tenantId, edge.getName(), ruleChainMetadataRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
RuleChainId ruleChainId =
new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(),
saveEdgeEvent(tenantId, edge.getId(),
EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't save edge event [{}]", ruleChainMetadataRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
return Futures.immediateFuture(null);
}
@Override
@ -154,8 +141,8 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
if (type != null) {
SettableFuture<Void> futureToSet = SettableFuture.create();
String scope = attributesRequestMsg.getScope();
ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = attributesService.findAll(tenantId, entityId, scope);
Futures.addCallback(ssAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
ListenableFuture<List<AttributeKvEntry>> findAttrFuture = attributesService.findAll(tenantId, entityId, scope);
Futures.addCallback(findAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
@Override
public void onSuccess(@Nullable List<AttributeKvEntry> ssAttributes) {
if (ssAttributes != null && !ssAttributes.isEmpty()) {
@ -184,8 +171,9 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
entityId,
body);
} catch (Exception e) {
log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
throw new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e);
log.error("[{}] Failed to save attribute updates to the edge", edge.getName(), e);
futureToSet.setException(new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e));
return;
}
} else {
log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId,
@ -198,7 +186,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override
public void onFailure(Throwable t) {
log.error("Can't save attributes [{}]", attributesRequestMsg, t);
log.error("Can't find attributes [{}]", attributesRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
@ -273,82 +261,39 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
@Override
public ListenableFuture<Void> processDeviceCredentialsRequestMsg(TenantId tenantId, Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) {
log.trace("[{}] processDeviceCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), deviceCredentialsRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) {
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE,
EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't save edge event [{}]", deviceCredentialsRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
return Futures.immediateFuture(null);
}
@Override
public ListenableFuture<Void> processUserCredentialsRequestMsg(TenantId tenantId, Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) {
log.trace("[{}] processUserCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), userCredentialsRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) {
UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
EdgeEventActionType.CREDENTIALS_UPDATED, userId, null);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't save edge event [{}]", userCredentialsRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
return Futures.immediateFuture(null);
}
@Override
public ListenableFuture<Void> processDeviceProfileDevicesRequestMsg(TenantId tenantId, Edge edge, DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg) {
log.trace("[{}] processDeviceProfileDevicesRequestMsg [{}][{}]", tenantId, edge.getName(), deviceProfileDevicesRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB() != 0 && deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB() != 0) {
DeviceProfileId deviceProfileId = new DeviceProfileId(new UUID(deviceProfileDevicesRequestMsg.getDeviceProfileIdMSB(), deviceProfileDevicesRequestMsg.getDeviceProfileIdLSB()));
DeviceProfile deviceProfileById = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
List<ListenableFuture<EdgeEvent>> futures;
if (deviceProfileById != null) {
futures = syncDevices(tenantId, edge, deviceProfileById.getName());
} else {
futures = new ArrayList<>();
syncDevices(tenantId, edge, deviceProfileById.getName());
}
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<EdgeEvent> result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't sync devices by device profile [{}]", deviceProfileDevicesRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
return Futures.immediateFuture(null);
}
private List<ListenableFuture<EdgeEvent>> syncDevices(TenantId tenantId, Edge edge, String deviceType) {
List<ListenableFuture<EdgeEvent>> futures = new ArrayList<>();
private void syncDevices(TenantId tenantId, Edge edge, String deviceType) {
log.trace("[{}] syncDevices [{}][{}]", tenantId, edge.getName(), deviceType);
try {
PageLink pageLink = new PageLink(DEFAULT_PAGE_SIZE);
@ -358,7 +303,7 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Device device : pageData.getData()) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null));
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -368,40 +313,25 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
} catch (Exception e) {
log.error("Exception during loading edge device(s) on sync!", e);
}
return futures;
}
@Override
public ListenableFuture<Void> processWidgetBundleTypesRequestMsg(TenantId tenantId, Edge edge,
WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg) {
log.trace("[{}] processWidgetBundleTypesRequestMsg [{}][{}]", tenantId, edge.getName(), widgetBundleTypesRequestMsg);
SettableFuture<Void> futureToSet = SettableFuture.create();
if (widgetBundleTypesRequestMsg.getWidgetBundleIdMSB() != 0 && widgetBundleTypesRequestMsg.getWidgetBundleIdLSB() != 0) {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(new UUID(widgetBundleTypesRequestMsg.getWidgetBundleIdMSB(), widgetBundleTypesRequestMsg.getWidgetBundleIdLSB()));
WidgetsBundle widgetsBundleById = widgetsBundleService.findWidgetsBundleById(tenantId, widgetsBundleId);
List<ListenableFuture<EdgeEvent>> futures = new ArrayList<>();
if (widgetsBundleById != null) {
List<WidgetType> widgetTypesToPush =
widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundleById.getTenantId(), widgetsBundleById.getAlias());
for (WidgetType widgetType : widgetTypesToPush) {
futures.add(saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null));
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null);
}
}
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<EdgeEvent> result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't sync widget types by widget bundle [{}]", widgetBundleTypesRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
return Futures.immediateFuture(null);
}
@Override
@ -416,9 +346,12 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
public void onSuccess(@Nullable List<EntityView> entityViews) {
try {
if (entityViews != null && !entityViews.isEmpty()) {
List<ListenableFuture<Boolean>> futures = new ArrayList<>();
for (EntityView entityView : entityViews) {
Futures.addCallback(relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE), new FutureCallback<>() {
ListenableFuture<Boolean> future = relationService.checkRelation(tenantId, edge.getId(), entityView.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
futures.add(future);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (Boolean.TRUE.equals(result)) {
@ -426,16 +359,27 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
EdgeEventActionType.ADDED, entityView.getId(), null);
}
}
@Override
public void onFailure(Throwable t) {
log.error("Exception during loading relation [{}] to edge on sync!", t, t);
futureToSet.setException(t);
// Do nothing - error handles in allAsList
}
}, dbCallbackExecutorService);
}
Futures.addCallback(Futures.allAsList(futures), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<Boolean> result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Exception during loading relation [{}] to edge on sync!", t, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
} else {
futureToSet.set(null);
}
futureToSet.set(null);
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e);
futureToSet.setException(e);
@ -451,30 +395,19 @@ public class DefaultEdgeRequestsService implements EdgeRequestsService {
return futureToSet;
}
private ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
EntityId entityId,
JsonNode body) {
private void saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
EdgeEventActionType action,
EntityId entityId,
JsonNode body) {
log.trace("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = EdgeEventUtils.constructEdgeEvent(tenantId, edgeId, type, action, entityId, body);
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
Futures.addCallback(future, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
}
}, dbCallbackExecutorService);
return future;
edgeEventService.save(edgeEvent);
tbClusterService.onEdgeEventUpdate(tenantId, edgeId);
}
}

2
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -215,6 +215,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
node.put("password", "");
node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself)
node.put("enableProxy", false);
node.put("showChangePassword", false);
mailSettings.setJsonValue(node);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
}
@ -480,6 +481,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
device.setAdditionalInfo(additionalInfo);
}
device = deviceService.saveDevice(device);
//TODO: No access to cluster service, so we should manually update the status of device.
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, device.getId());
deviceCredentials.setCredentialsId(accessToken);
deviceCredentialsService.updateDeviceCredentials(TenantId.SYS_TENANT_ID, deviceCredentials);

8
application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.ota;
import com.google.common.util.concurrent.FutureCallback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
@ -49,7 +50,7 @@ import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import javax.annotation.Nullable;
import java.util.ArrayList;
@ -87,10 +88,11 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService {
private final RuleEngineTelemetryService telemetryService;
private final TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> otaPackageStateMsgProducer;
public DefaultOtaPackageStateService(TbClusterService tbClusterService, OtaPackageService otaPackageService,
public DefaultOtaPackageStateService(@Lazy TbClusterService tbClusterService,
OtaPackageService otaPackageService,
DeviceService deviceService,
DeviceProfileService deviceProfileService,
RuleEngineTelemetryService telemetryService,
@Lazy RuleEngineTelemetryService telemetryService,
TbCoreQueueFactory coreQueueFactory) {
this.tbClusterService = tbClusterService;
this.otaPackageService = otaPackageService;

122
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java

@ -16,11 +16,17 @@
package org.thingsboard.server.service.queue;
import com.google.protobuf.ByteString;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
@ -29,7 +35,6 @@ import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
@ -56,8 +61,9 @@ import org.thingsboard.server.queue.common.MultipleTbQueueCallbackWrapper;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import java.util.HashSet;
import java.util.Set;
@ -66,10 +72,13 @@ import java.util.concurrent.atomic.AtomicInteger;
@Service
@Slf4j
@RequiredArgsConstructor
public class DefaultTbClusterService implements TbClusterService {
@Value("${cluster.stats.enabled:false}")
private boolean statsEnabled;
@Value("${edges.enabled}")
protected boolean edgesEnabled;
private final AtomicInteger toCoreMsgs = new AtomicInteger(0);
private final AtomicInteger toCoreNfs = new AtomicInteger(0);
@ -81,13 +90,7 @@ public class DefaultTbClusterService implements TbClusterService {
private final PartitionService partitionService;
private final DataDecodingEncodingService encodingService;
private final TbDeviceProfileCache deviceProfileCache;
public DefaultTbClusterService(TbQueueProducerProvider producerProvider, PartitionService partitionService, DataDecodingEncodingService encodingService, TbDeviceProfileCache deviceProfileCache) {
this.producerProvider = producerProvider;
this.partitionService = partitionService;
this.encodingService = encodingService;
this.deviceProfileCache = deviceProfileCache;
}
private final OtaPackageStateService otaPackageStateService;
@Override
public void pushMsgToCore(TenantId tenantId, EntityId entityId, ToCoreMsg msg, TbQueueCallback callback) {
@ -200,55 +203,52 @@ public class DefaultTbClusterService implements TbClusterService {
}
@Override
public void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
public void broadcastEntityStateChangeEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
log.trace("[{}] Processing {} state change event: {}", tenantId, entityId.getEntityType(), state);
broadcast(new ComponentLifecycleMsg(tenantId, entityId, state));
}
@Override
public void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback) {
onEntityChange(deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile, callback);
broadcastEntityChangeToTransport(deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile, callback);
}
@Override
public void onTenantProfileChange(TenantProfile tenantProfile, TbQueueCallback callback) {
onEntityChange(TenantId.SYS_TENANT_ID, tenantProfile.getId(), tenantProfile, callback);
broadcastEntityChangeToTransport(TenantId.SYS_TENANT_ID, tenantProfile.getId(), tenantProfile, callback);
}
@Override
public void onTenantChange(Tenant tenant, TbQueueCallback callback) {
onEntityChange(TenantId.SYS_TENANT_ID, tenant.getId(), tenant, callback);
broadcastEntityChangeToTransport(TenantId.SYS_TENANT_ID, tenant.getId(), tenant, callback);
}
@Override
public void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback) {
onEntityChange(apiUsageState.getTenantId(), apiUsageState.getId(), apiUsageState, callback);
broadcastEntityChangeToTransport(apiUsageState.getTenantId(), apiUsageState.getId(), apiUsageState, callback);
broadcast(new ComponentLifecycleMsg(apiUsageState.getTenantId(), apiUsageState.getId(), ComponentLifecycleEvent.UPDATED));
}
@Override
public void onDeviceProfileDelete(DeviceProfile entity, TbQueueCallback callback) {
onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback);
broadcastEntityDeleteToTransport(entity.getTenantId(), entity.getId(), entity.getName(), callback);
}
@Override
public void onTenantProfileDelete(TenantProfile entity, TbQueueCallback callback) {
onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
broadcastEntityDeleteToTransport(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
}
@Override
public void onTenantDelete(Tenant entity, TbQueueCallback callback) {
onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
broadcastEntityDeleteToTransport(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback);
}
@Override
public void onDeviceChange(Device entity, TbQueueCallback callback) {
onEntityChange(entity.getTenantId(), entity.getId(), entity, callback);
}
@Override
public void onDeviceDeleted(Device entity, TbQueueCallback callback) {
onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback);
public void onDeviceDeleted(Device device, TbQueueCallback callback) {
broadcastEntityDeleteToTransport(device.getTenantId(), device.getId(), device.getName(), callback);
sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), false, false, true);
broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), ComponentLifecycleEvent.DELETED);
}
@Override
@ -278,7 +278,7 @@ public class DefaultTbClusterService implements TbClusterService {
broadcast(transportMsg, callback);
}
public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
public <T> void broadcastEntityChangeToTransport(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) {
String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName();
log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName);
TransportProtos.EntityUpdateMsg entityUpdateMsg = TransportProtos.EntityUpdateMsg.newBuilder()
@ -288,7 +288,7 @@ public class DefaultTbClusterService implements TbClusterService {
broadcast(transportMsg, callback);
}
private void onEntityDelete(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) {
private void broadcastEntityDeleteToTransport(TenantId tenantId, EntityId entityId, String name, TbQueueCallback callback) {
log.trace("[{}][{}][{}] Processing [{}] delete event", tenantId, entityId.getEntityType(), entityId.getId(), name);
TransportProtos.EntityDeleteMsg entityDeleteMsg = TransportProtos.EntityDeleteMsg.newBuilder()
.setEntityType(entityId.getEntityType().name())
@ -369,4 +369,72 @@ public class DefaultTbClusterService implements TbClusterService {
}
}
}
private void sendDeviceStateServiceEvent(TenantId tenantId, DeviceId deviceId, boolean added, boolean updated, boolean deleted) {
TransportProtos.DeviceStateServiceMsgProto.Builder builder = TransportProtos.DeviceStateServiceMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setDeviceIdMSB(deviceId.getId().getMostSignificantBits());
builder.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits());
builder.setAdded(added);
builder.setUpdated(updated);
builder.setDeleted(deleted);
TransportProtos.DeviceStateServiceMsgProto msg = builder.build();
pushMsgToCore(tenantId, deviceId, TransportProtos.ToCoreMsg.newBuilder().setDeviceStateServiceMsg(msg).build(), null);
}
@Override
public void onDeviceUpdated(Device device, Device old) {
var created = old == null;
broadcastEntityChangeToTransport(device.getTenantId(), device.getId(), device, null);
if (old != null && (!device.getName().equals(old.getName()) || !device.getType().equals(old.getType()))) {
pushMsgToCore(new DeviceNameOrTypeUpdateMsg(device.getTenantId(), device.getId(), device.getName(), device.getType()), null);
}
broadcastEntityStateChangeEvent(device.getTenantId(), device.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
sendDeviceStateServiceEvent(device.getTenantId(), device.getId(), created, !created, false);
otaPackageStateService.update(device, old);
if (!created) {
sendNotificationMsgToEdgeService(device.getTenantId(), null, device.getId(), null, null, EdgeEventActionType.UPDATED);
}
}
@Override
public void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
if (!edgesEnabled) {
return;
}
if (type == null) {
if (entityId != null) {
type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
} else {
log.trace("[{}] entity id and type are null. Ignoring this notification", tenantId);
return;
}
if (type == null) {
log.trace("[{}] edge event type is null. Ignoring this notification [{}]", tenantId, entityId);
return;
}
}
TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setType(type.name());
builder.setAction(action.name());
if (entityId != null) {
builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
builder.setEntityType(entityId.getEntityType().name());
}
if (edgeId != null) {
builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
}
if (body != null) {
builder.setBody(body);
}
TransportProtos.EdgeNotificationMsgProto msg = builder.build();
log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
pushMsgToCore(tenantId, entityId != null ? entityId : tenantId, TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
}
}

4
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -26,7 +26,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.TenantId;
@ -64,7 +64,7 @@ import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
import org.thingsboard.server.service.queue.processing.IdMsgPair;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
import org.thingsboard.server.service.state.DeviceStateService;

4
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbMsg;
@ -55,7 +55,7 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStr
import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory;
import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy;
import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
import org.thingsboard.server.service.stats.RuleEngineStatisticsService;

13
application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbCoreDeviceRpcService.java

@ -22,18 +22,19 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
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.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.annotation.PostConstruct;
@ -138,6 +139,12 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
}
}
@Override
public void processRemoveRpc(RemoveRpcActorMsg removeRpcMsg) {
log.trace("[{}][{}] Processing remove RPC [{}]", removeRpcMsg.getTenantId(), removeRpcMsg.getRequestId(), removeRpcMsg.getDeviceId());
actorContext.tellWithHighPriority(removeRpcMsg);
}
private void sendRpcResponseToTbRuleEngine(String originServiceId, FromDeviceRpcResponse response) {
if (serviceId.equals(originServiceId)) {
if (tbRuleEngineRpcService.isPresent()) {
@ -168,6 +175,8 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
entityNode.put("method", msg.getBody().getMethod());
entityNode.put("params", msg.getBody().getParams());
entityNode.put(DataConstants.ADDITIONAL_INFO, msg.getAdditionalInfo());
try {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE, msg.getDeviceId(), currentUser.getCustomerId(), metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
clusterService.pushMsgToRuleEngine(msg.getTenantId(), msg.getDeviceId(), tbMsg, null);

7
application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java

@ -19,18 +19,19 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.rpc.RpcError;
import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse;
import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.util.TbRuleEngineComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@ -100,7 +101,7 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
@Override
public void sendRpcRequestToDevice(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), src.getTenantId(), src.getDeviceId(),
src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted());
src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()), src.isPersisted(), src.getAdditionalInfo());
forwardRpcRequestToDeviceActor(request, response -> {
if (src.isRestApiCall()) {
sendRpcResponseToTbCore(src.getOriginServiceId(), response);

3
application/src/main/java/org/thingsboard/server/service/rpc/FromDeviceRpcResponseActorMsg.java

@ -18,10 +18,11 @@ package org.thingsboard.server.service.rpc;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
@ToString
@RequiredArgsConstructor

44
application/src/main/java/org/thingsboard/server/service/rpc/RemoveRpcActorMsg.java

@ -0,0 +1,44 @@
/**
* Copyright © 2016-2021 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.rpc;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import java.util.UUID;
@ToString
@RequiredArgsConstructor
public class RemoveRpcActorMsg implements ToDeviceActorNotificationMsg {
@Getter
private final TenantId tenantId;
@Getter
private final DeviceId deviceId;
@Getter
private final UUID requestId;
@Override
public MsgType getMsgType() {
return MsgType.REMOVE_RPC_TO_DEVICE_ACTOR_MSG;
}
}

3
application/src/main/java/org/thingsboard/server/service/rpc/TbCoreDeviceRpcService.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.rpc;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -55,4 +56,6 @@ public interface TbCoreDeviceRpcService {
*/
void processRpcResponseFromDeviceActor(FromDeviceRpcResponse response);
void processRemoveRpc(RemoveRpcActorMsg removeRpcMsg);
}

2
application/src/main/java/org/thingsboard/server/service/rpc/TbRpcService.java

@ -31,7 +31,7 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.rpc.RpcService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
@TbCoreComponent
@Service

1
application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java

@ -16,6 +16,7 @@
package org.thingsboard.server.service.rpc;
import org.thingsboard.rule.engine.api.RuleEngineRpcService;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
/**
* Created by ashvayka on 16.04.18.

2
application/src/main/java/org/thingsboard/server/service/rpc/ToDeviceRpcRequestActorMsg.java

@ -18,7 +18,7 @@ package org.thingsboard.server.service.rpc;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.MsgType;

4
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java

@ -47,7 +47,7 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
@ -180,7 +180,7 @@ public abstract class AbstractOAuth2ClientMapper {
installScripts.createDefaultEdgeRuleChains(tenant.getId());
tenantProfileCache.evict(tenant.getId());
tbClusterService.onTenantChange(tenant, null);
tbClusterService.onEntityStateChange(tenant.getId(), tenant.getId(),
tbClusterService.broadcastEntityStateChangeEvent(tenant.getId(), tenant.getId(),
ComponentLifecycleEvent.CREATED);
} else {
tenant = tenants.get(0);

11
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.security.auth.oauth2;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
@ -29,6 +30,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
@Service(value = "customOAuth2ClientMapper")
@ -40,6 +42,15 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
@PostConstruct
public void init() {
// Register time module to parse Instant objects.
// com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
// Java 8 date/time type `java.time.Instant` not supported by default:
// add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
json.registerModule(new JavaTimeModule());
}
@Override
public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
OAuth2MapperConfig config = registration.getMapperConfig();

4
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.service.security.auth.oauth2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
@ -42,6 +43,7 @@ import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Slf4j
@Component(value = "oauth2AuthenticationSuccessHandler")
public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@ -99,6 +101,8 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
clearAuthenticationAttributes(request, response);
getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken());
} catch (Exception e) {
log.debug("Error occurred during processing authentication success result. " +
"request [{}], response [{}], authentication [{}]", request, response, authentication, e);
clearAuthenticationAttributes(request, response);
String errorPrefix;
if (!StringUtils.isEmpty(callbackUrlScheme)) {

113
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -22,7 +22,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -59,7 +58,7 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.annotation.Nonnull;
@ -71,7 +70,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
@ -137,7 +135,6 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
private ExecutorService deviceStateExecutor;
private final ConcurrentMap<TopicPartitionInfo, Set<DeviceId>> partitionedDevices = new ConcurrentHashMap<>();
final ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
private final ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>();
final Queue<Set<TopicPartitionInfo>> subscribeQueue = new ConcurrentLinkedQueue<>();
@ -177,22 +174,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
}
@Override
public void onDeviceAdded(Device device) {
sendDeviceEvent(device.getTenantId(), device.getId(), true, false, false);
}
@Override
public void onDeviceUpdated(Device device) {
sendDeviceEvent(device.getTenantId(), device.getId(), false, true, false);
}
@Override
public void onDeviceDeleted(Device device) {
sendDeviceEvent(device.getTenantId(), device.getId(), false, false, true);
}
@Override
public void onDeviceConnect(DeviceId deviceId) {
public void onDeviceConnect(TenantId tenantId, DeviceId deviceId) {
log.trace("on Device Connect [{}]", deviceId.getId());
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
long ts = System.currentTimeMillis();
@ -200,23 +182,23 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
save(deviceId, LAST_CONNECT_TIME, ts);
pushRuleEngineMessage(stateData, CONNECT_EVENT);
checkAndUpdateState(deviceId, stateData);
cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
}
@Override
public void onDeviceActivity(DeviceId deviceId, long lastReportedActivity) {
public void onDeviceActivity(TenantId tenantId, DeviceId deviceId, long lastReportedActivity) {
log.trace("on Device Activity [{}], lastReportedActivity [{}]", deviceId.getId(), lastReportedActivity);
long lastSavedActivity = deviceLastSavedActivity.getOrDefault(deviceId, 0L);
if (lastReportedActivity > 0 && lastReportedActivity > lastSavedActivity) {
final DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
final DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (lastReportedActivity > 0 && lastReportedActivity > stateData.getState().getLastActivityTime()) {
updateActivityState(deviceId, stateData, lastReportedActivity);
}
cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
}
void updateActivityState(DeviceId deviceId, DeviceStateData stateData, long lastReportedActivity) {
log.trace("updateActivityState - fetched state {} for device {}, lastReportedActivity {}", stateData, deviceId, lastReportedActivity);
if (stateData != null) {
save(deviceId, LAST_ACTIVITY_TIME, lastReportedActivity);
deviceLastSavedActivity.put(deviceId, lastReportedActivity);
DeviceState state = stateData.getState();
state.setLastActivityTime(lastReportedActivity);
if (!state.isActive()) {
@ -225,21 +207,23 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
}
} else {
log.warn("updateActivityState - fetched state IN NULL for device {}, lastReportedActivity {}", deviceId, lastReportedActivity);
log.debug("updateActivityState - fetched state IN NULL for device {}, lastReportedActivity {}", deviceId, lastReportedActivity);
cleanUpDeviceStateMap(deviceId);
}
}
@Override
public void onDeviceDisconnect(DeviceId deviceId) {
public void onDeviceDisconnect(TenantId tenantId, DeviceId deviceId) {
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
long ts = System.currentTimeMillis();
stateData.getState().setLastDisconnectTime(ts);
save(deviceId, LAST_DISCONNECT_TIME, ts);
pushRuleEngineMessage(stateData, DISCONNECT_EVENT);
cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
}
@Override
public void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout) {
public void onDeviceInactivityTimeoutUpdate(TenantId tenantId, DeviceId deviceId, long inactivityTimeout) {
if (inactivityTimeout <= 0L) {
return;
}
@ -247,6 +231,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
stateData.getState().setInactivityTimeout(inactivityTimeout);
checkAndUpdateState(deviceId, stateData);
cleanDeviceStateIfBelongsExternalPartition(tenantId, deviceId);
}
@Override
@ -267,6 +252,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, device.getId());
if (partitionedDevices.containsKey(tpi)) {
addDeviceUsingState(tpi, state);
save(deviceId, ACTIVITY_STATE, false);
callback.onSuccess();
} else {
log.warn("[{}][{}] Device belongs to external partition. Probably rebalancing is in progress. Topic: {}"
@ -283,12 +269,11 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
}, deviceStateExecutor);
} else if (proto.getUpdated()) {
DeviceStateData stateData = getOrFetchDeviceStateData(device.getId());
if (stateData != null) {
TbMsgMetaData md = new TbMsgMetaData();
md.putValue("deviceName", device.getName());
md.putValue("deviceType", device.getType());
stateData.setMetaData(md);
}
TbMsgMetaData md = new TbMsgMetaData();
md.putValue("deviceName", device.getName());
md.putValue("deviceType", device.getType());
stateData.setMetaData(md);
callback.onSuccess();
}
} else {
//Device was probably deleted while message was in queue;
@ -356,10 +341,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
// We no longer manage current partition of devices;
removedPartitions.forEach(partition -> {
Set<DeviceId> devices = partitionedDevices.remove(partition);
devices.forEach(deviceId -> {
deviceStates.remove(deviceId);
deviceLastSavedActivity.remove(deviceId);
});
devices.forEach(this::cleanUpDeviceStateMap);
});
addedPartitions.forEach(tpi -> partitionedDevices.computeIfAbsent(tpi, key -> ConcurrentHashMap.newKeySet()));
@ -457,17 +439,18 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
deviceStates.put(state.getDeviceId(), state);
} else {
log.warn("Device belongs to external partition {}" + tpi.getFullTopicName());
new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!");
throw new RuntimeException("Device belongs to external partition " + tpi.getFullTopicName() + "!");
}
}
void updateInactivityStateIfExpired() {
final long ts = System.currentTimeMillis();
log.debug("Calculating state updates for {} devices", deviceStates.size());
Set<DeviceId> deviceIds = new HashSet<>(deviceStates.keySet());
for (DeviceId deviceId : deviceIds) {
updateInactivityStateIfExpired(ts, deviceId);
}
partitionedDevices.forEach((tpi, deviceIds) -> {
log.debug("Calculating state updates. tpi {} for {} devices", tpi.getFullTopicName(), deviceIds.size());
for (DeviceId deviceId : deviceIds) {
updateInactivityStateIfExpired(ts, deviceId);
}
});
}
void updateInactivityStateIfExpired(long ts, DeviceId deviceId) {
@ -488,8 +471,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
}
} else {
log.debug("[{}] Device that belongs to other server is detected and removed.", deviceId);
deviceStates.remove(deviceId);
deviceLastSavedActivity.remove(deviceId);
cleanUpDeviceStateMap(deviceId);
}
}
@ -522,27 +504,26 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
}
}
private void sendDeviceEvent(TenantId tenantId, DeviceId deviceId, boolean added, boolean updated, boolean deleted) {
TransportProtos.DeviceStateServiceMsgProto.Builder builder = TransportProtos.DeviceStateServiceMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setDeviceIdMSB(deviceId.getId().getMostSignificantBits());
builder.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits());
builder.setAdded(added);
builder.setUpdated(updated);
builder.setDeleted(deleted);
TransportProtos.DeviceStateServiceMsgProto msg = builder.build();
clusterService.pushMsgToCore(tenantId, deviceId, TransportProtos.ToCoreMsg.newBuilder().setDeviceStateServiceMsg(msg).build(), null);
private void cleanDeviceStateIfBelongsExternalPartition(TenantId tenantId, final DeviceId deviceId) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
if (!partitionedDevices.containsKey(tpi)) {
cleanUpDeviceStateMap(deviceId);
log.debug("[{}][{}] device belongs to external partition. Probably rebalancing is in progress. Topic: {}"
, tenantId, deviceId, tpi.getFullTopicName());
}
}
private void onDeviceDeleted(TenantId tenantId, DeviceId deviceId) {
deviceStates.remove(deviceId);
deviceLastSavedActivity.remove(deviceId);
cleanUpDeviceStateMap(deviceId);
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, deviceId);
Set<DeviceId> deviceIdSet = partitionedDevices.get(tpi);
deviceIdSet.remove(deviceId);
}
private void cleanUpDeviceStateMap(DeviceId deviceId) {
deviceStates.remove(deviceId);
}
private ListenableFuture<DeviceStateData> fetchDeviceState(Device device) {
ListenableFuture<DeviceStateData> future;
if (persistToTelemetry) {
@ -573,7 +554,7 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
}
private <T extends KvEntry> Function<List<T>, DeviceStateData> extractDeviceStateData(Device device) {
return new Function<List<T>, DeviceStateData>() {
return new Function<>() {
@Nonnull
@Override
public DeviceStateData apply(@Nullable List<T> data) {
@ -660,9 +641,9 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
tsSubService.saveAndNotifyInternal(
TenantId.SYS_TENANT_ID, deviceId,
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))),
new AttributeSaveCallback<>(deviceId, key, value));
new TelemetrySaveCallback<>(deviceId, key, value));
} else {
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback<>(deviceId, key, value));
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value));
}
}
@ -671,18 +652,18 @@ public class DefaultDeviceStateService extends TbApplicationEventListener<Partit
tsSubService.saveAndNotifyInternal(
TenantId.SYS_TENANT_ID, deviceId,
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))),
new AttributeSaveCallback<>(deviceId, key, value));
new TelemetrySaveCallback<>(deviceId, key, value));
} else {
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback<>(deviceId, key, value));
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE, key, value, new TelemetrySaveCallback<>(deviceId, key, value));
}
}
private static class AttributeSaveCallback<T> implements FutureCallback<T> {
private static class TelemetrySaveCallback<T> implements FutureCallback<T> {
private final DeviceId deviceId;
private final String key;
private final Object value;
AttributeSaveCallback(DeviceId deviceId, String key, Object value) {
TelemetrySaveCallback(DeviceId deviceId, String key, Object value) {
this.deviceId = deviceId;
this.key = key;
this.value = value;

15
application/src/main/java/org/thingsboard/server/service/state/DeviceStateService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.state;
import org.springframework.context.ApplicationListener;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.common.msg.queue.TbCallback;
@ -27,19 +28,13 @@ import org.thingsboard.server.common.msg.queue.TbCallback;
*/
public interface DeviceStateService extends ApplicationListener<PartitionChangeEvent> {
void onDeviceAdded(Device device);
void onDeviceConnect(TenantId tenantId, DeviceId deviceId);
void onDeviceUpdated(Device device);
void onDeviceActivity(TenantId tenantId, DeviceId deviceId, long lastReportedActivityTime);
void onDeviceDeleted(Device device);
void onDeviceDisconnect(TenantId tenantId, DeviceId deviceId);
void onDeviceConnect(DeviceId deviceId);
void onDeviceActivity(DeviceId deviceId, long lastReportedActivityTime);
void onDeviceDisconnect(DeviceId deviceId);
void onDeviceInactivityTimeoutUpdate(DeviceId deviceId, long inactivityTimeout);
void onDeviceInactivityTimeoutUpdate(TenantId tenantId, DeviceId deviceId, long inactivityTimeout);
void onQueueMsg(TransportProtos.DeviceStateServiceMsgProto proto, TbCallback bytes);

10
application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java

@ -53,7 +53,7 @@ import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
@ -224,7 +224,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
return subscriptionUpdate;
});
if (entityId.getEntityType() == EntityType.DEVICE) {
updateDeviceInactivityTimeout(entityId, ts);
updateDeviceInactivityTimeout(tenantId, entityId, ts);
}
callback.onSuccess();
}
@ -259,7 +259,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
});
if (entityId.getEntityType() == EntityType.DEVICE) {
if (TbAttributeSubscriptionScope.SERVER_SCOPE.name().equalsIgnoreCase(scope)) {
updateDeviceInactivityTimeout(entityId, attributes);
updateDeviceInactivityTimeout(tenantId, entityId, attributes);
} else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) {
clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId,
new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes))
@ -269,10 +269,10 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene
callback.onSuccess();
}
private void updateDeviceInactivityTimeout(EntityId entityId, List<? extends KvEntry> kvEntries) {
private void updateDeviceInactivityTimeout(TenantId tenantId, EntityId entityId, List<? extends KvEntry> kvEntries) {
for (KvEntry kvEntry : kvEntries) {
if (kvEntry.getKey().equals(DefaultDeviceStateService.INACTIVITY_TIMEOUT)) {
deviceStateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), kvEntry.getLongValue().orElse(0L));
deviceStateService.onDeviceInactivityTimeoutUpdate(tenantId, new DeviceId(entityId.getId()), kvEntry.getLongValue().orElse(0L));
}
}
}

3
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate;
import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate;
@ -42,7 +42,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
@TbCoreComponent

4
application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java

@ -20,15 +20,13 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.EventListener;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService;
import javax.annotation.Nullable;

2
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java

@ -46,7 +46,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.subscription.SubscriptionManagerService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;

2
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -45,7 +45,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import javax.annotation.Nullable;

10
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -95,7 +95,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService;
import org.thingsboard.server.service.state.DeviceStateService;
@ -265,9 +265,11 @@ public class DefaultTransportApiService implements TransportApiService {
device.setCustomerId(gateway.getCustomerId());
DeviceProfile deviceProfile = deviceProfileCache.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType());
device.setDeviceProfileId(deviceProfile.getId());
device = deviceService.saveDevice(device);
Device savedDevice = deviceService.saveDevice(device);
tbClusterService.onDeviceUpdated(savedDevice, device);
device = savedDevice;
relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
deviceStateService.onDeviceAdded(device);
TbMsgMetaData metaData = new TbMsgMetaData();
CustomerId customerId = gateway.getCustomerId();
@ -581,7 +583,7 @@ public class DefaultTransportApiService implements TransportApiService {
device.setName(deviceName);
device.setType("LwM2M");
device = deviceService.saveDevice(device);
deviceStateService.onDeviceAdded(device);
tbClusterService.onDeviceUpdated(device, null);
}
TransportProtos.LwM2MRegistrationResponseMsg registrationResponseMsg =
TransportProtos.LwM2MRegistrationResponseMsg.newBuilder()

9
application/src/main/resources/thingsboard.yml

@ -588,6 +588,7 @@ transport:
bind_address: "${MQTT_BIND_ADDRESS:0.0.0.0}"
bind_port: "${MQTT_BIND_PORT:1883}"
timeout: "${MQTT_TIMEOUT:10000}"
msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism
netty:
leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}"
boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
@ -747,6 +748,8 @@ queue:
compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"
max.in.flight.requests.per.connection: "${TB_KAFKA_MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION:5}"
buffer.memory: "${TB_BUFFER_MEMORY:33554432}"
replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}"
max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:300000}"
@ -763,7 +766,11 @@ queue:
tb_ota_package:
- key: max.poll.records
value: 10
other:
other: # In this section you can specify custom parameters for Kafka consumer/producer and expose the env variables to configure outside
- key: "request.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/producer-configs.html#producerconfigs_request.timeout.ms
value: "${TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS:30000}" # (30 seconds)
- key: "session.timeout.ms" # refer to https://docs.confluent.io/platform/current/installation/configuration/consumer-configs.html#consumerconfigs_session.timeout.ms
value: "${TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS:10000}" # (10 seconds)
topic-properties:
rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"

33
application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java

@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -28,13 +29,14 @@ import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.Authority;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -83,34 +85,37 @@ public class BaseEdgeEventControllerTest extends AbstractControllerTest {
Device device = constructDevice("TestDevice", "default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/edge/" + edge.getId().toString() + "/device/" + savedDevice.getId().toString(), Device.class);
final EdgeId edgeId = edge.getId();
doPost("/api/edge/" + edgeId.toString() + "/device/" + savedDevice.getId().toString(), Device.class);
Asset asset = constructAsset("TestAsset", "default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/edge/" + edge.getId().toString() + "/asset/" + savedAsset.getId().toString(), Asset.class);
doPost("/api/edge/" + edgeId.toString() + "/asset/" + savedAsset.getId().toString(), Asset.class);
EntityRelation relation = new EntityRelation(savedAsset.getId(), savedDevice.getId(), EntityRelation.CONTAINS_TYPE);
doPost("/api/relation", relation);
// wait while edge event for the relation entity persisted to DB
Thread.sleep(100);
List<EdgeEvent> edgeEvents;
int attempt = 1;
do {
edgeEvents = doGetTypedWithTimePageLink("/api/edge/" + edge.getId().toString() + "/events?",
new TypeReference<PageData<EdgeEvent>>() {}, new TimePageLink(4)).getData();
attempt++;
Thread.sleep(100);
} while (edgeEvents.size() != 4 && attempt < 5);
Assert.assertEquals(4, edgeEvents.size());
Awaitility.await()
.atMost(30, TimeUnit.SECONDS)
.until(() -> {
List<EdgeEvent> edgeEvents = findEdgeEvents(edgeId);
return edgeEvents.size() == 4;
});
List<EdgeEvent> edgeEvents = findEdgeEvents(edgeId);
Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.RULE_CHAIN.equals(ee.getType())));
Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.DEVICE.equals(ee.getType())));
Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.ASSET.equals(ee.getType())));
Assert.assertTrue(edgeEvents.stream().anyMatch(ee -> EdgeEventType.RELATION.equals(ee.getType())));
}
private List<EdgeEvent> findEdgeEvents(EdgeId edgeId) throws Exception {
return doGetTypedWithTimePageLink("/api/edge/" + edgeId.toString() + "/events?",
new TypeReference<PageData<EdgeEvent>>() {
}, new TimePageLink(10)).getData();
}
private Device constructDevice(String name, String type) {
Device device = new Device();
device.setName(name);

62
application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java

@ -27,6 +27,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.awaitility.Awaitility;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@ -114,7 +115,7 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import java.util.ArrayList;
import java.util.List;
@ -126,6 +127,7 @@ import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.service.edge.rpc.EdgeProtoUtils.getStringValue;
@Slf4j
abstract public class BaseEdgeTest extends AbstractControllerTest {
@ -648,7 +650,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits());
Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits());
Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name());
Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name());
Assert.assertEquals(relationUpdateMsg.getTypeGroup().getValue(), relation.getTypeGroup().name());
// 2
edgeImitator.expectMessageAmount(1);
@ -672,7 +674,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(relationUpdateMsg.getFromIdMSB(), relation.getFrom().getId().getMostSignificantBits());
Assert.assertEquals(relationUpdateMsg.getToIdLSB(), relation.getTo().getId().getLeastSignificantBits());
Assert.assertEquals(relationUpdateMsg.getToEntityType(), relation.getTo().getEntityType().name());
Assert.assertEquals(relationUpdateMsg.getTypeGroup(), relation.getTypeGroup().name());
Assert.assertEquals(relationUpdateMsg.getTypeGroup().getValue(), relation.getTypeGroup().name());
log.info("Relations tested successfully");
}
@ -730,7 +732,15 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
edgeImitator.expectMessageAmount(1);
doDelete("/api/alarm/" + savedAlarm.getId().getId().toString())
.andExpect(status().isOk());
Assert.assertFalse(edgeImitator.waitForMessages(1));
Assert.assertTrue(edgeImitator.waitForMessages(1));
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof AlarmUpdateMsg);
alarmUpdateMsg = (AlarmUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, alarmUpdateMsg.getMsgType());
Assert.assertEquals(alarmUpdateMsg.getType(), savedAlarm.getType());
Assert.assertEquals(alarmUpdateMsg.getName(), savedAlarm.getName());
Assert.assertEquals(alarmUpdateMsg.getOriginatorName(), device.getName());
Assert.assertEquals(alarmUpdateMsg.getStatus(), AlarmStatus.CLEARED_ACK.name());
log.info("Alarms tested successfully");
}
@ -900,9 +910,9 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, widgetTypeUpdateMsg.getMsgType());
Assert.assertEquals(widgetTypeUpdateMsg.getIdMSB(), savedWidgetType.getUuidId().getMostSignificantBits());
Assert.assertEquals(widgetTypeUpdateMsg.getIdLSB(), savedWidgetType.getUuidId().getLeastSignificantBits());
Assert.assertEquals(widgetTypeUpdateMsg.getAlias(), savedWidgetType.getAlias());
Assert.assertEquals(widgetTypeUpdateMsg.getName(), savedWidgetType.getName());
Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson()), savedWidgetType.getDescriptor());
Assert.assertEquals(widgetTypeUpdateMsg.getAlias().getValue(), savedWidgetType.getAlias());
Assert.assertEquals(widgetTypeUpdateMsg.getName().getValue(), savedWidgetType.getName());
Assert.assertEquals(JacksonUtil.toJsonNode(widgetTypeUpdateMsg.getDescriptorJson().getValue()), savedWidgetType.getDescriptor());
// 3
edgeImitator.expectMessageAmount(1);
@ -939,7 +949,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
String timeseriesData = "{\"data\":{\"temperature\":25},\"ts\":" + System.currentTimeMillis() + "}";
JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
edgeEventService.saveAsync(edgeEvent);
edgeEventService.save(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -978,7 +988,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
JsonNode attributesEntityData = mapper.readTree(attributesData);
EdgeEvent edgeEvent1 = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_UPDATED, device.getId().getId(), EdgeEventType.DEVICE, attributesEntityData);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent1);
edgeEventService.save(edgeEvent1);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -1003,7 +1013,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
JsonNode postAttributesEntityData = mapper.readTree(postAttributesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.POST_ATTRIBUTES, device.getId().getId(), EdgeEventType.DEVICE, postAttributesEntityData);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent);
edgeEventService.save(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -1028,7 +1038,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
JsonNode deleteAttributesEntityData = mapper.readTree(deleteAttributesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.ATTRIBUTES_DELETED, device.getId().getId(), EdgeEventType.DEVICE, deleteAttributesEntityData);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent);
edgeEventService.save(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -1062,7 +1072,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.RPC_CALL, device.getId().getId(), EdgeEventType.DEVICE, body);
edgeImitator.expectMessageAmount(1);
edgeEventService.saveAsync(edgeEvent);
edgeEventService.save(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
Assert.assertTrue(edgeImitator.waitForMessages());
@ -1088,7 +1098,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
JsonNode timeseriesEntityData = mapper.readTree(timeseriesData);
EdgeEvent edgeEvent = constructEdgeEvent(tenantId, edge.getId(), EdgeEventActionType.TIMESERIES_UPDATED,
device.getId().getId(), EdgeEventType.DEVICE, timeseriesEntityData);
edgeEventService.saveAsync(edgeEvent);
edgeEventService.save(edgeEvent);
clusterService.onEdgeEventUpdate(tenantId, edge.getId());
}
@ -1202,7 +1212,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertTrue(latestMessage instanceof DeviceUpdateMsg);
DeviceUpdateMsg latestDeviceUpdateMsg = (DeviceUpdateMsg) latestMessage;
Assert.assertNotEquals(deviceOnCloudName, latestDeviceUpdateMsg.getName());
Assert.assertEquals(deviceOnCloudName, latestDeviceUpdateMsg.getConflictName());
Assert.assertEquals(deviceOnCloudName, latestDeviceUpdateMsg.getConflictName().getValue());
UUID newDeviceId = new UUID(latestDeviceUpdateMsg.getIdMSB(), latestDeviceUpdateMsg.getIdLSB());
@ -1269,7 +1279,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
EntityId toEntityId = EntityIdFactory.getByTypeAndUuid(relationUpdateMsg.getToEntityType(), toUUID);
Assert.assertEquals(relation.getTo(), toEntityId);
Assert.assertEquals(relation.getTypeGroup().name(), relationUpdateMsg.getTypeGroup());
Assert.assertEquals(relation.getTypeGroup().name(), relationUpdateMsg.getTypeGroup().getValue());
}
private void sendAlarm() throws Exception {
@ -1348,15 +1358,11 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
edgeImitator.sendUplinkMsg(uplinkMsgBuilder2.build());
Assert.assertTrue(edgeImitator.waitForResponses());
int attempt = 0;
Map<String, List<Map<String, String>>> timeseries;
do {
timeseries = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey,
new TypeReference<>() {});
// Wait before device attributes saved to database before requesting them from controller
Thread.sleep(100);
attempt++;
} while (!timeseries.containsKey(timeseriesKey) || attempt < 10);
Awaitility.await()
.atMost(2, TimeUnit.SECONDS)
.until(() -> loadDeviceTimeseries(device, timeseriesKey).containsKey(timeseriesKey));
Map<String, List<Map<String, String>>> timeseries = loadDeviceTimeseries(device, timeseriesKey);
Assert.assertTrue(timeseries.containsKey(timeseriesKey));
Assert.assertEquals(1, timeseries.get(timeseriesKey).size());
Assert.assertEquals(timeseriesValue, timeseries.get(timeseriesKey).get(0).get("value"));
@ -1365,7 +1371,11 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
Assert.assertEquals(1, attributes.size());
Assert.assertEquals(attributes.get(0).get("key"), attributesKey);
Assert.assertEquals(attributes.get(0).get("value"), attributesValue);
}
private Map<String, List<Map<String, String>>> loadDeviceTimeseries(Device device, String timeseriesKey) throws Exception {
return doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getUuidId() + "/values/timeseries?keys=" + timeseriesKey,
new TypeReference<>() {});
}
private void sendRelation() throws Exception {
@ -1381,7 +1391,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
RelationUpdateMsg.Builder relationUpdateMsgBuilder = RelationUpdateMsg.newBuilder();
relationUpdateMsgBuilder.setType("test");
relationUpdateMsgBuilder.setTypeGroup(RelationTypeGroup.COMMON.name());
relationUpdateMsgBuilder.setTypeGroup(getStringValue(RelationTypeGroup.COMMON.name()));
relationUpdateMsgBuilder.setToIdMSB(device1.getId().getId().getMostSignificantBits());
relationUpdateMsgBuilder.setToIdLSB(device1.getId().getId().getLeastSignificantBits());
relationUpdateMsgBuilder.setToEntityType(device1.getId().getEntityType().name());
@ -1447,7 +1457,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());;
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof RuleChainMetadataUpdateMsg);

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

@ -27,7 +27,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.cluster.TbClusterService;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

8
application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcDefaultIntegrationTest.java

@ -43,7 +43,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(504),
asyncContextTimeoutToUseRpcPlugin);
}
@ -52,7 +52,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
String nonExistentDeviceId = Uuids.timeBased().toString();
String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
String result = doPostAsync("/api/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
status().isNotFound());
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
}
@ -62,7 +62,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),
doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(504),
asyncContextTimeoutToUseRpcPlugin);
}
@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
String nonExistentDeviceId = Uuids.timeBased().toString();
String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
String result = doPostAsync("/api/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
status().isNotFound());
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
}

6
application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java

@ -71,7 +71,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
@ -99,14 +99,14 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
validateTwoWayStateChangedNotification(callback, 1, expectedResponseResult, actualResult);
latch = new CountDownLatch(1);
actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
actualResult = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
validateTwoWayStateChangedNotification(callback, 2, expectedResponseResult, actualResult);

3
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -27,6 +27,7 @@ import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
@ -261,7 +262,7 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
@Before
public void beforeTest() throws Exception {
executor = Executors.newScheduledThreadPool(10);
executor = Executors.newScheduledThreadPool(10, ThingsBoardThreadFactory.forName("test-lwm2m-scheduled"));
loginTenantAdmin();
String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"};

104
application/src/test/java/org/thingsboard/server/transport/lwm2m/NoSecLwM2MIntegrationTest.java

@ -16,6 +16,8 @@
package org.thingsboard.server.transport.lwm2m;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.thingsboard.server.common.data.Device;
@ -23,16 +25,18 @@ import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCr
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.is;
import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;
@ -43,8 +47,10 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDA
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED;
@Slf4j
public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
public static final int TIMEOUT = 30;
private final String OTA_TRANSPORT_CONFIGURATION = "{\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
@ -122,6 +128,15 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
" \"type\": \"LWM2M\"\n" +
"}";
LwM2MTestClient client = null;
@After
public void tearDown() {
if (client != null) {
client.destroy();
}
}
@Test
public void testConnectAndObserveTelemetry() throws Exception {
NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
@ -196,37 +211,68 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
}
}
/**
* This is the example how to use the AWAITILITY instead Thread.sleep()
* Test will finish as fast as possible, but will await until TIMEOUT if a build machine is busy or slow
* Check the detailed log output to learn how Awaitility polling the API and when exactly expected result appears
* */
@Test
public void testSoftwareUpdateByObject9() throws Exception {
LwM2MTestClient client = null;
try {
createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint("OTA_" + ENDPOINT);
Device device = createDevice(clientCredentials);
device.setSoftwareId(createSoftware().getId());
device = doPost("/api/device", device, Device.class);
//given
final List<OtaPackageUpdateStatus> expectedStatuses = Collections.unmodifiableList(Arrays.asList(
QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED));
Thread.sleep(1000);
client = new LwM2MTestClient(executor, "OTA_" + ENDPOINT);
client.init(SECURITY, COAP_CONFIG);
Thread.sleep(3000);
List<TsKvEntry> ts = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + device.getId().getId() + "/values/timeseries?orderBy=ASC&keys=sw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() {
}));
List<OtaPackageUpdateStatus> statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList());
createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
clientCredentials.setEndpoint("OTA_" + ENDPOINT);
final Device device = createDevice(clientCredentials);
device.setSoftwareId(createSoftware().getId());
log.warn("Saving by API " + device);
final Device savedDevice = doPost("/api/device", device, Device.class);
Assert.assertNotNull(savedDevice);
log.warn("Device saved by API {}", savedDevice);
log.warn("AWAIT atMost {} SECONDS on get device by API...", TIMEOUT);
await()
.atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getDeviceFromAPI(device.getId().getId()), is(savedDevice));
log.warn("Got device by API.");
//when
log.warn("Init the client...");
client = new LwM2MTestClient(executor, "OTA_" + ENDPOINT);
client.init(SECURITY, COAP_CONFIG);
log.warn("Init done");
log.warn("AWAIT atMost {} SECONDS on timeseries List<TsKvEntry> by API with list size {}...", TIMEOUT, expectedStatuses.size());
await()
.atMost(30, TimeUnit.SECONDS)
.until(() -> getSwStateTelemetryFromAPI(device.getId().getId())
.size(), is(expectedStatuses.size()));
log.warn("Got an expected await condition!");
//then
log.warn("Fetching ts for the final asserts");
List<TsKvEntry> ts = getSwStateTelemetryFromAPI(device.getId().getId());
log.warn("Got an ts {}", ts);
List<OtaPackageUpdateStatus> statuses = ts.stream().sorted(Comparator.comparingLong(TsKvEntry::getTs)).map(KvEntry::getValueAsString).map(OtaPackageUpdateStatus::valueOf).collect(Collectors.toList());
log.warn("Converted ts to statuses {}", statuses);
Assert.assertEquals(expectedStatuses, statuses);
}
List<OtaPackageUpdateStatus> expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADING, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATED);
private Device getDeviceFromAPI(UUID deviceId) throws Exception {
final Device device = doGet("/api/device/" + deviceId, Device.class);
log.warn("Fetched device by API for deviceId {}, device is {}", deviceId, device);
return device;
}
Assert.assertEquals(expectedStatuses, statuses);
} finally {
if (client != null) {
client.destroy();
}
}
private List<TsKvEntry> getSwStateTelemetryFromAPI(UUID deviceId) throws Exception {
final List<TsKvEntry> tsKvEntries = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?orderBy=ASC&keys=sw_state&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() {
}));
log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries);
return tsKvEntries;
}
}

8
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcDefaultIntegrationTest.java

@ -46,7 +46,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}";
String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409),
doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(504),
asyncContextTimeoutToUseRpcPlugin);
}
@ -55,7 +55,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
String nonExistentDeviceId = Uuids.timeBased().toString();
String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
String result = doPostAsync("/api/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
status().isNotFound());
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
}
@ -65,7 +65,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}";
String deviceId = savedDevice.getId().getId().toString();
doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409),
doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(504),
asyncContextTimeoutToUseRpcPlugin);
}
@ -74,7 +74,7 @@ public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends Ab
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
String nonExistentDeviceId = Uuids.timeBased().toString();
String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
String result = doPostAsync("/api/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
status().isNotFound());
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result);
}

8
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java

@ -69,7 +69,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
Assert.assertTrue(StringUtils.isEmpty(result));
latch.await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
@ -95,7 +95,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String expected = "{\"value1\":\"A\",\"value2\":\"B\"}";
latch.await(3, TimeUnit.SECONDS);
Assert.assertEquals(expected, result);
@ -130,7 +130,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
String result = doPostAsync("/api/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
Assert.assertTrue(StringUtils.isEmpty(result));
latch.await(3, TimeUnit.SECONDS);
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
@ -156,7 +156,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
String expected = "{\"success\":true}";
assertEquals(expected, result);

2
application/src/test/java/org/thingsboard/server/transport/mqtt/rpc/AbstractMqttServerSideRpcProtoIntegrationTest.java

@ -131,7 +131,7 @@ public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends Abst
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String result = doPostAsync("/api/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
latch.await(3, TimeUnit.SECONDS);
Assert.assertEquals(expected, result);

144
common/cluster-api/pom.xml

@ -0,0 +1,144 @@
<!--
Copyright © 2016-2021 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.3.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>
<artifactId>cluster-api</artifactId>
<packaging>jar</packaging>
<name>Thingsboard Server Common Cluster API</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>data</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>message</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>stats</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>com.github.fge</groupId>
<artifactId>json-schema-validator</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jmx</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>false</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

15
application/src/main/java/org/thingsboard/server/service/queue/TbClusterService.java → common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java

@ -13,26 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.queue;
package org.thingsboard.server.cluster;
import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import java.util.UUID;
@ -54,7 +57,7 @@ public interface TbClusterService {
void pushNotificationToTransport(String targetServiceId, ToTransportMsg response, TbQueueCallback callback);
void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
void broadcastEntityStateChangeEvent(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback);
@ -70,7 +73,7 @@ public interface TbClusterService {
void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback);
void onDeviceChange(Device device, TbQueueCallback callback);
void onDeviceUpdated(Device device, Device old);
void onDeviceDeleted(Device device, TbQueueCallback callback);
@ -79,4 +82,6 @@ public interface TbClusterService {
void onResourceDeleted(TbResource resource, TbQueueCallback callback);
void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId);
void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action);
}

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueAdmin.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueCallback.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueCallback.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueConsumer.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueHandler.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueHandler.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsg.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsg.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsgDecoder.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgDecoder.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsgHeaders.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgHeaders.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsgMetadata.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueMsgMetadata.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueProducer.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueProducer.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueRequestTemplate.java

0
common/queue/src/main/java/org/thingsboard/server/queue/TbQueueResponseTemplate.java → common/cluster-api/src/main/java/org/thingsboard/server/queue/TbQueueResponseTemplate.java

0
common/queue/src/main/proto/jsinvoke.proto → common/cluster-api/src/main/proto/jsinvoke.proto

0
common/queue/src/main/proto/queue.proto → common/cluster-api/src/main/proto/queue.proto

4
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -54,6 +54,8 @@ public interface DeviceService {
Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials);
Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
void createAccessTokenCredentials(Device device, String accessToken);
Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
@ -100,8 +102,6 @@ public interface DeviceService {
Device assignDeviceToTenant(TenantId tenantId, Device device);
Device saveDevice(ProvisionRequest provisionRequest, DeviceProfile profile);
PageData<UUID> findDevicesIdsByDeviceProfileTransportType(DeviceTransportType transportType, PageLink pageLink);
Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save