Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard

pull/4687/head
Igor Kulikov 5 years ago
parent
commit
1d0755f101
  1. 21
      application/src/main/data/json/demo/dashboards/firmware.json
  2. 14
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  3. 77
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  4. 5
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  5. 254
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  6. 19
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  7. 2
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  8. 222
      application/src/main/java/org/thingsboard/server/controller/FirmwareController.java
  9. 222
      application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
  10. 256
      application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java
  11. 136
      application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java
  12. 8
      application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java
  13. 31
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  14. 16
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  15. 24
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  16. 2
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  17. 2
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  18. 68
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  19. 6
      application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java
  20. 110
      application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java
  21. 2
      application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java
  22. 2
      application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java
  23. 2
      application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java
  24. 22
      application/src/main/resources/thingsboard.yml
  25. 115
      application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java
  26. 4
      application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java
  27. 68
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesIntegrationTest.java
  28. 5
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesJsonIntegrationTest.java
  29. 53
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java
  30. 2
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcDefaultIntegrationTest.java
  31. 108
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcIntegrationTest.java
  32. 2
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcJsonIntegrationTest.java
  33. 39
      application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcProtoIntegrationTest.java
  34. 13
      common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java
  35. 8
      common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java
  36. 19
      common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java
  37. 5
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  38. 52
      common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java
  39. 52
      common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java
  40. 3
      common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
  41. 1
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  42. 16
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  43. 8
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  44. 2
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  45. 8
      common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java
  46. 10
      common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java
  47. 40
      common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java
  48. 8
      common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java
  49. 1
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  50. 25
      common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java
  51. 4
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  52. 10
      common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java
  53. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java
  54. 6
      common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java
  55. 6
      common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java
  56. 4
      common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java
  57. 49
      common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java
  58. 7
      common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java
  59. 1
      common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java
  60. 2
      common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java
  61. 10
      common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java
  62. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java
  63. 8
      common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java
  64. 22
      common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java
  65. 22
      common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java
  66. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java
  67. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java
  68. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java
  69. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java
  70. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java
  71. 12
      common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java
  72. 6
      common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java
  73. 4
      common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java
  74. 4
      common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java
  75. 20
      common/queue/src/main/proto/queue.proto
  76. 15
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/AbstractCoapTransportResource.java
  77. 5
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java
  78. 144
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  79. 1
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java
  80. 1
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java
  81. 4
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java
  82. 37
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java
  83. 34
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java
  84. 103
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/NoSecObserveClient.java
  85. 2
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java
  86. 35
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  87. 351
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
  88. 98
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java
  89. 6
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java
  90. 5
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java
  91. 272
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  92. 11
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java
  93. 149
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java
  94. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java
  95. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java
  96. 20
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  97. 91
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java
  98. 279
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java
  99. 15
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java
  100. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java

21
application/src/main/data/json/demo/dashboards/firmware.json

@ -1,5 +1,6 @@
{
"title": "Firmware",
"image": null,
"configuration": {
"description": "",
"widgets": {
@ -247,7 +248,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -257,7 +258,7 @@
"name": "Download firware",
"icon": "file_download",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0"
},
{
@ -1021,7 +1022,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1031,7 +1032,7 @@
"name": "Download firware",
"icon": "file_download",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0"
},
{
@ -1297,7 +1298,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1307,7 +1308,7 @@
"name": "Download firware",
"icon": "file_download",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0"
},
{
@ -1573,7 +1574,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1583,7 +1584,7 @@
"name": "Download firware",
"icon": "file_download",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0"
},
{
@ -1849,7 +1850,7 @@
"name": "Edit firmware",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <tb-firmware-autocomplete\n [useFullEntityId]=\"true\"\n formControlName=\"firmwareId\">\n </tb-firmware-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit firmware {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div *ngIf=\"entity.deviceProfileId\" mat-dialog-content fxLayout=\"column\">\n <tb-ota-package-autocomplete\n [useFullEntityId]=\"true\"\n [deviceProfileId]=\"entity.deviceProfileId.id\"\n formControlName=\"firmwareId\">\n </tb-ota-package-autocomplete>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n </div>\n</form>",
"customCss": "",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}",
"customResources": [],
@ -1859,7 +1860,7 @@
"name": "Download firware",
"icon": "file_download",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}",
"id": "12533058-42f6-e75f-620c-219c48d01ec0"
},
{

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

@ -59,8 +59,8 @@ CREATE TABLE IF NOT EXISTS resource (
CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)
);
CREATE TABLE IF NOT EXISTS firmware (
id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY,
CREATE TABLE IF NOT EXISTS ota_package (
id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid NOT NULL,
device_profile_id uuid,
@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS firmware (
data_size bigint,
additional_info varchar,
search_text varchar(255),
CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version)
);
ALTER TABLE dashboard
@ -101,13 +101,13 @@ DO $$
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN
ALTER TABLE device_profile
ADD CONSTRAINT fk_firmware_device_profile
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN
ALTER TABLE device_profile
ADD CONSTRAINT fk_software_device_profile
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN
@ -119,13 +119,13 @@ DO $$
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN
ALTER TABLE device
ADD CONSTRAINT fk_firmware_device
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN
ALTER TABLE device
ADD CONSTRAINT fk_software_device
FOREIGN KEY (firmware_id) REFERENCES firmware(id);
FOREIGN KEY (firmware_id) REFERENCES ota_package(id);
END IF;
END;
$$;

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

@ -317,25 +317,48 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
int requestId = request.getRequestId();
Futures.addCallback(getAttributesKvEntries(request), new FutureCallback<List<List<AttributeKvEntry>>>() {
@Override
public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setRequestId(requestId)
.addAllClientAttributeList(toTsKvProtos(result.get(0)))
.addAllSharedAttributeList(toTsKvProtos(result.get(1)))
.build();
sendToTransport(responseMsg, sessionInfo);
}
if (request.getOnlyShared()) {
Futures.addCallback(findAllAttributesByScope(DataConstants.SHARED_SCOPE), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<AttributeKvEntry> result) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setRequestId(requestId)
.setSharedStateMsg(true)
.addAllSharedAttributeList(toTsKvProtos(result))
.build();
sendToTransport(responseMsg, sessionInfo);
}
@Override
public void onFailure(Throwable t) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setError(t.getMessage())
.build();
sendToTransport(responseMsg, sessionInfo);
}
}, MoreExecutors.directExecutor());
@Override
public void onFailure(Throwable t) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setError(t.getMessage())
.setSharedStateMsg(true)
.build();
sendToTransport(responseMsg, sessionInfo);
}
}, MoreExecutors.directExecutor());
} else {
Futures.addCallback(getAttributesKvEntries(request), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setRequestId(requestId)
.addAllClientAttributeList(toTsKvProtos(result.get(0)))
.addAllSharedAttributeList(toTsKvProtos(result.get(1)))
.build();
sendToTransport(responseMsg, sessionInfo);
}
@Override
public void onFailure(Throwable t) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setError(t.getMessage())
.build();
sendToTransport(responseMsg, sessionInfo);
}
}, MoreExecutors.directExecutor());
}
}
private ListenableFuture<List<List<AttributeKvEntry>>> getAttributesKvEntries(GetAttributeRequestMsg request) {
@ -403,9 +426,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
}
if (hasNotificationData) {
AttributeUpdateNotificationMsg finalNotification = notification.build();
attributeSubscriptions.entrySet().forEach(sub -> {
sendToTransport(finalNotification, sub.getKey(), sub.getValue().getNodeId());
});
attributeSubscriptions.forEach((key, value) -> sendToTransport(finalNotification, key, value.getNodeId()));
}
} else {
log.debug("[{}] No registered attributes subscriptions to process!", deviceId);
@ -475,7 +496,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {
UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);
if (sessionIdToRemove != null) {
notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove), "max concurrent sessions limit reached per device!");
}
}
sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
@ -521,7 +542,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
notifyTransportAboutProfileUpdate(k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials());
});
} else {
sessions.forEach(this::notifyTransportAboutClosedSession);
sessions.forEach((sessionId, sessionMd) -> notifyTransportAboutClosedSession(sessionId, sessionMd, "device credentials updated!"));
attributeSubscriptions.clear();
rpcSubscriptions.clear();
dumpSessions();
@ -529,11 +550,15 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
}
}
private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) {
private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) {
SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto
.newBuilder()
.setMessage(message).build();
ToTransportMsg msg = ToTransportMsg.newBuilder()
.setSessionIdMSB(sessionId.getMostSignificantBits())
.setSessionIdLSB(sessionId.getLeastSignificantBits())
.setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build();
.setSessionCloseNotification(sessionCloseNotificationProto)
.build();
systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
}
@ -741,7 +766,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
sessions.remove(sessionId);
rpcSubscriptions.remove(sessionId);
attributeSubscriptions.remove(sessionId);
notifyTransportAboutClosedSession(sessionId, sessionMD);
notifyTransportAboutClosedSession(sessionId, sessionMD, "session timeout!");
});
if (!sessionsToRemove.isEmpty()) {
dumpSessions();

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

@ -110,8 +110,11 @@ public class AlarmController extends BaseController {
checkParameter(ALARM_ID, strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
checkAlarmId(alarmId, Operation.WRITE);
Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
logEntityAction(alarm.getOriginator(), alarm,
getCurrentUser().getCustomerId(),
ActionType.ALARM_DELETE, null);
sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED);
return alarmService.deleteAlarm(getTenantId(), alarmId);

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

@ -17,7 +17,6 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -31,7 +30,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
@ -39,8 +37,8 @@ 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;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -70,7 +68,7 @@ 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.id.EntityViewId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
@ -79,10 +77,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -94,9 +88,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
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.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
@ -110,7 +101,7 @@ import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
@ -127,8 +118,9 @@ 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;
import org.thingsboard.server.service.action.RuleEngineEntityActionService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.firmware.FirmwareStateService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
@ -147,11 +139,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -250,10 +240,10 @@ public abstract class BaseController {
protected TbResourceService resourceService;
@Autowired
protected FirmwareService firmwareService;
protected OtaPackageService otaPackageService;
@Autowired
protected FirmwareStateService firmwareStateService;
protected OtaPackageStateService otaPackageStateService;
@Autowired
protected TbQueueProducerProvider producerProvider;
@ -279,6 +269,9 @@ public abstract class BaseController {
@Autowired(required = false)
protected EdgeGrpcService edgeGrpcService;
@Autowired
protected RuleEngineEntityActionService ruleEngineEntityActionService;
@Value("${server.log_controller_error_stack_trace}")
@Getter
private boolean logControllerErrorStackTrace;
@ -511,8 +504,8 @@ public abstract class BaseController {
case TB_RESOURCE:
checkResourceId(new TbResourceId(entityId.getId()), operation);
return;
case FIRMWARE:
checkFirmwareId(new FirmwareId(entityId.getId()), operation);
case OTA_PACKAGE:
checkOtaPackageId(new OtaPackageId(entityId.getId()), operation);
return;
default:
throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType());
@ -769,25 +762,25 @@ public abstract class BaseController {
}
}
Firmware checkFirmwareId(FirmwareId firmwareId, Operation operation) throws ThingsboardException {
OtaPackage checkOtaPackageId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException {
try {
validateId(firmwareId, "Incorrect firmwareId " + firmwareId);
Firmware firmware = firmwareService.findFirmwareById(getCurrentUser().getTenantId(), firmwareId);
checkNotNull(firmware);
accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmware);
return firmware;
validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId);
OtaPackage otaPackage = otaPackageService.findOtaPackageById(getCurrentUser().getTenantId(), otaPackageId);
checkNotNull(otaPackage);
accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackage);
return otaPackage;
} catch (Exception e) {
throw handleException(e, false);
}
}
FirmwareInfo checkFirmwareInfoId(FirmwareId firmwareId, Operation operation) throws ThingsboardException {
OtaPackageInfo checkOtaPackageInfoId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException {
try {
validateId(firmwareId, "Incorrect firmwareId " + firmwareId);
FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(getCurrentUser().getTenantId(), firmwareId);
checkNotNull(firmwareInfo);
accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmwareInfo);
return firmwareInfo;
validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId);
OtaPackageInfo otaPackageIn = otaPackageService.findOtaPackageInfoById(getCurrentUser().getTenantId(), otaPackageId);
checkNotNull(otaPackageIn);
accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackageIn);
return otaPackageIn;
} catch (Exception e) {
throw handleException(e, false);
}
@ -809,7 +802,7 @@ public abstract class BaseController {
customerId = user.getCustomerId();
}
if (e == null) {
pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo);
ruleEngineEntityActionService.pushEntityActionToRuleEngine(entityId, entity, user.getTenantId(), customerId, actionType, user, additionalInfo);
}
auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo);
}
@ -819,184 +812,6 @@ public abstract class BaseController {
return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null;
}
private <E extends HasName, I extends EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId,
ActionType actionType, Object... additionalInfo) {
String msgType = null;
switch (actionType) {
case ADDED:
msgType = DataConstants.ENTITY_CREATED;
break;
case DELETED:
msgType = DataConstants.ENTITY_DELETED;
break;
case UPDATED:
msgType = DataConstants.ENTITY_UPDATED;
break;
case ASSIGNED_TO_CUSTOMER:
msgType = DataConstants.ENTITY_ASSIGNED;
break;
case UNASSIGNED_FROM_CUSTOMER:
msgType = DataConstants.ENTITY_UNASSIGNED;
break;
case ATTRIBUTES_UPDATED:
msgType = DataConstants.ATTRIBUTES_UPDATED;
break;
case ATTRIBUTES_DELETED:
msgType = DataConstants.ATTRIBUTES_DELETED;
break;
case ALARM_ACK:
msgType = DataConstants.ALARM_ACK;
break;
case ALARM_CLEAR:
msgType = DataConstants.ALARM_CLEAR;
break;
case ASSIGNED_FROM_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
break;
case ASSIGNED_TO_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
break;
case PROVISION_SUCCESS:
msgType = DataConstants.PROVISION_SUCCESS;
break;
case PROVISION_FAILURE:
msgType = DataConstants.PROVISION_FAILURE;
break;
case TIMESERIES_UPDATED:
msgType = DataConstants.TIMESERIES_UPDATED;
break;
case TIMESERIES_DELETED:
msgType = DataConstants.TIMESERIES_DELETED;
break;
case ASSIGNED_TO_EDGE:
msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE;
break;
case UNASSIGNED_FROM_EDGE:
msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE;
break;
}
if (!StringUtils.isEmpty(msgType)) {
try {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("userId", user.getId().toString());
metaData.putValue("userName", user.getName());
if (customerId != null && !customerId.isNullUid()) {
metaData.putValue("customerId", customerId.toString());
}
if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) {
String strCustomerId = extractParameter(String.class, 1, additionalInfo);
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("assignedCustomerId", strCustomerId);
metaData.putValue("assignedCustomerName", strCustomerName);
} else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) {
String strCustomerId = extractParameter(String.class, 1, additionalInfo);
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedCustomerId", strCustomerId);
metaData.putValue("unassignedCustomerName", strCustomerName);
} else if (actionType == ActionType.ASSIGNED_FROM_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedFromTenantId", strTenantId);
metaData.putValue("assignedFromTenantName", strTenantName);
} else if (actionType == ActionType.ASSIGNED_TO_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedToTenantId", strTenantId);
metaData.putValue("assignedToTenantName", strTenantName);
} else if (actionType == ActionType.ASSIGNED_TO_EDGE) {
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
String strEdgeName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("assignedEdgeId", strEdgeId);
metaData.putValue("assignedEdgeName", strEdgeName);
} else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
String strEdgeName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedEdgeId", strEdgeId);
metaData.putValue("unassignedEdgeName", strEdgeName);
}
ObjectNode entityNode;
if (entity != null) {
entityNode = json.valueToTree(entity);
if (entityId.getEntityType() == EntityType.DASHBOARD) {
entityNode.put("configuration", "");
}
} else {
entityNode = json.createObjectNode();
if (actionType == ActionType.ATTRIBUTES_UPDATED) {
String scope = extractParameter(String.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
metaData.putValue(DataConstants.SCOPE, scope);
if (attributes != null) {
for (AttributeKvEntry attr : attributes) {
addKvEntry(entityNode, attr);
}
}
} else if (actionType == ActionType.ATTRIBUTES_DELETED) {
String scope = extractParameter(String.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List<String> keys = extractParameter(List.class, 1, additionalInfo);
metaData.putValue(DataConstants.SCOPE, scope);
ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) {
keys.forEach(attrsArrayNode::add);
}
} else if (actionType == ActionType.TIMESERIES_UPDATED) {
@SuppressWarnings("unchecked")
List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo);
addTimeseries(entityNode, timeseries);
} else if (actionType == ActionType.TIMESERIES_DELETED) {
@SuppressWarnings("unchecked")
List<String> keys = extractParameter(List.class, 0, additionalInfo);
if (keys != null) {
ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries");
keys.forEach(timeseriesArrayNode::add);
}
entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo));
entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo));
}
}
TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
TenantId tenantId = user.getTenantId();
if (tenantId.isNullUid()) {
if (entity instanceof HasTenantId) {
tenantId = ((HasTenantId) entity).getTenantId();
}
}
tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null);
} catch (Exception e) {
log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
}
}
}
private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception {
if (kvEntry.getDataType() == DataType.BOOLEAN) {
kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value));
} else if (kvEntry.getDataType() == DataType.DOUBLE) {
kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value));
} else if (kvEntry.getDataType() == DataType.LONG) {
kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value));
} else if (kvEntry.getDataType() == DataType.JSON) {
if (kvEntry.getJsonValue().isPresent()) {
entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get()));
}
} else {
entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString());
}
}
private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) {
T result = null;
if (additionalInfo != null && additionalInfo.length > index) {
Object paramObject = additionalInfo[index];
if (clazz.isInstance(paramObject)) {
result = clazz.cast(paramObject);
}
}
return result;
}
protected <E extends HasName> String entityToStr(E entity) {
try {
return json.writeValueAsString(json.valueToTree(entity));
@ -1093,23 +908,6 @@ public abstract class BaseController {
return result;
}
private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception {
if (timeseries != null && !timeseries.isEmpty()) {
ArrayNode result = entityNode.putArray("timeseries");
Map<Long, List<TsKvEntry>> groupedTelemetry = timeseries.stream()
.collect(Collectors.groupingBy(TsKvEntry::getTs));
for (Map.Entry<Long, List<TsKvEntry>> entry : groupedTelemetry.entrySet()) {
ObjectNode element = json.createObjectNode();
element.put("ts", entry.getKey());
ObjectNode values = element.putObject("values");
for (TsKvEntry tsKvEntry : entry.getValue()) {
addKvEntry(values, tsKvEntry);
}
result.add(element);
}
}
}
protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException {
String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null;
if (dashboardId != null && !dashboardId.equals("null")) {

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

@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
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;
@ -75,6 +76,7 @@ import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
@ -153,7 +155,7 @@ public class DeviceController extends BaseController {
deviceStateService.onDeviceUpdated(savedDevice);
}
firmwareStateService.update(savedDevice, oldDevice);
otaPackageStateService.update(savedDevice, oldDevice);
return savedDevice;
} catch (Exception e) {
@ -778,4 +780,19 @@ public class DeviceController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET)
@ResponseBody
public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,
@RequestParam String deviceProfileId) throws ThingsboardException {
checkParameter("OtaPackageType", otaPackageType);
checkParameter("DeviceProfileId", deviceProfileId);
try {
return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(
getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType));
} catch (Exception e) {
throw handleException(e);
}
}
}

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

@ -168,7 +168,7 @@ public class DeviceProfileController extends BaseController {
null,
created ? ActionType.ADDED : ActionType.UPDATED, null);
firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);

222
application/src/main/java/org/thingsboard/server/controller/FirmwareController.java

@ -1,222 +0,0 @@
/**
* 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.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
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.multipart.MultipartFile;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
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.nio.ByteBuffer;
@Slf4j
@RestController
@TbCoreComponent
@RequestMapping("/api")
public class FirmwareController extends BaseController {
public static final String FIRMWARE_ID = "firmwareId";
public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm";
@PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')")
@RequestMapping(value = "/firmware/{firmwareId}/download", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> downloadFirmware(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId);
try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
Firmware firmware = checkFirmwareId(firmwareId, Operation.READ);
ByteArrayResource resource = new ByteArrayResource(firmware.getData().array());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmware.getFileName())
.header("x-filename", firmware.getFileName())
.contentLength(resource.contentLength())
.contentType(parseMediaType(firmware.getContentType()))
.body(resource);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET)
@ResponseBody
public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId);
try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
return checkNotNull(firmwareService.findFirmwareInfoById(getTenantId(), firmwareId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.GET)
@ResponseBody
public Firmware getFirmwareById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId);
try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
return checkFirmwareId(firmwareId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/firmware", method = RequestMethod.POST)
@ResponseBody
public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException {
boolean created = firmwareInfo.getId() == null;
try {
firmwareInfo.setTenantId(getTenantId());
checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE);
FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo);
logEntityAction(savedFirmwareInfo.getId(), savedFirmwareInfo,
null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return savedFirmwareInfo;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.FIRMWARE), firmwareInfo,
null, created ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.POST)
@ResponseBody
public Firmware saveFirmwareData(@PathVariable(FIRMWARE_ID) String strFirmwareId,
@RequestParam(required = false) String checksum,
@RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr,
@RequestBody MultipartFile file) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId);
checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr);
try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.READ);
Firmware firmware = new Firmware(firmwareId);
firmware.setCreatedTime(info.getCreatedTime());
firmware.setTenantId(getTenantId());
firmware.setDeviceProfileId(info.getDeviceProfileId());
firmware.setType(info.getType());
firmware.setTitle(info.getTitle());
firmware.setVersion(info.getVersion());
firmware.setAdditionalInfo(info.getAdditionalInfo());
ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase());
byte[] bytes = file.getBytes();
if (StringUtils.isEmpty(checksum)) {
checksum = firmwareService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes));
}
firmware.setChecksumAlgorithm(checksumAlgorithm);
firmware.setChecksum(checksum);
firmware.setFileName(file.getOriginalFilename());
firmware.setContentType(file.getContentType());
firmware.setData(ByteBuffer.wrap(bytes));
firmware.setDataSize((long) bytes.length);
Firmware savedFirmware = firmwareService.saveFirmware(firmware);
logEntityAction(savedFirmware.getId(), savedFirmware, null, ActionType.UPDATED, null);
return savedFirmware;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.UPDATED, e, strFirmwareId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmwares", method = RequestMethod.GET)
@ResponseBody
public PageData<FirmwareInfo> getFirmwares(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(firmwareService.findTenantFirmwaresByTenantId(getTenantId(), pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET)
@ResponseBody
public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId,
@PathVariable("type") String strType,
@PathVariable("hasData") boolean hasData,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("deviceProfileId", strDeviceProfileId);
checkParameter("type", strType);
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE)
@ResponseBody
public void deleteFirmware(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException {
checkParameter(FIRMWARE_ID, strFirmwareId);
try {
FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId));
FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.DELETE);
firmwareService.deleteFirmware(getTenantId(), firmwareId);
logEntityAction(firmwareId, info, null, ActionType.DELETED, null, strFirmwareId);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.DELETED, e, strFirmwareId);
throw handleException(e);
}
}
}

222
application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java

@ -0,0 +1,222 @@
/**
* 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.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders;
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.multipart.MultipartFile;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
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.nio.ByteBuffer;
@Slf4j
@RestController
@TbCoreComponent
@RequestMapping("/api")
public class OtaPackageController extends BaseController {
public static final String OTA_PACKAGE_ID = "otaPackageId";
public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm";
@PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}/download", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<org.springframework.core.io.Resource> downloadOtaPackage(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
try {
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ);
ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array());
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName())
.header("x-filename", otaPackage.getFileName())
.contentLength(resource.contentLength())
.contentType(parseMediaType(otaPackage.getContentType()))
.body(resource);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET)
@ResponseBody
public OtaPackageInfo getOtaPackageInfoById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
try {
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET)
@ResponseBody
public OtaPackage getOtaPackageById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
try {
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
return checkOtaPackageId(otaPackageId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage", method = RequestMethod.POST)
@ResponseBody
public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException {
boolean created = otaPackageInfo.getId() == null;
try {
otaPackageInfo.setTenantId(getTenantId());
checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE);
OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(otaPackageInfo);
logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo,
null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return savedOtaPackageInfo;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.OTA_PACKAGE), otaPackageInfo,
null, created ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST)
@ResponseBody
public OtaPackage saveOtaPackageData(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId,
@RequestParam(required = false) String checksum,
@RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr,
@RequestBody MultipartFile file) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr);
try {
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.READ);
OtaPackage otaPackage = new OtaPackage(otaPackageId);
otaPackage.setCreatedTime(info.getCreatedTime());
otaPackage.setTenantId(getTenantId());
otaPackage.setDeviceProfileId(info.getDeviceProfileId());
otaPackage.setType(info.getType());
otaPackage.setTitle(info.getTitle());
otaPackage.setVersion(info.getVersion());
otaPackage.setAdditionalInfo(info.getAdditionalInfo());
ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase());
byte[] bytes = file.getBytes();
if (StringUtils.isEmpty(checksum)) {
checksum = otaPackageService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes));
}
otaPackage.setChecksumAlgorithm(checksumAlgorithm);
otaPackage.setChecksum(checksum);
otaPackage.setFileName(file.getOriginalFilename());
otaPackage.setContentType(file.getContentType());
otaPackage.setData(ByteBuffer.wrap(bytes));
otaPackage.setDataSize((long) bytes.length);
OtaPackage savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null);
return savedOtaPackage;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.UPDATED, e, strOtaPackageId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackages", method = RequestMethod.GET)
@ResponseBody
public PageData<OtaPackageInfo> getOtaPackages(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET)
@ResponseBody
public PageData<OtaPackageInfo> getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId,
@PathVariable("type") String strType,
@PathVariable("hasData") boolean hasData,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("deviceProfileId", strDeviceProfileId);
checkParameter("type", strType);
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(),
new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), hasData, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE)
@ResponseBody
public void deleteOtaPackage(@PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
try {
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.DELETE);
otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId);
logEntityAction(otaPackageId, info, null, ActionType.DELETED, null, strOtaPackageId);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId);
throw handleException(e);
}
}
}

256
application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java

@ -0,0 +1,256 @@
/**
* 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.action;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
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.queue.util.TbCoreComponent;
import org.thingsboard.server.service.queue.TbClusterService;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@TbCoreComponent
@Service
@RequiredArgsConstructor
@Slf4j
public class RuleEngineEntityActionService {
private final TbClusterService tbClusterService;
private static final ObjectMapper json = new ObjectMapper();
public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId,
ActionType actionType, User user, Object... additionalInfo) {
String msgType = null;
switch (actionType) {
case ADDED:
msgType = DataConstants.ENTITY_CREATED;
break;
case DELETED:
msgType = DataConstants.ENTITY_DELETED;
break;
case UPDATED:
msgType = DataConstants.ENTITY_UPDATED;
break;
case ASSIGNED_TO_CUSTOMER:
msgType = DataConstants.ENTITY_ASSIGNED;
break;
case UNASSIGNED_FROM_CUSTOMER:
msgType = DataConstants.ENTITY_UNASSIGNED;
break;
case ATTRIBUTES_UPDATED:
msgType = DataConstants.ATTRIBUTES_UPDATED;
break;
case ATTRIBUTES_DELETED:
msgType = DataConstants.ATTRIBUTES_DELETED;
break;
case ALARM_ACK:
msgType = DataConstants.ALARM_ACK;
break;
case ALARM_CLEAR:
msgType = DataConstants.ALARM_CLEAR;
break;
case ALARM_DELETE:
msgType = DataConstants.ALARM_DELETE;
break;
case ASSIGNED_FROM_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
break;
case ASSIGNED_TO_TENANT:
msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
break;
case PROVISION_SUCCESS:
msgType = DataConstants.PROVISION_SUCCESS;
break;
case PROVISION_FAILURE:
msgType = DataConstants.PROVISION_FAILURE;
break;
case TIMESERIES_UPDATED:
msgType = DataConstants.TIMESERIES_UPDATED;
break;
case TIMESERIES_DELETED:
msgType = DataConstants.TIMESERIES_DELETED;
break;
case ASSIGNED_TO_EDGE:
msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE;
break;
case UNASSIGNED_FROM_EDGE:
msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE;
break;
}
if (!StringUtils.isEmpty(msgType)) {
try {
TbMsgMetaData metaData = new TbMsgMetaData();
if (user != null) {
metaData.putValue("userId", user.getId().toString());
metaData.putValue("userName", user.getName());
}
if (customerId != null && !customerId.isNullUid()) {
metaData.putValue("customerId", customerId.toString());
}
if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) {
String strCustomerId = extractParameter(String.class, 1, additionalInfo);
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("assignedCustomerId", strCustomerId);
metaData.putValue("assignedCustomerName", strCustomerName);
} else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) {
String strCustomerId = extractParameter(String.class, 1, additionalInfo);
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedCustomerId", strCustomerId);
metaData.putValue("unassignedCustomerName", strCustomerName);
} else if (actionType == ActionType.ASSIGNED_FROM_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedFromTenantId", strTenantId);
metaData.putValue("assignedFromTenantName", strTenantName);
} else if (actionType == ActionType.ASSIGNED_TO_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedToTenantId", strTenantId);
metaData.putValue("assignedToTenantName", strTenantName);
} else if (actionType == ActionType.ASSIGNED_TO_EDGE) {
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
String strEdgeName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("assignedEdgeId", strEdgeId);
metaData.putValue("assignedEdgeName", strEdgeName);
} else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
String strEdgeName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedEdgeId", strEdgeId);
metaData.putValue("unassignedEdgeName", strEdgeName);
}
ObjectNode entityNode;
if (entity != null) {
entityNode = json.valueToTree(entity);
if (entityId.getEntityType() == EntityType.DASHBOARD) {
entityNode.put("configuration", "");
}
} else {
entityNode = json.createObjectNode();
if (actionType == ActionType.ATTRIBUTES_UPDATED) {
String scope = extractParameter(String.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List<AttributeKvEntry> attributes = extractParameter(List.class, 1, additionalInfo);
metaData.putValue(DataConstants.SCOPE, scope);
if (attributes != null) {
for (AttributeKvEntry attr : attributes) {
addKvEntry(entityNode, attr);
}
}
} else if (actionType == ActionType.ATTRIBUTES_DELETED) {
String scope = extractParameter(String.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List<String> keys = extractParameter(List.class, 1, additionalInfo);
metaData.putValue(DataConstants.SCOPE, scope);
ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) {
keys.forEach(attrsArrayNode::add);
}
} else if (actionType == ActionType.TIMESERIES_UPDATED) {
@SuppressWarnings("unchecked")
List<TsKvEntry> timeseries = extractParameter(List.class, 0, additionalInfo);
addTimeseries(entityNode, timeseries);
} else if (actionType == ActionType.TIMESERIES_DELETED) {
@SuppressWarnings("unchecked")
List<String> keys = extractParameter(List.class, 0, additionalInfo);
if (keys != null) {
ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries");
keys.forEach(timeseriesArrayNode::add);
}
entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo));
entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo));
}
}
TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode));
if (tenantId.isNullUid()) {
if (entity instanceof HasTenantId) {
tenantId = ((HasTenantId) entity).getTenantId();
}
}
tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null);
} catch (Exception e) {
log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e);
}
}
}
private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) {
T result = null;
if (additionalInfo != null && additionalInfo.length > index) {
Object paramObject = additionalInfo[index];
if (clazz.isInstance(paramObject)) {
result = clazz.cast(paramObject);
}
}
return result;
}
private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception {
if (timeseries != null && !timeseries.isEmpty()) {
ArrayNode result = entityNode.putArray("timeseries");
Map<Long, List<TsKvEntry>> groupedTelemetry = timeseries.stream()
.collect(Collectors.groupingBy(TsKvEntry::getTs));
for (Map.Entry<Long, List<TsKvEntry>> entry : groupedTelemetry.entrySet()) {
ObjectNode element = json.createObjectNode();
element.put("ts", entry.getKey());
ObjectNode values = element.putObject("values");
for (TsKvEntry tsKvEntry : entry.getValue()) {
addKvEntry(values, tsKvEntry);
}
result.add(element);
}
}
}
private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception {
if (kvEntry.getDataType() == DataType.BOOLEAN) {
kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value));
} else if (kvEntry.getDataType() == DataType.DOUBLE) {
kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value));
} else if (kvEntry.getDataType() == DataType.LONG) {
kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value));
} else if (kvEntry.getDataType() == DataType.JSON) {
if (kvEntry.getJsonValue().isPresent()) {
entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get()));
}
} else {
entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString());
}
}
}

136
application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java → application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.firmware;
package org.thingsboard.server.service.ota;
import com.google.common.util.concurrent.FutureCallback;
import lombok.extern.slf4j.Slf4j;
@ -23,12 +23,9 @@ import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
@ -37,13 +34,16 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
import org.thingsboard.server.common.data.ota.OtaPackageUtil;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
@ -58,44 +58,43 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey;
import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTargetTelemetryKey;
import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTelemetryKey;
@Slf4j
@Service
@TbCoreComponent
public class DefaultFirmwareStateService implements FirmwareStateService {
public class DefaultOtaPackageStateService implements OtaPackageStateService {
private final TbClusterService tbClusterService;
private final FirmwareService firmwareService;
private final OtaPackageService otaPackageService;
private final DeviceService deviceService;
private final DeviceProfileService deviceProfileService;
private final RuleEngineTelemetryService telemetryService;
private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer;
private final TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> otaPackageStateMsgProducer;
public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService,
DeviceService deviceService,
DeviceProfileService deviceProfileService,
RuleEngineTelemetryService telemetryService,
TbCoreQueueFactory coreQueueFactory) {
public DefaultOtaPackageStateService(TbClusterService tbClusterService, OtaPackageService otaPackageService,
DeviceService deviceService,
DeviceProfileService deviceProfileService,
RuleEngineTelemetryService telemetryService,
TbCoreQueueFactory coreQueueFactory) {
this.tbClusterService = tbClusterService;
this.firmwareService = firmwareService;
this.otaPackageService = otaPackageService;
this.deviceService = deviceService;
this.deviceProfileService = deviceProfileService;
this.telemetryService = telemetryService;
this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer();
this.otaPackageStateMsgProducer = coreQueueFactory.createToOtaPackageStateServiceMsgProducer();
}
@Override
@ -105,14 +104,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}
private void updateFirmware(Device device, Device oldDevice) {
FirmwareId newFirmwareId = device.getFirmwareId();
OtaPackageId newFirmwareId = device.getFirmwareId();
if (newFirmwareId == null) {
DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
newFirmwareId = newDeviceProfile.getFirmwareId();
}
if (oldDevice != null) {
if (newFirmwareId != null) {
FirmwareId oldFirmwareId = oldDevice.getFirmwareId();
OtaPackageId oldFirmwareId = oldDevice.getFirmwareId();
if (oldFirmwareId == null) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
oldFirmwareId = oldDeviceProfile.getFirmwareId();
@ -132,14 +131,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}
private void updateSoftware(Device device, Device oldDevice) {
FirmwareId newSoftwareId = device.getSoftwareId();
OtaPackageId newSoftwareId = device.getSoftwareId();
if (newSoftwareId == null) {
DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
newSoftwareId = newDeviceProfile.getSoftwareId();
}
if (oldDevice != null) {
if (newSoftwareId != null) {
FirmwareId oldSoftwareId = oldDevice.getSoftwareId();
OtaPackageId oldSoftwareId = oldDevice.getSoftwareId();
if (oldSoftwareId == null) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId());
oldSoftwareId = oldDeviceProfile.getSoftwareId();
@ -170,33 +169,20 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}
}
private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) {
Function<PageLink, PageData<Device>> getDevicesFunction;
private void update(TenantId tenantId, DeviceProfile deviceProfile, OtaPackageType otaPackageType) {
Consumer<Device> updateConsumer;
switch (firmwareType) {
case FIRMWARE:
getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl);
break;
case SOFTWARE:
getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl);
break;
default:
log.warn("Unsupported firmware type: [{}]", firmwareType);
return;
}
if (deviceProfile.getFirmwareId() != null) {
long ts = System.currentTimeMillis();
updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType);
updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, otaPackageType);
} else {
updateConsumer = d -> remove(d, firmwareType);
updateConsumer = d -> remove(d, otaPackageType);
}
PageLink pageLink = new PageLink(100);
PageData<Device> pageData;
do {
pageData = getDevicesFunction.apply(pageLink);
pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfile.getId(), otaPackageType, pageLink);
pageData.getData().forEach(updateConsumer);
if (pageData.hasNext()) {
@ -206,60 +192,60 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}
@Override
public boolean process(ToFirmwareStateServiceMsg msg) {
public boolean process(ToOtaPackageStateServiceMsg msg) {
boolean isSuccess = false;
FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()));
OtaPackageId targetOtaPackageId = new OtaPackageId(new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()));
DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB()));
FirmwareType firmwareType = FirmwareType.valueOf(msg.getType());
OtaPackageType firmwareType = OtaPackageType.valueOf(msg.getType());
long ts = msg.getTs();
Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device == null) {
log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId);
} else {
FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
if (currentFirmwareId == null) {
OtaPackageId currentOtaPackageId = OtaPackageUtil.getOtaPackageId(device, firmwareType);
if (currentOtaPackageId == null) {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId());
currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
currentOtaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, firmwareType);
}
if (targetFirmwareId.equals(currentFirmwareId)) {
update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts);
if (targetOtaPackageId.equals(currentOtaPackageId)) {
update(device, otaPackageService.findOtaPackageInfoById(device.getTenantId(), targetOtaPackageId), ts);
isSuccess = true;
} else {
log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId);
log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetOtaPackageId, currentOtaPackageId);
}
}
return isSuccess;
}
private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) {
ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder()
private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, long ts, OtaPackageType firmwareType) {
ToOtaPackageStateServiceMsg msg = ToOtaPackageStateServiceMsg.newBuilder()
.setTenantIdMSB(tenantId.getId().getMostSignificantBits())
.setTenantIdLSB(tenantId.getId().getLeastSignificantBits())
.setDeviceIdMSB(deviceId.getId().getMostSignificantBits())
.setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())
.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits())
.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits())
.setOtaPackageIdMSB(firmwareId.getId().getMostSignificantBits())
.setOtaPackageIdLSB(firmwareId.getId().getLeastSignificantBits())
.setType(firmwareType.name())
.setTs(ts)
.build();
FirmwareInfo firmware = firmwareService.findFirmwareInfoById(tenantId, firmwareId);
OtaPackageInfo firmware = otaPackageService.findOtaPackageInfoById(tenantId, firmwareId);
if (firmware == null) {
log.warn("[{}] Failed to send firmware update because firmware was already deleted", firmwareId);
return;
}
TopicPartitionInfo tpi = new TopicPartitionInfo(fwStateMsgProducer.getDefaultTopic(), null, null, false);
fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
TopicPartitionInfo tpi = new TopicPartitionInfo(otaPackageStateMsgProducer.getDefaultTopic(), null, null, false);
otaPackageStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null);
List<TsKvEntry> telemetry = new ArrayList<>();
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion())));
telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts)));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name())));
telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name())));
telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
@Override
@ -275,11 +261,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
}
private void update(Device device, FirmwareInfo firmware, long ts) {
private void update(Device device, OtaPackageInfo firmware, long ts) {
TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId();
BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name()));
BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.INITIATED.name()));
telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() {
@Override
@ -313,14 +299,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService {
});
}
private void remove(Device device, FirmwareType firmwareType) {
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType),
private void remove(Device device, OtaPackageType firmwareType) {
telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, OtaPackageUtil.getAttributeKeys(firmwareType),
new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void tmp) {
log.trace("[{}] Success remove target firmware attributes!", device.getId());
Set<AttributeKey> keysToNotify = new HashSet<>();
FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key)));
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null);
}

8
application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java → application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.firmware;
package org.thingsboard.server.service.ota;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
public interface FirmwareStateService {
public interface OtaPackageStateService {
void update(Device device, Device oldDevice);
void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged);
boolean process(ToFirmwareStateServiceMsg msg);
boolean process(ToOtaPackageStateServiceMsg msg);
}

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

@ -50,7 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP
import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
import org.thingsboard.server.queue.TbQueueConsumer;
@ -60,7 +60,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.firmware.FirmwareStateService;
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;
@ -75,7 +75,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ -101,9 +100,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
@Value("${queue.core.stats.enabled:false}")
private boolean statsEnabled;
@Value("${queue.core.firmware.pack-interval-ms:60000}")
@Value("${queue.core.ota.pack-interval-ms:60000}")
private long firmwarePackInterval;
@Value("${queue.core.firmware.pack-size:100}")
@Value("${queue.core.ota.pack-size:100}")
private int firmwarePackSize;
private final TbQueueConsumer<TbProtoQueueMsg<ToCoreMsg>> mainConsumer;
@ -113,10 +112,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
private final SubscriptionManagerService subscriptionManagerService;
private final TbCoreDeviceRpcService tbCoreDeviceRpcService;
private final EdgeNotificationService edgeNotificationService;
private final FirmwareStateService firmwareStateService;
private final OtaPackageStateService firmwareStateService;
private final TbCoreConsumerStats stats;
protected final TbQueueConsumer<TbProtoQueueMsg<ToUsageStatsServiceMsg>> usageStatsConsumer;
private final TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> firmwareStatesConsumer;
private final TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> firmwareStatesConsumer;
protected volatile ExecutorService usageStatsExecutor;
@ -135,11 +134,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
TbTenantProfileCache tenantProfileCache,
TbApiUsageStateService apiUsageStateService,
EdgeNotificationService edgeNotificationService,
FirmwareStateService firmwareStateService) {
OtaPackageStateService firmwareStateService) {
super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, apiUsageStateService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
this.usageStatsConsumer = tbCoreQueueFactory.createToUsageStatsServiceMsgConsumer();
this.firmwareStatesConsumer = tbCoreQueueFactory.createToFirmwareStateServiceMsgConsumer();
this.firmwareStatesConsumer = tbCoreQueueFactory.createToOtaPackageStateServiceMsgConsumer();
this.stateService = stateService;
this.localSubscriptionService = localSubscriptionService;
this.subscriptionManagerService = subscriptionManagerService;
@ -173,7 +172,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
public void onApplicationEvent(ApplicationReadyEvent event) {
super.onApplicationEvent(event);
launchUsageStatsConsumer();
launchFirmwareUpdateNotificationConsumer();
launchOtaPackageUpdateNotificationConsumer();
}
@Override
@ -361,20 +360,20 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
});
}
private void launchFirmwareUpdateNotificationConsumer() {
private void launchOtaPackageUpdateNotificationConsumer() {
long maxProcessingTimeoutPerRecord = firmwarePackInterval / firmwarePackSize;
firmwareStatesExecutor.submit(() -> {
while (!stopped) {
try {
List<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration());
List<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration());
if (msgs.isEmpty()) {
continue;
}
long timeToSleep = maxProcessingTimeoutPerRecord;
for (TbProtoQueueMsg<ToFirmwareStateServiceMsg> msg : msgs) {
for (TbProtoQueueMsg<ToOtaPackageStateServiceMsg> msg : msgs) {
try {
long startTime = System.currentTimeMillis();
boolean isSuccessUpdate = handleFirmwareUpdates(msg);
boolean isSuccessUpdate = handleOtaPackageUpdates(msg);
long endTime = System.currentTimeMillis();
long spentTime = endTime - startTime;
timeToSleep = timeToSleep - spentTime;
@ -402,7 +401,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
}
}
}
log.info("TB Firmware States Consumer stopped.");
log.info("TB Ota Package States Consumer stopped.");
});
}
@ -410,7 +409,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
statsService.process(msg, callback);
}
private boolean handleFirmwareUpdates(TbProtoQueueMsg<ToFirmwareStateServiceMsg> msg) {
private boolean handleOtaPackageUpdates(TbProtoQueueMsg<ToOtaPackageStateServiceMsg> msg) {
return firmwareStateService.process(msg.getValue());
}

16
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -89,7 +89,7 @@ public class DefaultTbResourceService implements TbResourceService {
} catch (InvalidDDFFileException | IOException e) {
throw new ThingsboardException(e, ThingsboardErrorCode.GENERAL);
}
if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource) == null) {
if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource, true) == null) {
throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText()));
}
} else {
@ -131,7 +131,7 @@ public class DefaultTbResourceService implements TbResourceService {
List<TbResource> resources = resourceService.findTenantResourcesByResourceTypeAndObjectIds(tenantId, ResourceType.LWM2M_MODEL,
objectIds);
return resources.stream()
.flatMap(s -> Stream.ofNullable(toLwM2mObject(s)))
.flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false)))
.sorted(getComparator(sortProperty, sortOrder))
.collect(Collectors.toList());
}
@ -142,7 +142,7 @@ public class DefaultTbResourceService implements TbResourceService {
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
PageData<TbResource> resourcePageData = resourceService.findTenantResourcesByResourceTypeAndPageLink(tenantId, ResourceType.LWM2M_MODEL, pageLink);
return resourcePageData.getData().stream()
.flatMap(s -> Stream.ofNullable(toLwM2mObject(s)))
.flatMap(s -> Stream.ofNullable(toLwM2mObject(s, false)))
.sorted(getComparator(sortProperty, sortOrder))
.collect(Collectors.toList());
}
@ -167,7 +167,7 @@ public class DefaultTbResourceService implements TbResourceService {
return "DESC".equals(sortOrder) ? comparator.reversed() : comparator;
}
private LwM2mObject toLwM2mObject(TbResource resource) {
private LwM2mObject toLwM2mObject(TbResource resource, boolean isSave) {
try {
DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
List<ObjectModel> objectModels =
@ -186,12 +186,16 @@ public class DefaultTbResourceService implements TbResourceService {
instance.setId(0);
List<LwM2mResourceObserve> resources = new ArrayList<>();
obj.resources.forEach((k, v) -> {
if (v.operations.isReadable()) {
if (isSave) {
LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
resources.add(lwM2MResourceObserve);
}
else if (v.operations.isReadable()) {
LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false);
resources.add(lwM2MResourceObserve);
}
});
if (resources.size() > 0) {
if (isSave || resources.size() > 0) {
instance.setResources(resources.toArray(LwM2mResourceObserve[]::new));
lwM2mObject.setInstances(new LwM2mInstance[]{instance});
return lwM2mObject;

24
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@ -46,7 +46,7 @@ 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.id.EntityViewId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TbResourceId;
@ -63,7 +63,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
@ -135,7 +135,7 @@ public class AccessValidator {
protected ResourceService resourceService;
@Autowired
protected FirmwareService firmwareService;
protected OtaPackageService otaPackageService;
private ExecutorService executor;
@ -232,8 +232,8 @@ public class AccessValidator {
case TB_RESOURCE:
validateResource(currentUser, operation, entityId, callback);
return;
case FIRMWARE:
validateFirmware(currentUser, operation, entityId, callback);
case OTA_PACKAGE:
validateOtaPackage(currentUser, operation, entityId, callback);
return;
default:
//TODO: add support of other entities
@ -300,20 +300,20 @@ public class AccessValidator {
}
}
private void validateFirmware(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
private void validateOtaPackage(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
if (currentUser.isSystemAdmin()) {
callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
} else {
FirmwareInfo firmware = firmwareService.findFirmwareInfoById(currentUser.getTenantId(), new FirmwareId(entityId.getId()));
if (firmware == null) {
callback.onSuccess(ValidationResult.entityNotFound("Firmware with requested id wasn't found!"));
OtaPackageInfo otaPackage = otaPackageService.findOtaPackageInfoById(currentUser.getTenantId(), new OtaPackageId(entityId.getId()));
if (otaPackage == null) {
callback.onSuccess(ValidationResult.entityNotFound("OtaPackage with requested id wasn't found!"));
} else {
try {
accessControlService.checkPermission(currentUser, Resource.FIRMWARE, operation, entityId, firmware);
accessControlService.checkPermission(currentUser, Resource.OTA_PACKAGE, operation, entityId, otaPackage);
} catch (ThingsboardException e) {
callback.onSuccess(ValidationResult.accessDenied(e.getMessage()));
}
callback.onSuccess(ValidationResult.ok(firmware));
callback.onSuccess(ValidationResult.ok(otaPackage));
}
}
}

2
application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java

@ -38,7 +38,7 @@ public enum Resource {
DEVICE_PROFILE(EntityType.DEVICE_PROFILE),
API_USAGE_STATE(EntityType.API_USAGE_STATE),
TB_RESOURCE(EntityType.TB_RESOURCE),
FIRMWARE(EntityType.FIRMWARE),
OTA_PACKAGE(EntityType.OTA_PACKAGE),
EDGE(EntityType.EDGE);
private final EntityType entityType;

2
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -42,7 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker);
put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker);
put(Resource.TB_RESOURCE, tbResourcePermissionChecker);
put(Resource.FIRMWARE, tenantEntityPermissionChecker);
put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker);
put(Resource.EDGE, tenantEntityPermissionChecker);
}

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

@ -26,27 +26,27 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData;
import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUtil;
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.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -64,7 +64,7 @@ import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.device.provision.ProvisionFailedException;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import org.thingsboard.server.dao.device.provision.ProvisionResponse;
import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -124,8 +124,8 @@ public class DefaultTransportApiService implements TransportApiService {
private final DataDecodingEncodingService dataDecodingEncodingService;
private final DeviceProvisionService deviceProvisionService;
private final TbResourceService resourceService;
private final FirmwareService firmwareService;
private final FirmwareDataCache firmwareDataCache;
private final OtaPackageService otaPackageService;
private final OtaPackageDataCache otaPackageDataCache;
private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
@ -134,7 +134,7 @@ public class DefaultTransportApiService implements TransportApiService {
RelationService relationService, DeviceCredentialsService deviceCredentialsService,
DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService,
DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) {
DeviceProvisionService deviceProvisionService, TbResourceService resourceService, OtaPackageService otaPackageService, OtaPackageDataCache otaPackageDataCache) {
this.deviceProfileCache = deviceProfileCache;
this.tenantProfileCache = tenantProfileCache;
this.apiUsageStateService = apiUsageStateService;
@ -147,8 +147,8 @@ public class DefaultTransportApiService implements TransportApiService {
this.dataDecodingEncodingService = dataDecodingEncodingService;
this.deviceProvisionService = deviceProvisionService;
this.resourceService = resourceService;
this.firmwareService = firmwareService;
this.firmwareDataCache = firmwareDataCache;
this.otaPackageService = otaPackageService;
this.otaPackageDataCache = otaPackageDataCache;
}
@Override
@ -184,8 +184,8 @@ public class DefaultTransportApiService implements TransportApiService {
result = handle(transportApiRequestMsg.getDeviceRequestMsg());
} else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) {
result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg());
} else if (transportApiRequestMsg.hasFirmwareRequestMsg()) {
result = handle(transportApiRequestMsg.getFirmwareRequestMsg());
} else if (transportApiRequestMsg.hasOtaPackageRequestMsg()) {
result = handle(transportApiRequestMsg.getOtaPackageRequestMsg());
}
return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture),
@ -511,50 +511,50 @@ public class DefaultTransportApiService implements TransportApiService {
}
}
private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) {
private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetOtaPackageRequestMsg requestMsg) {
TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB()));
FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType());
OtaPackageType otaPackageType = OtaPackageType.valueOf(requestMsg.getType());
Device device = deviceService.findDeviceById(tenantId, deviceId);
if (device == null) {
return getEmptyTransportApiResponseFuture();
}
FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType);
if (firmwareId == null) {
OtaPackageId otaPackageId = OtaPackageUtil.getOtaPackageId(device, otaPackageType);
if (otaPackageId == null) {
DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId());
firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType);
otaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, otaPackageType);
}
TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder();
TransportProtos.GetOtaPackageResponseMsg.Builder builder = TransportProtos.GetOtaPackageResponseMsg.newBuilder();
if (firmwareId == null) {
if (otaPackageId == null) {
builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
} else {
FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(tenantId, firmwareId);
OtaPackageInfo otaPackageInfo = otaPackageService.findOtaPackageInfoById(tenantId, otaPackageId);
if (firmwareInfo == null) {
if (otaPackageInfo == null) {
builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND);
} else {
builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS);
builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits());
builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits());
builder.setType(firmwareInfo.getType().name());
builder.setTitle(firmwareInfo.getTitle());
builder.setVersion(firmwareInfo.getVersion());
builder.setFileName(firmwareInfo.getFileName());
builder.setContentType(firmwareInfo.getContentType());
if (!firmwareDataCache.has(firmwareId.toString())) {
Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId);
firmwareDataCache.put(firmwareId.toString(), firmware.getData().array());
builder.setOtaPackageIdMSB(otaPackageId.getId().getMostSignificantBits());
builder.setOtaPackageIdLSB(otaPackageId.getId().getLeastSignificantBits());
builder.setType(otaPackageInfo.getType().name());
builder.setTitle(otaPackageInfo.getTitle());
builder.setVersion(otaPackageInfo.getVersion());
builder.setFileName(otaPackageInfo.getFileName());
builder.setContentType(otaPackageInfo.getContentType());
if (!otaPackageDataCache.has(otaPackageId.toString())) {
OtaPackage otaPackage = otaPackageService.findOtaPackageById(tenantId, otaPackageId);
otaPackageDataCache.put(otaPackageId.toString(), otaPackage.getData().array());
}
}
}
return Futures.immediateFuture(
TransportApiResponseMsg.newBuilder()
.setFirmwareResponseMsg(builder.build())
.setOtaPackageResponseMsg(builder.build())
.build());
}

6
application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java

@ -17,9 +17,9 @@ package org.thingsboard.server.service.ttl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.server.dao.util.PsqlDao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
@ -62,4 +62,8 @@ public abstract class AbstractCleanUpService {
protected abstract void doCleanUp(Connection connection) throws SQLException;
protected Connection getConnection() throws SQLException {
return DriverManager.getConnection(dbUrl, dbUserName, dbPassword);
}
}

110
application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java

@ -0,0 +1,110 @@
/**
* 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.ttl.alarms;
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.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.AlarmId;
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.page.SortOrder;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.dao.alarm.AlarmDao;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantDao;
import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.action.RuleEngineEntityActionService;
import org.thingsboard.server.service.ttl.AbstractCleanUpService;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@TbCoreComponent
@Service
@Slf4j
@RequiredArgsConstructor
public class AlarmsCleanUpService {
@Value("${sql.ttl.alarms.removal_batch_size}")
private Integer removalBatchSize;
private final TenantDao tenantDao;
private final AlarmDao alarmDao;
private final AlarmService alarmService;
private final RelationService relationService;
private final RuleEngineEntityActionService ruleEngineEntityActionService;
private final PartitionService partitionService;
private final TbTenantProfileCache tenantProfileCache;
@Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.alarms.checking_interval})}", fixedDelayString = "${sql.ttl.alarms.checking_interval}")
public void cleanUp() {
PageLink tenantsBatchRequest = new PageLink(10_000, 0);
PageLink removalBatchRequest = new PageLink(removalBatchSize, 0 );
PageData<TenantId> tenantsIds;
do {
tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest);
for (TenantId tenantId : tenantsIds.getData()) {
if (!partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) {
continue;
}
Optional<DefaultTenantProfileConfiguration> tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration();
if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) {
continue;
}
long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays());
long expirationTime = System.currentTimeMillis() - ttl;
long totalRemoved = 0;
while (true) {
PageData<AlarmId> toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest);
toRemove.getData().forEach(alarmId -> {
relationService.deleteEntityRelations(tenantId, alarmId);
Alarm alarm = alarmService.deleteAlarm(tenantId, alarmId).getAlarm();
ruleEngineEntityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null);
});
totalRemoved += toRemove.getTotalElements();
if (!toRemove.hasNext()) {
break;
}
}
if (totalRemoved > 0) {
log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime));
}
}
tenantsBatchRequest = tenantsBatchRequest.nextPageLink();
} while (tenantsIds.hasNext());
}
}

2
application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java

@ -40,7 +40,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService {
@Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}")
public void cleanUp() {
if (ttlTaskExecutionEnabled) {
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
try (Connection conn = getConnection()) {
doCleanUp(conn);
} catch (SQLException e) {
log.error("SQLException occurred during TTL task execution ", e);

2
application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java

@ -43,7 +43,7 @@ public class EventsCleanUpService extends AbstractCleanUpService {
@Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}")
public void cleanUp() {
if (ttlTaskExecutionEnabled) {
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
try (Connection conn = getConnection()) {
doCleanUp(conn);
} catch (SQLException e) {
log.error("SQLException occurred during TTL task execution ", e);

2
application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java

@ -36,7 +36,7 @@ public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpSe
@Scheduled(initialDelayString = "${sql.ttl.ts.execution_interval_ms}", fixedDelayString = "${sql.ttl.ts.execution_interval_ms}")
public void cleanUp() {
if (ttlTaskExecutionEnabled) {
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
try (Connection conn = getConnection()) {
doCleanUp(conn);
} catch (SQLException e) {
log.error("SQLException occurred during TTL task execution ", e);

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

@ -273,6 +273,9 @@ sql:
enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}"
execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day
edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month
alarms:
checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours
removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches
# Actor system parameters
actors:
@ -371,7 +374,10 @@ caffeine:
tokensOutdatageTime:
timeToLiveInMinutes: 20000
maxSize: 10000
firmwares:
otaPackages:
timeToLiveInMinutes: 60
maxSize: 10
otaPackagesData:
timeToLiveInMinutes: 60
maxSize: 10
edges:
@ -497,7 +503,7 @@ audit-log:
"device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}"
"edge": "${AUDIT_LOG_MASK_EDGE:W}"
"tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}"
"firmware": "${AUDIT_LOG_MASK_FIRMWARE:W}"
"ota_package": "${AUDIT_LOG_MASK_OTA_PACKAGE:W}"
sink:
# Type of external sink. possible options: none, elasticsearch
type: "${AUDIT_LOG_SINK_TYPE:none}"
@ -749,7 +755,7 @@ queue:
sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}"
security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
consumer-properties-per-topic:
tb_firmware:
tb_ota_package:
- key: max.poll.records
value: 10
other:
@ -759,7 +765,7 @@ queue:
transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}"
js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}"
fw-updates: "${TB_QUEUE_KAFKA_FW_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}"
consumer-stats:
enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}"
print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}"
@ -830,10 +836,10 @@ queue:
poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}"
partitions: "${TB_QUEUE_CORE_PARTITIONS:10}"
pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}"
firmware:
topic: "${TB_QUEUE_CORE_FW_TOPIC:tb_firmware}"
pack-interval-ms: "${TB_QUEUE_CORE_FW_PACK_INTERVAL_MS:60000}"
pack-size: "${TB_QUEUE_CORE_FW_PACK_SIZE:100}"
ota:
topic: "${TB_QUEUE_CORE_OTA_TOPIC:tb_ota_package}"
pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}"
pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}"
usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}"
stats:
enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}"

115
application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java → application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java

@ -25,11 +25,10 @@ import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequ
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -41,11 +40,11 @@ import java.util.Collections;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
public abstract class BaseFirmwareControllerTest extends AbstractControllerTest {
public abstract class BaseOtaPackageControllerTest extends AbstractControllerTest {
private IdComparator<FirmwareInfo> idComparator = new IdComparator<>();
private IdComparator<OtaPackageInfo> idComparator = new IdComparator<>();
public static final String TITLE = "My firmware";
private static final String FILE_NAME = "filename.txt";
@ -93,13 +92,13 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testSaveFirmware() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
Assert.assertNotNull(savedFirmwareInfo);
Assert.assertNotNull(savedFirmwareInfo.getId());
@ -112,19 +111,19 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
save(savedFirmwareInfo);
FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class);
OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class);
Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
}
@Test
public void testSaveFirmwareData() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
Assert.assertNotNull(savedFirmwareInfo);
Assert.assertNotNull(savedFirmwareInfo.getId());
@ -137,12 +136,12 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
save(savedFirmwareInfo);
FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class);
OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class);
Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle());
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
Assert.assertEquals(FILE_NAME, savedFirmware.getFileName());
Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType());
@ -150,97 +149,97 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
@Test
public void testUpdateFirmwareFromDifferentTenant() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
loginDifferentTenant();
doPost("/api/firmware", savedFirmwareInfo, FirmwareInfo.class, status().isForbidden());
doPost("/api/otaPackage", savedFirmwareInfo, OtaPackageInfo.class, status().isForbidden());
deleteDifferentTenant();
}
@Test
public void testFindFirmwareInfoById() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
FirmwareInfo foundFirmware = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class);
OtaPackageInfo foundFirmware = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class);
Assert.assertNotNull(foundFirmware);
Assert.assertEquals(savedFirmwareInfo, foundFirmware);
}
@Test
public void testFindFirmwareById() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
Firmware foundFirmware = doGet("/api/firmware/" + savedFirmwareInfo.getId().getId().toString(), Firmware.class);
OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class);
Assert.assertNotNull(foundFirmware);
Assert.assertEquals(savedFirmware, foundFirmware);
}
@Test
public void testDeleteFirmware() throws Exception {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
doDelete("/api/firmware/" + savedFirmwareInfo.getId().getId().toString())
doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString())
doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
public void testFindTenantFirmwares() throws Exception {
List<FirmwareInfo> firmwares = new ArrayList<>();
List<OtaPackageInfo> otaPackages = new ArrayList<>();
for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
if (i > 100) {
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
firmwares.add(new FirmwareInfo(savedFirmware));
OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
otaPackages.add(new OtaPackageInfo(savedFirmware));
} else {
firmwares.add(savedFirmwareInfo);
otaPackages.add(savedFirmwareInfo);
}
}
List<FirmwareInfo> loadedFirmwares = new ArrayList<>();
List<OtaPackageInfo> loadedFirmwares = new ArrayList<>();
PageLink pageLink = new PageLink(24);
PageData<FirmwareInfo> pageData;
PageData<OtaPackageInfo> pageData;
do {
pageData = doGetTypedWithPageLink("/api/firmwares?",
pageData = doGetTypedWithPageLink("/api/otaPackages?",
new TypeReference<>() {
}, pageLink);
loadedFirmwares.addAll(pageData.getData());
@ -249,41 +248,41 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
}
} while (pageData.hasNext());
Collections.sort(firmwares, idComparator);
Collections.sort(otaPackages, idComparator);
Collections.sort(loadedFirmwares, idComparator);
Assert.assertEquals(firmwares, loadedFirmwares);
Assert.assertEquals(otaPackages, loadedFirmwares);
}
@Test
public void testFindTenantFirmwaresByHasData() throws Exception {
List<FirmwareInfo> firmwaresWithData = new ArrayList<>();
List<FirmwareInfo> firmwaresWithoutData = new ArrayList<>();
List<OtaPackageInfo> otaPackagesWithData = new ArrayList<>();
List<OtaPackageInfo> otaPackagesWithoutData = new ArrayList<>();
for (int i = 0; i < 165; i++) {
FirmwareInfo firmwareInfo = new FirmwareInfo();
OtaPackageInfo firmwareInfo = new OtaPackageInfo();
firmwareInfo.setDeviceProfileId(deviceProfileId);
firmwareInfo.setType(FIRMWARE);
firmwareInfo.setTitle(TITLE);
firmwareInfo.setVersion(VERSION + i);
FirmwareInfo savedFirmwareInfo = save(firmwareInfo);
OtaPackageInfo savedFirmwareInfo = save(firmwareInfo);
if (i > 100) {
MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array());
Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
firmwaresWithData.add(new FirmwareInfo(savedFirmware));
OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM);
otaPackagesWithData.add(new OtaPackageInfo(savedFirmware));
} else {
firmwaresWithoutData.add(savedFirmwareInfo);
otaPackagesWithoutData.add(savedFirmwareInfo);
}
}
List<FirmwareInfo> loadedFirmwaresWithData = new ArrayList<>();
List<OtaPackageInfo> loadedFirmwaresWithData = new ArrayList<>();
PageLink pageLink = new PageLink(24);
PageData<FirmwareInfo> pageData;
PageData<OtaPackageInfo> pageData;
do {
pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?",
pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/true?",
new TypeReference<>() {
}, pageLink);
loadedFirmwaresWithData.addAll(pageData.getData());
@ -292,10 +291,10 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
}
} while (pageData.hasNext());
List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>();
List<OtaPackageInfo> loadedFirmwaresWithoutData = new ArrayList<>();
pageLink = new PageLink(24);
do {
pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?",
pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/false?",
new TypeReference<>() {
}, pageLink);
loadedFirmwaresWithoutData.addAll(pageData.getData());
@ -304,25 +303,25 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest
}
} while (pageData.hasNext());
Collections.sort(firmwaresWithData, idComparator);
Collections.sort(firmwaresWithoutData, idComparator);
Collections.sort(otaPackagesWithData, idComparator);
Collections.sort(otaPackagesWithoutData, idComparator);
Collections.sort(loadedFirmwaresWithData, idComparator);
Collections.sort(loadedFirmwaresWithoutData, idComparator);
Assert.assertEquals(firmwaresWithData, loadedFirmwaresWithData);
Assert.assertEquals(firmwaresWithoutData, loadedFirmwaresWithoutData);
Assert.assertEquals(otaPackagesWithData, loadedFirmwaresWithData);
Assert.assertEquals(otaPackagesWithoutData, loadedFirmwaresWithoutData);
}
private FirmwareInfo save(FirmwareInfo firmwareInfo) throws Exception {
return doPost("/api/firmware", firmwareInfo, FirmwareInfo.class);
private OtaPackageInfo save(OtaPackageInfo firmwareInfo) throws Exception {
return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
}
protected Firmware savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
protected OtaPackage savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
postRequest.file(content);
setJwtToken(postRequest);
return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), Firmware.class);
return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackage.class);
}
}

4
application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java → application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java

@ -15,9 +15,9 @@
*/
package org.thingsboard.server.controller.sql;
import org.thingsboard.server.controller.BaseFirmwareControllerTest;
import org.thingsboard.server.controller.BaseOtaPackageControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
@DaoSqlTest
public class FirmwareControllerSqlTest extends BaseFirmwareControllerTest {
public class OtaPackageControllerSqlTest extends BaseOtaPackageControllerTest {
}

68
application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesIntegrationTest.java

@ -44,6 +44,9 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}";
protected static final String POST_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION = "{\"attribute1\":\"value\",\"attribute2\":false,\"attribute3\":41.0,\"attribute4\":72," +
"\"attribute5\":{\"someNumber\":41,\"someArray\":[],\"someNestedObject\":{\"key\":\"value\"}}}";
@Before
public void beforeTest() throws Exception {
processBeforeTest("Test Subscribe to attribute updates", null, null);
@ -56,50 +59,85 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
@Test
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception {
processTestSubscribeToAttributesUpdates();
processTestSubscribeToAttributesUpdates(false);
}
protected void processTestSubscribeToAttributesUpdates() throws Exception {
@Test
public void testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification() throws Exception {
processTestSubscribeToAttributesUpdates(true);
}
protected void processTestSubscribeToAttributesUpdates(boolean emptyCurrentStateNotification) throws Exception {
if (!emptyCurrentStateNotification) {
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION, String.class, status().isOk());
}
CoapClient client = getCoapClient(FeatureType.ATTRIBUTES);
CountDownLatch latch = new CountDownLatch(1);
TestCoapCallback testCoapCallback = new TestCoapCallback(latch);
TestCoapCallback callback = new TestCoapCallback(latch);
Request request = Request.newGet().setObserve();
request.setType(CoAP.Type.CON);
CoapObserveRelation observeRelation = client.observe(request, testCoapCallback);
CoapObserveRelation observeRelation = client.observe(request, callback);
Thread.sleep(1000);
latch.await(3, TimeUnit.SECONDS);
if (emptyCurrentStateNotification) {
validateEmptyCurrentStateAttributesResponse(callback);
} else {
validateCurrentStateAttributesResponse(callback);
}
latch = new CountDownLatch(1);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
validateUpdateAttributesResponse(testCoapCallback);
validateUpdateAttributesResponse(callback);
latch = new CountDownLatch(1);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class);
latch.await(3, TimeUnit.SECONDS);
validateDeleteAttributesResponse(testCoapCallback);
validateDeleteAttributesResponse(callback);
observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled());
}
protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
protected void validateCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(0, callback.getObserve().intValue());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD_ON_CURRENT_STATE_NOTIFICATION), JacksonUtil.toJsonNode(response));
}
protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(0, callback.getObserve().intValue());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals("{}", response);
}
protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(1, callback.getObserve().intValue());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response));
}
protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(1, callback.getObserve().intValue());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(2, callback.getObserve().intValue());
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8);
assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response));
}
@ -110,13 +148,18 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
private Integer observe;
private byte[] payloadBytes;
private CoAP.ResponseCode responseCode;
public Integer getObserve() {
return observe;
}
public byte[] getPayloadBytes() {
return payloadBytes;
}
public Integer getObserve() {
return observe;
public CoAP.ResponseCode getResponseCode() {
return responseCode;
}
private TestCoapCallback(CountDownLatch latch) {
@ -125,10 +168,9 @@ public abstract class AbstractCoapAttributesUpdatesIntegrationTest extends Abstr
@Override
public void onLoad(CoapResponse response) {
assertNotNull(response.getPayload());
assertEquals(response.getCode(), CoAP.ResponseCode.CONTENT);
observe = response.getOptions().getObserve();
payloadBytes = response.getPayload();
responseCode = response.getCode();
latch.countDown();
}

5
application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesJsonIntegrationTest.java

@ -39,4 +39,9 @@ public abstract class AbstractCoapAttributesUpdatesJsonIntegrationTest extends A
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception {
super.testSubscribeToAttributesUpdatesFromTheServer();
}
@Test
public void testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification() throws Exception {
super.testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification();
}
}

53
application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/AbstractCoapAttributesUpdatesProtoIntegrationTest.java

@ -17,6 +17,7 @@ package org.thingsboard.server.transport.coap.attributes.updates;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -24,11 +25,15 @@ import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@Slf4j
@ -46,11 +51,54 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends
@Test
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception {
processTestSubscribeToAttributesUpdates();
processTestSubscribeToAttributesUpdates(false);
}
@Test
public void testSubscribeToAttributesUpdatesFromTheServerWithEmptyCurrentStateNotification() throws Exception {
processTestSubscribeToAttributesUpdates(true);
}
protected void validateCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(0, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder expectedCurrentStateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value", TransportProtos.KeyValueType.STRING_V);
TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "false", TransportProtos.KeyValueType.BOOLEAN_V);
TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "41.0", TransportProtos.KeyValueType.DOUBLE_V);
TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "72", TransportProtos.KeyValueType.LONG_V);
TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":41,\"someArray\":[],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V);
List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>();
tsKvProtoList.add(tsKvProtoAttribute1);
tsKvProtoList.add(tsKvProtoAttribute2);
tsKvProtoList.add(tsKvProtoAttribute3);
tsKvProtoList.add(tsKvProtoAttribute4);
tsKvProtoList.add(tsKvProtoAttribute5);
TransportProtos.AttributeUpdateNotificationMsg expectedCurrentStateNotificationMsg = expectedCurrentStateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList).build();
TransportProtos.AttributeUpdateNotificationMsg actualCurrentStateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes());
List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedCurrentStateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualCurrentStateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList());
assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size());
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList));
}
protected void validateEmptyCurrentStateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(0, callback.getObserve().intValue());
}
protected void validateUpdateAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(1, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList();
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
@ -68,6 +116,9 @@ public abstract class AbstractCoapAttributesUpdatesProtoIntegrationTest extends
protected void validateDeleteAttributesResponse(TestCoapCallback callback) throws InvalidProtocolBufferException {
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(2, callback.getObserve().intValue());
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5");

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

@ -83,7 +83,7 @@ public abstract class AbstractCoapServerSideRpcDefaultIntegrationTest extends Ab
@Test
public void testServerCoapTwoWayRpc() throws Exception {
processTwoWayRpcTest();
processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}");
}
}

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

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.transport.coap.rpc;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.californium.core.CoapClient;
@ -24,17 +25,18 @@ import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.coap.Request;
import org.junit.Assert;
import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -55,57 +57,66 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
client.useCONs();
CountDownLatch latch = new CountDownLatch(1);
TestCoapCallback testCoapCallback = new TestCoapCallback(client, latch, true);
TestCoapCallback callback = new TestCoapCallback(client, latch, true);
Request request = Request.newGet().setObserve();
CoapObserveRelation observeRelation = client.observe(request, testCoapCallback);
CoapObserveRelation observeRelation = client.observe(request, callback);
latch.await(3, TimeUnit.SECONDS);
validateCurrentStateNotification(callback);
latch = new CountDownLatch(1);
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());
Assert.assertTrue(StringUtils.isEmpty(result));
latch.await(3, TimeUnit.SECONDS);
assertEquals(0, testCoapCallback.getObserve().intValue());
validateOneWayStateChangedNotification(callback, result);
observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled());
}
protected void processTwoWayRpcTest() throws Exception {
protected void processTwoWayRpcTest(String expectedResponseResult) throws Exception {
CoapClient client = getCoapClient(FeatureType.RPC);
client.useCONs();
CountDownLatch latch = new CountDownLatch(1);
TestCoapCallback testCoapCallback = new TestCoapCallback(client, latch, false);
TestCoapCallback callback = new TestCoapCallback(client, latch, false);
Request request = Request.newGet().setObserve();
request.setType(CoAP.Type.CON);
CoapObserveRelation observeRelation = client.observe(request, testCoapCallback);
CoapObserveRelation observeRelation = client.observe(request, callback);
latch.await(3, TimeUnit.SECONDS);
validateCurrentStateNotification(callback);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String expected = "{\"value1\":\"A\",\"value2\":\"B\"}";
String actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
validateTwoWayStateChangedNotification(callback, 1, expectedResponseResult, actualResult);
String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch = new CountDownLatch(1);
actualResult = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
assertEquals(expected, result);
assertEquals(0, testCoapCallback.getObserve().intValue());
validateTwoWayStateChangedNotification(callback, 2, expectedResponseResult, actualResult);
observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled());
// // TODO: 3/11/21 Fix test to validate next RPC
// latch = new CountDownLatch(1);
//
// result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
// latch.await(3, TimeUnit.SECONDS);
//
// assertEquals(expected, result);
// assertEquals(1, testCoapCallback.getObserve().intValue());
}
protected void processOnLoadResponse(CoapResponse response, CoapClient client, Integer observe, CountDownLatch latch) {
client.setURI(getRpcResponseFeatureTokenUrl(accessToken, observe));
JsonNode responseJson = JacksonUtil.fromBytes(response.getPayload());
client.setURI(getRpcResponseFeatureTokenUrl(accessToken, responseJson.get("id").asInt()));
client.post(new CoapHandler() {
@Override
public void onLoad(CoapResponse response) {
@ -130,11 +141,21 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
private final CountDownLatch latch;
private final boolean isOneWayRpc;
private Integer observe;
private byte[] payloadBytes;
private CoAP.ResponseCode responseCode;
public Integer getObserve() {
return observe;
}
private Integer observe;
public byte[] getPayloadBytes() {
return payloadBytes;
}
public CoAP.ResponseCode getResponseCode() {
return responseCode;
}
TestCoapCallback(CoapClient client, CountDownLatch latch, boolean isOneWayRpc) {
this.client = client;
@ -144,14 +165,15 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
@Override
public void onLoad(CoapResponse response) {
log.warn("coap response: {}, {}", response.getResponseText(), response.getCode());
assertNotNull(response.getPayload());
assertEquals(response.getCode(), CoAP.ResponseCode.CONTENT);
payloadBytes = response.getPayload();
responseCode = response.getCode();
observe = response.getOptions().getObserve();
if (!isOneWayRpc) {
processOnLoadResponse(response, client, observe, latch);
} else {
latch.countDown();
if (observe != null) {
if (!isOneWayRpc && observe > 0) {
processOnLoadResponse(response, client, observe, latch);
} else {
latch.countDown();
}
}
}
@ -162,4 +184,28 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
}
private void validateCurrentStateNotification(TestCoapCallback callback) {
assertNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode.VALID);
assertEquals(0, callback.getObserve().intValue());
}
private void validateOneWayStateChangedNotification(TestCoapCallback callback, String result) {
assertTrue(StringUtils.isEmpty(result));
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(1, callback.getObserve().intValue());
}
private void validateTwoWayStateChangedNotification(TestCoapCallback callback, int expectedObserveNumber, String expectedResult, String actualResult) {
assertEquals(expectedResult, actualResult);
assertNotNull(callback.getPayloadBytes());
assertNotNull(callback.getObserve());
assertEquals(callback.getResponseCode(), CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
assertEquals(expectedObserveNumber, callback.getObserve().intValue());
}
}

2
application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcJsonIntegrationTest.java

@ -42,7 +42,7 @@ public abstract class AbstractCoapServerSideRpcJsonIntegrationTest extends Abstr
@Test
public void testServerCoapTwoWayRpc() throws Exception {
processTwoWayRpcTest();
processTwoWayRpcTest("{\"value1\":\"A\",\"value2\":\"B\"}");
}
}

39
application/src/test/java/org/thingsboard/server/transport/coap/rpc/AbstractCoapServerSideRpcProtoIntegrationTest.java

@ -85,37 +85,11 @@ public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends Abst
@Test
public void testServerCoapTwoWayRpc() throws Exception {
processTwoWayRpcTest();
}
protected void processTwoWayRpcTest() throws Exception {
CoapClient client = getCoapClient(FeatureType.RPC);
client.useCONs();
CountDownLatch latch = new CountDownLatch(1);
TestCoapCallback testCoapCallback = new TestCoapCallback(client, latch, false);
Request request = Request.newGet().setObserve();
request.setType(CoAP.Type.CON);
CoapObserveRelation observeRelation = client.observe(request, testCoapCallback);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
String expected = "{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}";
String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
latch.await(3, TimeUnit.SECONDS);
assertEquals(expected, result);
assertEquals(0, testCoapCallback.getObserve().intValue());
observeRelation.proactiveCancel();
assertTrue(observeRelation.isCanceled());
processTwoWayRpcTest("{\"payload\":\"{\\\"value1\\\":\\\"A\\\",\\\"value2\\\":\\\"B\\\"}\"}");
}
@Override
protected void processOnLoadResponse(CoapResponse response, CoapClient client, Integer observe, CountDownLatch latch) {
client.setURI(getRpcResponseFeatureTokenUrl(accessToken, observe));
ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = getProtoTransportPayloadConfiguration();
ProtoFileElement rpcRequestProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(RPC_REQUEST_PROTO_SCHEMA);
DynamicSchema rpcRequestProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(rpcRequestProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_REQUEST_PROTO_SCHEMA);
@ -123,25 +97,22 @@ public abstract class AbstractCoapServerSideRpcProtoIntegrationTest extends Abst
byte[] requestPayload = response.getPayload();
DynamicMessage.Builder rpcRequestMsg = rpcRequestProtoSchema.newMessageBuilder("RpcRequestMsg");
Descriptors.Descriptor rpcRequestMsgDescriptor = rpcRequestMsg.getDescriptorForType();
assertNotNull(rpcRequestMsgDescriptor);
try {
DynamicMessage dynamicMessage = DynamicMessage.parseFrom(rpcRequestMsgDescriptor, requestPayload);
List<Descriptors.FieldDescriptor> fields = rpcRequestMsgDescriptor.getFields();
for (Descriptors.FieldDescriptor fieldDescriptor: fields) {
assertTrue(dynamicMessage.hasField(fieldDescriptor));
}
Descriptors.FieldDescriptor requestIdDescriptor = rpcRequestMsgDescriptor.findFieldByName("requestId");
int requestId = (int) dynamicMessage.getField(requestIdDescriptor);
ProtoFileElement rpcResponseProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_RPC_RESPONSE_PROTO_SCHEMA);
DynamicSchema rpcResponseProtoSchema = protoTransportPayloadConfiguration.getDynamicSchema(rpcResponseProtoSchemaFile, ProtoTransportPayloadConfiguration.RPC_RESPONSE_PROTO_SCHEMA);
DynamicMessage.Builder rpcResponseBuilder = rpcResponseProtoSchema.newMessageBuilder("RpcResponseMsg");
Descriptors.Descriptor rpcResponseMsgDescriptor = rpcResponseBuilder.getDescriptorForType();
assertNotNull(rpcResponseMsgDescriptor);
DynamicMessage rpcResponseMsg = rpcResponseBuilder
.setField(rpcResponseMsgDescriptor.findFieldByName("payload"), DEVICE_RESPONSE)
.build();
client.setURI(getRpcResponseFeatureTokenUrl(accessToken, requestId));
client.post(new CoapHandler() {
@Override
public void onLoad(CoapResponse response) {
log.warn("Command Response Ack: {}, {}", response.getCode(), response.getResponseText());
log.warn("Command Response Ack: {}", response.getCode());
latch.countDown();
}

13
common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java → common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java

@ -13,19 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.cache.firmware;
package org.thingsboard.server.cache.ota;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE;
import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_DATA_CACHE;
@Service
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@RequiredArgsConstructor
public class CaffeineFirmwareCache implements FirmwareDataCache {
public class CaffeineOtaPackageCache implements OtaPackageDataCache {
private final CacheManager cacheManager;
@ -36,7 +37,7 @@ public class CaffeineFirmwareCache implements FirmwareDataCache {
@Override
public byte[] get(String key, int chunkSize, int chunk) {
byte[] data = cacheManager.getCache(FIRMWARE_CACHE).get(key, byte[].class);
byte[] data = cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).get(key, byte[].class);
if (chunkSize < 1) {
return data;
@ -58,11 +59,11 @@ public class CaffeineFirmwareCache implements FirmwareDataCache {
@Override
public void put(String key, byte[] value) {
cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value);
cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).putIfAbsent(key, value);
}
@Override
public void evict(String key) {
cacheManager.getCache(FIRMWARE_CACHE).evict(key);
cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).evict(key);
}
}

8
common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java → common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java

@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.cache.firmware;
package org.thingsboard.server.cache.ota;
public interface FirmwareDataCache {
public interface OtaPackageDataCache {
byte[] get(String key);
@ -25,8 +25,8 @@ public interface FirmwareDataCache {
void evict(String key);
default boolean has(String firmwareId) {
byte[] data = get(firmwareId, 1, 0);
default boolean has(String otaPackageId) {
byte[] data = get(otaPackageId, 1, 0);
return data != null && data.length > 0;
}
}

19
common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java → common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.cache.firmware;
package org.thingsboard.server.cache.ota;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -21,12 +21,13 @@ import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE;
import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE;
import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_DATA_CACHE;
@Service
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@RequiredArgsConstructor
public class RedisFirmwareDataCache implements FirmwareDataCache {
public class RedisOtaPackageDataCache implements OtaPackageDataCache {
private final RedisConnectionFactory redisConnectionFactory;
@ -39,30 +40,30 @@ public class RedisFirmwareDataCache implements FirmwareDataCache {
public byte[] get(String key, int chunkSize, int chunk) {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
if (chunkSize == 0) {
return connection.get(toFirmwareCacheKey(key));
return connection.get(toOtaPackageCacheKey(key));
}
int startIndex = chunkSize * chunk;
int endIndex = startIndex + chunkSize - 1;
return connection.getRange(toFirmwareCacheKey(key), startIndex, endIndex);
return connection.getRange(toOtaPackageCacheKey(key), startIndex, endIndex);
}
}
@Override
public void put(String key, byte[] value) {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
connection.set(toFirmwareCacheKey(key), value);
connection.set(toOtaPackageCacheKey(key), value);
}
}
@Override
public void evict(String key) {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
connection.del(toFirmwareCacheKey(key));
connection.del(toOtaPackageCacheKey(key));
}
}
private byte[] toFirmwareCacheKey(String key) {
return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes();
private byte[] toOtaPackageCacheKey(String key) {
return String.format("%s::%s", OTA_PACKAGE_DATA_CACHE, key).getBytes();
}
}

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

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
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.security.DeviceCredentials;
@ -63,9 +64,9 @@ public interface DeviceService {
PageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);

52
common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java

@ -1,52 +0,0 @@
/**
* 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.dao.firmware;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Firmware;
import org.thingsboard.server.common.data.FirmwareInfo;
import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
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 java.nio.ByteBuffer;
public interface FirmwareService {
FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo);
Firmware saveFirmware(Firmware firmware);
String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data);
Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId);
FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId);
ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId);
PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);
void deleteFirmwaresByTenantId(TenantId tenantId);
}

52
common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.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.dao.ota;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
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 java.nio.ByteBuffer;
public interface OtaPackageService {
OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo);
OtaPackage saveOtaPackage(OtaPackage otaPackage);
String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data);
OtaPackage findOtaPackageById(TenantId tenantId, OtaPackageId otaPackageId);
OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId);
ListenableFuture<OtaPackageInfo> findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId);
PageData<OtaPackageInfo> findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink);
PageData<OtaPackageInfo> findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink);
void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId);
void deleteOtaPackagesByTenantId(TenantId tenantId);
}

3
common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java

@ -29,5 +29,6 @@ public class CacheConstants {
public static final String DEVICE_PROFILE_CACHE = "deviceProfiles";
public static final String ATTRIBUTES_CACHE = "attributes";
public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime";
public static final String FIRMWARE_CACHE = "firmwares";
public static final String OTA_PACKAGE_CACHE = "otaPackages";
public static final String OTA_PACKAGE_DATA_CACHE = "otaPackagesData";
}

1
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -66,6 +66,7 @@ public class DataConstants {
public static final String TIMESERIES_DELETED = "TIMESERIES_DELETED";
public static final String ALARM_ACK = "ALARM_ACK";
public static final String ALARM_CLEAR = "ALARM_CLEAR";
public static final String ALARM_DELETE = "ALARM_DELETE";
public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT";
public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT";
public static final String PROVISION_SUCCESS = "PROVISION_SUCCESS";

16
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData;
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.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.NoXss;
@ -32,7 +32,7 @@ import java.io.IOException;
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware {
public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasOtaPackage {
private static final long serialVersionUID = 2807343040519543363L;
@ -49,8 +49,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
@JsonIgnore
private byte[] deviceDataBytes;
private FirmwareId firmwareId;
private FirmwareId softwareId;
private OtaPackageId firmwareId;
private OtaPackageId softwareId;
public Device() {
super();
@ -167,19 +167,19 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
return getName();
}
public FirmwareId getFirmwareId() {
public OtaPackageId getFirmwareId() {
return firmwareId;
}
public void setFirmwareId(FirmwareId firmwareId) {
public void setFirmwareId(OtaPackageId firmwareId) {
this.firmwareId = firmwareId;
}
public FirmwareId getSoftwareId() {
public OtaPackageId getSoftwareId() {
return softwareId;
}
public void setSoftwareId(FirmwareId softwareId) {
public void setSoftwareId(OtaPackageId softwareId) {
this.softwareId = softwareId;
}

8
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java

@ -23,7 +23,7 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.NoXss;
@ -37,7 +37,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware {
public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasOtaPackage {
private TenantId tenantId;
@NoXss
@ -60,9 +60,9 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
@NoXss
private String provisionDeviceKey;
private FirmwareId firmwareId;
private OtaPackageId firmwareId;
private FirmwareId softwareId;
private OtaPackageId softwareId;
public DeviceProfile() {
super();

2
common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java

@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
* @author Andrew Shvayka
*/
public enum EntityType {
TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, FIRMWARE, EDGE;
TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE;
}

8
common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java → common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java

@ -15,11 +15,11 @@
*/
package org.thingsboard.server.common.data;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
public interface HasFirmware {
public interface HasOtaPackage {
FirmwareId getFirmwareId();
OtaPackageId getFirmwareId();
FirmwareId getSoftwareId();
OtaPackageId getSoftwareId();
}

10
common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java → common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java

@ -17,27 +17,27 @@ package org.thingsboard.server.common.data;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import java.nio.ByteBuffer;
@Data
@EqualsAndHashCode(callSuper = true)
public class Firmware extends FirmwareInfo {
public class OtaPackage extends OtaPackageInfo {
private static final long serialVersionUID = 3091601761339422546L;
private transient ByteBuffer data;
public Firmware() {
public OtaPackage() {
super();
}
public Firmware(FirmwareId id) {
public OtaPackage(OtaPackageId id) {
super(id);
}
public Firmware(Firmware firmware) {
public OtaPackage(OtaPackage firmware) {
super(firmware);
this.data = firmware.getData();
}

40
common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java → common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java

@ -19,22 +19,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId;
@Slf4j
@Data
@EqualsAndHashCode(callSuper = true)
public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> implements HasName, HasTenantId {
public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackageId> implements HasName, HasTenantId {
private static final long serialVersionUID = 3168391583570815419L;
private TenantId tenantId;
private DeviceProfileId deviceProfileId;
private FirmwareType type;
private OtaPackageType type;
private String title;
private String version;
private boolean hasData;
@ -45,27 +45,27 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId>
private Long dataSize;
public FirmwareInfo() {
public OtaPackageInfo() {
super();
}
public FirmwareInfo(FirmwareId id) {
public OtaPackageInfo(OtaPackageId id) {
super(id);
}
public FirmwareInfo(FirmwareInfo firmwareInfo) {
super(firmwareInfo);
this.tenantId = firmwareInfo.getTenantId();
this.deviceProfileId = firmwareInfo.getDeviceProfileId();
this.type = firmwareInfo.getType();
this.title = firmwareInfo.getTitle();
this.version = firmwareInfo.getVersion();
this.hasData = firmwareInfo.isHasData();
this.fileName = firmwareInfo.getFileName();
this.contentType = firmwareInfo.getContentType();
this.checksumAlgorithm = firmwareInfo.getChecksumAlgorithm();
this.checksum = firmwareInfo.getChecksum();
this.dataSize = firmwareInfo.getDataSize();
public OtaPackageInfo(OtaPackageInfo otaPackageInfo) {
super(otaPackageInfo);
this.tenantId = otaPackageInfo.getTenantId();
this.deviceProfileId = otaPackageInfo.getDeviceProfileId();
this.type = otaPackageInfo.getType();
this.title = otaPackageInfo.getTitle();
this.version = otaPackageInfo.getVersion();
this.hasData = otaPackageInfo.isHasData();
this.fileName = otaPackageInfo.getFileName();
this.contentType = otaPackageInfo.getContentType();
this.checksumAlgorithm = otaPackageInfo.getChecksumAlgorithm();
this.checksum = otaPackageInfo.getChecksum();
this.dataSize = otaPackageInfo.getDataSize();
}
@Override

8
common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Optional;
import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper;
@ -92,6 +93,13 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H
}
}
@JsonIgnore
public Optional<DefaultTenantProfileConfiguration> getProfileConfiguration() {
return Optional.ofNullable(getProfileData().getConfiguration())
.filter(profileConfiguration -> profileConfiguration instanceof DefaultTenantProfileConfiguration)
.map(profileConfiguration -> (DefaultTenantProfileConfiguration) profileConfiguration);
}
public TenantProfileData createDefaultTenantProfileData() {
TenantProfileData tpd = new TenantProfileData();
tpd.setConfiguration(new DefaultTenantProfileConfiguration());

1
common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java

@ -39,6 +39,7 @@ public enum ActionType {
RELATIONS_DELETED(false),
ALARM_ACK(false),
ALARM_CLEAR(false),
ALARM_DELETE(false),
LOGIN(false),
LOGOUT(false),
LOCKOUT(false),

25
common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java

@ -0,0 +1,25 @@
/**
* 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.common.data.exception;
public class ApiUsageLimitsExceededException extends RuntimeException {
public ApiUsageLimitsExceededException(String message) {
super(message);
}
public ApiUsageLimitsExceededException() {
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java

@ -71,8 +71,8 @@ public class EntityIdFactory {
return new ApiUsageStateId(uuid);
case TB_RESOURCE:
return new TbResourceId(uuid);
case FIRMWARE:
return new FirmwareId(uuid);
case OTA_PACKAGE:
return new OtaPackageId(uuid);
case EDGE:
return new EdgeId(uuid);
}

10
common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java → common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java

@ -22,23 +22,23 @@ import org.thingsboard.server.common.data.EntityType;
import java.util.UUID;
public class FirmwareId extends UUIDBased implements EntityId {
public class OtaPackageId extends UUIDBased implements EntityId {
private static final long serialVersionUID = 1L;
@JsonCreator
public FirmwareId(@JsonProperty("id") UUID id) {
public OtaPackageId(@JsonProperty("id") UUID id) {
super(id);
}
public static FirmwareId fromString(String firmwareId) {
return new FirmwareId(UUID.fromString(firmwareId));
public static OtaPackageId fromString(String firmwareId) {
return new OtaPackageId(UUID.fromString(firmwareId));
}
@JsonIgnore
@Override
public EntityType getEntityType() {
return EntityType.FIRMWARE;
return EntityType.OTA_PACKAGE;
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java → common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.firmware;
package org.thingsboard.server.common.data.ota;
public enum ChecksumAlgorithm {
MD5,

6
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java → common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.firmware;
package org.thingsboard.server.common.data.ota;
import lombok.Getter;
public enum FirmwareKey {
public enum OtaPackageKey {
TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm");
@Getter
private final String value;
FirmwareKey(String value) {
OtaPackageKey(String value) {
this.value = value;
}
}

6
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java → common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java

@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.firmware;
package org.thingsboard.server.common.data.ota;
import lombok.Getter;
public enum FirmwareType {
public enum OtaPackageType {
FIRMWARE("fw"), SOFTWARE("sw");
@Getter
private final String keyPrefix;
FirmwareType(String keyPrefix) {
OtaPackageType(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java → common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.firmware;
package org.thingsboard.server.common.data.ota;
public enum FirmwareUpdateStatus {
public enum OtaPackageUpdateStatus {
QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
}

49
common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java → common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java

@ -13,21 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.firmware;
package org.thingsboard.server.common.data.ota;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.HasFirmware;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.HasOtaPackage;
import org.thingsboard.server.common.data.id.OtaPackageId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
import java.util.function.Supplier;
@Slf4j
public class FirmwareUtil {
public class OtaPackageUtil {
public static final List<String> ALL_FW_ATTRIBUTE_KEYS;
@ -35,19 +33,19 @@ public class FirmwareUtil {
static {
ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>();
for (FirmwareKey key : FirmwareKey.values()) {
ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key));
for (OtaPackageKey key : OtaPackageKey.values()) {
ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(OtaPackageType.FIRMWARE, key));
}
ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>();
for (FirmwareKey key : FirmwareKey.values()) {
ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key));
for (OtaPackageKey key : OtaPackageKey.values()) {
ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(OtaPackageType.SOFTWARE, key));
}
}
public static List<String> getAttributeKeys(FirmwareType firmwareType) {
public static List<String> getAttributeKeys(OtaPackageType firmwareType) {
switch (firmwareType) {
case FIRMWARE:
return ALL_FW_ATTRIBUTE_KEYS;
@ -57,35 +55,46 @@ public class FirmwareUtil {
return Collections.emptyList();
}
public static String getAttributeKey(FirmwareType type, FirmwareKey key) {
public static String getAttributeKey(OtaPackageType type, OtaPackageKey key) {
return type.getKeyPrefix() + "_" + key.getValue();
}
public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) {
public static String getTargetTelemetryKey(OtaPackageType type, OtaPackageKey key) {
return getTelemetryKey("target_", type, key);
}
public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) {
public static String getCurrentTelemetryKey(OtaPackageType type, OtaPackageKey key) {
return getTelemetryKey("current_", type, key);
}
private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) {
private static String getTelemetryKey(String prefix, OtaPackageType type, OtaPackageKey key) {
return prefix + type.getKeyPrefix() + "_" + key.getValue();
}
public static String getTelemetryKey(FirmwareType type, FirmwareKey key) {
public static String getTelemetryKey(OtaPackageType type, OtaPackageKey key) {
return type.getKeyPrefix() + "_" + key.getValue();
}
public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) {
switch (firmwareType) {
public static OtaPackageId getOtaPackageId(HasOtaPackage entity, OtaPackageType type) {
switch (type) {
case FIRMWARE:
return entity.getFirmwareId();
case SOFTWARE:
return entity.getSoftwareId();
default:
log.warn("Unsupported firmware type: [{}]", firmwareType);
log.warn("Unsupported ota package type: [{}]", type);
return null;
}
}
public static <T> T getByOtaPackageType(Supplier<T> firmwareSupplier, Supplier<T> softwareSupplier, OtaPackageType type) {
switch (type) {
case FIRMWARE:
return firmwareSupplier.get();
case SOFTWARE:
return softwareSupplier.get();
default:
throw new RuntimeException("Unsupported OtaPackage type: " + type);
}
}
}

7
common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java

@ -17,10 +17,11 @@ package org.thingsboard.server.common.data.page;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.BaseData;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class PageData<T> {
@ -61,4 +62,8 @@ public class PageData<T> {
return hasNext;
}
public <D> PageData<D> mapData(Function<T, D> mapper) {
return new PageData<>(getData().stream().map(mapper).collect(Collectors.toList()), getTotalPages(), getTotalElements(), hasNext());
}
}

1
common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java

@ -53,6 +53,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
private long maxCreatedAlarms;
private int defaultStorageTtlDays;
private int alarmsTtlDays;
private double warnThreshold;

2
common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java

@ -38,7 +38,7 @@ public class TbKafkaTopicConfigs {
private String notificationsProperties;
@Value("${queue.kafka.topic-properties.js-executor}")
private String jsExecutorProperties;
@Value("${queue.kafka.topic-properties.fw-updates:}")
@Value("${queue.kafka.topic-properties.ota-updates:}")
private String fwUpdatesProperties;
@Getter

10
common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java

@ -187,14 +187,14 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
}
@PreDestroy

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -177,14 +177,14 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory {
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic());
}
@PreDestroy

8
common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java

@ -131,13 +131,13 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new InMemoryTbQueueConsumer<>(coreSettings.getFirmwareTopic());
public TbQueueConsumer<TbProtoQueueMsg<TransportProtos.ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new InMemoryTbQueueConsumer<>(coreSettings.getOtaPackageTopic());
}
@Override
public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new InMemoryTbQueueProducer<>(coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<TransportProtos.ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new InMemoryTbQueueProducer<>(coreSettings.getOtaPackageTopic());
}
@Override

22
common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -277,24 +277,24 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder();
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder();
consumerBuilder.settings(kafkaSettings);
consumerBuilder.topic(coreSettings.getFirmwareTopic());
consumerBuilder.clientId("monolith-fw-consumer-" + serviceInfoProvider.getServiceId());
consumerBuilder.groupId("monolith-fw-consumer");
consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
consumerBuilder.topic(coreSettings.getOtaPackageTopic());
consumerBuilder.clientId("monolith-ota-consumer-" + serviceInfoProvider.getServiceId());
consumerBuilder.groupId("monolith-ota-consumer");
consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
consumerBuilder.admin(fwUpdatesAdmin);
consumerBuilder.statsService(consumerStatsService);
return consumerBuilder.build();
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
requestBuilder.settings(kafkaSettings);
requestBuilder.clientId("monolith-fw-producer-" + serviceInfoProvider.getServiceId());
requestBuilder.defaultTopic(coreSettings.getFirmwareTopic());
requestBuilder.clientId("monolith-ota-producer-" + serviceInfoProvider.getServiceId());
requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic());
requestBuilder.admin(fwUpdatesAdmin);
return requestBuilder.build();
}

22
common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -245,24 +245,24 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory {
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder();
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> consumerBuilder = TbKafkaConsumerTemplate.builder();
consumerBuilder.settings(kafkaSettings);
consumerBuilder.topic(coreSettings.getFirmwareTopic());
consumerBuilder.clientId("tb-core-fw-consumer-" + serviceInfoProvider.getServiceId());
consumerBuilder.groupId("tb-core-fw-consumer");
consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
consumerBuilder.topic(coreSettings.getOtaPackageTopic());
consumerBuilder.clientId("tb-core-ota-consumer-" + serviceInfoProvider.getServiceId());
consumerBuilder.groupId("tb-core-ota-consumer");
consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
consumerBuilder.admin(fwUpdatesAdmin);
consumerBuilder.statsService(consumerStatsService);
return consumerBuilder.build();
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> requestBuilder = TbKafkaProducerTemplate.builder();
requestBuilder.settings(kafkaSettings);
requestBuilder.clientId("tb-core-fw-producer-" + serviceInfoProvider.getServiceId());
requestBuilder.defaultTopic(coreSettings.getFirmwareTopic());
requestBuilder.clientId("tb-core-ota-producer-" + serviceInfoProvider.getServiceId());
requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic());
requestBuilder.admin(fwUpdatesAdmin);
return requestBuilder.build();
}

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java

@ -22,9 +22,9 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest;
import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.*;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -192,14 +192,14 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic());
}
@Override

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -166,14 +166,14 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory {
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic());
}
@Override

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java

@ -24,7 +24,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest;
import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -190,14 +190,14 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic());
}
@Override

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -172,14 +172,14 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory {
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic());
}
@Override

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -189,14 +189,14 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic());
}
@Override

12
common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
@ -172,14 +172,14 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory {
}
@Override
public TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer() {
return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
public TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer() {
return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(),
msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders()));
}
@Override
public TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer() {
return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic());
public TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer() {
return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic());
}
@Override

6
common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java

@ -16,7 +16,7 @@
package org.thingsboard.server.queue.provider;
import org.thingsboard.server.gen.js.JsInvokeProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;
@ -91,14 +91,14 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory {
*
* @return
*/
TbQueueConsumer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgConsumer();
TbQueueConsumer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgConsumer();
/**
* Used to consume messages about firmware update notifications by TB Core Service
*
* @return
*/
TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> createToFirmwareStateServiceMsgProducer();
TbQueueProducer<TbProtoQueueMsg<ToOtaPackageStateServiceMsg>> createToOtaPackageStateServiceMsgProducer();
/**
* Used to consume high priority messages by TB Core Service

4
common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.queue.provider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
@ -25,11 +24,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg;
import org.thingsboard.server.queue.TbQueueProducer;
import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import javax.annotation.PostConstruct;
@Service
@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'")
@TbCoreComponent
public class TbCoreQueueProducerProvider implements TbQueueProducerProvider {
private final TbCoreQueueFactory tbQueueProvider;

4
common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java

@ -26,8 +26,8 @@ public class TbQueueCoreSettings {
@Value("${queue.core.topic}")
private String topic;
@Value("${queue.core.firmware.topic:tb_firmware}")
private String firmwareTopic;
@Value("${queue.core.ota.topic:tb_ota_package}")
private String otaPackageTopic;
@Value("${queue.core.usage-stats-topic:tb_usage_stats}")
private String usageStatsTopic;

20
common/queue/src/main/proto/queue.proto

@ -137,6 +137,7 @@ message GetAttributeRequestMsg {
int32 requestId = 1;
repeated string clientAttributeNames = 2;
repeated string sharedAttributeNames = 3;
bool onlyShared = 4;
}
message GetAttributeResponseMsg {
@ -144,6 +145,7 @@ message GetAttributeResponseMsg {
repeated TsKvProto clientAttributeList = 2;
repeated TsKvProto sharedAttributeList = 3;
string error = 5;
bool sharedStateMsg = 6;
}
message AttributeUpdateNotificationMsg {
@ -386,7 +388,7 @@ enum ResponseStatus {
FAILURE = 3;
}
message GetFirmwareRequestMsg {
message GetOtaPackageRequestMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
int64 tenantIdMSB = 3;
@ -394,10 +396,10 @@ message GetFirmwareRequestMsg {
string type = 5;
}
message GetFirmwareResponseMsg {
message GetOtaPackageResponseMsg {
ResponseStatus responseStatus = 1;
int64 firmwareIdMSB = 2;
int64 firmwareIdLSB = 3;
int64 otaPackageIdMSB = 2;
int64 otaPackageIdLSB = 3;
string type = 4;
string title = 5;
string version = 6;
@ -625,7 +627,7 @@ message TransportApiRequestMsg {
ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7;
ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8;
GetResourceRequestMsg resourceRequestMsg = 9;
GetFirmwareRequestMsg firmwareRequestMsg = 10;
GetOtaPackageRequestMsg otaPackageRequestMsg = 10;
GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11;
GetDeviceRequestMsg deviceRequestMsg = 12;
GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13;
@ -640,7 +642,7 @@ message TransportApiResponseMsg {
GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5;
LwM2MResponseMsg lwM2MResponseMsg = 6;
GetResourceResponseMsg resourceResponseMsg = 7;
GetFirmwareResponseMsg firmwareResponseMsg = 8;
GetOtaPackageResponseMsg otaPackageResponseMsg = 8;
GetDeviceResponseMsg deviceResponseMsg = 9;
GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10;
}
@ -708,13 +710,13 @@ message ToUsageStatsServiceMsg {
int64 customerIdLSB = 7;
}
message ToFirmwareStateServiceMsg {
message ToOtaPackageStateServiceMsg {
int64 ts = 1;
int64 tenantIdMSB = 2;
int64 tenantIdLSB = 3;
int64 deviceIdMSB = 4;
int64 deviceIdLSB = 5;
int64 firmwareIdMSB = 6;
int64 firmwareIdLSB = 7;
int64 otaPackageIdMSB = 6;
int64 otaPackageIdLSB = 7;
string type = 8;
}

15
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/AbstractCoapTransportResource.java

@ -18,6 +18,7 @@ package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Response;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.transport.TransportContext;
@ -56,7 +57,7 @@ public abstract class AbstractCoapTransportResource extends CoapResource {
protected abstract void processHandlePost(CoapExchange exchange);
protected void reportActivity(TransportProtos.SessionInfoProto sessionInfo, boolean hasAttributeSubscription, boolean hasRpcSubscription) {
protected void reportSubscriptionInfo(TransportProtos.SessionInfoProto sessionInfo, boolean hasAttributeSubscription, boolean hasRpcSubscription) {
transportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(hasAttributeSubscription)
.setRpcSubscription(hasRpcSubscription)
@ -64,6 +65,10 @@ public abstract class AbstractCoapTransportResource extends CoapResource {
.build(), TransportServiceCallback.EMPTY);
}
protected void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
transportService.reportActivity(sessionInfo);
}
protected static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) {
return TransportProtos.SessionEventMsg.newBuilder()
.setSessionType(TransportProtos.SessionType.ASYNC)
@ -112,13 +117,19 @@ public abstract class AbstractCoapTransportResource extends CoapResource {
@Override
public void onSuccess(Void msg) {
exchange.respond(onSuccessResponse);
Response response = new Response(onSuccessResponse);
response.setAcknowledged(isConRequest());
exchange.respond(response);
}
@Override
public void onError(Throwable e) {
exchange.respond(onFailureResponse);
}
private boolean isConRequest() {
return exchange.advanced().getRequest().isConfirmable();
}
}
public static class CoapNoOpCallback implements TransportServiceCallback<Void> {

5
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportContext.java

@ -18,6 +18,7 @@ package org.thingsboard.server.transport.coap;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.transport.TransportContext;
@ -34,6 +35,10 @@ import org.thingsboard.server.transport.coap.efento.adaptor.EfentoCoapAdaptor;
@Component
public class CoapTransportContext extends TransportContext {
@Getter
@Value("${transport.sessions.report_timeout}")
private long sessionReportTimeout;
@Getter
@Autowired
private JsonCoapAdaptor jsonCoapAdaptor;

144
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java

@ -28,6 +28,7 @@ import org.eclipse.californium.core.observe.ObserveRelation;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
import org.eclipse.californium.core.server.resources.ResourceObserver;
import org.springframework.util.CollectionUtils;
import org.thingsboard.server.coapserver.CoapServerService;
import org.thingsboard.server.coapserver.TbCoapDtlsSessionInfo;
import org.thingsboard.server.common.data.DataConstants;
@ -43,7 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC
import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.security.DeviceTokenCredentials;
import org.thingsboard.server.common.msg.session.FeatureType;
import org.thingsboard.server.common.msg.session.SessionMsgType;
@ -55,11 +56,14 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@ -72,25 +76,30 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
private static final int REQUEST_ID_POSITION_CERTIFICATE_REQUEST = 4;
private static final String DTLS_SESSION_ID_KEY = "DTLS_SESSION_ID";
private final ConcurrentMap<String, TransportProtos.SessionInfoProto> tokenToSessionIdMap = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AtomicInteger> tokenToNotificationCounterMap = new ConcurrentHashMap<>();
private final ConcurrentMap<String, TransportProtos.SessionInfoProto> tokenToSessionInfoMap = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AtomicInteger> tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>();
private final ConcurrentMap<TransportProtos.SessionInfoProto, ObserveRelation> sessionInfoToObserveRelationMap = new ConcurrentHashMap<>();
private final Set<UUID> rpcSubscriptions = ConcurrentHashMap.newKeySet();
private final Set<UUID> attributeSubscriptions = ConcurrentHashMap.newKeySet();
private ConcurrentMap<String, TbCoapDtlsSessionInfo> dtlsSessionIdMap;
private long timeout;
private long sessionReportTimeout;
public CoapTransportResource(CoapTransportContext coapTransportContext, CoapServerService coapServerService, String name) {
super(coapTransportContext, name);
public CoapTransportResource(CoapTransportContext ctx, CoapServerService coapServerService, String name) {
super(ctx, name);
this.setObservable(true); // enable observing
this.addObserver(new CoapResourceObserver());
this.dtlsSessionIdMap = coapServerService.getDtlsSessionsMap();
this.timeout = coapServerService.getTimeout();
// this.setObservable(false); // disable observing
// this.setObserveType(CoAP.Type.CON); // configure the notification type to CONs
// this.getAttributes().setObservable(); // mark observable in the Link-Format
this.sessionReportTimeout = ctx.getSessionReportTimeout();
ctx.getScheduler().scheduleAtFixedRate(() -> {
Set<TransportProtos.SessionInfoProto> observeSessions = sessionInfoToObserveRelationMap.keySet();
observeSessions.forEach(this::reportActivity);
}, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS);
}
@Override
public void checkObserveRelation(Exchange exchange, Response response) {
String token = getTokenFromRequest(exchange.getRequest());
final ObserveRelation relation = exchange.getRelation();
@ -103,11 +112,20 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
relation.setEstablished();
addObserveRelation(relation);
}
AtomicInteger notificationCounter = tokenToNotificationCounterMap.computeIfAbsent(token, s -> new AtomicInteger(0));
AtomicInteger notificationCounter = tokenToObserveNotificationSeqMap.computeIfAbsent(token, s -> new AtomicInteger(0));
response.getOptions().setObserve(notificationCounter.getAndIncrement());
} // ObserveLayer takes care of the else case
}
public void clearAndNotifyObserveRelation(ObserveRelation relation, CoAP.ResponseCode code) {
relation.cancel();
relation.getExchange().sendResponse(new Response(code));
}
public Map<TransportProtos.SessionInfoProto, ObserveRelation> getSessionInfoToObserveRelationMap() {
return sessionInfoToObserveRelationMap;
}
@Override
protected void processHandleGet(CoapExchange exchange) {
Optional<FeatureType> featureType = getFeatureType(exchange.advanced().getRequest());
@ -239,7 +257,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
}
private void processRequest(CoapExchange exchange, SessionMsgType type, Request request, TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile) {
UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
UUID sessionId = toSessionId(sessionInfo);
try {
TransportConfigurationContainer transportConfigurationContainer = getTransportConfigurationContainer(deviceProfile);
CoapTransportAdaptor coapTransportAdaptor = getCoapTransportAdaptor(transportConfigurationContainer.isJsonPayload());
@ -249,14 +267,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
coapTransportAdaptor.convertToPostAttributes(sessionId, request,
transportConfigurationContainer.getAttributesMsgDescriptor()),
new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
reportSubscriptionInfo(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
break;
case POST_TELEMETRY_REQUEST:
transportService.process(sessionInfo,
coapTransportAdaptor.convertToPostTelemetry(sessionId, request,
transportConfigurationContainer.getTelemetryMsgDescriptor()),
new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
reportActivity(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
reportSubscriptionInfo(sessionInfo, attributeSubscriptions.contains(sessionId), rpcSubscriptions.contains(sessionId));
break;
case CLAIM_REQUEST:
transportService.process(sessionInfo,
@ -264,49 +282,52 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
break;
case SUBSCRIBE_ATTRIBUTES_REQUEST:
TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
TransportProtos.SessionInfoProto currentAttrSession = tokenToSessionInfoMap.get(getTokenFromRequest(request));
if (currentAttrSession == null) {
attributeSubscriptions.add(sessionId);
registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor,
transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), getTokenFromRequest(request));
transportService.process(sessionInfo,
TransportProtos.SubscribeToAttributeUpdatesMsg.getDefaultInstance(), new CoapNoOpCallback(exchange));
transportService.process(sessionInfo,
TransportProtos.GetAttributeRequestMsg.newBuilder().setOnlyShared(true).build(),
new CoapNoOpCallback(exchange));
}
break;
case UNSUBSCRIBE_ATTRIBUTES_REQUEST:
TransportProtos.SessionInfoProto attrSession = lookupAsyncSessionInfo(getTokenFromRequest(request));
if (attrSession != null) {
UUID attrSessionId = new UUID(attrSession.getSessionIdMSB(), attrSession.getSessionIdLSB());
UUID attrSessionId = toSessionId(attrSession);
attributeSubscriptions.remove(attrSessionId);
sessionInfoToObserveRelationMap.remove(attrSession);
transportService.process(attrSession,
TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setUnsubscribe(true).build(),
new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
closeAndDeregister(sessionInfo, sessionId);
closeAndDeregister(sessionInfo);
}
break;
case SUBSCRIBE_RPC_COMMANDS_REQUEST:
TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionIdMap.get(getTokenFromRequest(request));
TransportProtos.SessionInfoProto currentRpcSession = tokenToSessionInfoMap.get(getTokenFromRequest(request));
if (currentRpcSession == null) {
rpcSubscriptions.add(sessionId);
registerAsyncCoapSession(exchange, sessionInfo, coapTransportAdaptor,
transportConfigurationContainer.getRpcRequestDynamicMessageBuilder(), getTokenFromRequest(request));
transportService.process(sessionInfo,
TransportProtos.SubscribeToRPCMsg.getDefaultInstance(),
new CoapNoOpCallback(exchange));
} else {
UUID rpcSessionId = new UUID(currentRpcSession.getSessionIdMSB(), currentRpcSession.getSessionIdLSB());
reportActivity(currentRpcSession, attributeSubscriptions.contains(rpcSessionId), rpcSubscriptions.contains(rpcSessionId));
new CoapOkCallback(exchange, CoAP.ResponseCode.VALID, CoAP.ResponseCode.INTERNAL_SERVER_ERROR)
);
}
break;
case UNSUBSCRIBE_RPC_COMMANDS_REQUEST:
TransportProtos.SessionInfoProto rpcSession = lookupAsyncSessionInfo(getTokenFromRequest(request));
if (rpcSession != null) {
UUID rpcSessionId = new UUID(rpcSession.getSessionIdMSB(), rpcSession.getSessionIdLSB());
UUID rpcSessionId = toSessionId(rpcSession);
rpcSubscriptions.remove(rpcSessionId);
sessionInfoToObserveRelationMap.remove(rpcSession);
transportService.process(rpcSession,
TransportProtos.SubscribeToRPCMsg.newBuilder().setUnsubscribe(true).build(),
new CoapOkCallback(exchange, CoAP.ResponseCode.DELETED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
closeAndDeregister(sessionInfo, sessionId);
closeAndDeregister(sessionInfo);
}
break;
case TO_DEVICE_RPC_RESPONSE:
@ -329,10 +350,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
new CoapNoOpCallback(exchange));
break;
case GET_FIRMWARE_REQUEST:
getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE);
getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.FIRMWARE);
break;
case GET_SOFTWARE_REQUEST:
getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE);
getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.SOFTWARE);
break;
}
} catch (AdaptorException e) {
@ -341,29 +362,33 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
}
}
private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) {
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
private UUID toSessionId(TransportProtos.SessionInfoProto sessionInfoProto) {
return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB());
}
private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) {
TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder()
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange));
transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange));
}
private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) {
tokenToNotificationCounterMap.remove(token);
return tokenToSessionIdMap.remove(token);
tokenToObserveNotificationSeqMap.remove(token);
return tokenToSessionInfoMap.remove(token);
}
private void registerAsyncCoapSession(CoapExchange exchange, TransportProtos.SessionInfoProto sessionInfo, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder, String token) {
tokenToSessionIdMap.putIfAbsent(token, sessionInfo);
tokenToSessionInfoMap.putIfAbsent(token, sessionInfo);
transportService.registerAsyncSession(sessionInfo, getCoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder));
transportService.process(sessionInfo, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null);
}
private CoapSessionListener getCoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
return new CoapSessionListener(exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder);
return new CoapSessionListener(this, exchange, coapTransportAdaptor, rpcRequestDynamicMessageBuilder);
}
private String getTokenFromRequest(Request request) {
@ -445,25 +470,25 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
}
}
private class FirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
private class OtaPackageCallback implements TransportServiceCallback<TransportProtos.GetOtaPackageResponseMsg> {
private final CoapExchange exchange;
FirmwareCallback(CoapExchange exchange) {
OtaPackageCallback(CoapExchange exchange) {
this.exchange = exchange;
}
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg msg) {
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) {
String title = exchange.getQueryParameter("title");
String version = exchange.getQueryParameter("version");
if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) {
if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) {
String firmwareId = new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()).toString();
String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString();
String strChunkSize = exchange.getQueryParameter("size");
String strChunk = exchange.getQueryParameter("chunk");
int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize);
int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk);
exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getFirmwareDataCache().get(firmwareId, chunkSize, chunk));
exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk));
} else {
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
}
@ -481,11 +506,13 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
private static class CoapSessionListener implements SessionMsgListener {
private final CoapTransportResource coapTransportResource;
private final CoapExchange exchange;
private final CoapTransportAdaptor coapTransportAdaptor;
private final DynamicMessage.Builder rpcRequestDynamicMessageBuilder;
CoapSessionListener(CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
CoapSessionListener(CoapTransportResource coapTransportResource, CoapExchange exchange, CoapTransportAdaptor coapTransportAdaptor, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) {
this.coapTransportResource = coapTransportResource;
this.exchange = exchange;
this.coapTransportAdaptor = coapTransportAdaptor;
this.rpcRequestDynamicMessageBuilder = rpcRequestDynamicMessageBuilder;
@ -494,7 +521,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
@Override
public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg msg) {
try {
exchange.respond(coapTransportAdaptor.convertToPublish(msg));
exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg));
} catch (AdaptorException e) {
log.trace("Failed to reply due to error", e);
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
@ -512,8 +539,21 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
}
@Override
public void onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
exchange.respond(CoAP.ResponseCode.SERVICE_UNAVAILABLE);
public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) {
log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage());
Map<TransportProtos.SessionInfoProto, ObserveRelation> sessionToObserveRelationMap = coapTransportResource.getSessionInfoToObserveRelationMap();
if (coapTransportResource.getObserverCount() > 0 && !CollectionUtils.isEmpty(sessionToObserveRelationMap)) {
Set<TransportProtos.SessionInfoProto> observeSessions = sessionToObserveRelationMap.keySet();
Optional<TransportProtos.SessionInfoProto> observeSessionToClose = observeSessions.stream().filter(sessionInfoProto -> {
UUID observeSessionId = new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB());
return observeSessionId.equals(sessionId);
}).findFirst();
if (observeSessionToClose.isPresent()) {
TransportProtos.SessionInfoProto sessionInfoProto = observeSessionToClose.get();
ObserveRelation observeRelation = sessionToObserveRelationMap.get(sessionInfoProto);
coapTransportResource.clearAndNotifyObserveRelation(observeRelation, CoAP.ResponseCode.SERVICE_UNAVAILABLE);
}
}
}
@Override
@ -529,7 +569,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
@Override
public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg msg) {
try {
exchange.respond(coapTransportAdaptor.convertToPublish(msg));
exchange.respond(coapTransportAdaptor.convertToPublish(isConRequest(), msg));
} catch (AdaptorException e) {
log.trace("Failed to reply due to error", e);
exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
@ -561,29 +601,29 @@ public class CoapTransportResource extends AbstractCoapTransportResource {
@Override
public void addedObserveRelation(ObserveRelation relation) {
if (log.isTraceEnabled()) {
Request request = relation.getExchange().getRequest();
log.trace("Added Observe relation for token: {}", getTokenFromRequest(request));
}
Request request = relation.getExchange().getRequest();
String token = getTokenFromRequest(request);
sessionInfoToObserveRelationMap.putIfAbsent(tokenToSessionInfoMap.get(token), relation);
log.trace("Added Observe relation for token: {}", token);
}
@Override
public void removedObserveRelation(ObserveRelation relation) {
Request request = relation.getExchange().getRequest();
String tokenFromRequest = getTokenFromRequest(request);
log.trace("Relation removed for token: {}", tokenFromRequest);
TransportProtos.SessionInfoProto sessionInfoToRemove = lookupAsyncSessionInfo(tokenFromRequest);
if (sessionInfoToRemove != null) {
closeAndDeregister(sessionInfoToRemove, new UUID(sessionInfoToRemove.getSessionIdMSB(), sessionInfoToRemove.getDeviceIdLSB()));
}
String token = getTokenFromRequest(request);
TransportProtos.SessionInfoProto session = tokenToSessionInfoMap.get(token);
sessionInfoToObserveRelationMap.remove(session);
log.trace("Relation removed for token: {}", token);
}
}
private void closeAndDeregister(TransportProtos.SessionInfoProto session, UUID sessionId) {
private void closeAndDeregister(TransportProtos.SessionInfoProto session) {
UUID sessionId = toSessionId(session);
transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.CLOSED), null);
transportService.deregisterSession(session);
rpcSubscriptions.remove(sessionId);
attributeSubscriptions.remove(sessionId);
sessionInfoToObserveRelationMap.remove(session);
}
private TransportConfigurationContainer getTransportConfigurationContainer(DeviceProfile deviceProfile) throws AdaptorException {

1
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportService.java

@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.coapserver.CoapServerService;

1
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java

@ -40,6 +40,7 @@ public class CoapAdaptorUtils {
result.addAllSharedAttributeNames(sharedKeys);
}
}
result.setOnlyShared(false);
return result.build();
}

4
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapTransportAdaptor.java

@ -39,13 +39,13 @@ public interface CoapTransportAdaptor {
TransportProtos.ClaimDeviceMsg convertToClaimDevice(UUID sessionId, Request inbound, TransportProtos.SessionInfoProto sessionInfo) throws AdaptorException;
Response convertToPublish(TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException;
Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException;
Response convertToPublish(boolean isConfirmable, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException;
Response convertToPublish(boolean isConfirmable, TransportProtos.ToDeviceRpcRequestMsg rpcRequest, DynamicMessage.Builder rpcRequestDynamicMessageBuilder) throws AdaptorException;
Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException;
Response convertToPublish(boolean isConfirmable, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException;
ProvisionDeviceRequestMsg convertToProvisionRequestMsg(UUID sessionId, Request inbound) throws AdaptorException;

37
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java

@ -26,12 +26,14 @@ import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.coap.CoapTransportResource;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ -101,10 +103,11 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
}
@Override
public Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
public Response convertToPublish(boolean isConfirmable, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
Response response = new Response(CoAP.ResponseCode.CONTENT);
JsonElement result = JsonConverter.toJson(msg);
response.setPayload(result.toString());
response.setAcknowledged(isConfirmable);
return response;
}
@ -119,21 +122,35 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor {
}
@Override
public Response convertToPublish(TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) {
return new Response(CoAP.ResponseCode.NOT_FOUND);
public Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
if (msg.getSharedStateMsg()) {
if (StringUtils.isEmpty(msg.getError())) {
Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
response.setAcknowledged(isConfirmable);
TransportProtos.AttributeUpdateNotificationMsg notificationMsg = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(msg.getSharedAttributeListList()).build();
JsonObject result = JsonConverter.toJson(notificationMsg);
response.setPayload(result.toString());
return response;
} else {
return new Response(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
}
} else {
Response response = new Response(CoAP.ResponseCode.CONTENT);
JsonObject result = JsonConverter.toJson(msg);
response.setPayload(result.toString());
return response;
if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) {
return new Response(CoAP.ResponseCode.NOT_FOUND);
} else {
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setAcknowledged(isConfirmable);
JsonObject result = JsonConverter.toJson(msg);
response.setPayload(result.toString());
return response;
}
}
}
private Response getObserveNotification(boolean confirmable, JsonElement json) {
Response response = new Response(CoAP.ResponseCode.CONTENT);
Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
response.setPayload(json.toString());
response.setConfirmable(confirmable);
response.setAcknowledged(confirmable);
return response;
}

34
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java

@ -16,6 +16,7 @@
package org.thingsboard.server.transport.coap.adaptors;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
@ -26,6 +27,7 @@ import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.coap.Response;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
@ -118,27 +120,41 @@ public class ProtoCoapAdaptor implements CoapTransportAdaptor {
}
@Override
public Response convertToPublish(TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
public Response convertToPublish(boolean isConfirmable, TransportProtos.ToServerRpcResponseMsg msg) throws AdaptorException {
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setAcknowledged(isConfirmable);
response.setPayload(msg.toByteArray());
return response;
}
@Override
public Response convertToPublish(TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) {
return new Response(CoAP.ResponseCode.NOT_FOUND);
public Response convertToPublish(boolean isConfirmable, TransportProtos.GetAttributeResponseMsg msg) throws AdaptorException {
if (msg.getSharedStateMsg()) {
if (StringUtils.isEmpty(msg.getError())) {
Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
response.setAcknowledged(isConfirmable);
TransportProtos.AttributeUpdateNotificationMsg notificationMsg = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(msg.getSharedAttributeListList()).build();
response.setPayload(notificationMsg.toByteArray());
return response;
} else {
return new Response(CoAP.ResponseCode.INTERNAL_SERVER_ERROR);
}
} else {
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setPayload(msg.toByteArray());
return response;
if (msg.getClientAttributeListCount() == 0 && msg.getSharedAttributeListCount() == 0) {
return new Response(CoAP.ResponseCode.NOT_FOUND);
} else {
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setAcknowledged(isConfirmable);
response.setPayload(msg.toByteArray());
return response;
}
}
}
private Response getObserveNotification(boolean confirmable, byte[] notification) {
Response response = new Response(CoAP.ResponseCode.CONTENT);
Response response = new Response(CoAP.ResponseCode._UNKNOWN_SUCCESS_CODE);
response.setPayload(notification);
response.setConfirmable(confirmable);
response.setAcknowledged(confirmable);
return response;
}

103
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/NoSecObserveClient.java

@ -0,0 +1,103 @@
/**
* 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.transport.coap.client;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapHandler;
import org.eclipse.californium.core.CoapObserveRelation;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Request;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class NoSecObserveClient {
private static final long INFINIT_EXCHANGE_LIFETIME = 0L;
private CoapClient coapClient;
private CoapObserveRelation observeRelation;
private ExecutorService executor = Executors.newFixedThreadPool(1);
private CountDownLatch latch;
public NoSecObserveClient(String host, int port, String accessToken) throws URISyntaxException {
URI uri = new URI(getFutureUrl(host, port, accessToken));
this.coapClient = new CoapClient(uri);
coapClient.setTimeout(INFINIT_EXCHANGE_LIFETIME);
this.latch = new CountDownLatch(5);
}
public void start() {
executor.submit(() -> {
try {
Request request = Request.newGet();
request.setObserve();
observeRelation = coapClient.observe(request, new CoapHandler() {
@Override
public void onLoad(CoapResponse response) {
String responseText = response.getResponseText();
CoAP.ResponseCode code = response.getCode();
Integer observe = response.getOptions().getObserve();
log.info("CoAP Response received! " +
"responseText: {}, " +
"code: {}, " +
"observe seq number: {}",
responseText,
code,
observe);
latch.countDown();
}
@Override
public void onError() {
log.error("Ack error!");
latch.countDown();
}
});
} catch (Exception e) {
log.error("Error occurred while sending COAP requests: ");
}
});
try {
latch.await();
observeRelation.proactiveCancel();
} catch (InterruptedException e) {
log.error("Error occurred: ", e);
}
}
private String getFutureUrl(String host, Integer port, String accessToken) {
return "coap://" + host + ":" + port + "/api/v1/" + accessToken + "/attributes";
}
public static void main(String[] args) throws URISyntaxException {
log.info("Usage: java -cp ... org.thingsboard.server.transport.coap.client.NoSecObserveClient " +
"host port accessToken");
String host = args[0];
int port = Integer.parseInt(args[1]);
String accessToken = args[2];
final NoSecObserveClient client = new NoSecObserveClient(host, port, accessToken);
client.start();
}
}

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

@ -87,7 +87,7 @@ public class CoapEfentoTransportResource extends AbstractCoapTransportResource {
transportService.process(sessionInfo,
transportContext.getEfentoCoapAdaptor().convertToPostTelemetry(sessionId, efentoMeasurements),
new CoapOkCallback(exchange, CoAP.ResponseCode.CREATED, CoAP.ResponseCode.INTERNAL_SERVER_ERROR));
reportActivity(sessionInfo, false, false);
reportSubscriptionInfo(sessionInfo, false, false);
} catch (AdaptorException e) {
log.error("[{}] Failed to decode Efento ProtoMeasurements: ", sessionId, e);
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);

35
common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java

@ -34,7 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.TbTransportService;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
@ -213,7 +213,7 @@ public class DeviceApiController implements TbTransportService {
@RequestParam(value = "version") String version,
@RequestParam(value = "size", required = false, defaultValue = "0") int size,
@RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE);
return getOtaPackageCallback(deviceToken, title, version, size, chunk, OtaPackageType.FIRMWARE);
}
@RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET)
@ -222,7 +222,7 @@ public class DeviceApiController implements TbTransportService {
@RequestParam(value = "version") String version,
@RequestParam(value = "size", required = false, defaultValue = "0") int size,
@RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) {
return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE);
return getOtaPackageCallback(deviceToken, title, version, size, chunk, OtaPackageType.SOFTWARE);
}
@RequestMapping(value = "/provision", method = RequestMethod.POST)
@ -233,17 +233,17 @@ public class DeviceApiController implements TbTransportService {
return responseWriter;
}
private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) {
private DeferredResult<ResponseEntity> getOtaPackageCallback(String deviceToken, String title, String version, int size, int chunk, OtaPackageType firmwareType) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(),
new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> {
TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder()
TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder()
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
.setTenantIdLSB(sessionInfo.getTenantIdLSB())
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setType(firmwareType.name()).build();
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk));
transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(responseWriter, title, version, size, chunk));
}));
return responseWriter;
}
@ -294,14 +294,14 @@ public class DeviceApiController implements TbTransportService {
}
}
private class GetFirmwareCallback implements TransportServiceCallback<TransportProtos.GetFirmwareResponseMsg> {
private class GetOtaPackageCallback implements TransportServiceCallback<TransportProtos.GetOtaPackageResponseMsg> {
private final DeferredResult<ResponseEntity> responseWriter;
private final String title;
private final String version;
private final int chuckSize;
private final int chuck;
GetFirmwareCallback(DeferredResult<ResponseEntity> responseWriter, String title, String version, int chuckSize, int chuck) {
GetOtaPackageCallback(DeferredResult<ResponseEntity> responseWriter, String title, String version, int chuckSize, int chuck) {
this.responseWriter = responseWriter;
this.title = title;
this.version = version;
@ -310,17 +310,17 @@ public class DeviceApiController implements TbTransportService {
}
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg firmwareResponseMsg) {
if (!TransportProtos.ResponseStatus.SUCCESS.equals(firmwareResponseMsg.getResponseStatus())) {
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg otaPackageResponseMsg) {
if (!TransportProtos.ResponseStatus.SUCCESS.equals(otaPackageResponseMsg.getResponseStatus())) {
responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND));
} else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) {
String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString();
ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareDataCache().get(firmwareId, chuckSize, chuck));
} else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) {
String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString();
ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chuckSize, chuck));
ResponseEntity<ByteArrayResource> response = ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName())
.header("x-filename", firmwareResponseMsg.getFileName())
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName())
.header("x-filename", otaPackageResponseMsg.getFileName())
.contentLength(resource.contentLength())
.contentType(parseMediaType(firmwareResponseMsg.getContentType()))
.contentType(parseMediaType(otaPackageResponseMsg.getContentType()))
.body(resource);
responseWriter.setResult(response);
} else {
@ -391,7 +391,8 @@ public class DeviceApiController implements TbTransportService {
}
@Override
public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
public void onRemoteSessionCloseCommand(UUID sessionId, SessionCloseNotificationProto sessionCloseNotification) {
log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage());
responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT));
}

351
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java

@ -23,6 +23,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mObject;
@ -38,13 +39,13 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.cache.firmware.FirmwareDataCache;
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.firmware.FirmwareKey;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUtil;
import org.thingsboard.server.common.data.id.FirmwareId;
import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUtil;
import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
@ -61,6 +62,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto;
import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
@ -85,8 +87,8 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED;
@ -99,21 +101,21 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.setValidTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey;
@ -125,32 +127,33 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
private ExecutorService registrationExecutor;
private ExecutorService updateRegistrationExecutor;
private ExecutorService unRegistrationExecutor;
private LwM2mValueConverterImpl converter;
public LwM2mValueConverterImpl converter;
private final TransportService transportService;
private final LwM2mTransportContext context;
public final LwM2MTransportServerConfig config;
public final FirmwareDataCache firmwareDataCache;
public final OtaPackageDataCache otaPackageDataCache;
public final LwM2mTransportServerHelper helper;
private final LwM2MJsonAdaptor adaptor;
private final LwM2mClientContext clientContext;
private final LwM2mTransportRequest lwM2mTransportRequest;
private final TbLwM2MDtlsSessionStore sessionStore;
public final LwM2mClientContext clientContext;
public final LwM2mTransportRequest lwM2mTransportRequest;
private final Map<UUID, Long> rpcSubscriptions;
public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
LwM2mClientContext clientContext,
@Lazy LwM2mTransportRequest lwM2mTransportRequest,
FirmwareDataCache firmwareDataCache,
OtaPackageDataCache otaPackageDataCache,
LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) {
this.transportService = transportService;
this.config = config;
this.helper = helper;
this.clientContext = clientContext;
this.lwM2mTransportRequest = lwM2mTransportRequest;
this.firmwareDataCache = firmwareDataCache;
this.otaPackageDataCache = otaPackageDataCache;
this.context = context;
this.adaptor = adaptor;
this.rpcSubscriptions = new ConcurrentHashMap<>();
this.sessionStore = sessionStore;
}
@ -193,8 +196,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
.setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build())
.build();
transportService.process(msg, null);
this.getInfoFirmwareUpdate(lwM2MClient);
this.getInfoSoftwareUpdate(lwM2MClient);
this.getInfoFirmwareUpdate(lwM2MClient, null);
this.getInfoSoftwareUpdate(lwM2MClient, null);
this.initLwM2mFromClientValue(registration, lwM2MClient);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId());
} else {
@ -241,16 +244,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
/**
* @param registration - Registration LwM2M Client
* @param observations - All paths observations before unReg
* !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
* @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
*/
public void unReg(Registration registration, Collection<Observation> observations) {
unRegistrationExecutor.submit(() -> {
try {
this.setCancelObservationsAll(registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
this.closeClientSession(registration);
;
} catch (Throwable t) {
log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId());
@ -285,12 +285,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override
public void setCancelObservationsAll(Registration registration) {
if (registration != null) {
lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL,
this.lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL,
null, null, this.config.getTimeout(), null);
// Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
// observations.forEach(observation -> lwM2mTransportRequest.sendAllRequest(registration,
// convertPathFromObjectIdToIdVer(observation.getPath().toString(), registration), OBSERVE_CANCEL,
// null, null, this.config.getTimeout(), null));
}
}
@ -338,7 +334,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
READ, pathIdVer, value);
this.sendLogsToThingsboard(msg, registration.getId());
rpcRequest.setValueMsg(String.format("%s", value));
this.sentRpcRequest(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE);
}
/**
@ -354,22 +350,23 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
if (msg.getSharedUpdatedCount() > 0) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo);
if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) {
log.warn ("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList());
msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey();
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName);
Object valueNew = getValueFromKvProto(tsKvProto.getKv());
if ((FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName)
if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName)
&& (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())))
|| (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName)
|| (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName)
&& (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) {
this.getInfoFirmwareUpdate(lwM2MClient);
} else if ((FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName)
this.getInfoFirmwareUpdate(lwM2MClient, null);
} else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName)
&& (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion())))
|| (FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName)
|| (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName)
&& (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) {
this.getInfoSoftwareUpdate(lwM2MClient);
this.getInfoSoftwareUpdate(lwM2MClient, null);
}
if (pathIdVer != null) {
ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config
@ -382,7 +379,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
LOG_LW2M_ERROR, pathIdVer, valueNew);
this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId());
}
} else {
} else if (!isFwSwWords(pathName)) {
log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew);
String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated",
LOG_LW2M_ERROR, pathName, valueNew);
@ -390,16 +387,19 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
});
} else if (msg.getSharedDeletedCount() > 0) {
} else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) {
msg.getSharedUpdatedList().forEach(tsKvProto -> {
String pathName = tsKvProto.getKv().getKey();
Object valueNew = getValueFromKvProto(tsKvProto.getKv());
if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) {
if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) {
lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew);
}
});
log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo);
}
else if (lwM2MClient == null) {
log.error ("OnAttributeUpdate, lwM2MClient is null");
}
}
/**
@ -438,124 +438,52 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider()));
}
/**
* #1 del from rpcSubscriptions by timeout
* #2 if not present in rpcSubscriptions by requestId: create new Lwm2mClientRpcRequest, after success - add requestId, timeout
*/
@Override
public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) {
log.warn("4) RPC-OK finish to [{}]", toDeviceRpcRequestMsg);
Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
try {
Registration registration = clientContext.getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())).getRegistration();
lwm2mClientRpcRequest = this.getDeviceRpcRequest(toDeviceRpcRequestMsg, sessionInfo, registration);
if (lwm2mClientRpcRequest.getErrorMsg() != null) {
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
} else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), lwm2mClientRpcRequest.getContentFormatName(),
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.config.getTimeout(), lwm2mClientRpcRequest);
}
} catch (Exception e) {
if (lwm2mClientRpcRequest == null) {
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
}
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
if (lwm2mClientRpcRequest.getErrorMsg() == null) {
lwm2mClientRpcRequest.setErrorMsg(e.getMessage());
}
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
}
}
private Lwm2mClientRpcRequest getDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest,
SessionInfoProto sessionInfo, Registration registration) throws IllegalArgumentException {
Lwm2mClientRpcRequest lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
try {
lwm2mClientRpcRequest.setRequestId(toDeviceRequest.getRequestId());
lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
lwm2mClientRpcRequest.setValidTypeOper(toDeviceRequest.getMethodName());
JsonObject rpcRequest = LwM2mTransportUtil.validateJson(toDeviceRequest.getParams());
if (rpcRequest != null) {
if (rpcRequest.has(lwm2mClientRpcRequest.keyNameKey)) {
String targetIdVer = this.getPresentPathIntoProfile(sessionInfo,
rpcRequest.get(lwm2mClientRpcRequest.keyNameKey).getAsString());
if (targetIdVer != null) {
lwm2mClientRpcRequest.setTargetIdVer(targetIdVer);
lwm2mClientRpcRequest.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s",
rpcRequest.get(lwm2mClientRpcRequest.keyNameKey).getAsString(), targetIdVer));
}
}
if (lwm2mClientRpcRequest.getTargetIdVer() == null) {
lwm2mClientRpcRequest.setValidTargetIdVerKey(rpcRequest, registration);
}
if (rpcRequest.has(lwm2mClientRpcRequest.contentFormatNameKey)) {
lwm2mClientRpcRequest.setValidContentFormatName(rpcRequest);
}
if (rpcRequest.has(lwm2mClientRpcRequest.timeoutInMsKey) && rpcRequest.get(lwm2mClientRpcRequest.timeoutInMsKey).getAsLong() > 0) {
lwm2mClientRpcRequest.setTimeoutInMs(rpcRequest.get(lwm2mClientRpcRequest.timeoutInMsKey).getAsLong());
}
if (rpcRequest.has(lwm2mClientRpcRequest.valueKey)) {
lwm2mClientRpcRequest.setValue(rpcRequest.get(lwm2mClientRpcRequest.valueKey).getAsString());
}
if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) {
ConcurrentHashMap<String, Object> params = new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
.getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType());
if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) {
ConcurrentHashMap<String, Object> paramsResourceId = convertParamsToResourceId(params, sessionInfo);
if (paramsResourceId.size() > 0) {
lwm2mClientRpcRequest.setParams(paramsResourceId);
}
} else {
lwm2mClientRpcRequest.setParams(params);
}
} else if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonArray()) {
new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey)
.getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType());
// #1
this.checkRpcRequestTimeout();
log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null";
LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName());
UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB());
if (!this.rpcSubscriptions.containsKey(requestUUID)) {
this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime());
Lwm2mClientRpcRequest lwm2mClientRpcRequest = null;
try {
Registration registration = clientContext.getClient(sessionInfo).getRegistration();
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this);
if (lwm2mClientRpcRequest.getErrorMsg() != null) {
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
} else {
lwM2mTransportRequest.sendAllRequest(registration, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(),
null,
lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(),
this.config.getTimeout(), lwm2mClientRpcRequest);
}
lwm2mClientRpcRequest.setSessionInfo(sessionInfo);
if (!(OBSERVE_READ_ALL == lwm2mClientRpcRequest.getTypeOper()
|| DISCOVER_All == lwm2mClientRpcRequest.getTypeOper()
|| OBSERVE_CANCEL == lwm2mClientRpcRequest.getTypeOper())
&& lwm2mClientRpcRequest.getTargetIdVer() == null) {
lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " +
lwm2mClientRpcRequest.keyNameKey + " is null or bad format");
} catch (Exception e) {
if (lwm2mClientRpcRequest == null) {
lwm2mClientRpcRequest = new Lwm2mClientRpcRequest();
}
/**
* EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance
*/
else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper()
|| WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper())
&& lwm2mClientRpcRequest.getTargetIdVer() != null
&& !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource()
|| new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) {
lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey
+ ". Only Resource or ResourceInstance can be this operation");
lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name());
if (lwm2mClientRpcRequest.getErrorMsg() == null) {
lwm2mClientRpcRequest.setErrorMsg(e.getMessage());
}
} else {
lwm2mClientRpcRequest.setErrorMsg("Params of request is bad Json format.");
this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo);
}
} catch (Exception e) {
throw new IllegalArgumentException(lwm2mClientRpcRequest.getErrorMsg());
}
return lwm2mClientRpcRequest;
}
private ConcurrentHashMap<String, Object> convertParamsToResourceId(ConcurrentHashMap<String, Object> params,
SessionInfoProto sessionInfo) {
ConcurrentHashMap<String, Object> paramsIdVer = new ConcurrentHashMap<>();
params.forEach((k, v) -> {
String targetIdVer = this.getPresentPathIntoProfile(sessionInfo, k);
if (targetIdVer != null) {
LwM2mPath targetId = new LwM2mPath(convertPathFromIdVerToObjectId(targetIdVer));
if (targetId.isResource()) {
paramsIdVer.put(String.valueOf(targetId.getResourceId()), v);
}
}
});
return paramsIdVer;
private void checkRpcRequestTimeout() {
Set<UUID> rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet());
rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove);
}
public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
public void sentRpcResponse(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) {
rpcRequest.setResponseCode(requestCode);
if (LOG_LW2M_ERROR.equals(typeMsg)) {
rpcRequest.setInfoMsg(null);
@ -578,6 +506,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
@Override
public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) {
log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
transportService.process(sessionInfo, toDeviceResponse, null);
}
@ -722,10 +651,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* set setClient_fw_info... = value
**/
if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getFwUpdate().initReadValue(this, lwM2mTransportRequest, path);
lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
}
if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
lwM2MClient.getSwUpdate().initReadValue(this, lwM2mTransportRequest, path);
lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path);
}
/**
@ -742,7 +671,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
&& (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
&& lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) {
lwM2MClient.getFwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
lwM2MClient.getFwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
} else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
&& lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) {
lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true);
@ -767,7 +696,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
&& (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) {
if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
&& lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) {
lwM2MClient.getSwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
lwM2MClient.getSwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest);
} else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
&& lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) {
lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true);
@ -958,10 +887,16 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) {
LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer);
ResourceModel.Type currentType = resourceValue.getType();
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
if (resourceValue != null) {
ResourceModel.Type currentType = resourceValue.getType();
ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer);
return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType,
new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)));
}
else {
return null;
}
}
/**
@ -970,11 +905,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @return - return value of Resource by idPath
*/
private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) {
LwM2mResource resourceValue = null;
if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) {
resourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
LwM2mResource lwm2mResourceValue = null;
ResourceValue resourceValue = lwM2MClient.getResources().get(path);
if (resourceValue != null) {
if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) {
lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource();
}
}
return resourceValue;
return lwm2mResourceValue;
}
/**
@ -1281,7 +1219,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
* @param name -
* @return -
*/
private String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) {
LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB()));
LwM2mClient lwM2mClient = clientContext.getClient(sessionInfo);
return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream()
@ -1301,10 +1239,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) {
try {
List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList();
this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo);
} catch (Exception e) {
log.error(String.valueOf(e));
log.error("", e);
}
}
@ -1320,22 +1257,28 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
*/
public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) {
LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo);
tsKvProtos.forEach(tsKvProto -> {
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, tsKvProto.getKv().getKey());
if (pathIdVer != null) {
// #1.1
if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) {
lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto);
} else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) {
lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto);
if (lwM2MClient != null) {
log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos);
tsKvProtos.forEach(tsKvProto -> {
String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, tsKvProto.getKv().getKey());
if (pathIdVer != null) {
// #1.1
if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) {
lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto);
} else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) {
lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto);
}
}
}
});
// #2.1
lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> {
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
});
});
// #2.1
lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> {
this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer),
getValueFromKvProto(tsKvProto.getKv()), pathIdVer);
});
}
else {
log.error("UpdateAttributeFromThingsboard, lwM2MClient is null");
}
}
/**
@ -1378,6 +1321,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
private void reportActivityAndRegister(SessionInfoProto sessionInfo) {
if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) {
transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo));
this.reportActivitySubscription(sessionInfo);
}
}
@ -1413,23 +1357,30 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
}
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) {
public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler serviceImpl = this;
transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.FIRMWARE.name()),
DefaultLwM2MTransportMsgHandler handler = this;
this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()),
new TransportServiceCallback<>() {
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
&& response.getType().equals(FirmwareType.FIRMWARE.name())) {
&& response.getType().equals(OtaPackageType.FIRMWARE.name())) {
log.warn ("7) firmware start with ver: [{}]", response.getVersion());
lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest);
lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion());
lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
if (rpcRequest == null) {
lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
}
else {
lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
}
} else {
log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
}
}
@ -1442,21 +1393,28 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
}
public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient) {
public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) {
if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) {
SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient);
if (sessionInfo != null) {
DefaultLwM2MTransportMsgHandler serviceImpl = this;
transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.SOFTWARE.name()),
DefaultLwM2MTransportMsgHandler handler = this;
transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()),
new TransportServiceCallback<>() {
@Override
public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) {
public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) {
if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())
&& response.getType().equals(FirmwareType.SOFTWARE.name())) {
&& response.getType().equals(OtaPackageType.SOFTWARE.name())) {
lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest);
lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion());
lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle());
lwM2MClient.getSwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId());
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
if (rpcRequest == null) {
lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
}
else {
lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest);
}
} else {
log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
}
@ -1471,8 +1429,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
}
}
private TransportProtos.GetFirmwareRequestMsg createFirmwareRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) {
return TransportProtos.GetFirmwareRequestMsg.newBuilder()
private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) {
return TransportProtos.GetOtaPackageRequestMsg.newBuilder()
.setDeviceIdMSB(sessionInfo.getDeviceIdMSB())
.setDeviceIdLSB(sessionInfo.getDeviceIdLSB())
.setTenantIdMSB(sessionInfo.getTenantIdMSB())
@ -1510,4 +1468,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
return this.config;
}
private void reportActivitySubscription(TransportProtos.SessionInfoProto sessionInfo) {
transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(true)
.setRpcSubscription(true)
.setLastActivityTime(System.currentTimeMillis())
.build(), TransportServiceCallback.EMPTY);
}
}

98
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java

@ -23,26 +23,26 @@ public class LwM2mNetworkConfig {
NetworkConfig coapConfig = new NetworkConfig();
coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec);
coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort);
/*
Example:Property for large packet:
#NetworkConfig config = new NetworkConfig();
#config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
#config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
#config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
#config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
#config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
/**
Example:Property for large packet:
#NetworkConfig config = new NetworkConfig();
#config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32);
#config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32);
#config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048);
#config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3);
#config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000);
*/
/*
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required.
CoAP client will try to use block mode
or adapt the block size when receiving a 4.13 Entity too large response code
/**
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
- value of true indicate that the server will response with block2 option event if no further blocks are required.
CoAP client will try to use block mode
or adapt the block size when receiving a 4.13 Entity too large response code
*/
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true);
/*
/**
Property to indicate if the response should always include the Block2 option \
when client request early blockwise negociation but the response can be sent on one packet.
- value of false indicate that the server will respond without block2 option if no further blocks are required.
@ -51,44 +51,44 @@ public class LwM2mNetworkConfig {
coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true);
coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000);
/*
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted
as the payload of a POST/PUT or the response to a GET request in a
transparent> blockwise transfer.
This option serves as a safeguard against excessive memory
consumption when many resources contain large bodies that cannot be
transferred in a single CoAP message. This option has no impact on
*manually* managed blockwise transfers in which the blocks are handled individually.
Note that this option does not prevent local clients or resource
implementations from sending large bodies as part of a request or response to a peer.
The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
/**
!!! REQUEST_ENTITY_TOO_LARGE CODE=4.13
The maximum size of a resource body (in bytes) that will be accepted
as the payload of a POST/PUT or the response to a GET request in a
transparent> blockwise transfer.
This option serves as a safeguard against excessive memory
consumption when many resources contain large bodies that cannot be
transferred in a single CoAP message. This option has no impact on
*manually* managed blockwise transfers in which the blocks are handled individually.
Note that this option does not prevent local clients or resource
implementations from sending large bodies as part of a request or response to a peer.
The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192
A value of {@code 0} turns off transparent handling of blockwise transfers altogether.
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 256 * 1024 * 1024);
/*
The default DTLS response matcher.
Supported values are STRICT, RELAXED, or PRINCIPAL.
The default value is STRICT.
Create new instance of udp endpoint context matcher.
Params:
checkAddress
true with address check, (STRICT, UDP)
- false, without
/**
The default DTLS response matcher.
Supported values are STRICT, RELAXED, or PRINCIPAL.
The default value is STRICT.
Create new instance of udp endpoint context matcher.
Params:
checkAddress
true with address check, (STRICT, UDP)
- false, without
*/
coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT");
/*
https://tools.ietf.org/html/rfc7959#section-2.9.3
The block size (number of bytes) to use when doing a blockwise transfer. \
This value serves as the upper limit for block size in blockwise transfers
/**
https://tools.ietf.org/html/rfc7959#section-2.9.3
The block size (number of bytes) to use when doing a blockwise transfer. \
This value serves as the upper limit for block size in blockwise transfers
*/
coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 1024);
/*
The maximum payload size (in bytes) that can be transferred in a
single message, i.e. without requiring a blockwise transfer.
NB: this value MUST be adapted to the maximum message size supported by the transport layer.
In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
DEFAULT_VALUE = 1024
/**
The maximum payload size (in bytes) that can be transferred in a
single message, i.e. without requiring a blockwise transfer.
NB: this value MUST be adapted to the maximum message size supported by the transport layer.
In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol
DEFAULT_VALUE = 1024
*/
coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024);
@ -96,4 +96,4 @@ public class LwM2mNetworkConfig {
return coapConfig;
}
}
}

6
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mServerListener.java

@ -94,12 +94,6 @@ public class LwM2mServerListener {
@Override
public void onResponse(Observation observation, Registration registration, ObserveResponse response) {
if (registration != null) {
// if (observation.getPath().isResource() || observation.getPath().isResourceInstance()) {
// String msg = String.format("%s: Successful Observation %s.", LOG_LW2M_INFO,
// observation.getPath());
// log.warn(msg);
// service.sendLogsToThingsboard(msg, registration.getId());
// }
service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(),
registration), response, null);
}

5
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java

@ -32,6 +32,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseM
import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
import java.util.Optional;
import java.util.UUID;
@Slf4j
public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener {
@ -54,8 +55,8 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
}
@Override
public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
log.info("[{}] sessionCloseNotification", sessionCloseNotification);
public void onRemoteSessionCloseCommand(UUID sessionId, SessionCloseNotificationProto sessionCloseNotification) {
log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage());
}
@Override

272
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java

@ -22,6 +22,8 @@ import org.eclipse.californium.core.coap.Response;
import org.eclipse.leshan.core.Link;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.node.LwM2mObject;
import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
@ -34,6 +36,7 @@ import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.ExecuteRequest;
import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ReadRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.core.request.exception.ClientSleepingException;
import org.eclipse.leshan.core.response.DeleteResponse;
@ -68,8 +71,8 @@ import java.util.stream.Collectors;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST;
import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID;
@ -79,10 +82,10 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_All;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL;
@ -123,86 +126,106 @@ public class LwM2mTransportRequest {
*/
public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper,
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) {
String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) {
try {
String target = convertPathFromIdVerToObjectId(targetIdVer);
ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT;
LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration);
LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null;
if (!OBSERVE_READ_ALL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) {
if (lwM2MClient.isValidObjectVersion(targetIdVer)) {
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
DownlinkRequest request = createRequest (registration, lwM2MClient, typeOper, contentFormat, target,
targetIdVer, resultIds, params, rpcRequest);
DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target,
targetIdVer, resultIds, params, lwm2mClientRpcRequest);
if (request != null) {
try {
this.sendRequest(registration, lwM2MClient, request, timeoutInMs, rpcRequest);
this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest);
} catch (ClientSleepingException e) {
DownlinkRequest finalRequest = request;
long finalTimeoutInMs = timeoutInMs;
Lwm2mClientRpcRequest finalRpcRequest = rpcRequest;
Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest;
lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest));
} catch (Exception e) {
log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e);
}
}
else if (WRITE_UPDATE.name().equals(typeOper.name())) {
Lwm2mClientRpcRequest rpcRequestClone = (Lwm2mClientRpcRequest) rpcRequest.clone();
if (rpcRequestClone != null) {
} else if (WRITE_UPDATE.name().equals(typeOper.name())) {
if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s params is not valid", targetIdVer);
handler.sentRpcRequest(rpcRequestClone, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
rpcRequest = null;
handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
}
}
else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) {
} else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) {
if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s object model is absent", targetIdVer);
handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
}
} else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) {
log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer);
if (rpcRequest != null) {
if (lwm2mClientRpcRequest != null) {
ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
}
} else if (rpcRequest != null) {
} else if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s not found in object version", targetIdVer);
handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
} else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_All.name().equals(typeOper.name())) {
Set<String> paths;
if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
} else {
assert registration != null;
Link[] objectLinks = registration.getSortedObjectLinks();
paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet());
String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
typeOper.name(), paths);
handler.sendLogsToThingsboard(msg, registration.getId());
this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
if (rpcRequest != null) {
String valueMsg = String.format("Paths - %s", paths);
handler.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
} else {
switch (typeOper) {
case OBSERVE_READ_ALL:
case DISCOVER_ALL:
Set<String> paths;
if (OBSERVE_READ_ALL.name().equals(typeOper.name())) {
Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet());
} else {
assert registration != null;
Link[] objectLinks = registration.getSortedObjectLinks();
paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet());
}
String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
typeOper.name(), paths);
this.handler.sendLogsToThingsboard(msg, registration.getId());
if (lwm2mClientRpcRequest != null) {
String valueMsg = String.format("Paths - %s", paths);
this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
}
break;
case OBSERVE_CANCEL:
case OBSERVE_CANCEL_ALL:
int observeCancelCnt = 0;
String observeCancelMsg = null;
if (OBSERVE_CANCEL.name().equals(typeOper)) {
observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target);
observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), target, observeCancelCnt);
} else {
observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration);
observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), observeCancelCnt);
}
this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest);
break;
// lwm2mClientRpcRequest != null
case FW_UPDATE:
this.handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest);
break;
}
} else if (OBSERVE_CANCEL.name().equals(typeOper.name())) {
int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration);
String observeCancelMsgAll = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), observeCancelCnt);
this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, rpcRequest);
}
} catch (Exception e) {
String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
typeOper.name(), e.getMessage());
handler.sendLogsToThingsboard(msg, registration.getId());
if (rpcRequest != null) {
if (lwm2mClientRpcRequest != null) {
String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
}
}
}
private DownlinkRequest createRequest (Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
ContentFormat contentFormat, String target, String targetIdVer,
LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper,
ContentFormat contentFormat, String target, String targetIdVer,
LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) {
DownlinkRequest request = null;
switch (typeOper) {
case READ:
@ -212,7 +235,7 @@ public class LwM2mTransportRequest {
request = new DiscoverRequest(target);
break;
case OBSERVE:
String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer);
String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer);
log.warn(msg);
if (resultIds.isResource()) {
Set<Observation> observations = context.getServer().getObservationService().getObservations(registration);
@ -228,23 +251,14 @@ public class LwM2mTransportRequest {
request = new ObserveRequest(contentFormat, resultIds.getObjectId());
}
break;
case OBSERVE_CANCEL:
/*
lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout());
At server side this will not remove the observation from the observation store, to do it you need to use
{@code ObservationService#cancelObservation()}
*/
int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target);
String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO,
OBSERVE_CANCEL.name(), target, observeCancelCnt);
this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, rpcRequest);
break;
case EXECUTE:
ResourceModel resourceModelExe = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
if (params != null && !resourceModelExe.multiple) {
request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExe.type, ResourceModel.Type.STRING, resultIds));
} else {
request = new ExecuteRequest(target);
ResourceModel resourceModelExecute = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
if (resourceModelExecute != null) {
if (params != null && !resourceModelExecute.multiple) {
request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds));
} else {
request = new ExecuteRequest(target);
}
}
break;
case WRITE_REPLACE:
@ -255,10 +269,12 @@ public class LwM2mTransportRequest {
* JSON, TEXT;
**/
ResourceModel resourceModelWrite = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
registration, rpcRequest);
if (resourceModelWrite != null) {
contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat);
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(),
resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type,
registration, rpcRequest);
}
break;
case WRITE_UPDATE:
if (resultIds.isResource()) {
@ -297,7 +313,7 @@ public class LwM2mTransportRequest {
}
break;
case WRITE_ATTRIBUTES:
request = createWriteAttributeRequest(target, params);
request = createWriteAttributeRequest(target, params, this.handler);
break;
case DELETE:
request = new DeleteRequest(target);
@ -333,7 +349,7 @@ public class LwM2mTransportRequest {
}
/** Not Found */
if (rpcRequest != null) {
handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
}
/** Not Found
set setClient_fw_info... = empty
@ -375,7 +391,7 @@ public class LwM2mTransportRequest {
handler.sendLogsToThingsboard(msg, registration.getId());
log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
if (rpcRequest != null) {
handler.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
}
});
}
@ -421,7 +437,7 @@ public class LwM2mTransportRequest {
log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
if (rpcRequest != null) {
String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
handler.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
}
return null;
}
@ -452,68 +468,92 @@ public class LwM2mTransportRequest {
if (response instanceof ReadResponse) {
handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
} else if (response instanceof DeleteResponse) {
log.warn("[{}] Path [{}] DeleteResponse 5_Send", pathIdVer, response);
log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response);
if (rpcRequest != null) {
rpcRequest.setInfoMsg(null);
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null);
}
} else if (response instanceof DiscoverResponse) {
String discoverValue = Link.serialize(((DiscoverResponse)response).getObjectLinks());
String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks());
msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
handler.sendLogsToThingsboard(msgLog, registration.getId());
log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
if (rpcRequest != null) {
handler.sentRpcRequest(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
}
} else if (response instanceof ExecuteResponse) {
log.warn("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response);
msgLog = String.format("%s: type operation: %s path: %s",
LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString());
log.warn("9) [{}] ", msgLog);
handler.sendLogsToThingsboard(msgLog, registration.getId());
if (rpcRequest != null) {
msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg());
rpcRequest.setInfoMsg(msgLog);
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO);
}
} else if (response instanceof WriteAttributesResponse) {
log.warn("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response);
msgLog = String.format("%s: type operation: %s path: %s value: %s",
LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString());
handler.sendLogsToThingsboard(msgLog, registration.getId());
log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response);
if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE);
}
} else if (response instanceof WriteResponse) {
log.warn("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
this.infoWriteResponse(registration, response, request);
msgLog = String.format("Type operation: Write path: %s", pathIdVer);
log.warn("10) [{}] response: [{}]", msgLog, response);
this.infoWriteResponse(registration, response, request, rpcRequest);
handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
}
if (rpcRequest != null) {
if (response instanceof ExecuteResponse
|| response instanceof WriteAttributesResponse
|| response instanceof DeleteResponse) {
rpcRequest.setInfoMsg(null);
handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
} else if (response instanceof WriteResponse) {
handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
}
}
}
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) {
private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) {
try {
LwM2mNode node = ((WriteRequest) request).getNode();
String msg;
String msg = null;
Object value;
LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
int valueLength;
if (singleResource.getType() == ResourceModel.Type.STRING) {
valueLength = ((String) singleResource.getValue()).length();
value = ((String) singleResource.getValue())
.substring(Math.min(valueLength, config.getLogMaxLength()));
if (node instanceof LwM2mObject) {
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString());
} else if (node instanceof LwM2mObjectInstance) {
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint());
} else if (node instanceof LwM2mSingleResource) {
LwM2mSingleResource singleResource = (LwM2mSingleResource) node;
if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) {
int valueLength;
if (singleResource.getType() == ResourceModel.Type.STRING) {
valueLength = ((String) singleResource.getValue()).length();
value = ((String) singleResource.getValue())
.substring(Math.min(valueLength, config.getLogMaxLength())).trim();
} else {
valueLength = ((byte[]) singleResource.getValue()).length;
value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
Math.min(valueLength, config.getLogMaxLength()))).trim();
}
value = valueLength > config.getLogMaxLength() ? value + "..." : value;
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
} else {
valueLength = ((byte[]) singleResource.getValue()).length;
value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()),
Math.min(valueLength, config.getLogMaxLength())));
value = this.converter.convertValue(singleResource.getValue(),
singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
}
value = valueLength > config.getLogMaxLength() ? value + "..." : value;
msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value);
} else {
value = this.converter.convertValue(singleResource.getValue(),
singleResource.getType(), ResourceModel.Type.STRING, request.getPath());
msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s",
LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
}
if (msg != null) {
handler.sendLogsToThingsboard(msg, registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
this.afterWriteSuccessFwSwUpdate(registration, request);
if (rpcRequest != null) {
rpcRequest.setInfoMsg(msg);
}
}
else if (rpcRequest != null) {
handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO);
}
}
} catch (Exception e) {
@ -530,11 +570,11 @@ public class LwM2mTransportRequest {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
}
if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name());
lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
}
}
@ -545,21 +585,21 @@ public class LwM2mTransportRequest {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
}
if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name());
lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
}
}
private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) {
LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
lwM2MClient.getFwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
}
if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) {
lwM2MClient.getSwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
lwM2MClient.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
}
}
@ -568,7 +608,7 @@ public class LwM2mTransportRequest {
log.warn("[{}]", observeCancelMsg);
if (rpcRequest != null) {
rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));
handler.sentRpcRequest(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO);
handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO);
}
}
}

11
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java

@ -53,6 +53,8 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V;
@ -64,6 +66,13 @@ public class LwM2mTransportServerHelper {
private final LwM2mTransportContext context;
private final LwM2MJsonAdaptor adaptor;
private final AtomicInteger atomicTs = new AtomicInteger(0);
public long getTS() {
int addTs = atomicTs.getAndIncrement() >= 1000 ? atomicTs.getAndSet(0) : atomicTs.get();
return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + addTs;
}
/**
* send to Thingsboard Attribute || Telemetry
@ -96,7 +105,7 @@ public class LwM2mTransportServerHelper {
public void sendParametersOnThingsboardTelemetry(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) {
PostTelemetryMsg.Builder request = PostTelemetryMsg.newBuilder();
TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder();
builder.setTs(System.currentTimeMillis());
builder.setTs(this.getTS());
builder.addAllKv(result);
request.addTsKvList(builder.build());
PostTelemetryMsg postTelemetryMsg = request.build();

149
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java

@ -43,7 +43,10 @@ import org.eclipse.leshan.server.registration.Registration;
import org.nustaq.serialization.FSTConfiguration;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
import org.thingsboard.server.common.data.ota.OtaPackageKey;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
import org.thingsboard.server.common.data.ota.OtaPackageUtil;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
@ -61,9 +64,12 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION;
import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN;
import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN;
import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.eclipse.leshan.core.attributes.Attribute.STEP;
import static org.eclipse.leshan.core.model.ResourceModel.Type.BOOLEAN;
import static org.eclipse.leshan.core.model.ResourceModel.Type.FLOAT;
import static org.eclipse.leshan.core.model.ResourceModel.Type.INTEGER;
@ -71,12 +77,12 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK;
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING;
import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADING;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATED;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.VERIFIED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
@ -104,8 +110,7 @@ public class LwM2mTransportUtil {
public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
public static final String
LOG_LW2M_TELEMETRY = "LwM2MLog";
public static final String LOG_LW2M_TELEMETRY = "logLwm2m";
public static final String LOG_LW2M_INFO = "info";
public static final String LOG_LW2M_ERROR = "error";
public static final String LOG_LW2M_WARN = "warn";
@ -117,7 +122,24 @@ public class LwM2mTransportUtil {
public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized";
public static final String LWM2M_VERSION_DEFAULT = "1.0";
// FirmWare
// RPC
public static final String TYPE_OPER_KEY = "typeOper";
public static final String TARGET_ID_VER_KEY = "targetIdVer";
public static final String KEY_NAME_KEY = "key";
public static final String VALUE_KEY = "value";
public static final String PARAMS_KEY = "params";
public static final String SEPARATOR_KEY = ":";
public static final String FINISH_VALUE_KEY = ",";
public static final String START_JSON_KEY = "{";
public static final String FINISH_JSON_KEY = "}";
// public static final String contentFormatNameKey = "contentFormatName";
public static final String INFO_KEY = "info";
// public static final String TIME_OUT_IN_MS = "timeOutInMs";
public static final String RESULT_KEY = "result";
public static final String ERROR_KEY = "error";
public static final String METHOD_KEY = "methodName";
// Firmware
public static final String FW_UPDATE = "Firmware update";
public static final Integer FW_ID = 5;
// Package W
@ -133,7 +155,7 @@ public class LwM2mTransportUtil {
// Update E
public static final String FW_UPDATE_ID = "/5/0/2";
// SoftWare
// Software
public static final String SW_UPDATE = "Software update";
public static final Integer SW_ID = 9;
// Package W
@ -182,20 +204,21 @@ public class LwM2mTransportUtil {
*/
READ(0, "Read"),
DISCOVER(1, "Discover"),
DISCOVER_All(2, "DiscoverAll"),
DISCOVER_ALL(2, "DiscoverAll"),
OBSERVE_READ_ALL(3, "ObserveReadAll"),
/**
* POST
*/
OBSERVE(4, "Observe"),
OBSERVE_CANCEL(5, "ObserveCancel"),
EXECUTE(6, "Execute"),
OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"),
EXECUTE(7, "Execute"),
/**
* Replaces the Object Instance or the Resource(s) with the new value provided in the Write operation. (see
* section 5.3.3 of the LW M2M spec).
* if all resources are to be replaced
*/
WRITE_REPLACE(7, "WriteReplace"),
WRITE_REPLACE(8, "WriteReplace"),
/*
PUT
*/
@ -204,18 +227,17 @@ public class LwM2mTransportUtil {
* 5.3.3 of the LW M2M spec).
* if this is a partial update request
*/
WRITE_UPDATE(8, "WriteUpdate"),
WRITE_ATTRIBUTES(9, "WriteAttributes"),
DELETE(10, "Delete"),
WRITE_UPDATE(9, "WriteUpdate"),
WRITE_ATTRIBUTES(10, "WriteAttributes"),
DELETE(11, "Delete"),
// only for RPC
FW_READ_INFO(11, "FirmwareReadInfo"),
FW_UPDATE(12, "FirmwareUpdate"),
FW_UPDATE_URL(14, "FirmwareUpdateUrl"),
SW_READ_INFO(15, "SoftwareReadInfo"),
SW_UPDATE(16, "SoftwareUpdate"),
SW_UPDATE_URL(17, "SoftwareUpdateUrl"),
SW_UNINSTALL(18, "SoftwareUninstall");
FW_UPDATE(12,"FirmwareUpdate");
// FW_READ_INFO(12, "FirmwareReadInfo"),
// SW_READ_INFO(15, "SoftwareReadInfo"),
// SW_UPDATE(16, "SoftwareUpdate"),
// SW_UNINSTALL(18, "SoftwareUninstall");
public int code;
public String type;
@ -333,7 +355,7 @@ public class LwM2mTransportUtil {
* FirmwareUpdateStatus {
* DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
*/
public static FirmwareUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) {
public static OtaPackageUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) {
switch (updateResultFw) {
case INITIAL:
switch (stateFw) {
@ -479,7 +501,7 @@ public class LwM2mTransportUtil {
* FirmwareUpdateStatus {
* DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED
*/
public static FirmwareUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) {
public static OtaPackageUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) {
switch (updateResultSw) {
case INITIAL:
switch (updateStateSw) {
@ -817,26 +839,29 @@ public class LwM2mTransportUtil {
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st};
*/
public static DownlinkRequest createWriteAttributeRequest(String target, Object params) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params));
public static DownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
}
private static Attribute[] createWriteAttributes(Object params) {
private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MTransportMsgHandler serviceImpl, String target) {
List<Attribute> attributeLists = new ArrayList<>();
ObjectMapper oMapper = new ObjectMapper();
Map<String, Object> map = oMapper.convertValue(params, ConcurrentHashMap.class);
map.forEach((k, v) -> {
if (!v.toString().isEmpty() || (v.toString().isEmpty() && OBJECT_VERSION.equals(k))) {
attributeLists.add(new Attribute(k,
(DIMENSION.equals(k) || MINIMUM_PERIOD.equals(k) || MAXIMUM_PERIOD.equals(k)) ?
((Double) v).longValue() : v));
if (StringUtils.trimToNull(v.toString()) != null) {
Object attrValue = convertWriteAttributes(k, v, serviceImpl, target);
if (attrValue != null) {
Attribute attribute = createAttribute(k, attrValue);
if (attribute != null) {
attributeLists.add(new Attribute(k, attrValue));
}
}
}
});
return attributeLists.toArray(Attribute[]::new);
}
public static Set<String> convertJsonArrayToSet(JsonArray jsonArray) {
List<String> attributeListOld = new Gson().fromJson(jsonArray, new TypeToken<List<String>>() {
}.getType());
@ -863,4 +888,60 @@ public class LwM2mTransportUtil {
return null;
}
}
public static LwM2mTypeOper setValidTypeOper(String typeOper) {
try {
return LwM2mTransportUtil.LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper);
} catch (Exception e) {
return null;
}
}
public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MTransportMsgHandler serviceImpl, String target) {
switch (type) {
/** Integer [0:255]; */
case DIMENSION:
Long dim = (Long) serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
return dim >= 0 && dim <= 255 ? dim : null;
/**String;*/
case OBJECT_VERSION:
return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), STRING, new LwM2mPath(target));
/**INTEGER */
case MINIMUM_PERIOD:
case MAXIMUM_PERIOD:
return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), INTEGER, new LwM2mPath(target));
/**Float; */
case GREATER_THAN:
case LESSER_THAN:
case STEP:
if (value.getClass().getSimpleName().equals("String") ) {
value = Double.valueOf((String) value);
}
return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target));
default:
return null;
}
}
private static Attribute createAttribute(String key, Object attrValue) {
try {
return new Attribute(key, attrValue);
} catch (Exception e) {
log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage());
return null;
}
}
public static boolean isFwSwWords (String pathName) {
return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName)
|| OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName);
}
}

47
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java

@ -31,8 +31,8 @@ import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
@ -117,12 +117,11 @@ public class LwM2mClient implements Cloneable {
this.pendingReadRequests = new CopyOnWriteArrayList<>();
this.resources = new ConcurrentHashMap<>();
this.profileId = profileId;
this.sessionId = sessionId;
this.init = false;
this.queuedRequests = new ConcurrentLinkedQueue<>();
this.fwUpdate = new LwM2mFwSwUpdate(this, FirmwareType.FIRMWARE);
this.swUpdate = new LwM2mFwSwUpdate(this, FirmwareType.SOFTWARE);
this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE);
this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE);
if (this.credentials != null && this.credentials.hasDeviceInfo()) {
this.session = createSession(nodeId, sessionId, credentials);
this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB());
@ -194,17 +193,31 @@ public class LwM2mClient implements Cloneable {
public Object getResourceValue(String pathRezIdVer, String pathRezId) {
String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
if (this.resources.get(pathRez) != null) {
return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
this.resources.get(pathRez).getLwM2mResource().getValues() :
this.resources.get(pathRez).getLwM2mResource().getValue();
}
return null;
}
public Object getResourceName (String pathRezIdVer, String pathRezId) {
public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) {
String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
if (this.resources.get(pathRez) != null) {
return this.resources.get(pathRez).getResourceModel().name;
return this.resources.get(pathRez).getResourceModel().name;
}
return null;
}
public String getRezIdByResourceNameAndObjectInstanceId(String resourceName, String pathObjectInstanceIdVer, LwM2mModelProvider modelProvider) {
LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathObjectInstanceIdVer));
if (pathIds.isObjectInstance()) {
Set<Integer> rezIds = modelProvider.getObjectModel(registration)
.getObjectModel(pathIds.getObjectId()).resources.entrySet()
.stream()
.filter(map -> resourceName.equals(map.getValue().name))
.map(map -> map.getKey())
.collect(Collectors.toSet());
return rezIds.size() > 0 ? String.valueOf(rezIds.stream().findFirst().get()) : null;
}
return null;
}
@ -225,11 +238,11 @@ public class LwM2mClient implements Cloneable {
.getObjectModel(pathIds.getObjectId()) : null;
}
public String objectToString (LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) {
public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) {
StringBuilder builder = new StringBuilder();
builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={");
lwM2mObject.getInstances().forEach((instId, inst) -> {
builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", ");
builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", ");
});
int startInd = builder.lastIndexOf(", ");
if (startInd > 0) {
@ -238,11 +251,12 @@ public class LwM2mClient implements Cloneable {
builder.append("}]");
return builder.toString();
}
public String instanceToString (LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) {
public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) {
StringBuilder builder = new StringBuilder();
builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={");
objectInstance.getResources().forEach((resId, res) -> {
builder.append(resId).append("=").append(this.resourceToString (res, converter, pathIdVer)).append(", ");
builder.append(resId).append("=").append(this.resourceToString(res, converter, pathIdVer)).append(", ");
});
int startInd = builder.lastIndexOf(", ");
if (startInd > 0) {
@ -252,12 +266,11 @@ public class LwM2mClient implements Cloneable {
return builder.toString();
}
public String resourceToString (LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) {
public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) {
if (!OPAQUE.equals(lwM2mResource.getType())) {
return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() :
((LwM2mSingleResource) lwM2mResource).toString();
}
else {
} else {
return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(),
converter.convertValue(lwM2mResource.getValue(),
OPAQUE, STRING, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))), lwM2mResource.getType().name());
@ -275,7 +288,8 @@ public class LwM2mClient implements Cloneable {
resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(params,
equalsResourceTypeGetSimpleName(params), resourceModel.type, pathIds), resourceModel.type));
}});
}
});
return resources;
}
@ -291,7 +305,8 @@ public class LwM2mClient implements Cloneable {
resources.add(LwM2mSingleResource.newResource(resId,
converter.convertValue(value, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type));
}});
}
});
return resources;
}

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java

@ -35,8 +35,6 @@ public interface LwM2mClientContext {
LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo);
LwM2mClient getClient(UUID sessionId);
LwM2mClient getOrRegister(Registration registration);
LwM2mClient registerOrUpdate(Registration registration);

20
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java

@ -83,13 +83,17 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override
public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) {
return getClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()));
}
@Override
public LwM2mClient getClient(UUID sessionId) {
//TODO: refactor this to search by sessionId efficiently.
return lwM2mClientsByEndpoint.values().stream().filter(c -> c.getSessionId().equals(sessionId)).findAny().get();
LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c ->
(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()))
.equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB())))
).findAny().get();
if (lwM2mClient == null) {
log.warn("Device TimeOut? lwM2mClient is null.");
log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size());
log.error("", new RuntimeException());
}
return lwM2mClient;
}
@Override
@ -118,7 +122,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
securityInfo.getDeviceProfile().getUuidId() : null;
// TODO: for tests bug.
if (profileUuid== null) {
log.warn("input parameters toClientProfile if the result is null: [{}]", securityInfo.getDeviceProfile());
log.trace("input parameters toClientProfile if the result is null: [{}]", securityInfo.getDeviceProfile());
}
LwM2mClient client;
if (securityInfo.getSecurityInfo() != null) {

91
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java

@ -20,8 +20,8 @@ import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.leshan.core.request.ContentFormat;
import org.thingsboard.server.common.data.firmware.FirmwareType;
import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest;
@ -32,11 +32,12 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE;
import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey;
import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING;
import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey;
import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID;
@ -97,14 +98,17 @@ public class LwM2mFwSwUpdate {
@Getter
@Setter
private volatile boolean infoFwSwUpdate = false;
private final FirmwareType type;
private final OtaPackageType type;
@Getter
LwM2mClient lwM2MClient;
@Getter
@Setter
private final List<String> pendingInfoRequestsStart;
@Getter
@Setter
private volatile Lwm2mClientRpcRequest rpcRequest;
public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, FirmwareType type) {
public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type) {
this.lwM2MClient = lwM2MClient;
this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>();
this.type = type;
@ -139,7 +143,7 @@ public class LwM2mFwSwUpdate {
}
if (this.pendingInfoRequestsStart.size() == 0) {
this.infoFwSwUpdate = false;
if (!FirmwareUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) {
if (!OtaPackageUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) {
boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() :
this.conditionalSwUpdateStart();
if (conditionalStart) {
@ -153,16 +157,30 @@ public class LwM2mFwSwUpdate {
* Send FsSw to Lwm2mClient:
* before operation Write: fw_state = DOWNLOADING
*/
private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name();
// this.observeStateUpdate();
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
int chunkSize = 0;
int chunk = 0;
byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk);
String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
firmwareChunk, handler.config.getTimeout(), null);
public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
if (this.currentId != null) {
this.stateUpdate = OtaPackageUpdateStatus.DOWNLOADING.name();
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
int chunkSize = 0;
int chunk = 0;
byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk);
String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO,
LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID);
handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId());
log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer);
request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
firmwareChunk, handler.config.getTimeout(), this.rpcRequest);
}
else {
String msgError = "FirmWareId is null.";
log.warn("6) [{}]", msgError);
if (this.rpcRequest != null) {
handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR);
}
log.error (msgError);
this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
}
}
public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) {
@ -185,17 +203,16 @@ public class LwM2mFwSwUpdate {
this.setStateUpdate(UPDATING.name());
this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(),
null, 0, null);
null, 0, this.rpcRequest);
}
/**
* Firmware start:
* -- Если Update Result -errors (более 1) - Это означает что пред. апдейт не прошел.
* - Запускаем апдейт в независимости от состяния прошивки и ее версии.
* -- Если Update Result - не errors (менее или равно 1) и ver не пустой - Это означает что пред. апдейт прошел.
* -- Если Update Result - не errors и ver пустой - Это означает что апдейта еще не было.
* - Проверяем поменялась ли версия и запускаем новый апдейт.
* -- If the result of the update - errors (more than 1) - This means that the previous. the update failed.
* - We launch the update regardless of the state of the firmware and its version.
* -- If the result of the update is not errors (equal to 1 or 0) and ver is not empty - This means that before the update has passed.
* -- If the result of the update is not errors and is empty - This means that there has not been an update yet.
* - Check if the version has changed and launch a new update.
*/
private boolean conditionalFwUpdateStart() {
Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
@ -241,12 +258,12 @@ public class LwM2mFwSwUpdate {
/**
* Software start
* -- Если Update Result -errors (равно и более 50) - Это означает что пред. апдейт не прошел.
* * - Запускаем апдейт в независимости от состяния прошивки и ее версии.
* -- Если Update Result - не errors (менее 50) и ver не пустой - Это означает что пред. апдейт прошел.
* -- Если Update Result - не errors и ver пустой - Это означает что апдейта еще не было или пред. апдейт UnInstall
* -- Если Update Result - не errors и ver не пустой - Это означает что пред. апдейт UnInstall
* - Проверяем поменялась ли версия и запускаем новый апдейт.
* - If Update Result -errors (equal or more than 50) - This means that the previous. the update failed.
* * - We launch the update regardless of the state of the firmware and its version.
* - If Update Result is not errors (less than 50) and ver is not empty - This means that before. the update has passed.
* - If Update Result is not errors and ver is empty - This means that there was no update yet or before. UnInstall update
* - If Update Result is not errors and ver is not empty - This means that before unInstall update
* * - Check if the version has changed and launch a new update.
*/
private boolean conditionalSwUpdateStart() {
Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
@ -286,12 +303,12 @@ public class LwM2mFwSwUpdate {
Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type :
LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type;
String key = splitCamelCaseString((String) this.lwM2MClient.getResourceName(null, this.pathResultId));
String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId));
if (success) {
this.stateUpdate = FirmwareUpdateStatus.UPDATED.name();
this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name();
this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
} else {
this.stateUpdate = FirmwareUpdateStatus.FAILED.name();
this.stateUpdate = OtaPackageUpdateStatus.FAILED.name();
this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value);
}
handler.helper.sendParametersOnThingsboardTelemetry(
@ -348,7 +365,7 @@ public class LwM2mFwSwUpdate {
this.pathResultId, this.lwM2MClient.getRegistration()));
this.pendingInfoRequestsStart.forEach(pathIdVer -> {
request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(),
null, 0, null);
null, 0, this.rpcRequest);
});
}

279
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java

@ -15,94 +15,94 @@
*/
package org.thingsboard.server.transport.lwm2m.server.client;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.request.ContentFormat;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.server.registration.Registration;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.ERROR_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_JSON_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_VALUE_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.INFO_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.KEY_NAME_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.METHOD_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.PARAMS_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESULT_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SEPARATOR_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.START_JSON_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TARGET_ID_VER_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.VALUE_KEY;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer;
@Slf4j
@Data
public class Lwm2mClientRpcRequest {
public final String targetIdVerKey = "targetIdVer";
public final String keyNameKey = "key";
public final String typeOperKey = "typeOper";
public final String contentFormatNameKey = "contentFormatName";
public final String valueKey = "value";
public final String infoKey = "info";
public final String paramsKey = "params";
public final String timeoutInMsKey = "timeOutInMs";
public final String resultKey = "result";
public final String errorKey = "error";
public final String methodKey = "methodName";
private Registration registration;
private TransportProtos.SessionInfoProto sessionInfo;
private String bodyParams;
private int requestId;
private LwM2mTypeOper typeOper;
private String key;
private String targetIdVer;
private String contentFormatName;
private long timeoutInMs;
private Object value;
private ConcurrentHashMap<String, Object> params;
private SessionInfoProto sessionInfo;
private int requestId;
private Map<String, Object> params;
private String errorMsg;
private String valueMsg;
private String infoMsg;
private String responseCode;
public void setValidTypeOper(String typeOper) {
try {
this.typeOper = LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper);
} catch (Exception e) {
this.errorMsg = this.methodKey + " - " + typeOper + " is not valid.";
}
public Lwm2mClientRpcRequest() {
}
public void setValidContentFormatName(JsonObject rpcRequest) {
try {
if (ContentFormat.fromName(rpcRequest.get(this.contentFormatNameKey).getAsString()) != null) {
this.contentFormatName = rpcRequest.get(this.contentFormatNameKey).getAsString();
} else {
this.errorMsg = this.contentFormatNameKey + " - " + rpcRequest.get(this.contentFormatNameKey).getAsString() + " is not valid.";
}
} catch (Exception e) {
this.errorMsg = this.contentFormatNameKey + " - " + rpcRequest.get(this.contentFormatNameKey).getAsString() + " is not valid.";
public Lwm2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId,
TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) {
this.registration = registration;
this.sessionInfo = sessionInfo;
this.requestId = requestId;
if (lwM2mTypeOper != null) {
this.typeOper = lwM2mTypeOper;
} else {
this.errorMsg = METHOD_KEY + " - " + typeOper + " is not valid.";
}
}
public void setValidTargetIdVerKey(JsonObject rpcRequest, Registration registration) {
if (rpcRequest.has(this.targetIdVerKey)) {
String targetIdVerStr = rpcRequest.get(targetIdVerKey).getAsString();
// targetIdVer without ver - ok
try {
// targetIdVer with/without ver - ok
this.targetIdVer = validPathIdVer(targetIdVerStr, registration);
if (this.targetIdVer != null) {
this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer);
}
} catch (Exception e) {
if (this.targetIdVer == null) {
this.errorMsg = this.targetIdVerKey + " - " + targetIdVerStr + " is not valid.";
}
}
if (this.errorMsg == null && !bodyParams.equals("null")) {
this.bodyParams = bodyParams;
this.init(handler);
}
}
public TransportProtos.ToDeviceRpcResponseMsg getDeviceRpcResponseResultMsg() {
JsonObject payloadResp = new JsonObject();
payloadResp.addProperty(this.resultKey, this.responseCode);
payloadResp.addProperty(RESULT_KEY, this.responseCode);
if (this.errorMsg != null) {
payloadResp.addProperty(this.errorKey, this.errorMsg);
payloadResp.addProperty(ERROR_KEY, this.errorMsg);
} else if (this.valueMsg != null) {
payloadResp.addProperty(this.valueKey, this.valueMsg);
payloadResp.addProperty(VALUE_KEY, this.valueMsg);
} else if (this.infoMsg != null) {
payloadResp.addProperty(this.infoKey, this.infoMsg);
payloadResp.addProperty(INFO_KEY, this.infoMsg);
}
return TransportProtos.ToDeviceRpcResponseMsg.newBuilder()
.setPayload(payloadResp.getAsJsonObject().toString())
@ -110,13 +110,172 @@ public class Lwm2mClientRpcRequest {
.build();
}
@Override
public Object clone() {
private void init(DefaultLwM2MTransportMsgHandler handler) {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
log.error("", e);
// #1
if (this.bodyParams.contains(KEY_NAME_KEY)) {
String targetIdVerStr = this.getValueKeyFromBody(KEY_NAME_KEY);
if (targetIdVerStr != null) {
String targetIdVer = handler.getPresentPathIntoProfile(sessionInfo, targetIdVerStr);
if (targetIdVer != null) {
this.targetIdVer = targetIdVer;
this.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s",
targetIdVerStr, targetIdVer));
}
}
}
if (this.getTargetIdVer() == null && this.bodyParams.contains(TARGET_ID_VER_KEY)) {
this.setValidTargetIdVerKey();
}
if (this.bodyParams.contains(VALUE_KEY)) {
this.value = this.getValueKeyFromBody(VALUE_KEY);
}
try {
if (this.bodyParams.contains(PARAMS_KEY)) {
this.setValidParamsKey(handler);
}
} catch (Exception e) {
this.setErrorMsg(String.format("Params of request is bad Json format. %s", e.getMessage()));
}
if (this.getTargetIdVer() == null
&& !(OBSERVE_READ_ALL == this.getTypeOper()
|| DISCOVER_ALL == this.getTypeOper()
|| OBSERVE_CANCEL == this.getTypeOper()
|| FW_UPDATE == this.getTypeOper())) {
this.setErrorMsg(TARGET_ID_VER_KEY + " and " +
KEY_NAME_KEY + " is null or bad format");
}
/**
* EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance
*/
else if (this.getTargetIdVer() != null
&& (EXECUTE == this.getTypeOper()
|| WRITE_REPLACE == this.getTypeOper())
&& !(new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResource()
|| new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResourceInstance())) {
this.setErrorMsg("Invalid parameter " + TARGET_ID_VER_KEY
+ ". Only Resource or ResourceInstance can be this operation");
}
} catch (Exception e) {
this.setErrorMsg(String.format("Bad format request. %s", e.getMessage()));
}
}
private void setValidTargetIdVerKey() {
String targetIdVerStr = this.getValueKeyFromBody(TARGET_ID_VER_KEY);
// targetIdVer without ver - ok
try {
// targetIdVer with/without ver - ok
this.targetIdVer = validPathIdVer(targetIdVerStr, this.registration);
if (this.targetIdVer != null) {
this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer);
}
} catch (Exception e) {
if (this.targetIdVer == null) {
this.errorMsg = TARGET_ID_VER_KEY + " - " + targetIdVerStr + " is not valid.";
}
}
}
private void setValidParamsKey(DefaultLwM2MTransportMsgHandler handler) {
String paramsStr = this.getValueKeyFromBody(PARAMS_KEY);
if (paramsStr != null) {
String params2Json =
START_JSON_KEY
+ "\""
+ paramsStr
.replaceAll(SEPARATOR_KEY, "\"" + SEPARATOR_KEY + "\"")
.replaceAll(FINISH_VALUE_KEY, "\"" + FINISH_VALUE_KEY + "\"")
+ "\""
+ FINISH_JSON_KEY;
// jsonObject
Map<String, Object> params = new Gson().fromJson(params2Json, new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType());
if (WRITE_UPDATE == this.getTypeOper()) {
if (this.targetIdVer != null) {
Map<String, Object> paramsResourceId = this.convertParamsToResourceId((ConcurrentHashMap<String, Object>) params, handler);
if (paramsResourceId.size() > 0) {
this.setParams(paramsResourceId);
}
}
} else if (WRITE_ATTRIBUTES == this.getTypeOper()) {
this.setParams(params);
}
}
}
private String getValueKeyFromBody(String key) {
String valueKey = null;
int startInd = -1;
int finishInd = -1;
try {
switch (key) {
case KEY_NAME_KEY:
case TARGET_ID_VER_KEY:
case VALUE_KEY:
startInd = this.bodyParams.indexOf(SEPARATOR_KEY, this.bodyParams.indexOf(key));
finishInd = this.bodyParams.indexOf(FINISH_VALUE_KEY, this.bodyParams.indexOf(key));
if (startInd >= 0 && finishInd < 0) {
finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key));
}
break;
case PARAMS_KEY:
startInd = this.bodyParams.indexOf(START_JSON_KEY, this.bodyParams.indexOf(key));
finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key));
}
if (startInd >= 0 && finishInd > 0) {
valueKey = this.bodyParams.substring(startInd + 1, finishInd);
}
} catch (Exception e) {
log.error("", new TimeoutException());
}
/**
* ReplaceAll "\""
*/
if (StringUtils.trimToNull(valueKey) != null) {
char[] chars = valueKey.toCharArray();
for (int i = 0; i < chars.length; i++) {
if (chars[i] == 92 || chars[i] == 34) chars[i] = 32;
}
return key.equals(PARAMS_KEY) ? String.valueOf(chars) : String.valueOf(chars).replaceAll(" ", "");
}
return null;
}
private ConcurrentHashMap<String, Object> convertParamsToResourceId(ConcurrentHashMap<String, Object> params,
DefaultLwM2MTransportMsgHandler serviceImpl) {
Map<String, Object> paramsIdVer = new ConcurrentHashMap<>();
LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.targetIdVer)));
if (targetId.isObjectInstance()) {
params.forEach((k, v) -> {
try {
int id = Integer.parseInt(k);
paramsIdVer.put(String.valueOf(id), v);
} catch (NumberFormatException e) {
String targetIdVer = serviceImpl.getPresentPathIntoProfile(sessionInfo, k);
if (targetIdVer != null) {
LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(targetIdVer)));
paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v);
}
/** WRITE_UPDATE*/
else {
String rezId = this.getRezIdByResourceNameAndObjectInstanceId(k, serviceImpl);
if (rezId != null) {
paramsIdVer.put(rezId, v);
}
}
}
});
}
return (ConcurrentHashMap<String, Object>) paramsIdVer;
}
private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) {
LwM2mClient lwM2mClient = handler.clientContext.getClient(this.sessionInfo);
return lwM2mClient != null ?
lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) :
null;
}
}

15
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java

@ -214,9 +214,9 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
// Add or update expiration
addOrUpdateExpiration(connection, updatedRegistration);
// Update secondary index :
// If registration is already associated to this address we don't care as we only want to keep the most
// recent binding.
/** Update secondary index :
* If registration is already associated to this address we don't care as we only want to keep the most
* recent binding. */
byte[] addr_idx = toRegAddrKey(updatedRegistration.getSocketAddress());
connection.set(addr_idx, updatedRegistration.getEndpoint().getBytes(UTF_8));
if (!r.getSocketAddress().equals(updatedRegistration.getSocketAddress())) {
@ -337,23 +337,24 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto
}
}
//TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies
private void removeAddrIndex(RedisConnection connection, Registration registration) {
// Watch the key to remove.
byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress());
connection.watch(regAddrKey);
// connection.watch(regAddrKey);
byte[] epFromAddr = connection.get(regAddrKey);
// Delete the key if needed.
if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) {
// Try to delete the key
connection.multi();
// connection.multi();
connection.del(regAddrKey);
connection.exec();
// connection.exec();
// if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to
// delete it anymore.
} else {
// the key must not be deleted.
connection.unwatch();
// connection.unwatch();
}
}

3
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java

@ -43,6 +43,9 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter {
@Override
public Object convertValue(Object value, Type currentType, Type expectedType, LwM2mPath resourcePath)
throws CodecException {
if (value == null) {
return null;
}
if (expectedType == null) {
/** unknown resource, trusted value */
return value;

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

Loading…
Cancel
Save