Commit f07d9c64404eaa77de8f530558fdebce157cc063

Authored by Andrew Shvayka
Committed by GitHub
2 parents 99ef1cba 38ebbe25

Merge pull request #2667 from thingsboard/revert-2663-develop/3.0-merge

Revert "Merge with master. AlarmRepository.findAlarms is failing"
Showing 33 changed files with 1357 additions and 2423 deletions

Too many changes to show.

To preserve performance only 33 of 471 files are displayed.

1 -{  
2 - "title": "Gateways",  
3 - "configuration": {  
4 - "widgets": {  
5 - "94715984-ae74-76e4-20b7-2f956b01ed80": {  
6 - "isSystemType": true,  
7 - "bundleAlias": "entity_admin_widgets",  
8 - "typeAlias": "device_admin_table2",  
9 - "type": "latest",  
10 - "title": "New widget",  
11 - "sizeX": 24,  
12 - "sizeY": 12,  
13 - "config": {  
14 - "timewindow": {  
15 - "realtime": {  
16 - "interval": 1000,  
17 - "timewindowMs": 86400000  
18 - },  
19 - "aggregation": {  
20 - "type": "NONE",  
21 - "limit": 200  
22 - }  
23 - },  
24 - "showTitle": true,  
25 - "backgroundColor": "rgb(255, 255, 255)",  
26 - "color": "rgba(0, 0, 0, 0.87)",  
27 - "padding": "4px",  
28 - "settings": {  
29 - "enableSearch": true,  
30 - "displayPagination": true,  
31 - "defaultPageSize": 10,  
32 - "defaultSortOrder": "entityName",  
33 - "displayEntityName": true,  
34 - "displayEntityType": false,  
35 - "entitiesTitle": "List of gateways",  
36 - "enableSelectColumnDisplay": true,  
37 - "displayEntityLabel": false,  
38 - "entityNameColumnTitle": "Gateway Name"  
39 - },  
40 - "title": "Devices gateway table",  
41 - "dropShadow": true,  
42 - "enableFullscreen": true,  
43 - "titleStyle": {  
44 - "fontSize": "16px",  
45 - "fontWeight": 400,  
46 - "padding": "5px 10px 5px 10px"  
47 - },  
48 - "useDashboardTimewindow": false,  
49 - "showLegend": false,  
50 - "datasources": [  
51 - {  
52 - "type": "entity",  
53 - "dataKeys": [  
54 - {  
55 - "name": "active",  
56 - "type": "attribute",  
57 - "label": "Active",  
58 - "color": "#2196f3",  
59 - "settings": {  
60 - "columnWidth": "0px",  
61 - "useCellStyleFunction": true,  
62 - "useCellContentFunction": true,  
63 - "cellContentFunction": "value = '⬤';\nreturn value;",  
64 - "cellStyleFunction": "var color;\nif (value == 'false') {\n color = '#EB5757';\n} else {\n color = '#27AE60';\n}\nreturn {\n color: color,\n fontSize: '18px'\n};"  
65 - },  
66 - "_hash": 0.3646047595211721  
67 - },  
68 - {  
69 - "name": "eventsSent",  
70 - "type": "timeseries",  
71 - "label": "Sent",  
72 - "color": "#4caf50",  
73 - "settings": {  
74 - "columnWidth": "0px",  
75 - "useCellStyleFunction": false,  
76 - "useCellContentFunction": false  
77 - },  
78 - "_hash": 0.7235710720767985  
79 - },  
80 - {  
81 - "name": "eventsProduced",  
82 - "type": "timeseries",  
83 - "label": "Events",  
84 - "color": "#f44336",  
85 - "settings": {  
86 - "columnWidth": "0px",  
87 - "useCellStyleFunction": false,  
88 - "useCellContentFunction": false  
89 - },  
90 - "_hash": 0.5085933386303254  
91 - },  
92 - {  
93 - "name": "LOGS",  
94 - "type": "timeseries",  
95 - "label": "Latest log",  
96 - "color": "#ffc107",  
97 - "settings": {  
98 - "columnWidth": "0px",  
99 - "useCellStyleFunction": false,  
100 - "useCellContentFunction": false  
101 - },  
102 - "_hash": 0.3504240371585048,  
103 - "postFuncBody": "if(value) {\n return value.substring(0, 31) + \"...\";\n} else {\n return '';\n}"  
104 - },  
105 - {  
106 - "name": "RemoteLoggingLevel",  
107 - "type": "attribute",  
108 - "label": "Log level",  
109 - "color": "#607d8b",  
110 - "settings": {  
111 - "columnWidth": "0px",  
112 - "useCellStyleFunction": false,  
113 - "useCellContentFunction": false  
114 - },  
115 - "_hash": 0.9785994222542516  
116 - }  
117 - ],  
118 - "entityAliasId": "3e0f533a-0db1-3292-184f-06e73535061a"  
119 - }  
120 - ],  
121 - "showTitleIcon": true,  
122 - "titleIcon": "list",  
123 - "iconColor": "rgba(0, 0, 0, 0.87)",  
124 - "iconSize": "24px",  
125 - "titleTooltip": "List device",  
126 - "widgetStyle": {},  
127 - "displayTimewindow": true,  
128 - "actions": {  
129 - "headerButton": [  
130 - {  
131 - "id": "70837a9d-c3de-a9a7-03c5-dccd14998758",  
132 - "name": "Add device",  
133 - "icon": "add",  
134 - "type": "customPretty",  
135 - "customHtml": "<md-dialog aria-label=\"Add entity\" style=\"width: 480px\">\n <form name=\"addDeviceForm\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Add device</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-progress-linear class=\"md-warn\" md-mode=\"indeterminate\" ng-disabled=\"!$root.loading && !vm.loading\" ng-show=\"$root.loading || vm.loading\"></md-progress-linear>\n <span style=\"min-height: 5px;\" flex=\"\" ng-show=\"!$root.loading && !vm.loading\"></span>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <fieldset ng-disabled=\"$root.loading || vm.loading\">\n <md-input-container flex class=\"md-block\">\n <label>Device name</label>\n <input ng-model=\"vm.deviceName\" name=deviceName required>\n <div ng-messages=\"addDeviceForm.deviceName.$error\">\n <div ng-message=\"required\">Device name is required.</div>\n </div>\n </md-input-container>\n <div flex layout=\"row\">\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Latitude</label>\n <input type=\"number\" step=\"any\" name=\"latitude\" ng-model=\"vm.attributes.latitude\">\n </md-input-container>\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Longitude</label>\n <input type=\"number\" step=\"any\" name=\"longitude\" ng-model=\"vm.attributes.longitude\">\n </md-input-container>\n </div>\n <md-input-container class=\"md-block\">\n <label>Label</label>\n <input name=\"deviceLabel\" ng-model=\"vm.deviceLabel\">\n </md-input-container>\n </fieldset>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"vm.loading || addDeviceForm.$invalid || !addDeviceForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>\n",  
136 - "customCss": "",  
137 - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n $mdDialog.show({\n controller: ['$scope', '$mdDialog',\n AddDeviceDialogController\n ],\n controllerAs: 'vm',\n template: htmlTemplate,\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction AddDeviceDialogController($scope, $mdDialog) {\n let vm = this;\n vm.types = types;\n vm.attributes = {};\n vm.deviceType = \"gateway\";\n\n vm.cancel = () => {\n $mdDialog.hide();\n };\n\n vm.save = () => {\n vm.loading = true;\n $scope.addDeviceForm.$setPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.deviceName,\n type: vm.deviceType,\n label: vm.deviceLabel\n };\n deviceService.saveDevice(device).then(\n (device) => {\n saveAttributes(device.id).then(\n () => {\n vm.loading = false;\n updateAliasData();\n $mdDialog.hide();\n }\n );\n },\n () => {\n vm.loading = false;\n }\n );\n };\n\n function saveAttributes(entityId) {\n let attributesArray = [];\n for (let key in vm.attributes) {\n attributesArray.push({\n key: key,\n value: vm.attributes[key]\n });\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(\n entityId.entityType, entityId.id,\n \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when([]);\n }\n }\n\n function updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController\n .resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController\n .setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController\n .getAliasInfo(aliasId));\n });\n $q.all(tasks).then(() => {\n $rootScope.$broadcast(\n 'widgetForceReInit');\n });\n }\n}"  
138 - }  
139 - ],  
140 - "actionCellButton": [  
141 - {  
142 - "id": "78845501-234e-a452-6819-82b5b776e99f",  
143 - "name": "Configuration",  
144 - "icon": "settings",  
145 - "type": "openDashboardState",  
146 - "targetDashboardStateId": "__entityname__config",  
147 - "openRightLayout": false,  
148 - "setEntityId": true  
149 - },  
150 - {  
151 - "id": "f6ffdba8-e40f-2b8d-851b-f5ecaf18606b",  
152 - "name": "Graphs",  
153 - "icon": "show_chart",  
154 - "type": "openDashboardState",  
155 - "targetDashboardStateId": "__entityname_grafic",  
156 - "setEntityId": true  
157 - },  
158 - {  
159 - "id": "242671f3-76c6-6982-7acc-6f12addf0ccc",  
160 - "name": "Edit device",  
161 - "icon": "edit",  
162 - "type": "customPretty",  
163 - "customHtml": "<md-dialog aria-label=\"Edit entity\" style=\"width: 480px\">\n <form name=\"editDeviceForm\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Edit device</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-progress-linear class=\"md-warn\" md-mode=\"indeterminate\" ng-disabled=\"!$root.loading && !vm.loading\" ng-show=\"$root.loading || vm.loading\"></md-progress-linear>\n <span style=\"min-height: 5px;\" flex=\"\" ng-show=\"!$root.loading && !vm.loading\"></span>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <fieldset ng-disabled=\"$root.loading || vm.loading\">\n <md-input-container flex class=\"md-block\">\n <label>Device name</label>\n <input ng-model=\"vm.device.name\" name=deviceName required>\n <div ng-messages=\"editDeviceForm.deviceName.$error\">\n <div ng-message=\"required\">Device name is required.</div>\n </div>\n </md-input-container>\n <!--<div flex layout=\"row\">-->\n <!--<tb-entity-subtype-autocomplete flex=\"50\"-->\n <!-- ng-disabled=\"true\"-->\n <!-- tb-required=\"true\"-->\n <!-- the-form=\"editDeviceForm\"-->\n <!-- ng-model=\"vm.device.type\"-->\n <!-- entity-type=\"vm.types.entityType.device\">-->\n <!--</tb-entity-subtype-autocomplete>-->\n <!-- <md-input-container flex=\"50\" class=\"md-block\">-->\n <!-- <label>Label</label>-->\n <!-- <input name=\"deviceLabel\" ng-model=\"vm.device.label\">-->\n <!-- </md-input-container>-->\n <!--</div>-->\n <div flex layout=\"row\">\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Latitude</label>\n <input type=\"number\" step=\"any\" name=\"latitude\" ng-model=\"vm.attributes.latitude\">\n </md-input-container>\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Longitude</label>\n <input type=\"number\" step=\"any\" name=\"longitude\" ng-model=\"vm.attributes.longitude\">\n </md-input-container>\n </div>\n <md-input-container class=\"md-block\">\n <label>Label</label>\n <input name=\"deviceLabel\" ng-model=\"vm.device.label\">\n </md-input-container>\n </fieldset>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"vm.loading || editDeviceForm.$invalid || !editDeviceForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",  
164 - "customCss": "/*=======================================================================*/\n/*========== There are two examples: for edit and add entity ==========*/\n/*=======================================================================*/\n/*======================== Edit entity example ========================*/\n/*=======================================================================*/\n/*\n.edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n\n.relations-list.old-relations tb-entity-select tb-entity-autocomplete button {\n display: none;\n} \n*/\n/*========================================================================*/\n/*========================= Add entity example =========================*/\n/*========================================================================*/\n/*\n.add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n*/\n",  
165 - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n \nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', EditDeviceDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction EditDeviceDialogController($scope,$mdDialog) {\n let vm = this;\n vm.types = types;\n vm.loading = false;\n vm.attributes = {};\n \n getEntityInfo();\n \n function getEntityInfo() {\n vm.loading = true;\n deviceService.getDevice(entityId.id).then(\n (device) => {\n attributeService.getEntityAttributesValues(entityId.entityType, entityId.id, 'SERVER_SCOPE').then(\n (data) => {\n if (data.length) {\n getEntityAttributes(data);\n }\n vm.device = device;\n vm.loading = false;\n } \n );\n }\n )\n }\n \n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n vm.save = () => {\n vm.loading = true;\n $scope.editDeviceForm.$setPristine();\n deviceService.saveDevice(vm.device).then(\n () => {\n saveAttributes().then(\n () => {\n updateAliasData();\n vm.loading = false;\n $mdDialog.hide();\n }\n );\n },\n () => {\n vm.loading = false;\n }\n );\n }\n \n function getEntityAttributes(attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n }\n \n function saveAttributes() {\n let attributesArray = [];\n for (let key in vm.attributes) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when([]);\n }\n }\n \n function updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n console.log(widgetContext);\n $q.all(tasks).then(() => {\n $rootScope.$broadcast('widgetForceReInit');\n });\n }\n}\n"  
166 - },  
167 - {  
168 - "id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa",  
169 - "name": "Delete device",  
170 - "icon": "delete",  
171 - "type": "custom",  
172 - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n $rootScope = $injector.get('$rootScope'),\n $q = $injector.get('$q');\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n let confirm = $mdDialog.confirm()\n .targetEvent($event)\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .cancel('Cancel')\n .ok('Delete');\n $mdDialog.show(confirm).then(() => {\n deleteDevice();\n })\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).then(\n () => {\n updateAliasData();\n }\n );\n}\n\nfunction updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(() => {\n $rootScope.$broadcast('entityAliasesChanged', aliasIds);\n });\n}"  
173 - }  
174 - ],  
175 - "rowClick": [  
176 - {  
177 - "id": "ad5fc7e1-5e60-e056-6940-a75a383466a1",  
178 - "name": "to_entityname__config",  
179 - "icon": "more_horiz",  
180 - "type": "openDashboardState",  
181 - "targetDashboardStateId": "__entityname__config",  
182 - "setEntityId": true,  
183 - "stateEntityParamName": ""  
184 - }  
185 - ]  
186 - }  
187 - },  
188 - "id": "94715984-ae74-76e4-20b7-2f956b01ed80"  
189 - },  
190 - "eadabbc7-519e-76fc-ba10-b3fe8c18da10": {  
191 - "isSystemType": true,  
192 - "bundleAlias": "cards",  
193 - "typeAlias": "timeseries_table",  
194 - "type": "timeseries",  
195 - "title": "New widget",  
196 - "sizeX": 14,  
197 - "sizeY": 13,  
198 - "config": {  
199 - "datasources": [  
200 - {  
201 - "type": "entity",  
202 - "dataKeys": [  
203 - {  
204 - "name": "LOGS",  
205 - "type": "timeseries",  
206 - "label": "LOGS",  
207 - "color": "#2196f3",  
208 - "settings": {  
209 - "useCellStyleFunction": false,  
210 - "useCellContentFunction": false  
211 - },  
212 - "_hash": 0.3496649158709739,  
213 - "postFuncBody": "return value.replace(/ - (.*) - \\[/gi, ' - <b style=\"color:#0f0;\">$1</b> - [');"  
214 - }  
215 - ],  
216 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
217 - }  
218 - ],  
219 - "timewindow": {  
220 - "realtime": {  
221 - "interval": 1000,  
222 - "timewindowMs": 2592000000  
223 - },  
224 - "aggregation": {  
225 - "type": "NONE",  
226 - "limit": 200  
227 - }  
228 - },  
229 - "showTitle": true,  
230 - "backgroundColor": "rgb(255, 255, 255)",  
231 - "color": "rgba(0, 0, 0, 0.87)",  
232 - "padding": "8px",  
233 - "settings": {  
234 - "showTimestamp": true,  
235 - "displayPagination": true,  
236 - "defaultPageSize": 10  
237 - },  
238 - "title": "Debug events (logs)",  
239 - "dropShadow": true,  
240 - "enableFullscreen": true,  
241 - "titleStyle": {  
242 - "fontSize": "16px",  
243 - "fontWeight": 400  
244 - },  
245 - "useDashboardTimewindow": false,  
246 - "showLegend": false,  
247 - "widgetStyle": {},  
248 - "actions": {},  
249 - "showTitleIcon": false,  
250 - "titleIcon": null,  
251 - "iconColor": "rgba(0, 0, 0, 0.87)",  
252 - "iconSize": "24px",  
253 - "titleTooltip": "",  
254 - "displayTimewindow": true  
255 - },  
256 - "id": "eadabbc7-519e-76fc-ba10-b3fe8c18da10"  
257 - },  
258 - "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": {  
259 - "isSystemType": true,  
260 - "bundleAlias": "charts",  
261 - "typeAlias": "basic_timeseries",  
262 - "type": "timeseries",  
263 - "title": "New widget",  
264 - "sizeX": 17,  
265 - "sizeY": 4,  
266 - "config": {  
267 - "datasources": [  
268 - {  
269 - "type": "entity",  
270 - "dataKeys": [  
271 - {  
272 - "name": "opcuaEventsProduced",  
273 - "type": "timeseries",  
274 - "label": "opcuaEventsProduced",  
275 - "color": "#2196f3",  
276 - "settings": {  
277 - "excludeFromStacking": false,  
278 - "hideDataByDefault": false,  
279 - "disableDataHiding": false,  
280 - "removeFromLegend": false,  
281 - "showLines": true,  
282 - "fillLines": false,  
283 - "showPoints": false,  
284 - "showPointShape": "circle",  
285 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
286 - "showPointsLineWidth": 5,  
287 - "showPointsRadius": 3,  
288 - "tooltipValueFormatter": "",  
289 - "showSeparateAxis": false,  
290 - "axisTitle": "",  
291 - "axisPosition": "left",  
292 - "axisTicksFormatter": "",  
293 - "comparisonSettings": {  
294 - "showValuesForComparison": true,  
295 - "comparisonValuesLabel": "",  
296 - "color": ""  
297 - }  
298 - },  
299 - "_hash": 0.1477920581839779  
300 - },  
301 - {  
302 - "name": "opcuaEventsSent",  
303 - "type": "timeseries",  
304 - "label": "opcuaEventsSent",  
305 - "color": "#4caf50",  
306 - "settings": {  
307 - "excludeFromStacking": false,  
308 - "hideDataByDefault": false,  
309 - "disableDataHiding": false,  
310 - "removeFromLegend": false,  
311 - "showLines": true,  
312 - "fillLines": false,  
313 - "showPoints": false,  
314 - "showPointShape": "circle",  
315 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
316 - "showPointsLineWidth": 5,  
317 - "showPointsRadius": 3,  
318 - "tooltipValueFormatter": "",  
319 - "showSeparateAxis": false,  
320 - "axisTitle": "",  
321 - "axisPosition": "left",  
322 - "axisTicksFormatter": "",  
323 - "comparisonSettings": {  
324 - "showValuesForComparison": true,  
325 - "comparisonValuesLabel": "",  
326 - "color": ""  
327 - }  
328 - },  
329 - "_hash": 0.6500957113784758  
330 - }  
331 - ],  
332 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
333 - }  
334 - ],  
335 - "timewindow": {  
336 - "realtime": {  
337 - "interval": 1000,  
338 - "timewindowMs": 120000  
339 - },  
340 - "aggregation": {  
341 - "type": "NONE",  
342 - "limit": 25000  
343 - },  
344 - "hideInterval": false,  
345 - "hideAggregation": false  
346 - },  
347 - "showTitle": true,  
348 - "backgroundColor": "#fff",  
349 - "color": "rgba(0, 0, 0, 0.87)",  
350 - "padding": "8px",  
351 - "settings": {  
352 - "shadowSize": 4,  
353 - "fontColor": "#545454",  
354 - "fontSize": 10,  
355 - "xaxis": {  
356 - "showLabels": true,  
357 - "color": "#545454"  
358 - },  
359 - "yaxis": {  
360 - "showLabels": true,  
361 - "color": "#545454"  
362 - },  
363 - "grid": {  
364 - "color": "#545454",  
365 - "tickColor": "#DDDDDD",  
366 - "verticalLines": true,  
367 - "horizontalLines": true,  
368 - "outlineWidth": 1  
369 - },  
370 - "stack": false,  
371 - "tooltipIndividual": false,  
372 - "timeForComparison": "months",  
373 - "xaxisSecond": {  
374 - "axisPosition": "top",  
375 - "showLabels": true  
376 - }  
377 - },  
378 - "title": "Real time information",  
379 - "dropShadow": true,  
380 - "enableFullscreen": true,  
381 - "titleStyle": {  
382 - "fontSize": "16px",  
383 - "fontWeight": 400  
384 - },  
385 - "mobileHeight": null,  
386 - "showTitleIcon": false,  
387 - "titleIcon": null,  
388 - "iconColor": "rgba(0, 0, 0, 0.87)",  
389 - "iconSize": "24px",  
390 - "titleTooltip": "",  
391 - "widgetStyle": {},  
392 - "useDashboardTimewindow": false,  
393 - "displayTimewindow": true,  
394 - "showLegend": true,  
395 - "legendConfig": {  
396 - "direction": "column",  
397 - "position": "right",  
398 - "showMin": true,  
399 - "showMax": true,  
400 - "showAvg": true,  
401 - "showTotal": true  
402 - },  
403 - "actions": {}  
404 - },  
405 - "id": "f928afc4-30d1-8d0c-e3cf-777f9f9d1155"  
406 - },  
407 - "2a95b473-042d-59d0-2da2-40d0cccb6c8a": {  
408 - "isSystemType": true,  
409 - "bundleAlias": "cards",  
410 - "typeAlias": "timeseries_table",  
411 - "type": "timeseries",  
412 - "title": "New widget",  
413 - "sizeX": 7,  
414 - "sizeY": 7,  
415 - "config": {  
416 - "datasources": [  
417 - {  
418 - "type": "entity",  
419 - "dataKeys": [  
420 - {  
421 - "name": "eventsSent",  
422 - "type": "timeseries",  
423 - "label": "Events",  
424 - "color": "#2196f3",  
425 - "settings": {  
426 - "useCellStyleFunction": false,  
427 - "useCellContentFunction": false  
428 - },  
429 - "_hash": 0.8156044798125357  
430 - },  
431 - {  
432 - "name": "eventsProduced",  
433 - "type": "timeseries",  
434 - "label": "Produced",  
435 - "color": "#4caf50",  
436 - "settings": {  
437 - "useCellStyleFunction": false,  
438 - "useCellContentFunction": false  
439 - },  
440 - "_hash": 0.6538259344015449  
441 - }  
442 - ],  
443 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
444 - }  
445 - ],  
446 - "timewindow": {  
447 - "realtime": {  
448 - "interval": 1000,  
449 - "timewindowMs": 604800000  
450 - },  
451 - "aggregation": {  
452 - "type": "NONE",  
453 - "limit": 200  
454 - }  
455 - },  
456 - "showTitle": true,  
457 - "backgroundColor": "rgb(255, 255, 255)",  
458 - "color": "rgba(0, 0, 0, 0.87)",  
459 - "padding": "8px",  
460 - "settings": {  
461 - "showTimestamp": true,  
462 - "displayPagination": true,  
463 - "defaultPageSize": 6,  
464 - "hideEmptyLines": true  
465 - },  
466 - "title": "Total Messages",  
467 - "dropShadow": true,  
468 - "enableFullscreen": true,  
469 - "titleStyle": {  
470 - "fontSize": "16px",  
471 - "fontWeight": 400  
472 - },  
473 - "useDashboardTimewindow": false,  
474 - "showLegend": false,  
475 - "widgetStyle": {},  
476 - "actions": {},  
477 - "showTitleIcon": false,  
478 - "titleIcon": null,  
479 - "iconColor": "rgba(0, 0, 0, 0.87)",  
480 - "iconSize": "24px",  
481 - "titleTooltip": "",  
482 - "displayTimewindow": true,  
483 - "legendConfig": {  
484 - "direction": "column",  
485 - "position": "bottom",  
486 - "showMin": false,  
487 - "showMax": false,  
488 - "showAvg": true,  
489 - "showTotal": false  
490 - }  
491 - },  
492 - "id": "2a95b473-042d-59d0-2da2-40d0cccb6c8a"  
493 - },  
494 - "aaa69366-aacc-9028-65aa-645c0f8533ec": {  
495 - "isSystemType": true,  
496 - "bundleAlias": "charts",  
497 - "typeAlias": "basic_timeseries",  
498 - "type": "timeseries",  
499 - "title": "New widget",  
500 - "sizeX": 17,  
501 - "sizeY": 4,  
502 - "config": {  
503 - "datasources": [  
504 - {  
505 - "type": "entity",  
506 - "dataKeys": [  
507 - {  
508 - "name": "eventsSent",  
509 - "type": "timeseries",  
510 - "label": "eventsSent",  
511 - "color": "#2196f3",  
512 - "settings": {  
513 - "excludeFromStacking": false,  
514 - "hideDataByDefault": false,  
515 - "disableDataHiding": false,  
516 - "removeFromLegend": false,  
517 - "showLines": true,  
518 - "fillLines": false,  
519 - "showPoints": false,  
520 - "showPointShape": "circle",  
521 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
522 - "showPointsLineWidth": 5,  
523 - "showPointsRadius": 3,  
524 - "tooltipValueFormatter": "",  
525 - "showSeparateAxis": false,  
526 - "axisTitle": "",  
527 - "axisPosition": "left",  
528 - "axisTicksFormatter": "",  
529 - "comparisonSettings": {  
530 - "showValuesForComparison": true,  
531 - "comparisonValuesLabel": "",  
532 - "color": ""  
533 - }  
534 - },  
535 - "_hash": 0.41414001784591314  
536 - },  
537 - {  
538 - "name": "eventsProduced",  
539 - "type": "timeseries",  
540 - "label": "eventsProduced",  
541 - "color": "#4caf50",  
542 - "settings": {  
543 - "excludeFromStacking": false,  
544 - "hideDataByDefault": false,  
545 - "disableDataHiding": false,  
546 - "removeFromLegend": false,  
547 - "showLines": true,  
548 - "fillLines": false,  
549 - "showPoints": false,  
550 - "showPointShape": "circle",  
551 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
552 - "showPointsLineWidth": 5,  
553 - "showPointsRadius": 3,  
554 - "tooltipValueFormatter": "",  
555 - "showSeparateAxis": false,  
556 - "axisTitle": "",  
557 - "axisPosition": "left",  
558 - "axisTicksFormatter": "",  
559 - "comparisonSettings": {  
560 - "showValuesForComparison": true,  
561 - "comparisonValuesLabel": "",  
562 - "color": ""  
563 - }  
564 - },  
565 - "_hash": 0.7819101846284422  
566 - }  
567 - ],  
568 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
569 - }  
570 - ],  
571 - "timewindow": {  
572 - "realtime": {  
573 - "timewindowMs": 60000  
574 - }  
575 - },  
576 - "showTitle": true,  
577 - "backgroundColor": "#fff",  
578 - "color": "rgba(0, 0, 0, 0.87)",  
579 - "padding": "8px",  
580 - "settings": {  
581 - "shadowSize": 4,  
582 - "fontColor": "#545454",  
583 - "fontSize": 10,  
584 - "xaxis": {  
585 - "showLabels": true,  
586 - "color": "#545454"  
587 - },  
588 - "yaxis": {  
589 - "showLabels": true,  
590 - "color": "#545454"  
591 - },  
592 - "grid": {  
593 - "color": "#545454",  
594 - "tickColor": "#DDDDDD",  
595 - "verticalLines": true,  
596 - "horizontalLines": true,  
597 - "outlineWidth": 1  
598 - },  
599 - "stack": false,  
600 - "tooltipIndividual": false,  
601 - "timeForComparison": "months",  
602 - "xaxisSecond": {  
603 - "axisPosition": "top",  
604 - "showLabels": true  
605 - }  
606 - },  
607 - "title": "History information",  
608 - "dropShadow": true,  
609 - "enableFullscreen": true,  
610 - "titleStyle": {  
611 - "fontSize": "16px",  
612 - "fontWeight": 400  
613 - },  
614 - "mobileHeight": null,  
615 - "showTitleIcon": false,  
616 - "titleIcon": null,  
617 - "iconColor": "rgba(0, 0, 0, 0.87)",  
618 - "iconSize": "24px",  
619 - "titleTooltip": "",  
620 - "widgetStyle": {},  
621 - "useDashboardTimewindow": true,  
622 - "displayTimewindow": true,  
623 - "showLegend": true,  
624 - "legendConfig": {  
625 - "direction": "column",  
626 - "position": "right",  
627 - "showMin": true,  
628 - "showMax": true,  
629 - "showAvg": true,  
630 - "showTotal": true  
631 - },  
632 - "actions": {}  
633 - },  
634 - "id": "aaa69366-aacc-9028-65aa-645c0f8533ec"  
635 - },  
636 - "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": {  
637 - "isSystemType": true,  
638 - "bundleAlias": "charts",  
639 - "typeAlias": "basic_timeseries",  
640 - "type": "timeseries",  
641 - "title": "New widget",  
642 - "sizeX": 17,  
643 - "sizeY": 4,  
644 - "config": {  
645 - "datasources": [  
646 - {  
647 - "type": "entity",  
648 - "dataKeys": [  
649 - {  
650 - "name": "bleEventsProduced",  
651 - "type": "timeseries",  
652 - "label": "bleEventsProduced",  
653 - "color": "#2196f3",  
654 - "settings": {  
655 - "excludeFromStacking": false,  
656 - "hideDataByDefault": false,  
657 - "disableDataHiding": false,  
658 - "removeFromLegend": false,  
659 - "showLines": true,  
660 - "fillLines": false,  
661 - "showPoints": false,  
662 - "showPointShape": "circle",  
663 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
664 - "showPointsLineWidth": 5,  
665 - "showPointsRadius": 3,  
666 - "tooltipValueFormatter": "",  
667 - "showSeparateAxis": false,  
668 - "axisTitle": "",  
669 - "axisPosition": "left",  
670 - "axisTicksFormatter": "",  
671 - "comparisonSettings": {  
672 - "showValuesForComparison": true,  
673 - "comparisonValuesLabel": "",  
674 - "color": ""  
675 - }  
676 - },  
677 - "_hash": 0.5625165504526104  
678 - },  
679 - {  
680 - "name": "bleEventsSent",  
681 - "type": "timeseries",  
682 - "label": "bleEventsSent",  
683 - "color": "#4caf50",  
684 - "settings": {  
685 - "excludeFromStacking": false,  
686 - "hideDataByDefault": false,  
687 - "disableDataHiding": false,  
688 - "removeFromLegend": false,  
689 - "showLines": true,  
690 - "fillLines": false,  
691 - "showPoints": false,  
692 - "showPointShape": "circle",  
693 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
694 - "showPointsLineWidth": 5,  
695 - "showPointsRadius": 3,  
696 - "tooltipValueFormatter": "",  
697 - "showSeparateAxis": false,  
698 - "axisTitle": "",  
699 - "axisPosition": "left",  
700 - "axisTicksFormatter": "",  
701 - "comparisonSettings": {  
702 - "showValuesForComparison": true,  
703 - "comparisonValuesLabel": "",  
704 - "color": ""  
705 - }  
706 - },  
707 - "_hash": 0.6817950080745288  
708 - }  
709 - ],  
710 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
711 - }  
712 - ],  
713 - "timewindow": {  
714 - "realtime": {  
715 - "interval": 5000,  
716 - "timewindowMs": 120000  
717 - },  
718 - "aggregation": {  
719 - "type": "AVG",  
720 - "limit": 25000  
721 - }  
722 - },  
723 - "showTitle": true,  
724 - "backgroundColor": "#fff",  
725 - "color": "rgba(0, 0, 0, 0.87)",  
726 - "padding": "8px",  
727 - "settings": {  
728 - "shadowSize": 4,  
729 - "fontColor": "#545454",  
730 - "fontSize": 10,  
731 - "xaxis": {  
732 - "showLabels": true,  
733 - "color": "#545454"  
734 - },  
735 - "yaxis": {  
736 - "showLabels": true,  
737 - "color": "#545454"  
738 - },  
739 - "grid": {  
740 - "color": "#545454",  
741 - "tickColor": "#DDDDDD",  
742 - "verticalLines": true,  
743 - "horizontalLines": true,  
744 - "outlineWidth": 1  
745 - },  
746 - "stack": false,  
747 - "tooltipIndividual": false,  
748 - "timeForComparison": "months",  
749 - "xaxisSecond": {  
750 - "axisPosition": "top",  
751 - "showLabels": true  
752 - }  
753 - },  
754 - "title": "Real time information",  
755 - "dropShadow": true,  
756 - "enableFullscreen": true,  
757 - "titleStyle": {  
758 - "fontSize": "16px",  
759 - "fontWeight": 400  
760 - },  
761 - "mobileHeight": null,  
762 - "showTitleIcon": false,  
763 - "titleIcon": null,  
764 - "iconColor": "rgba(0, 0, 0, 0.87)",  
765 - "iconSize": "24px",  
766 - "titleTooltip": "",  
767 - "widgetStyle": {},  
768 - "useDashboardTimewindow": false,  
769 - "displayTimewindow": true,  
770 - "showLegend": true,  
771 - "legendConfig": {  
772 - "direction": "column",  
773 - "position": "right",  
774 - "showMin": true,  
775 - "showMax": true,  
776 - "showAvg": true,  
777 - "showTotal": true  
778 - },  
779 - "actions": {}  
780 - },  
781 - "id": "ce5c7d01-a3ef-5cf0-4578-8505135c23a0"  
782 - },  
783 - "466f046d-6005-a168-b107-60fcb2469cd5": {  
784 - "isSystemType": true,  
785 - "bundleAlias": "gateway_widgets",  
786 - "typeAlias": "attributes_card",  
787 - "type": "latest",  
788 - "title": "New widget",  
789 - "sizeX": 7,  
790 - "sizeY": 5,  
791 - "config": {  
792 - "datasources": [  
793 - {  
794 - "type": "entity",  
795 - "dataKeys": [],  
796 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
797 - }  
798 - ],  
799 - "timewindow": {  
800 - "realtime": {  
801 - "timewindowMs": 60000  
802 - }  
803 - },  
804 - "showTitle": true,  
805 - "backgroundColor": "#fff",  
806 - "color": "rgba(0, 0, 0, 0.87)",  
807 - "padding": "8px",  
808 - "settings": {  
809 - "eventsTitle": "Gateway Events Form",  
810 - "eventsReg": [  
811 - "EventsProduced",  
812 - "EventsSent"  
813 - ]  
814 - },  
815 - "title": "Gateway events",  
816 - "showTitleIcon": false,  
817 - "titleIcon": null,  
818 - "iconColor": "rgba(0, 0, 0, 0.87)",  
819 - "iconSize": "24px",  
820 - "titleTooltip": "",  
821 - "dropShadow": true,  
822 - "enableFullscreen": true,  
823 - "widgetStyle": {},  
824 - "titleStyle": {  
825 - "fontSize": "16px",  
826 - "fontWeight": 400  
827 - },  
828 - "useDashboardTimewindow": true,  
829 - "displayTimewindow": true,  
830 - "showLegend": false,  
831 - "actions": {}  
832 - },  
833 - "id": "466f046d-6005-a168-b107-60fcb2469cd5"  
834 - },  
835 - "8fc32225-164f-3258-73f7-e6b6d959cf0b": {  
836 - "isSystemType": true,  
837 - "bundleAlias": "gateway_widgets",  
838 - "typeAlias": "config_form_latest",  
839 - "type": "latest",  
840 - "title": "New widget",  
841 - "sizeX": 10,  
842 - "sizeY": 9,  
843 - "config": {  
844 - "datasources": [  
845 - {  
846 - "type": "entity",  
847 - "dataKeys": [],  
848 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
849 - }  
850 - ],  
851 - "timewindow": {  
852 - "realtime": {  
853 - "timewindowMs": 60000  
854 - }  
855 - },  
856 - "showTitle": true,  
857 - "backgroundColor": "#fff",  
858 - "color": "rgba(0, 0, 0, 0.87)",  
859 - "padding": "8px",  
860 - "settings": {  
861 - "gatewayTitle": "Gateway configuration (Single device)",  
862 - "readOnly": false  
863 - },  
864 - "title": "New Gateway configuration (Single device)",  
865 - "showTitleIcon": false,  
866 - "titleIcon": null,  
867 - "iconColor": "rgba(0, 0, 0, 0.87)",  
868 - "iconSize": "24px",  
869 - "titleTooltip": "",  
870 - "dropShadow": true,  
871 - "enableFullscreen": true,  
872 - "widgetStyle": {},  
873 - "titleStyle": {  
874 - "fontSize": "16px",  
875 - "fontWeight": 400  
876 - },  
877 - "useDashboardTimewindow": true,  
878 - "displayTimewindow": true,  
879 - "showLegend": false,  
880 - "actions": {}  
881 - },  
882 - "id": "8fc32225-164f-3258-73f7-e6b6d959cf0b"  
883 - },  
884 - "063fc179-c9fd-f952-e714-f24e9c43c05c": {  
885 - "isSystemType": true,  
886 - "bundleAlias": "control_widgets",  
887 - "typeAlias": "rpcbutton",  
888 - "type": "rpc",  
889 - "title": "New widget",  
890 - "sizeX": 4,  
891 - "sizeY": 2,  
892 - "config": {  
893 - "targetDeviceAliases": [],  
894 - "showTitle": false,  
895 - "backgroundColor": "#e6e7e8",  
896 - "color": "rgba(0, 0, 0, 0.87)",  
897 - "padding": "0px",  
898 - "settings": {  
899 - "requestTimeout": 5000,  
900 - "oneWayElseTwoWay": true,  
901 - "styleButton": {  
902 - "isRaised": true,  
903 - "isPrimary": false  
904 - },  
905 - "methodParams": "{}",  
906 - "methodName": "gateway_reboot",  
907 - "buttonText": "GATEWAY REBOOT"  
908 - },  
909 - "title": "New RPC Button",  
910 - "dropShadow": true,  
911 - "enableFullscreen": false,  
912 - "widgetStyle": {},  
913 - "titleStyle": {  
914 - "fontSize": "16px",  
915 - "fontWeight": 400  
916 - },  
917 - "useDashboardTimewindow": true,  
918 - "showLegend": false,  
919 - "actions": {},  
920 - "datasources": [],  
921 - "showTitleIcon": false,  
922 - "titleIcon": null,  
923 - "iconColor": "rgba(0, 0, 0, 0.87)",  
924 - "iconSize": "24px",  
925 - "titleTooltip": "",  
926 - "displayTimewindow": true,  
927 - "targetDeviceAliasIds": [  
928 - "b2487e75-2fa4-f211-142c-434dfd50c70c"  
929 - ]  
930 - },  
931 - "id": "063fc179-c9fd-f952-e714-f24e9c43c05c"  
932 - },  
933 - "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": {  
934 - "isSystemType": true,  
935 - "bundleAlias": "control_widgets",  
936 - "typeAlias": "rpcbutton",  
937 - "type": "rpc",  
938 - "title": "New widget",  
939 - "sizeX": 4,  
940 - "sizeY": 2,  
941 - "config": {  
942 - "targetDeviceAliases": [],  
943 - "showTitle": false,  
944 - "backgroundColor": "#e6e7e8",  
945 - "color": "rgba(0, 0, 0, 0.87)",  
946 - "padding": "0px",  
947 - "settings": {  
948 - "requestTimeout": 5000,  
949 - "oneWayElseTwoWay": true,  
950 - "styleButton": {  
951 - "isRaised": true,  
952 - "isPrimary": false  
953 - },  
954 - "methodName": "gateway_restart",  
955 - "methodParams": "{}",  
956 - "buttonText": "gateway restart"  
957 - },  
958 - "title": "New RPC Button",  
959 - "dropShadow": true,  
960 - "enableFullscreen": false,  
961 - "widgetStyle": {},  
962 - "titleStyle": {  
963 - "fontSize": "16px",  
964 - "fontWeight": 400  
965 - },  
966 - "useDashboardTimewindow": true,  
967 - "showLegend": false,  
968 - "actions": {},  
969 - "datasources": [],  
970 - "showTitleIcon": false,  
971 - "titleIcon": null,  
972 - "iconColor": "rgba(0, 0, 0, 0.87)",  
973 - "iconSize": "24px",  
974 - "titleTooltip": "",  
975 - "displayTimewindow": true,  
976 - "targetDeviceAliasIds": [  
977 - "b2487e75-2fa4-f211-142c-434dfd50c70c"  
978 - ]  
979 - },  
980 - "id": "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7"  
981 - },  
982 - "6770b6ba-eff8-df05-75f8-c1f9326d4842": {  
983 - "isSystemType": true,  
984 - "bundleAlias": "input_widgets",  
985 - "typeAlias": "markers_placement_openstreetmap",  
986 - "type": "latest",  
987 - "title": "New widget",  
988 - "sizeX": 6,  
989 - "sizeY": 4,  
990 - "config": {  
991 - "datasources": [  
992 - {  
993 - "type": "entity",  
994 - "dataKeys": [  
995 - {  
996 - "name": "latitude",  
997 - "type": "attribute",  
998 - "label": "latitude",  
999 - "color": "#2196f3",  
1000 - "settings": {},  
1001 - "_hash": 0.9743324774725604  
1002 - },  
1003 - {  
1004 - "name": "longitude",  
1005 - "type": "attribute",  
1006 - "label": "longitude",  
1007 - "color": "#4caf50",  
1008 - "settings": {},  
1009 - "_hash": 0.5530093635101525  
1010 - }  
1011 - ],  
1012 - "entityAliasId": "b2487e75-2fa4-f211-142c-434dfd50c70c"  
1013 - }  
1014 - ],  
1015 - "timewindow": {  
1016 - "realtime": {  
1017 - "timewindowMs": 60000  
1018 - }  
1019 - },  
1020 - "showTitle": false,  
1021 - "backgroundColor": "#fff",  
1022 - "color": "rgba(0, 0, 0, 0.87)",  
1023 - "padding": "8px",  
1024 - "settings": {  
1025 - "fitMapBounds": true,  
1026 - "latKeyName": "latitude",  
1027 - "lngKeyName": "longitude",  
1028 - "showLabel": true,  
1029 - "label": "${entityName}",  
1030 - "tooltipPattern": "<b>${entityName}</b><br/><br/><b>Latitude:</b> ${latitude:7}<br/><b>Longitude:</b> ${longitude:7}<br/><br/><link-act name='delete'>Delete</link-act>",  
1031 - "markerImageSize": 34,  
1032 - "useColorFunction": false,  
1033 - "markerImages": [],  
1034 - "useMarkerImageFunction": false,  
1035 - "color": "#fe7569",  
1036 - "mapProvider": "OpenStreetMap.Mapnik",  
1037 - "showTooltip": true,  
1038 - "autocloseTooltip": true,  
1039 - "defaultCenterPosition": [  
1040 - 0,  
1041 - 0  
1042 - ],  
1043 - "customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",  
1044 - "showTooltipAction": "click",  
1045 - "polygonKeyName": "coordinates",  
1046 - "polygonOpacity": 0.5,  
1047 - "polygonStrokeOpacity": 1,  
1048 - "polygonStrokeWeight": 1,  
1049 - "zoomOnClick": true,  
1050 - "showCoverageOnHover": true,  
1051 - "animate": true,  
1052 - "maxClusterRadius": 80,  
1053 - "removeOutsideVisibleBounds": true,  
1054 - "defaultZoomLevel": 5  
1055 - },  
1056 - "title": "Gateway Location",  
1057 - "dropShadow": true,  
1058 - "enableFullscreen": false,  
1059 - "titleStyle": {  
1060 - "fontSize": "16px",  
1061 - "fontWeight": 400  
1062 - },  
1063 - "useDashboardTimewindow": true,  
1064 - "showLegend": false,  
1065 - "widgetStyle": {},  
1066 - "actions": {  
1067 - "tooltipAction": [  
1068 - {  
1069 - "id": "54c293c4-9ca6-e34f-dc6a-0271944c1c66",  
1070 - "name": "delete",  
1071 - "icon": "more_horiz",  
1072 - "type": "custom",  
1073 - "customFunction": "var $rootScope = widgetContext.$scope.$injector.get('$rootScope');\nvar entityDatasource = widgetContext.map.subscription.datasources.filter(\n function(entity) {\n return entity.entityId === entityId.id\n });\n\nwidgetContext.map.saveMarkerLocation(entityDatasource[0],\n widgetContext.map.locations[0], {\n \"lat\": null,\n \"lng\": null\n }).then(function succes() {\n $rootScope.$broadcast('widgetForceReInit');\n });"  
1074 - }  
1075 - ]  
1076 - },  
1077 - "showTitleIcon": false,  
1078 - "titleIcon": null,  
1079 - "iconColor": "rgba(0, 0, 0, 0.87)",  
1080 - "iconSize": "24px",  
1081 - "titleTooltip": "",  
1082 - "displayTimewindow": true  
1083 - },  
1084 - "id": "6770b6ba-eff8-df05-75f8-c1f9326d4842"  
1085 - }  
1086 - },  
1087 - "states": {  
1088 - "main_gateway": {  
1089 - "name": "Gateways",  
1090 - "root": true,  
1091 - "layouts": {  
1092 - "main": {  
1093 - "widgets": {  
1094 - "94715984-ae74-76e4-20b7-2f956b01ed80": {  
1095 - "sizeX": 24,  
1096 - "sizeY": 12,  
1097 - "row": 0,  
1098 - "col": 0  
1099 - }  
1100 - },  
1101 - "gridSettings": {  
1102 - "backgroundColor": "#eeeeee",  
1103 - "color": "rgba(0,0,0,0.870588)",  
1104 - "columns": 24,  
1105 - "margins": [  
1106 - 10,  
1107 - 10  
1108 - ],  
1109 - "backgroundSizeMode": "100%",  
1110 - "autoFillHeight": true,  
1111 - "mobileAutoFillHeight": false,  
1112 - "mobileRowHeight": 70  
1113 - }  
1114 - }  
1115 - }  
1116 - },  
1117 - "__entityname__config": {  
1118 - "name": "${entityName} Configuration",  
1119 - "root": false,  
1120 - "layouts": {  
1121 - "main": {  
1122 - "widgets": {  
1123 - "eadabbc7-519e-76fc-ba10-b3fe8c18da10": {  
1124 - "sizeX": 14,  
1125 - "sizeY": 13,  
1126 - "row": 0,  
1127 - "col": 10  
1128 - },  
1129 - "8fc32225-164f-3258-73f7-e6b6d959cf0b": {  
1130 - "sizeX": 10,  
1131 - "sizeY": 9,  
1132 - "row": 0,  
1133 - "col": 0  
1134 - },  
1135 - "063fc179-c9fd-f952-e714-f24e9c43c05c": {  
1136 - "sizeX": 4,  
1137 - "sizeY": 2,  
1138 - "row": 9,  
1139 - "col": 0  
1140 - },  
1141 - "3c2134cc-27a0-93e1-dbe1-2fa7c1ce16b7": {  
1142 - "sizeX": 4,  
1143 - "sizeY": 2,  
1144 - "row": 11,  
1145 - "col": 0  
1146 - },  
1147 - "6770b6ba-eff8-df05-75f8-c1f9326d4842": {  
1148 - "sizeX": 6,  
1149 - "sizeY": 4,  
1150 - "row": 9,  
1151 - "col": 4  
1152 - }  
1153 - },  
1154 - "gridSettings": {  
1155 - "backgroundColor": "#eeeeee",  
1156 - "color": "rgba(0,0,0,0.870588)",  
1157 - "columns": 24,  
1158 - "margins": [  
1159 - 10,  
1160 - 10  
1161 - ],  
1162 - "backgroundSizeMode": "100%",  
1163 - "autoFillHeight": true,  
1164 - "mobileAutoFillHeight": false,  
1165 - "mobileRowHeight": 70  
1166 - }  
1167 - }  
1168 - }  
1169 - },  
1170 - "__entityname_grafic": {  
1171 - "name": "${entityName} Details",  
1172 - "root": false,  
1173 - "layouts": {  
1174 - "main": {  
1175 - "widgets": {  
1176 - "f928afc4-30d1-8d0c-e3cf-777f9f9d1155": {  
1177 - "sizeX": 17,  
1178 - "sizeY": 4,  
1179 - "mobileHeight": null,  
1180 - "row": 4,  
1181 - "col": 7  
1182 - },  
1183 - "2a95b473-042d-59d0-2da2-40d0cccb6c8a": {  
1184 - "sizeX": 7,  
1185 - "sizeY": 7,  
1186 - "row": 5,  
1187 - "col": 0  
1188 - },  
1189 - "aaa69366-aacc-9028-65aa-645c0f8533ec": {  
1190 - "sizeX": 17,  
1191 - "sizeY": 4,  
1192 - "mobileHeight": null,  
1193 - "row": 0,  
1194 - "col": 7  
1195 - },  
1196 - "ce5c7d01-a3ef-5cf0-4578-8505135c23a0": {  
1197 - "sizeX": 17,  
1198 - "sizeY": 4,  
1199 - "mobileHeight": null,  
1200 - "row": 8,  
1201 - "col": 7  
1202 - },  
1203 - "466f046d-6005-a168-b107-60fcb2469cd5": {  
1204 - "sizeX": 7,  
1205 - "sizeY": 5,  
1206 - "row": 0,  
1207 - "col": 0  
1208 - }  
1209 - },  
1210 - "gridSettings": {  
1211 - "backgroundColor": "#eeeeee",  
1212 - "color": "rgba(0,0,0,0.870588)",  
1213 - "columns": 24,  
1214 - "margins": [  
1215 - 10,  
1216 - 10  
1217 - ],  
1218 - "backgroundSizeMode": "auto 100%",  
1219 - "autoFillHeight": true,  
1220 - "mobileAutoFillHeight": true,  
1221 - "mobileRowHeight": 70  
1222 - }  
1223 - }  
1224 - }  
1225 - }  
1226 - },  
1227 - "entityAliases": {  
1228 - "3e0f533a-0db1-3292-184f-06e73535061a": {  
1229 - "id": "3e0f533a-0db1-3292-184f-06e73535061a",  
1230 - "alias": "Gateways",  
1231 - "filter": {  
1232 - "type": "deviceType",  
1233 - "resolveMultiple": true,  
1234 - "deviceType": "gateway",  
1235 - "deviceNameFilter": ""  
1236 - }  
1237 - },  
1238 - "b2487e75-2fa4-f211-142c-434dfd50c70c": {  
1239 - "id": "b2487e75-2fa4-f211-142c-434dfd50c70c",  
1240 - "alias": "Current Gateway",  
1241 - "filter": {  
1242 - "type": "stateEntity",  
1243 - "resolveMultiple": false,  
1244 - "stateEntityParamName": "",  
1245 - "defaultStateEntity": null  
1246 - }  
1247 - }  
1248 - },  
1249 - "timewindow": {  
1250 - "realtime": {  
1251 - "interval": 1000,  
1252 - "timewindowMs": 86400000  
1253 - },  
1254 - "aggregation": {  
1255 - "type": "NONE",  
1256 - "limit": 25000  
1257 - },  
1258 - "hideInterval": false,  
1259 - "hideAggregation": false,  
1260 - "hideAggInterval": false  
1261 - },  
1262 - "settings": {  
1263 - "stateControllerId": "entity",  
1264 - "showTitle": true,  
1265 - "showDashboardsSelect": true,  
1266 - "showEntitiesSelect": true,  
1267 - "showDashboardTimewindow": true,  
1268 - "showDashboardExport": true,  
1269 - "toolbarAlwaysOpen": true,  
1270 - "titleColor": "rgba(0,0,0,0.870588)"  
1271 - }  
1272 - },  
1273 - "name": "Gateways"  
1274 -}  
1 -{  
2 - "title": "Rule Engine Statistics",  
3 - "configuration": {  
4 - "widgets": {  
5 - "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {  
6 - "isSystemType": true,  
7 - "bundleAlias": "charts",  
8 - "typeAlias": "basic_timeseries",  
9 - "type": "timeseries",  
10 - "title": "New widget",  
11 - "sizeX": 12,  
12 - "sizeY": 7,  
13 - "config": {  
14 - "datasources": [  
15 - {  
16 - "type": "entity",  
17 - "dataKeys": [  
18 - {  
19 - "name": "successfulMsgs",  
20 - "type": "timeseries",  
21 - "label": "${entityName} Successful",  
22 - "color": "#4caf50",  
23 - "settings": {  
24 - "excludeFromStacking": false,  
25 - "hideDataByDefault": false,  
26 - "disableDataHiding": false,  
27 - "removeFromLegend": false,  
28 - "showLines": true,  
29 - "fillLines": false,  
30 - "showPoints": false,  
31 - "showPointShape": "circle",  
32 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
33 - "showPointsLineWidth": 5,  
34 - "showPointsRadius": 3,  
35 - "showSeparateAxis": false,  
36 - "axisPosition": "left",  
37 - "thresholds": [  
38 - {  
39 - "thresholdValueSource": "predefinedValue"  
40 - }  
41 - ],  
42 - "comparisonSettings": {  
43 - "showValuesForComparison": true  
44 - }  
45 - },  
46 - "_hash": 0.15490750967648736  
47 - },  
48 - {  
49 - "name": "failedMsgs",  
50 - "type": "timeseries",  
51 - "label": "${entityName} Permanent Failures",  
52 - "color": "#ef5350",  
53 - "settings": {  
54 - "excludeFromStacking": false,  
55 - "hideDataByDefault": false,  
56 - "disableDataHiding": false,  
57 - "removeFromLegend": false,  
58 - "showLines": true,  
59 - "fillLines": false,  
60 - "showPoints": false,  
61 - "showPointShape": "circle",  
62 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
63 - "showPointsLineWidth": 5,  
64 - "showPointsRadius": 3,  
65 - "showSeparateAxis": false,  
66 - "axisPosition": "left",  
67 - "thresholds": [  
68 - {  
69 - "thresholdValueSource": "predefinedValue"  
70 - }  
71 - ],  
72 - "comparisonSettings": {  
73 - "showValuesForComparison": true  
74 - }  
75 - },  
76 - "_hash": 0.4186621166514697  
77 - },  
78 - {  
79 - "name": "tmpFailed",  
80 - "type": "timeseries",  
81 - "label": "${entityName} Processing Failures",  
82 - "color": "#ffc107",  
83 - "settings": {  
84 - "excludeFromStacking": false,  
85 - "hideDataByDefault": false,  
86 - "disableDataHiding": false,  
87 - "removeFromLegend": false,  
88 - "showLines": true,  
89 - "fillLines": false,  
90 - "showPoints": false,  
91 - "showPointShape": "circle",  
92 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
93 - "showPointsLineWidth": 5,  
94 - "showPointsRadius": 3,  
95 - "showSeparateAxis": false,  
96 - "axisPosition": "left",  
97 - "thresholds": [  
98 - {  
99 - "thresholdValueSource": "predefinedValue"  
100 - }  
101 - ],  
102 - "comparisonSettings": {  
103 - "showValuesForComparison": true  
104 - }  
105 - },  
106 - "_hash": 0.49891007198715376  
107 - }  
108 - ],  
109 - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"  
110 - }  
111 - ],  
112 - "timewindow": {  
113 - "realtime": {  
114 - "interval": 1000,  
115 - "timewindowMs": 300000  
116 - },  
117 - "aggregation": {  
118 - "type": "NONE",  
119 - "limit": 8640  
120 - },  
121 - "hideInterval": false,  
122 - "hideAggregation": false,  
123 - "hideAggInterval": false  
124 - },  
125 - "showTitle": true,  
126 - "backgroundColor": "#fff",  
127 - "color": "rgba(0, 0, 0, 0.87)",  
128 - "padding": "8px",  
129 - "settings": {  
130 - "shadowSize": 4,  
131 - "fontColor": "#545454",  
132 - "fontSize": 10,  
133 - "xaxis": {  
134 - "showLabels": true,  
135 - "color": "#545454"  
136 - },  
137 - "yaxis": {  
138 - "showLabels": true,  
139 - "color": "#545454"  
140 - },  
141 - "grid": {  
142 - "color": "#545454",  
143 - "tickColor": "#DDDDDD",  
144 - "verticalLines": true,  
145 - "horizontalLines": true,  
146 - "outlineWidth": 1  
147 - },  
148 - "stack": false,  
149 - "tooltipIndividual": false,  
150 - "timeForComparison": "months",  
151 - "xaxisSecond": {  
152 - "axisPosition": "top",  
153 - "showLabels": true  
154 - }  
155 - },  
156 - "title": "Queue Stats",  
157 - "dropShadow": true,  
158 - "enableFullscreen": true,  
159 - "titleStyle": {  
160 - "fontSize": "16px",  
161 - "fontWeight": 400  
162 - },  
163 - "mobileHeight": null,  
164 - "showTitleIcon": false,  
165 - "titleIcon": null,  
166 - "iconColor": "rgba(0, 0, 0, 0.87)",  
167 - "iconSize": "24px",  
168 - "titleTooltip": "",  
169 - "widgetStyle": {},  
170 - "useDashboardTimewindow": false,  
171 - "displayTimewindow": true,  
172 - "showLegend": true,  
173 - "actions": {},  
174 - "legendConfig": {  
175 - "direction": "column",  
176 - "position": "bottom",  
177 - "showMin": true,  
178 - "showMax": true,  
179 - "showAvg": false,  
180 - "showTotal": true  
181 - }  
182 - },  
183 - "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0"  
184 - },  
185 - "5eb79712-5c24-3060-7e4f-6af36b8f842d": {  
186 - "isSystemType": true,  
187 - "bundleAlias": "cards",  
188 - "typeAlias": "timeseries_table",  
189 - "type": "timeseries",  
190 - "title": "New widget",  
191 - "sizeX": 24,  
192 - "sizeY": 5,  
193 - "config": {  
194 - "datasources": [  
195 - {  
196 - "type": "entity",  
197 - "dataKeys": [  
198 - {  
199 - "name": "ruleEngineException",  
200 - "type": "timeseries",  
201 - "label": "Rule Chain",  
202 - "color": "#2196f3",  
203 - "settings": {  
204 - "useCellStyleFunction": false,  
205 - "useCellContentFunction": true,  
206 - "cellContentFunction": "return JSON.parse(value).ruleChainName;"  
207 - },  
208 - "_hash": 0.9954481282345906  
209 - },  
210 - {  
211 - "name": "ruleEngineException",  
212 - "type": "timeseries",  
213 - "label": "Rule Node",  
214 - "color": "#4caf50",  
215 - "settings": {  
216 - "useCellStyleFunction": false,  
217 - "useCellContentFunction": true,  
218 - "cellContentFunction": "return JSON.parse(value).ruleNodeName;"  
219 - },  
220 - "_hash": 0.18580357036589978  
221 - },  
222 - {  
223 - "name": "ruleEngineException",  
224 - "type": "timeseries",  
225 - "label": "Latest Error",  
226 - "color": "#f44336",  
227 - "settings": {  
228 - "useCellStyleFunction": false,  
229 - "useCellContentFunction": true,  
230 - "cellContentFunction": "return JSON.parse(value).message;"  
231 - },  
232 - "_hash": 0.7255162989552142  
233 - }  
234 - ],  
235 - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"  
236 - }  
237 - ],  
238 - "timewindow": {  
239 - "realtime": {  
240 - "interval": 1000,  
241 - "timewindowMs": 86400000  
242 - },  
243 - "aggregation": {  
244 - "type": "NONE",  
245 - "limit": 200  
246 - }  
247 - },  
248 - "showTitle": true,  
249 - "backgroundColor": "rgb(255, 255, 255)",  
250 - "color": "rgba(0, 0, 0, 0.87)",  
251 - "padding": "8px",  
252 - "settings": {  
253 - "showTimestamp": true,  
254 - "displayPagination": true,  
255 - "defaultPageSize": 10  
256 - },  
257 - "title": "Exceptions",  
258 - "dropShadow": true,  
259 - "enableFullscreen": true,  
260 - "titleStyle": {  
261 - "fontSize": "16px",  
262 - "fontWeight": 400  
263 - },  
264 - "useDashboardTimewindow": false,  
265 - "showLegend": false,  
266 - "widgetStyle": {},  
267 - "actions": {},  
268 - "showTitleIcon": false,  
269 - "titleIcon": null,  
270 - "iconColor": "rgba(0, 0, 0, 0.87)",  
271 - "iconSize": "24px",  
272 - "titleTooltip": "",  
273 - "displayTimewindow": true  
274 - },  
275 - "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d"  
276 - },  
277 - "ad3f1417-87a8-750e-fc67-49a2de1466d4": {  
278 - "isSystemType": true,  
279 - "bundleAlias": "charts",  
280 - "typeAlias": "basic_timeseries",  
281 - "type": "timeseries",  
282 - "title": "New widget",  
283 - "sizeX": 12,  
284 - "sizeY": 7,  
285 - "config": {  
286 - "datasources": [  
287 - {  
288 - "type": "entity",  
289 - "dataKeys": [  
290 - {  
291 - "name": "timeoutMsgs",  
292 - "type": "timeseries",  
293 - "label": "${entityName} Permanent Timeouts",  
294 - "color": "#4caf50",  
295 - "settings": {  
296 - "excludeFromStacking": false,  
297 - "hideDataByDefault": false,  
298 - "disableDataHiding": false,  
299 - "removeFromLegend": false,  
300 - "showLines": true,  
301 - "fillLines": false,  
302 - "showPoints": false,  
303 - "showPointShape": "circle",  
304 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
305 - "showPointsLineWidth": 5,  
306 - "showPointsRadius": 3,  
307 - "showSeparateAxis": false,  
308 - "axisPosition": "left",  
309 - "thresholds": [  
310 - {  
311 - "thresholdValueSource": "predefinedValue"  
312 - }  
313 - ],  
314 - "comparisonSettings": {  
315 - "showValuesForComparison": true  
316 - }  
317 - },  
318 - "_hash": 0.565222981550328  
319 - },  
320 - {  
321 - "name": "tmpTimeout",  
322 - "type": "timeseries",  
323 - "label": "${entityName} Processing Timeouts",  
324 - "color": "#9c27b0",  
325 - "settings": {  
326 - "excludeFromStacking": false,  
327 - "hideDataByDefault": false,  
328 - "disableDataHiding": false,  
329 - "removeFromLegend": false,  
330 - "showLines": true,  
331 - "fillLines": false,  
332 - "showPoints": false,  
333 - "showPointShape": "circle",  
334 - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",  
335 - "showPointsLineWidth": 5,  
336 - "showPointsRadius": 3,  
337 - "showSeparateAxis": false,  
338 - "axisPosition": "left",  
339 - "thresholds": [  
340 - {  
341 - "thresholdValueSource": "predefinedValue"  
342 - }  
343 - ],  
344 - "comparisonSettings": {  
345 - "showValuesForComparison": true  
346 - }  
347 - },  
348 - "_hash": 0.2679547062508352  
349 - }  
350 - ],  
351 - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"  
352 - }  
353 - ],  
354 - "timewindow": {  
355 - "realtime": {  
356 - "interval": 1000,  
357 - "timewindowMs": 300000  
358 - },  
359 - "aggregation": {  
360 - "type": "NONE",  
361 - "limit": 8640  
362 - },  
363 - "hideInterval": false,  
364 - "hideAggregation": false,  
365 - "hideAggInterval": false  
366 - },  
367 - "showTitle": true,  
368 - "backgroundColor": "#fff",  
369 - "color": "rgba(0, 0, 0, 0.87)",  
370 - "padding": "8px",  
371 - "settings": {  
372 - "shadowSize": 4,  
373 - "fontColor": "#545454",  
374 - "fontSize": 10,  
375 - "xaxis": {  
376 - "showLabels": true,  
377 - "color": "#545454"  
378 - },  
379 - "yaxis": {  
380 - "showLabels": true,  
381 - "color": "#545454"  
382 - },  
383 - "grid": {  
384 - "color": "#545454",  
385 - "tickColor": "#DDDDDD",  
386 - "verticalLines": true,  
387 - "horizontalLines": true,  
388 - "outlineWidth": 1  
389 - },  
390 - "stack": false,  
391 - "tooltipIndividual": false,  
392 - "timeForComparison": "months",  
393 - "xaxisSecond": {  
394 - "axisPosition": "top",  
395 - "showLabels": true  
396 - }  
397 - },  
398 - "title": "Processing Failures and Timeouts",  
399 - "dropShadow": true,  
400 - "enableFullscreen": true,  
401 - "titleStyle": {  
402 - "fontSize": "16px",  
403 - "fontWeight": 400  
404 - },  
405 - "mobileHeight": null,  
406 - "showTitleIcon": false,  
407 - "titleIcon": null,  
408 - "iconColor": "rgba(0, 0, 0, 0.87)",  
409 - "iconSize": "24px",  
410 - "titleTooltip": "",  
411 - "widgetStyle": {},  
412 - "useDashboardTimewindow": false,  
413 - "displayTimewindow": true,  
414 - "showLegend": true,  
415 - "actions": {},  
416 - "legendConfig": {  
417 - "direction": "column",  
418 - "position": "bottom",  
419 - "showMin": true,  
420 - "showMax": true,  
421 - "showAvg": false,  
422 - "showTotal": true  
423 - }  
424 - },  
425 - "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4"  
426 - }  
427 - },  
428 - "states": {  
429 - "default": {  
430 - "name": "Rule Engine Statistics",  
431 - "root": true,  
432 - "layouts": {  
433 - "main": {  
434 - "widgets": {  
435 - "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {  
436 - "sizeX": 12,  
437 - "sizeY": 7,  
438 - "mobileHeight": null,  
439 - "row": 0,  
440 - "col": 0  
441 - },  
442 - "5eb79712-5c24-3060-7e4f-6af36b8f842d": {  
443 - "sizeX": 24,  
444 - "sizeY": 5,  
445 - "row": 7,  
446 - "col": 0  
447 - },  
448 - "ad3f1417-87a8-750e-fc67-49a2de1466d4": {  
449 - "sizeX": 12,  
450 - "sizeY": 7,  
451 - "mobileHeight": null,  
452 - "row": 0,  
453 - "col": 12  
454 - }  
455 - },  
456 - "gridSettings": {  
457 - "backgroundColor": "#eeeeee",  
458 - "color": "rgba(0,0,0,0.870588)",  
459 - "columns": 24,  
460 - "margins": [  
461 - 10,  
462 - 10  
463 - ],  
464 - "backgroundSizeMode": "100%",  
465 - "autoFillHeight": true,  
466 - "mobileAutoFillHeight": false,  
467 - "mobileRowHeight": 70  
468 - }  
469 - }  
470 - }  
471 - }  
472 - },  
473 - "entityAliases": {  
474 - "140f23dd-e3a0-ed98-6189-03c49d2d8018": {  
475 - "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018",  
476 - "alias": "TbServiceQueues",  
477 - "filter": {  
478 - "type": "assetType",  
479 - "resolveMultiple": true,  
480 - "assetType": "TbServiceQueue",  
481 - "assetNameFilter": ""  
482 - }  
483 - }  
484 - },  
485 - "timewindow": {  
486 - "displayValue": "",  
487 - "selectedTab": 0,  
488 - "hideInterval": false,  
489 - "hideAggregation": false,  
490 - "hideAggInterval": false,  
491 - "realtime": {  
492 - "interval": 1000,  
493 - "timewindowMs": 60000  
494 - },  
495 - "history": {  
496 - "historyType": 0,  
497 - "interval": 1000,  
498 - "timewindowMs": 60000,  
499 - "fixedTimewindow": {  
500 - "startTimeMs": 1586176634823,  
501 - "endTimeMs": 1586263034823  
502 - }  
503 - },  
504 - "aggregation": {  
505 - "type": "AVG",  
506 - "limit": 25000  
507 - }  
508 - },  
509 - "settings": {  
510 - "stateControllerId": "entity",  
511 - "showTitle": false,  
512 - "showDashboardsSelect": true,  
513 - "showEntitiesSelect": true,  
514 - "showDashboardTimewindow": true,  
515 - "showDashboardExport": true,  
516 - "toolbarAlwaysOpen": true  
517 - }  
518 - },  
519 - "name": "Rule Engine Statistics"  
520 -}  
@@ -123,28 +123,3 @@ BEGIN @@ -123,28 +123,3 @@ BEGIN
123 END LOOP; 123 END LOOP;
124 END 124 END
125 $$; 125 $$;
126 -  
127 -CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint)  
128 - LANGUAGE plpgsql AS  
129 -$$  
130 -DECLARE  
131 - ttl_ts bigint;  
132 - debug_ttl_ts bigint;  
133 - ttl_deleted_count bigint DEFAULT 0;  
134 - debug_ttl_deleted_count bigint DEFAULT 0;  
135 -BEGIN  
136 - IF ttl > 0 THEN  
137 - ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;  
138 - EXECUTE format(  
139 - 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type != %L::varchar AND event_type != %L::varchar) RETURNING *) SELECT count(*) FROM deleted', ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into ttl_deleted_count;  
140 - END IF;  
141 - IF debug_ttl > 0 THEN  
142 - debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;  
143 - EXECUTE format(  
144 - 'WITH deleted AS (DELETE FROM event WHERE ts < %L::bigint AND (event_type = %L::varchar OR event_type = %L::varchar) RETURNING *) SELECT count(*) FROM deleted', debug_ttl_ts, 'DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN') into debug_ttl_deleted_count;  
145 - END IF;  
146 - RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;  
147 - RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;  
148 - deleted := ttl_deleted_count + debug_ttl_deleted_count;  
149 -END  
150 -$$;  
@@ -32,10 +32,12 @@ import lombok.Setter; @@ -32,10 +32,12 @@ import lombok.Setter;
32 import lombok.extern.slf4j.Slf4j; 32 import lombok.extern.slf4j.Slf4j;
33 import org.springframework.beans.factory.annotation.Autowired; 33 import org.springframework.beans.factory.annotation.Autowired;
34 import org.springframework.beans.factory.annotation.Value; 34 import org.springframework.beans.factory.annotation.Value;
  35 +import org.springframework.context.annotation.Lazy;
35 import org.springframework.data.redis.core.RedisTemplate; 36 import org.springframework.data.redis.core.RedisTemplate;
36 import org.springframework.scheduling.annotation.Scheduled; 37 import org.springframework.scheduling.annotation.Scheduled;
37 import org.springframework.stereotype.Component; 38 import org.springframework.stereotype.Component;
38 import org.thingsboard.rule.engine.api.MailService; 39 import org.thingsboard.rule.engine.api.MailService;
  40 +import org.thingsboard.rule.engine.api.RuleChainTransactionService;
39 import org.thingsboard.server.actors.service.ActorService; 41 import org.thingsboard.server.actors.service.ActorService;
40 import org.thingsboard.server.actors.tenant.DebugTbRateLimits; 42 import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
41 import org.thingsboard.server.common.data.DataConstants; 43 import org.thingsboard.server.common.data.DataConstants;
@@ -43,11 +45,10 @@ import org.thingsboard.server.common.data.Event; @@ -43,11 +45,10 @@ import org.thingsboard.server.common.data.Event;
43 import org.thingsboard.server.common.data.id.EntityId; 45 import org.thingsboard.server.common.data.id.EntityId;
44 import org.thingsboard.server.common.data.id.TenantId; 46 import org.thingsboard.server.common.data.id.TenantId;
45 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 47 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
46 -import org.thingsboard.server.common.msg.TbActorMsg;  
47 import org.thingsboard.server.common.msg.TbMsg; 48 import org.thingsboard.server.common.msg.TbMsg;
48 -import org.thingsboard.server.common.msg.queue.ServiceType;  
49 -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 49 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
50 import org.thingsboard.server.common.msg.tools.TbRateLimits; 50 import org.thingsboard.server.common.msg.tools.TbRateLimits;
  51 +import org.thingsboard.server.common.transport.auth.DeviceAuthService;
51 import org.thingsboard.server.dao.alarm.AlarmService; 52 import org.thingsboard.server.dao.alarm.AlarmService;
52 import org.thingsboard.server.dao.asset.AssetService; 53 import org.thingsboard.server.dao.asset.AssetService;
53 import org.thingsboard.server.dao.attributes.AttributesService; 54 import org.thingsboard.server.dao.attributes.AttributesService;
@@ -64,23 +65,24 @@ import org.thingsboard.server.dao.rule.RuleChainService; @@ -64,23 +65,24 @@ import org.thingsboard.server.dao.rule.RuleChainService;
64 import org.thingsboard.server.dao.tenant.TenantService; 65 import org.thingsboard.server.dao.tenant.TenantService;
65 import org.thingsboard.server.dao.timeseries.TimeseriesService; 66 import org.thingsboard.server.dao.timeseries.TimeseriesService;
66 import org.thingsboard.server.dao.user.UserService; 67 import org.thingsboard.server.dao.user.UserService;
67 -import org.thingsboard.server.queue.discovery.PartitionService;  
68 -import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 68 +import org.thingsboard.server.kafka.TbNodeIdProvider;
  69 +import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
  70 +import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
  71 +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
69 import org.thingsboard.server.service.component.ComponentDiscoveryService; 72 import org.thingsboard.server.service.component.ComponentDiscoveryService;
70 import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 73 import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
  74 +import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService;
71 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 75 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
72 import org.thingsboard.server.service.executors.ExternalCallExecutorService; 76 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
73 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 77 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
74 import org.thingsboard.server.service.mail.MailExecutorService; 78 import org.thingsboard.server.service.mail.MailExecutorService;
75 -import org.thingsboard.server.service.queue.TbClusterService;  
76 -import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;  
77 -import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 79 +import org.thingsboard.server.service.rpc.DeviceRpcService;
78 import org.thingsboard.server.service.script.JsExecutorService; 80 import org.thingsboard.server.service.script.JsExecutorService;
79 import org.thingsboard.server.service.script.JsInvokeService; 81 import org.thingsboard.server.service.script.JsInvokeService;
80 import org.thingsboard.server.service.session.DeviceSessionCacheService; 82 import org.thingsboard.server.service.session.DeviceSessionCacheService;
81 import org.thingsboard.server.service.state.DeviceStateService; 83 import org.thingsboard.server.service.state.DeviceStateService;
82 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; 84 import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
83 -import org.thingsboard.server.service.transport.TbCoreToTransportService; 85 +import org.thingsboard.server.service.transport.RuleEngineTransportService;
84 86
85 import javax.annotation.Nullable; 87 import javax.annotation.Nullable;
86 import java.io.IOException; 88 import java.io.IOException;
@@ -104,14 +106,13 @@ public class ActorSystemContext { @@ -104,14 +106,13 @@ public class ActorSystemContext {
104 return debugPerTenantLimits; 106 return debugPerTenantLimits;
105 } 107 }
106 108
107 - @Autowired  
108 @Getter 109 @Getter
109 @Setter 110 @Setter
110 - private TbServiceInfoProvider serviceInfoProvider; 111 + private ActorService actorService;
111 112
  113 + @Autowired
112 @Getter 114 @Getter
113 - @Setter  
114 - private ActorService actorService; 115 + private DiscoveryService discoveryService;
115 116
116 @Autowired 117 @Autowired
117 @Getter 118 @Getter
@@ -120,10 +121,22 @@ public class ActorSystemContext { @@ -120,10 +121,22 @@ public class ActorSystemContext {
120 121
121 @Autowired 122 @Autowired
122 @Getter 123 @Getter
  124 + private ClusterRoutingService routingService;
  125 +
  126 + @Autowired
  127 + @Getter
  128 + private ClusterRpcService rpcService;
  129 +
  130 + @Autowired
  131 + @Getter
123 private DataDecodingEncodingService encodingService; 132 private DataDecodingEncodingService encodingService;
124 133
125 @Autowired 134 @Autowired
126 @Getter 135 @Getter
  136 + private DeviceAuthService deviceAuthService;
  137 +
  138 + @Autowired
  139 + @Getter
127 private DeviceService deviceService; 140 private DeviceService deviceService;
128 141
129 @Autowired 142 @Autowired
@@ -151,13 +164,6 @@ public class ActorSystemContext { @@ -151,13 +164,6 @@ public class ActorSystemContext {
151 private RuleChainService ruleChainService; 164 private RuleChainService ruleChainService;
152 165
153 @Autowired 166 @Autowired
154 - private PartitionService partitionService;  
155 -  
156 - @Autowired  
157 - @Getter  
158 - private TbClusterService clusterService;  
159 -  
160 - @Autowired  
161 @Getter 167 @Getter
162 private TimeseriesService tsService; 168 private TimeseriesService tsService;
163 169
@@ -191,6 +197,10 @@ public class ActorSystemContext { @@ -191,6 +197,10 @@ public class ActorSystemContext {
191 197
192 @Autowired 198 @Autowired
193 @Getter 199 @Getter
  200 + private DeviceRpcService deviceRpcService;
  201 +
  202 + @Autowired
  203 + @Getter
194 private JsInvokeService jsSandbox; 204 private JsInvokeService jsSandbox;
195 205
196 @Autowired 206 @Autowired
@@ -203,6 +213,10 @@ public class ActorSystemContext { @@ -203,6 +213,10 @@ public class ActorSystemContext {
203 213
204 @Autowired 214 @Autowired
205 @Getter 215 @Getter
  216 + private ClusterRpcCallbackExecutorService clusterRpcCallbackExecutor;
  217 +
  218 + @Autowired
  219 + @Getter
206 private DbCallbackExecutorService dbCallbackExecutor; 220 private DbCallbackExecutorService dbCallbackExecutor;
207 221
208 @Autowired 222 @Autowired
@@ -217,32 +231,27 @@ public class ActorSystemContext { @@ -217,32 +231,27 @@ public class ActorSystemContext {
217 @Getter 231 @Getter
218 private MailService mailService; 232 private MailService mailService;
219 233
220 - //TODO: separate context for TbCore and TbRuleEngine  
221 - @Autowired(required = false) 234 + @Autowired
222 @Getter 235 @Getter
223 private DeviceStateService deviceStateService; 236 private DeviceStateService deviceStateService;
224 237
225 - @Autowired(required = false) 238 + @Autowired
226 @Getter 239 @Getter
227 private DeviceSessionCacheService deviceSessionCacheService; 240 private DeviceSessionCacheService deviceSessionCacheService;
228 241
229 - @Autowired(required = false) 242 + @Lazy
  243 + @Autowired
230 @Getter 244 @Getter
231 - private TbCoreToTransportService tbCoreToTransportService; 245 + private RuleEngineTransportService ruleEngineTransportService;
232 246
233 - /**  
234 - * The following Service will be null if we operate in tb-core mode  
235 - */  
236 - @Autowired(required = false) 247 + @Lazy
  248 + @Autowired
237 @Getter 249 @Getter
238 - private TbRuleEngineDeviceRpcService tbRuleEngineDeviceRpcService; 250 + private RuleChainTransactionService ruleChainTransactionService;
239 251
240 - /**  
241 - * The following Service will be null if we operate in tb-rule-engine mode  
242 - */  
243 - @Autowired(required = false) 252 + @Value("${cluster.partition_id}")
244 @Getter 253 @Getter
245 - private TbCoreDeviceRpcService tbCoreDeviceRpcService; 254 + private long queuePartitionId;
246 255
247 @Value("${actors.session.max_concurrent_sessions_per_device:1}") 256 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
248 @Getter 257 @Getter
@@ -260,6 +269,10 @@ public class ActorSystemContext { @@ -260,6 +269,10 @@ public class ActorSystemContext {
260 @Getter 269 @Getter
261 private long queuePersistenceTimeout; 270 private long queuePersistenceTimeout;
262 271
  272 + @Value("${actors.client_side_rpc.timeout}")
  273 + @Getter
  274 + private long clientSideRpcTimeout;
  275 +
263 @Value("${actors.rule.chain.error_persist_frequency}") 276 @Value("${actors.rule.chain.error_persist_frequency}")
264 @Getter 277 @Getter
265 private long ruleChainErrorPersistFrequency; 278 private long ruleChainErrorPersistFrequency;
@@ -321,6 +334,11 @@ public class ActorSystemContext { @@ -321,6 +334,11 @@ public class ActorSystemContext {
321 @Setter 334 @Setter
322 private ActorSystem actorSystem; 335 private ActorSystem actorSystem;
323 336
  337 + @Autowired
  338 + @Getter
  339 + private TbNodeIdProvider nodeIdProvider;
  340 +
  341 + @Getter
324 @Setter 342 @Setter
325 private ActorRef appActor; 343 private ActorRef appActor;
326 344
@@ -347,8 +365,6 @@ public class ActorSystemContext { @@ -347,8 +365,6 @@ public class ActorSystemContext {
347 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load()); 365 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
348 } 366 }
349 367
350 -  
351 -  
352 public Scheduler getScheduler() { 368 public Scheduler getScheduler() {
353 return actorSystem.scheduler(); 369 return actorSystem.scheduler();
354 } 370 }
@@ -358,7 +374,7 @@ public class ActorSystemContext { @@ -358,7 +374,7 @@ public class ActorSystemContext {
358 event.setTenantId(tenantId); 374 event.setTenantId(tenantId);
359 event.setEntityId(entityId); 375 event.setEntityId(entityId);
360 event.setType(DataConstants.ERROR); 376 event.setType(DataConstants.ERROR);
361 - event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), method, toString(e))); 377 + event.setBody(toBodyJson(discoveryService.getCurrentServer().getServerAddress(), method, toString(e)));
362 persistEvent(event); 378 persistEvent(event);
363 } 379 }
364 380
@@ -367,7 +383,7 @@ public class ActorSystemContext { @@ -367,7 +383,7 @@ public class ActorSystemContext {
367 event.setTenantId(tenantId); 383 event.setTenantId(tenantId);
368 event.setEntityId(entityId); 384 event.setEntityId(entityId);
369 event.setType(DataConstants.LC_EVENT); 385 event.setType(DataConstants.LC_EVENT);
370 - event.setBody(toBodyJson(serviceInfoProvider.getServiceInfo().getServiceId(), lcEvent, Optional.ofNullable(e))); 386 + event.setBody(toBodyJson(discoveryService.getCurrentServer().getServerAddress(), lcEvent, Optional.ofNullable(e)));
371 persistEvent(event); 387 persistEvent(event);
372 } 388 }
373 389
@@ -381,8 +397,8 @@ public class ActorSystemContext { @@ -381,8 +397,8 @@ public class ActorSystemContext {
381 return sw.toString(); 397 return sw.toString();
382 } 398 }
383 399
384 - private JsonNode toBodyJson(String serviceId, ComponentLifecycleEvent event, Optional<Exception> e) {  
385 - ObjectNode node = mapper.createObjectNode().put("server", serviceId).put("event", event.name()); 400 + private JsonNode toBodyJson(ServerAddress server, ComponentLifecycleEvent event, Optional<Exception> e) {
  401 + ObjectNode node = mapper.createObjectNode().put("server", server.toString()).put("event", event.name());
386 if (e.isPresent()) { 402 if (e.isPresent()) {
387 node = node.put("success", false); 403 node = node.put("success", false);
388 node = node.put("error", toString(e.get())); 404 node = node.put("error", toString(e.get()));
@@ -392,21 +408,12 @@ public class ActorSystemContext { @@ -392,21 +408,12 @@ public class ActorSystemContext {
392 return node; 408 return node;
393 } 409 }
394 410
395 - private JsonNode toBodyJson(String serviceId, String method, String body) {  
396 - return mapper.createObjectNode().put("server", serviceId).put("method", method).put("error", body); 411 + private JsonNode toBodyJson(ServerAddress server, String method, String body) {
  412 + return mapper.createObjectNode().put("server", server.toString()).put("method", method).put("error", body);
397 } 413 }
398 414
399 - public TopicPartitionInfo resolve(ServiceType serviceType, TenantId tenantId, EntityId entityId) {  
400 - return partitionService.resolve(serviceType, tenantId, entityId);  
401 - }  
402 -  
403 - public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, TenantId tenantId, EntityId entityId) {  
404 - return partitionService.resolve(serviceType, queueName, tenantId, entityId);  
405 - }  
406 -  
407 -  
408 - public String getServiceId() {  
409 - return serviceInfoProvider.getServiceId(); 415 + public String getServerAddress() {
  416 + return discoveryService.getCurrentServer().getServerAddress().toString();
410 } 417 }
411 418
412 public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) { 419 public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
@@ -437,7 +444,7 @@ public class ActorSystemContext { @@ -437,7 +444,7 @@ public class ActorSystemContext {
437 444
438 ObjectNode node = mapper.createObjectNode() 445 ObjectNode node = mapper.createObjectNode()
439 .put("type", type) 446 .put("type", type)
440 - .put("server", getServiceId()) 447 + .put("server", getServerAddress())
441 .put("entityId", tbMsg.getOriginator().getId().toString()) 448 .put("entityId", tbMsg.getOriginator().getId().toString())
442 .put("entityName", tbMsg.getOriginator().getEntityType().name()) 449 .put("entityName", tbMsg.getOriginator().getEntityType().name())
443 .put("msgId", tbMsg.getId().toString()) 450 .put("msgId", tbMsg.getId().toString())
@@ -497,7 +504,7 @@ public class ActorSystemContext { @@ -497,7 +504,7 @@ public class ActorSystemContext {
497 504
498 ObjectNode node = mapper.createObjectNode() 505 ObjectNode node = mapper.createObjectNode()
499 //todo: what fields are needed here? 506 //todo: what fields are needed here?
500 - .put("server", getServiceId()) 507 + .put("server", getServerAddress())
501 .put("message", "Reached debug mode rate limit!"); 508 .put("message", "Reached debug mode rate limit!");
502 509
503 if (error != null) { 510 if (error != null) {
@@ -523,7 +530,4 @@ public class ActorSystemContext { @@ -523,7 +530,4 @@ public class ActorSystemContext {
523 return Exception.class.isInstance(error) ? (Exception) error : new Exception(error); 530 return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
524 } 531 }
525 532
526 - public void tell(TbActorMsg tbActorMsg, ActorRef sender) {  
527 - appActor.tell(tbActorMsg, sender);  
528 - }  
529 } 533 }
@@ -20,13 +20,19 @@ import akka.actor.LocalActorRef; @@ -20,13 +20,19 @@ import akka.actor.LocalActorRef;
20 import akka.actor.OneForOneStrategy; 20 import akka.actor.OneForOneStrategy;
21 import akka.actor.Props; 21 import akka.actor.Props;
22 import akka.actor.SupervisorStrategy; 22 import akka.actor.SupervisorStrategy;
  23 +import akka.actor.SupervisorStrategy.Directive;
23 import akka.actor.Terminated; 24 import akka.actor.Terminated;
  25 +import akka.event.Logging;
  26 +import akka.event.LoggingAdapter;
  27 +import akka.japi.Function;
24 import com.google.common.collect.BiMap; 28 import com.google.common.collect.BiMap;
25 import com.google.common.collect.HashBiMap; 29 import com.google.common.collect.HashBiMap;
  30 +import lombok.extern.slf4j.Slf4j;
26 import org.thingsboard.server.actors.ActorSystemContext; 31 import org.thingsboard.server.actors.ActorSystemContext;
27 -import org.thingsboard.server.actors.service.ContextAwareActor; 32 +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
28 import org.thingsboard.server.actors.service.ContextBasedCreator; 33 import org.thingsboard.server.actors.service.ContextBasedCreator;
29 import org.thingsboard.server.actors.service.DefaultActorService; 34 import org.thingsboard.server.actors.service.DefaultActorService;
  35 +import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
30 import org.thingsboard.server.actors.tenant.TenantActor; 36 import org.thingsboard.server.actors.tenant.TenantActor;
31 import org.thingsboard.server.common.data.EntityType; 37 import org.thingsboard.server.common.data.EntityType;
32 import org.thingsboard.server.common.data.Tenant; 38 import org.thingsboard.server.common.data.Tenant;
@@ -36,32 +42,29 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -36,32 +42,29 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
36 import org.thingsboard.server.common.msg.MsgType; 42 import org.thingsboard.server.common.msg.MsgType;
37 import org.thingsboard.server.common.msg.TbActorMsg; 43 import org.thingsboard.server.common.msg.TbActorMsg;
38 import org.thingsboard.server.common.msg.aware.TenantAwareMsg; 44 import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  45 +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
  46 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 47 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
40 -import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;  
41 -import org.thingsboard.server.common.msg.queue.RuleEngineException;  
42 -import org.thingsboard.server.common.msg.queue.ServiceType; 48 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
43 import org.thingsboard.server.dao.model.ModelConstants; 49 import org.thingsboard.server.dao.model.ModelConstants;
44 import org.thingsboard.server.dao.tenant.TenantService; 50 import org.thingsboard.server.dao.tenant.TenantService;
45 -import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;  
46 import scala.concurrent.duration.Duration; 51 import scala.concurrent.duration.Duration;
47 52
48 -import java.util.HashSet; 53 +import java.util.HashMap;
  54 +import java.util.Map;
49 import java.util.Optional; 55 import java.util.Optional;
50 -import java.util.Set;  
51 56
52 -public class AppActor extends ContextAwareActor { 57 +public class AppActor extends RuleChainManagerActor {
53 58
54 private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); 59 private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
55 private final TenantService tenantService; 60 private final TenantService tenantService;
56 private final BiMap<TenantId, ActorRef> tenantActors; 61 private final BiMap<TenantId, ActorRef> tenantActors;
57 - private final Set<TenantId> deletedTenants;  
58 private boolean ruleChainsInitialized; 62 private boolean ruleChainsInitialized;
59 63
60 private AppActor(ActorSystemContext systemContext) { 64 private AppActor(ActorSystemContext systemContext) {
61 - super(systemContext); 65 + super(systemContext, new SystemRuleChainManager(systemContext));
62 this.tenantService = systemContext.getTenantService(); 66 this.tenantService = systemContext.getTenantService();
63 this.tenantActors = HashBiMap.create(); 67 this.tenantActors = HashBiMap.create();
64 - this.deletedTenants = new HashSet<>();  
65 } 68 }
66 69
67 @Override 70 @Override
@@ -76,7 +79,7 @@ public class AppActor extends ContextAwareActor { @@ -76,7 +79,7 @@ public class AppActor extends ContextAwareActor {
76 @Override 79 @Override
77 protected boolean process(TbActorMsg msg) { 80 protected boolean process(TbActorMsg msg) {
78 if (!ruleChainsInitialized) { 81 if (!ruleChainsInitialized) {
79 - initTenantActors(); 82 + initRuleChainsAndTenantActors();
80 ruleChainsInitialized = true; 83 ruleChainsInitialized = true;
81 if (msg.getMsgType() != MsgType.APP_INIT_MSG) { 84 if (msg.getMsgType() != MsgType.APP_INIT_MSG) {
82 log.warn("Rule Chains initialized by unexpected message: {}", msg); 85 log.warn("Rule Chains initialized by unexpected message: {}", msg);
@@ -85,14 +88,17 @@ public class AppActor extends ContextAwareActor { @@ -85,14 +88,17 @@ public class AppActor extends ContextAwareActor {
85 switch (msg.getMsgType()) { 88 switch (msg.getMsgType()) {
86 case APP_INIT_MSG: 89 case APP_INIT_MSG:
87 break; 90 break;
88 - case PARTITION_CHANGE_MSG: 91 + case SEND_TO_CLUSTER_MSG:
  92 + onPossibleClusterMsg((SendToClusterMsg) msg);
  93 + break;
  94 + case CLUSTER_EVENT_MSG:
89 broadcast(msg); 95 broadcast(msg);
90 break; 96 break;
91 case COMPONENT_LIFE_CYCLE_MSG: 97 case COMPONENT_LIFE_CYCLE_MSG:
92 onComponentLifecycleMsg((ComponentLifecycleMsg) msg); 98 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
93 break; 99 break;
94 - case QUEUE_TO_RULE_ENGINE_MSG:  
95 - onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); 100 + case SERVICE_TO_RULE_ENGINE_MSG:
  101 + onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
96 break; 102 break;
97 case TRANSPORT_TO_DEVICE_ACTOR_MSG: 103 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
98 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 104 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
@@ -100,6 +106,7 @@ public class AppActor extends ContextAwareActor { @@ -100,6 +106,7 @@ public class AppActor extends ContextAwareActor {
100 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 106 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
101 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 107 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
102 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: 108 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  109 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
103 onToDeviceActorMsg((TenantAwareMsg) msg); 110 onToDeviceActorMsg((TenantAwareMsg) msg);
104 break; 111 break;
105 default: 112 default:
@@ -108,30 +115,16 @@ public class AppActor extends ContextAwareActor { @@ -108,30 +115,16 @@ public class AppActor extends ContextAwareActor {
108 return true; 115 return true;
109 } 116 }
110 117
111 - private void initTenantActors() { 118 + private void initRuleChainsAndTenantActors() {
112 log.info("Starting main system actor."); 119 log.info("Starting main system actor.");
113 try { 120 try {
114 - // This Service may be started for specific tenant only.  
115 - Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();  
116 - if (isolatedTenantId.isPresent()) {  
117 - Tenant tenant = systemContext.getTenantService().findTenantById(isolatedTenantId.get());  
118 - if (tenant != null) { 121 + initRuleChains();
  122 + if (systemContext.isTenantComponentsInitEnabled()) {
  123 + PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
  124 + for (Tenant tenant : tenantIterator) {
119 log.debug("[{}] Creating tenant actor", tenant.getId()); 125 log.debug("[{}] Creating tenant actor", tenant.getId());
120 getOrCreateTenantActor(tenant.getId()); 126 getOrCreateTenantActor(tenant.getId());
121 log.debug("Tenant actor created."); 127 log.debug("Tenant actor created.");
122 - } else {  
123 - log.error("[{}] Tenant with such ID does not exist", isolatedTenantId.get());  
124 - }  
125 - } else if (systemContext.isTenantComponentsInitEnabled()) {  
126 - PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);  
127 - boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);  
128 - boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);  
129 - for (Tenant tenant : tenantIterator) {  
130 - if (isCore || (isRuleEngine && !tenant.isIsolatedTbRuleEngine())) {  
131 - log.debug("[{}] Creating tenant actor", tenant.getId());  
132 - getOrCreateTenantActor(tenant.getId());  
133 - log.debug("[{}] Tenant actor created.", tenant.getId());  
134 - }  
135 } 128 }
136 } 129 }
137 log.info("Main system actor started."); 130 log.info("Main system actor started.");
@@ -140,33 +133,40 @@ public class AppActor extends ContextAwareActor { @@ -140,33 +133,40 @@ public class AppActor extends ContextAwareActor {
140 } 133 }
141 } 134 }
142 135
143 - private void onQueueToRuleEngineMsg(QueueToRuleEngineMsg msg) { 136 + private void onPossibleClusterMsg(SendToClusterMsg msg) {
  137 + Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(msg.getEntityId());
  138 + if (address.isPresent()) {
  139 + systemContext.getRpcService().tell(
  140 + systemContext.getEncodingService().convertToProtoDataMessage(address.get(), msg.getMsg()));
  141 + } else {
  142 + self().tell(msg.getMsg(), ActorRef.noSender());
  143 + }
  144 + }
  145 +
  146 + private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
144 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 147 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
145 - msg.getTbMsg().getCallback().onFailure(new RuleEngineException("Message has system tenant id!")); 148 +// this may be a notification about system entities created.
  149 +// log.warn("[{}] Invalid service to rule engine msg called. System messages are not supported yet: {}", SYSTEM_TENANT, msg);
146 } else { 150 } else {
147 - if (!deletedTenants.contains(msg.getTenantId())) {  
148 - getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());  
149 - } else {  
150 - msg.getTbMsg().getCallback().onSuccess();  
151 - } 151 + getOrCreateTenantActor(msg.getTenantId()).tell(msg, self());
152 } 152 }
153 } 153 }
154 154
  155 + @Override
155 protected void broadcast(Object msg) { 156 protected void broadcast(Object msg) {
  157 + super.broadcast(msg);
156 tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); 158 tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
157 } 159 }
158 160
159 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) { 161 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
160 ActorRef target = null; 162 ActorRef target = null;
161 if (SYSTEM_TENANT.equals(msg.getTenantId())) { 163 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
162 - log.warn("Message has system tenant id: {}", msg); 164 + target = getEntityActorRef(msg.getEntityId());
163 } else { 165 } else {
164 if (msg.getEntityId().getEntityType() == EntityType.TENANT 166 if (msg.getEntityId().getEntityType() == EntityType.TENANT
165 && msg.getEvent() == ComponentLifecycleEvent.DELETED) { 167 && msg.getEvent() == ComponentLifecycleEvent.DELETED) {
166 - log.info("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);  
167 - TenantId tenantId = new TenantId(msg.getEntityId().getId());  
168 - deletedTenants.add(tenantId);  
169 - ActorRef tenantActor = tenantActors.get(tenantId); 168 + log.debug("[{}] Handling tenant deleted notification: {}", msg.getTenantId(), msg);
  169 + ActorRef tenantActor = tenantActors.remove(new TenantId(msg.getEntityId().getId()));
170 if (tenantActor != null) { 170 if (tenantActor != null) {
171 log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor); 171 log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor);
172 context().stop(tenantActor); 172 context().stop(tenantActor);
@@ -183,22 +183,16 @@ public class AppActor extends ContextAwareActor { @@ -183,22 +183,16 @@ public class AppActor extends ContextAwareActor {
183 } 183 }
184 184
185 private void onToDeviceActorMsg(TenantAwareMsg msg) { 185 private void onToDeviceActorMsg(TenantAwareMsg msg) {
186 - if (!deletedTenants.contains(msg.getTenantId())) {  
187 - getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());  
188 - } else {  
189 - if (msg instanceof TransportToDeviceActorMsgWrapper) {  
190 - ((TransportToDeviceActorMsgWrapper) msg).getCallback().onSuccess();  
191 - }  
192 - } 186 + getOrCreateTenantActor(msg.getTenantId()).tell(msg, ActorRef.noSender());
193 } 187 }
194 188
195 private ActorRef getOrCreateTenantActor(TenantId tenantId) { 189 private ActorRef getOrCreateTenantActor(TenantId tenantId) {
196 return tenantActors.computeIfAbsent(tenantId, k -> { 190 return tenantActors.computeIfAbsent(tenantId, k -> {
197 - log.info("[{}] Creating tenant actor.", tenantId); 191 + log.debug("[{}] Creating tenant actor.", tenantId);
198 ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId)) 192 ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
199 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString()); 193 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString());
200 context().watch(tenantActor); 194 context().watch(tenantActor);
201 - log.info("[{}] Created tenant actor: {}.", tenantId, tenantActor); 195 + log.debug("[{}] Created tenant actor: {}.", tenantId, tenantActor);
202 return tenantActor; 196 return tenantActor;
203 }); 197 });
204 } 198 }
@@ -22,8 +22,10 @@ import org.thingsboard.server.actors.service.ContextAwareActor; @@ -22,8 +22,10 @@ import org.thingsboard.server.actors.service.ContextAwareActor;
22 import org.thingsboard.server.common.data.id.DeviceId; 22 import org.thingsboard.server.common.data.id.DeviceId;
23 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.msg.TbActorMsg; 24 import org.thingsboard.server.common.msg.TbActorMsg;
  25 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
25 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 26 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
26 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 27 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  28 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
27 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 29 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
28 30
29 public class DeviceActor extends ContextAwareActor { 31 public class DeviceActor extends ContextAwareActor {
@@ -47,11 +49,6 @@ public class DeviceActor extends ContextAwareActor { @@ -47,11 +49,6 @@ public class DeviceActor extends ContextAwareActor {
47 } 49 }
48 50
49 @Override 51 @Override
50 - public void postStop() {  
51 -  
52 - }  
53 -  
54 - @Override  
55 protected boolean process(TbActorMsg msg) { 52 protected boolean process(TbActorMsg msg) {
56 switch (msg.getMsgType()) { 53 switch (msg.getMsgType()) {
57 case TRANSPORT_TO_DEVICE_ACTOR_MSG: 54 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
@@ -69,9 +66,15 @@ public class DeviceActor extends ContextAwareActor { @@ -69,9 +66,15 @@ public class DeviceActor extends ContextAwareActor {
69 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 66 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
70 processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg); 67 processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
71 break; 68 break;
  69 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  70 + processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg);
  71 + break;
72 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG: 72 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
73 processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg); 73 processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg);
74 break; 74 break;
  75 + case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
  76 + processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
  77 + break;
75 case SESSION_TIMEOUT_MSG: 78 case SESSION_TIMEOUT_MSG:
76 processor.checkSessionsTimeout(); 79 processor.checkSessionsTimeout();
77 break; 80 break;
@@ -16,10 +16,13 @@ @@ -16,10 +16,13 @@
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
18 import akka.actor.ActorContext; 18 import akka.actor.ActorContext;
  19 +import com.datastax.driver.core.utils.UUIDs;
19 import com.google.common.util.concurrent.FutureCallback; 20 import com.google.common.util.concurrent.FutureCallback;
20 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
21 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
22 import com.google.common.util.concurrent.MoreExecutors; 23 import com.google.common.util.concurrent.MoreExecutors;
  24 +import com.google.gson.Gson;
  25 +import com.google.gson.JsonObject;
23 import com.google.protobuf.InvalidProtocolBufferException; 26 import com.google.protobuf.InvalidProtocolBufferException;
24 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
25 import org.apache.commons.collections.CollectionUtils; 28 import org.apache.commons.collections.CollectionUtils;
@@ -35,34 +38,38 @@ import org.thingsboard.server.common.data.kv.AttributeKey; @@ -35,34 +38,38 @@ import org.thingsboard.server.common.data.kv.AttributeKey;
35 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 38 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
36 import org.thingsboard.server.common.data.kv.KvEntry; 39 import org.thingsboard.server.common.data.kv.KvEntry;
37 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 40 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  41 +import org.thingsboard.server.common.msg.TbMsg;
  42 +import org.thingsboard.server.common.msg.TbMsgDataType;
38 import org.thingsboard.server.common.msg.TbMsgMetaData; 43 import org.thingsboard.server.common.msg.TbMsgMetaData;
39 -import org.thingsboard.server.common.msg.queue.TbCallback;  
40 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 44 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  45 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  46 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
41 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 47 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  48 +import org.thingsboard.server.gen.transport.TransportProtos;
42 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 49 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
43 -import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; 50 +import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
44 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 51 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
45 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; 52 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
46 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; 53 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
47 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; 54 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
  55 +import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
  56 +import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
48 import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; 57 import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
49 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; 58 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
50 import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; 59 import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
51 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; 60 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
52 -import org.thingsboard.server.gen.transport.TransportProtos.SessionSubscriptionInfoProto;  
53 -import org.thingsboard.server.gen.transport.TransportProtos.SessionType;  
54 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; 61 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
55 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; 62 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
56 -import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;  
57 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; 63 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
58 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; 64 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
59 -import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;  
60 -import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;  
61 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 65 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
  66 +import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
62 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 67 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
63 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 68 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
64 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 69 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  70 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
65 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 71 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
  72 +import org.thingsboard.server.utils.JsonUtils;
66 73
67 import javax.annotation.Nullable; 74 import javax.annotation.Nullable;
68 import java.util.ArrayList; 75 import java.util.ArrayList;
@@ -93,6 +100,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -93,6 +100,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
93 private final Map<UUID, SessionInfo> attributeSubscriptions; 100 private final Map<UUID, SessionInfo> attributeSubscriptions;
94 private final Map<UUID, SessionInfo> rpcSubscriptions; 101 private final Map<UUID, SessionInfo> rpcSubscriptions;
95 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; 102 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
  103 + private final Map<Integer, ToServerRpcRequestMetadata> toServerRpcPendingMap;
  104 +
  105 + private final Gson gson = new Gson();
96 106
97 private int rpcSeq = 0; 107 private int rpcSeq = 0;
98 private String deviceName; 108 private String deviceName;
@@ -107,6 +117,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -107,6 +117,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
107 this.attributeSubscriptions = new HashMap<>(); 117 this.attributeSubscriptions = new HashMap<>();
108 this.rpcSubscriptions = new HashMap<>(); 118 this.rpcSubscriptions = new HashMap<>();
109 this.toDeviceRpcPendingMap = new HashMap<>(); 119 this.toDeviceRpcPendingMap = new HashMap<>();
  120 + this.toServerRpcPendingMap = new HashMap<>();
110 if (initAttributes()) { 121 if (initAttributes()) {
111 restoreSessions(); 122 restoreSessions();
112 } 123 }
@@ -142,7 +153,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -142,7 +153,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
142 Set<UUID> syncSessionSet = new HashSet<>(); 153 Set<UUID> syncSessionSet = new HashSet<>();
143 rpcSubscriptions.forEach((key, value) -> { 154 rpcSubscriptions.forEach((key, value) -> {
144 sendToTransport(rpcRequest, key, value.getNodeId()); 155 sendToTransport(rpcRequest, key, value.getNodeId());
145 - if (SessionType.SYNC == value.getType()) { 156 + if (TransportProtos.SessionType.SYNC == value.getType()) {
146 syncSessionSet.add(key); 157 syncSessionSet.add(key);
147 } 158 }
148 }); 159 });
@@ -150,7 +161,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -150,7 +161,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
150 161
151 if (request.isOneway() && sent) { 162 if (request.isOneway() && sent) {
152 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 163 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
153 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); 164 + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
154 } else { 165 } else {
155 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout); 166 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
156 } 167 }
@@ -171,16 +182,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -171,16 +182,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
171 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); 182 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
172 if (requestMd != null) { 183 if (requestMd != null) {
173 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); 184 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
174 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 185 + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
175 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); 186 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
176 } 187 }
177 } 188 }
178 189
179 private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) { 190 private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
180 - SessionType sessionType = getSessionType(sessionId); 191 + TransportProtos.SessionType sessionType = getSessionType(sessionId);
181 if (!toDeviceRpcPendingMap.isEmpty()) { 192 if (!toDeviceRpcPendingMap.isEmpty()) {
182 log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); 193 log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);
183 - if (sessionType == SessionType.SYNC) { 194 + if (sessionType == TransportProtos.SessionType.SYNC) {
184 log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); 195 log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
185 rpcSubscriptions.remove(sessionId); 196 rpcSubscriptions.remove(sessionId);
186 } 197 }
@@ -188,7 +199,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -188,7 +199,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
188 log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId); 199 log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
189 } 200 }
190 Set<Integer> sentOneWayIds = new HashSet<>(); 201 Set<Integer> sentOneWayIds = new HashSet<>();
191 - if (sessionType == SessionType.ASYNC) { 202 + if (sessionType == TransportProtos.SessionType.ASYNC) {
192 toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds)); 203 toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
193 } else { 204 } else {
194 toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds)); 205 toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
@@ -203,7 +214,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -203,7 +214,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
203 ToDeviceRpcRequestBody body = request.getBody(); 214 ToDeviceRpcRequestBody body = request.getBody();
204 if (request.isOneway()) { 215 if (request.isOneway()) {
205 sentOneWayIds.add(entry.getKey()); 216 sentOneWayIds.add(entry.getKey());
206 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null)); 217 + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null));
207 } 218 }
208 ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId( 219 ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
209 entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build(); 220 entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build();
@@ -212,8 +223,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -212,8 +223,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
212 } 223 }
213 224
214 void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) { 225 void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
  226 + boolean reportDeviceActivity = false;
215 TransportToDeviceActorMsg msg = wrapper.getMsg(); 227 TransportToDeviceActorMsg msg = wrapper.getMsg();
216 - TbCallback callback = wrapper.getCallback();  
217 if (msg.hasSessionEvent()) { 228 if (msg.hasSessionEvent()) {
218 processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); 229 processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
219 } 230 }
@@ -223,16 +234,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -223,16 +234,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
223 if (msg.hasSubscribeToRPC()) { 234 if (msg.hasSubscribeToRPC()) {
224 processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC()); 235 processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC());
225 } 236 }
  237 + if (msg.hasPostAttributes()) {
  238 + handlePostAttributesRequest(context, msg.getSessionInfo(), msg.getPostAttributes());
  239 + reportDeviceActivity = true;
  240 + }
  241 + if (msg.hasPostTelemetry()) {
  242 + handlePostTelemetryRequest(context, msg.getSessionInfo(), msg.getPostTelemetry());
  243 + reportDeviceActivity = true;
  244 + }
226 if (msg.hasGetAttributes()) { 245 if (msg.hasGetAttributes()) {
227 handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); 246 handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
228 } 247 }
229 if (msg.hasToDeviceRPCCallResponse()) { 248 if (msg.hasToDeviceRPCCallResponse()) {
230 processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); 249 processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse());
231 } 250 }
  251 + if (msg.hasToServerRPCCallRequest()) {
  252 + handleClientSideRPCRequest(context, msg.getSessionInfo(), msg.getToServerRPCCallRequest());
  253 + reportDeviceActivity = true;
  254 + }
232 if (msg.hasSubscriptionInfo()) { 255 if (msg.hasSubscriptionInfo()) {
233 handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); 256 handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo());
234 } 257 }
235 - callback.onSuccess(); 258 + if (reportDeviceActivity) {
  259 + reportLogicalDeviceActivity();
  260 + }
  261 + }
  262 +
  263 + private void reportLogicalDeviceActivity() {
  264 + systemContext.getDeviceStateService().onDeviceActivity(deviceId);
236 } 265 }
237 266
238 private void reportSessionOpen() { 267 private void reportSessionOpen() {
@@ -297,8 +326,67 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -297,8 +326,67 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
297 return new HashSet<>(strings); 326 return new HashSet<>(strings);
298 } 327 }
299 328
300 - private SessionType getSessionType(UUID sessionId) {  
301 - return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC; 329 + private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) {
  330 + JsonObject json = JsonUtils.getJsonObject(postAttributes.getKvList());
  331 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(),
  332 + TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  333 + pushToRuleEngine(context, tbMsg);
  334 + }
  335 +
  336 + private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) {
  337 + for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) {
  338 + JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
  339 + TbMsgMetaData metaData = defaultMetaData.copy();
  340 + metaData.putValue("ts", tsKv.getTs() + "");
  341 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  342 + pushToRuleEngine(context, tbMsg);
  343 + }
  344 + }
  345 +
  346 + private void handleClientSideRPCRequest(ActorContext context, SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg request) {
  347 + UUID sessionId = getSessionId(sessionInfo);
  348 + JsonObject json = new JsonObject();
  349 + json.addProperty("method", request.getMethodName());
  350 + json.add("params", JsonUtils.parse(request.getParams()));
  351 +
  352 + TbMsgMetaData requestMetaData = defaultMetaData.copy();
  353 + requestMetaData.putValue("requestId", Integer.toString(request.getRequestId()));
  354 + TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.TO_SERVER_RPC_REQUEST.name(), deviceId, requestMetaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L);
  355 + context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
  356 +
  357 + scheduleMsgWithDelay(context, new DeviceActorClientSideRpcTimeoutMsg(request.getRequestId(), systemContext.getClientSideRpcTimeout()), systemContext.getClientSideRpcTimeout());
  358 + toServerRpcPendingMap.put(request.getRequestId(), new ToServerRpcRequestMetadata(sessionId, getSessionType(sessionId), sessionInfo.getNodeId()));
  359 + }
  360 +
  361 + private TransportProtos.SessionType getSessionType(UUID sessionId) {
  362 + return sessions.containsKey(sessionId) ? TransportProtos.SessionType.ASYNC : TransportProtos.SessionType.SYNC;
  363 + }
  364 +
  365 + void processClientSideRpcTimeout(ActorContext context, DeviceActorClientSideRpcTimeoutMsg msg) {
  366 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(msg.getId());
  367 + if (data != null) {
  368 + log.debug("[{}] Client side RPC request [{}] timeout detected!", deviceId, msg.getId());
  369 + sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
  370 + .setRequestId(msg.getId()).setError("timeout").build()
  371 + , data.getSessionId(), data.getNodeId());
  372 + }
  373 + }
  374 +
  375 + void processToServerRPCResponse(ActorContext context, ToServerRpcResponseActorMsg msg) {
  376 + int requestId = msg.getMsg().getRequestId();
  377 + ToServerRpcRequestMetadata data = toServerRpcPendingMap.remove(requestId);
  378 + if (data != null) {
  379 + log.debug("[{}] Pushing reply to [{}][{}]!", deviceId, data.getNodeId(), data.getSessionId());
  380 + sendToTransport(TransportProtos.ToServerRpcResponseMsg.newBuilder()
  381 + .setRequestId(requestId).setPayload(msg.getMsg().getData()).build()
  382 + , data.getSessionId(), data.getNodeId());
  383 + } else {
  384 + log.debug("[{}][{}] Pending RPC request to server not found!", deviceId, requestId);
  385 + }
  386 + }
  387 +
  388 + private void pushToRuleEngine(ActorContext context, TbMsg tbMsg) {
  389 + context.parent().tell(new DeviceActorToRuleEngineMsg(context.self(), tbMsg), context.self());
302 } 390 }
303 391
304 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) { 392 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
@@ -346,7 +434,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -346,7 +434,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
346 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); 434 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
347 boolean success = requestMd != null; 435 boolean success = requestMd != null;
348 if (success) { 436 if (success) {
349 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 437 + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
350 responseMsg.getPayload(), null)); 438 responseMsg.getPayload(), null));
351 } else { 439 } else {
352 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); 440 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
@@ -361,7 +449,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -361,7 +449,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
361 } else { 449 } else {
362 SessionInfoMetaData sessionMD = sessions.get(sessionId); 450 SessionInfoMetaData sessionMD = sessions.get(sessionId);
363 if (sessionMD == null) { 451 if (sessionMD == null) {
364 - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); 452 + sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()));
365 } 453 }
366 sessionMD.setSubscribedToAttributes(true); 454 sessionMD.setSubscribedToAttributes(true);
367 log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); 455 log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
@@ -382,7 +470,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -382,7 +470,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
382 } else { 470 } else {
383 SessionInfoMetaData sessionMD = sessions.get(sessionId); 471 SessionInfoMetaData sessionMD = sessions.get(sessionId);
384 if (sessionMD == null) { 472 if (sessionMD == null) {
385 - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); 473 + sessionMD = new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.SYNC, sessionInfo.getNodeId()));
386 } 474 }
387 sessionMD.setSubscribedToRPC(true); 475 sessionMD.setSubscribedToRPC(true);
388 log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); 476 log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
@@ -406,11 +494,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -406,11 +494,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
406 notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove)); 494 notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove));
407 } 495 }
408 } 496 }
409 - sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId()))); 497 + sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfo.getNodeId())));
410 if (sessions.size() == 1) { 498 if (sessions.size() == 1) {
411 reportSessionOpen(); 499 reportSessionOpen();
412 } 500 }
413 - systemContext.getDeviceStateService().onDeviceActivity(deviceId, System.currentTimeMillis());  
414 dumpSessions(); 501 dumpSessions();
415 } else if (msg.getEvent() == SessionEvent.CLOSED) { 502 } else if (msg.getEvent() == SessionEvent.CLOSED) {
416 log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); 503 log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
@@ -424,10 +511,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -424,10 +511,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
424 } 511 }
425 } 512 }
426 513
427 - private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { 514 + private void handleSessionActivity(ActorContext context, SessionInfoProto sessionInfoProto, TransportProtos.SubscriptionInfoProto subscriptionInfo) {
428 UUID sessionId = getSessionId(sessionInfoProto); 515 UUID sessionId = getSessionId(sessionInfoProto);
429 SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId, 516 SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId,
430 - id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L)); 517 + id -> new SessionInfoMetaData(new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L));
431 518
432 sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime()); 519 sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
433 sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription()); 520 sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
@@ -438,7 +525,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -438,7 +525,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
438 if (subscriptionInfo.getRpcSubscription()) { 525 if (subscriptionInfo.getRpcSubscription()) {
439 rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo()); 526 rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
440 } 527 }
441 - systemContext.getDeviceStateService().onDeviceActivity(deviceId, subscriptionInfo.getLastActivityTime());  
442 dumpSessions(); 528 dumpSessions();
443 } 529 }
444 530
@@ -450,11 +536,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -450,11 +536,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
450 } 536 }
451 537
452 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) { 538 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) {
453 - ToTransportMsg msg = ToTransportMsg.newBuilder() 539 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
454 .setSessionIdMSB(sessionId.getMostSignificantBits()) 540 .setSessionIdMSB(sessionId.getMostSignificantBits())
455 .setSessionIdLSB(sessionId.getLeastSignificantBits()) 541 .setSessionIdLSB(sessionId.getLeastSignificantBits())
456 .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build(); 542 .setSessionCloseNotification(SessionCloseNotificationProto.getDefaultInstance()).build();
457 - systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg); 543 + systemContext.getRuleEngineTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);
458 } 544 }
459 545
460 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) { 546 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
@@ -466,35 +552,35 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -466,35 +552,35 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
466 } 552 }
467 553
468 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) { 554 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
469 - ToTransportMsg msg = ToTransportMsg.newBuilder() 555 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
470 .setSessionIdMSB(sessionInfo.getSessionIdMSB()) 556 .setSessionIdMSB(sessionInfo.getSessionIdMSB())
471 .setSessionIdLSB(sessionInfo.getSessionIdLSB()) 557 .setSessionIdLSB(sessionInfo.getSessionIdLSB())
472 .setGetAttributesResponse(responseMsg).build(); 558 .setGetAttributesResponse(responseMsg).build();
473 - systemContext.getTbCoreToTransportService().process(sessionInfo.getNodeId(), msg); 559 + systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
474 } 560 }
475 561
476 private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) { 562 private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
477 - ToTransportMsg msg = ToTransportMsg.newBuilder() 563 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
478 .setSessionIdMSB(sessionId.getMostSignificantBits()) 564 .setSessionIdMSB(sessionId.getMostSignificantBits())
479 .setSessionIdLSB(sessionId.getLeastSignificantBits()) 565 .setSessionIdLSB(sessionId.getLeastSignificantBits())
480 .setAttributeUpdateNotification(notificationMsg).build(); 566 .setAttributeUpdateNotification(notificationMsg).build();
481 - systemContext.getTbCoreToTransportService().process(nodeId, msg); 567 + systemContext.getRuleEngineTransportService().process(nodeId, msg);
482 } 568 }
483 569
484 private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) { 570 private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
485 - ToTransportMsg msg = ToTransportMsg.newBuilder() 571 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
486 .setSessionIdMSB(sessionId.getMostSignificantBits()) 572 .setSessionIdMSB(sessionId.getMostSignificantBits())
487 .setSessionIdLSB(sessionId.getLeastSignificantBits()) 573 .setSessionIdLSB(sessionId.getLeastSignificantBits())
488 .setToDeviceRequest(rpcMsg).build(); 574 .setToDeviceRequest(rpcMsg).build();
489 - systemContext.getTbCoreToTransportService().process(nodeId, msg); 575 + systemContext.getRuleEngineTransportService().process(nodeId, msg);
490 } 576 }
491 577
492 - private void sendToTransport(ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {  
493 - ToTransportMsg msg = ToTransportMsg.newBuilder() 578 + private void sendToTransport(TransportProtos.ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {
  579 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
494 .setSessionIdMSB(sessionId.getMostSignificantBits()) 580 .setSessionIdMSB(sessionId.getMostSignificantBits())
495 .setSessionIdLSB(sessionId.getLeastSignificantBits()) 581 .setSessionIdLSB(sessionId.getLeastSignificantBits())
496 .setToServerResponse(rpcMsg).build(); 582 .setToServerResponse(rpcMsg).build();
497 - systemContext.getTbCoreToTransportService().process(nodeId, msg); 583 + systemContext.getRuleEngineTransportService().process(nodeId, msg);
498 } 584 }
499 585
500 586
@@ -546,9 +632,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -546,9 +632,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
546 632
547 private void restoreSessions() { 633 private void restoreSessions() {
548 log.debug("[{}] Restoring sessions from cache", deviceId); 634 log.debug("[{}] Restoring sessions from cache", deviceId);
549 - DeviceSessionsCacheEntry sessionsDump = null; 635 + TransportProtos.DeviceSessionsCacheEntry sessionsDump = null;
550 try { 636 try {
551 - sessionsDump = DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId)); 637 + sessionsDump = TransportProtos.DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId));
552 } catch (InvalidProtocolBufferException e) { 638 } catch (InvalidProtocolBufferException e) {
553 log.warn("[{}] Failed to decode device sessions from cache", deviceId); 639 log.warn("[{}] Failed to decode device sessions from cache", deviceId);
554 return; 640 return;
@@ -557,11 +643,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -557,11 +643,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
557 log.debug("[{}] No session information found", deviceId); 643 log.debug("[{}] No session information found", deviceId);
558 return; 644 return;
559 } 645 }
560 - for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { 646 + for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
561 SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo(); 647 SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
562 UUID sessionId = getSessionId(sessionInfoProto); 648 UUID sessionId = getSessionId(sessionInfoProto);
563 - SessionInfo sessionInfo = new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId());  
564 - SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo(); 649 + SessionInfo sessionInfo = new SessionInfo(TransportProtos.SessionType.ASYNC, sessionInfoProto.getNodeId());
  650 + TransportProtos.SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo();
565 SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime()); 651 SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime());
566 sessions.put(sessionId, sessionMD); 652 sessions.put(sessionId, sessionMD);
567 if (subInfo.getAttributeSubscription()) { 653 if (subInfo.getAttributeSubscription()) {
@@ -579,27 +665,27 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -579,27 +665,27 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
579 665
580 private void dumpSessions() { 666 private void dumpSessions() {
581 log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); 667 log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
582 - List<SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size()); 668 + List<TransportProtos.SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size());
583 sessions.forEach((uuid, sessionMD) -> { 669 sessions.forEach((uuid, sessionMD) -> {
584 - if (sessionMD.getSessionInfo().getType() == SessionType.SYNC) { 670 + if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) {
585 return; 671 return;
586 } 672 }
587 SessionInfo sessionInfo = sessionMD.getSessionInfo(); 673 SessionInfo sessionInfo = sessionMD.getSessionInfo();
588 - SubscriptionInfoProto subscriptionInfoProto = SubscriptionInfoProto.newBuilder() 674 + TransportProtos.SubscriptionInfoProto subscriptionInfoProto = TransportProtos.SubscriptionInfoProto.newBuilder()
589 .setLastActivityTime(sessionMD.getLastActivityTime()) 675 .setLastActivityTime(sessionMD.getLastActivityTime())
590 .setAttributeSubscription(sessionMD.isSubscribedToAttributes()) 676 .setAttributeSubscription(sessionMD.isSubscribedToAttributes())
591 .setRpcSubscription(sessionMD.isSubscribedToRPC()).build(); 677 .setRpcSubscription(sessionMD.isSubscribedToRPC()).build();
592 - SessionInfoProto sessionInfoProto = SessionInfoProto.newBuilder() 678 + TransportProtos.SessionInfoProto sessionInfoProto = TransportProtos.SessionInfoProto.newBuilder()
593 .setSessionIdMSB(uuid.getMostSignificantBits()) 679 .setSessionIdMSB(uuid.getMostSignificantBits())
594 .setSessionIdLSB(uuid.getLeastSignificantBits()) 680 .setSessionIdLSB(uuid.getLeastSignificantBits())
595 .setNodeId(sessionInfo.getNodeId()).build(); 681 .setNodeId(sessionInfo.getNodeId()).build();
596 - sessionsList.add(SessionSubscriptionInfoProto.newBuilder() 682 + sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder()
597 .setSessionInfo(sessionInfoProto) 683 .setSessionInfo(sessionInfoProto)
598 .setSubscriptionInfo(subscriptionInfoProto).build()); 684 .setSubscriptionInfo(subscriptionInfoProto).build());
599 log.debug("[{}] Dumping session: {}", deviceId, sessionMD); 685 log.debug("[{}] Dumping session: {}", deviceId, sessionMD);
600 }); 686 });
601 systemContext.getDeviceSessionCacheService() 687 systemContext.getDeviceSessionCacheService()
602 - .put(deviceId, DeviceSessionsCacheEntry.newBuilder() 688 + .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder()
603 .addAllSessions(sessionsList).build().toByteArray()); 689 .addAllSessions(sessionsList).build().toByteArray());
604 } 690 }
605 691
@@ -620,5 +706,4 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -620,5 +706,4 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
620 dumpSessions(); 706 dumpSessions();
621 } 707 }
622 } 708 }
623 -  
624 } 709 }
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorToRuleEngineMsg.java renamed from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeConfiguration.java
@@ -13,20 +13,25 @@ @@ -13,20 +13,25 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.rule.engine.flow; 16 +package org.thingsboard.server.actors.device;
17 17
  18 +import akka.actor.ActorRef;
18 import lombok.Data; 19 import lombok.Data;
19 -import org.thingsboard.rule.engine.api.NodeConfiguration; 20 +import org.thingsboard.server.common.msg.MsgType;
  21 +import org.thingsboard.server.common.msg.TbActorMsg;
  22 +import org.thingsboard.server.common.msg.TbMsg;
20 23
  24 +/**
  25 + * Created by ashvayka on 15.03.18.
  26 + */
21 @Data 27 @Data
22 -public class TbCheckpointNodeConfiguration implements NodeConfiguration<TbCheckpointNodeConfiguration> { 28 +public final class DeviceActorToRuleEngineMsg implements TbActorMsg {
23 29
24 - private String queueName; 30 + private final ActorRef callbackRef;
  31 + private final TbMsg tbMsg;
25 32
26 @Override 33 @Override
27 - public TbCheckpointNodeConfiguration defaultConfiguration() {  
28 - TbCheckpointNodeConfiguration configuration = new TbCheckpointNodeConfiguration();  
29 - configuration.setQueueName("HighPriority");  
30 - return configuration; 34 + public MsgType getMsgType() {
  35 + return MsgType.DEVICE_ACTOR_TO_RULE_ENGINE_MSG;
31 } 36 }
32 } 37 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.rpc;
  17 +
  18 +import akka.actor.ActorRef;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.actors.ActorSystemContext;
  21 +import org.thingsboard.server.actors.service.ActorService;
  22 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
  23 +import org.thingsboard.server.service.cluster.rpc.GrpcSession;
  24 +import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
  25 +import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService;
  26 +
  27 +/**
  28 + * @author Andrew Shvayka
  29 + */
  30 +@Slf4j
  31 +public class BasicRpcSessionListener implements GrpcSessionListener {
  32 +
  33 + private final ClusterRpcCallbackExecutorService callbackExecutorService;
  34 + private final ActorService service;
  35 + private final ActorRef manager;
  36 + private final ActorRef self;
  37 +
  38 + BasicRpcSessionListener(ActorSystemContext context, ActorRef manager, ActorRef self) {
  39 + this.service = context.getActorService();
  40 + this.callbackExecutorService = context.getClusterRpcCallbackExecutor();
  41 + this.manager = manager;
  42 + this.self = self;
  43 + }
  44 +
  45 + @Override
  46 + public void onConnected(GrpcSession session) {
  47 + log.info("[{}][{}] session started", session.getRemoteServer(), getType(session));
  48 + if (!session.isClient()) {
  49 + manager.tell(new RpcSessionConnectedMsg(session.getRemoteServer(), session.getSessionId()), self);
  50 + }
  51 + }
  52 +
  53 + @Override
  54 + public void onDisconnected(GrpcSession session) {
  55 + log.info("[{}][{}] session closed", session.getRemoteServer(), getType(session));
  56 + manager.tell(new RpcSessionDisconnectedMsg(session.isClient(), session.getRemoteServer()), self);
  57 + }
  58 +
  59 + @Override
  60 + public void onReceiveClusterGrpcMsg(GrpcSession session, ClusterAPIProtos.ClusterMessage clusterMessage) {
  61 + log.trace("Received session actor msg from [{}][{}]: {}", session.getRemoteServer(), getType(session), clusterMessage);
  62 + callbackExecutorService.execute(() -> {
  63 + try {
  64 + service.onReceivedMsg(session.getRemoteServer(), clusterMessage);
  65 + } catch (Exception e) {
  66 + log.debug("[{}][{}] Failed to process cluster message: {}", session.getRemoteServer(), getType(session), clusterMessage, e);
  67 + }
  68 + });
  69 + }
  70 +
  71 + @Override
  72 + public void onError(GrpcSession session, Throwable t) {
  73 + log.warn("[{}][{}] session got error -> {}", session.getRemoteServer(), getType(session), t);
  74 + manager.tell(new RpcSessionClosedMsg(session.isClient(), session.getRemoteServer()), self);
  75 + session.close();
  76 + }
  77 +
  78 + private static String getType(GrpcSession session) {
  79 + return session.isClient() ? "Client" : "Server";
  80 + }
  81 +
  82 +
  83 +}
application/src/main/java/org/thingsboard/server/actors/rpc/RpcBroadcastMsg.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueSubmitStrategyConfiguration.java
@@ -13,14 +13,15 @@ @@ -13,14 +13,15 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.settings; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
19 20
  21 +/**
  22 + * @author Andrew Shvayka
  23 + */
20 @Data 24 @Data
21 -public class TbRuleEngineQueueSubmitStrategyConfiguration {  
22 -  
23 - private String type;  
24 - private int batchSize;  
25 - 25 +public final class RpcBroadcastMsg {
  26 + private final ClusterAPIProtos.ClusterMessage msg;
26 } 27 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.rpc;
  17 +
  18 +import akka.actor.ActorRef;
  19 +import akka.actor.OneForOneStrategy;
  20 +import akka.actor.Props;
  21 +import akka.actor.SupervisorStrategy;
  22 +import akka.event.Logging;
  23 +import akka.event.LoggingAdapter;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.thingsboard.server.actors.ActorSystemContext;
  26 +import org.thingsboard.server.actors.service.ContextAwareActor;
  27 +import org.thingsboard.server.actors.service.ContextBasedCreator;
  28 +import org.thingsboard.server.actors.service.DefaultActorService;
  29 +import org.thingsboard.server.common.msg.TbActorMsg;
  30 +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  31 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  32 +import org.thingsboard.server.common.msg.cluster.ServerType;
  33 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
  34 +import org.thingsboard.server.service.cluster.discovery.ServerInstance;
  35 +import scala.concurrent.duration.Duration;
  36 +
  37 +import java.util.*;
  38 +
  39 +/**
  40 + * @author Andrew Shvayka
  41 + */
  42 +public class RpcManagerActor extends ContextAwareActor {
  43 +
  44 + private final Map<ServerAddress, SessionActorInfo> sessionActors;
  45 + private final Map<ServerAddress, Queue<ClusterAPIProtos.ClusterMessage>> pendingMsgs;
  46 + private final ServerAddress instance;
  47 +
  48 + private RpcManagerActor(ActorSystemContext systemContext) {
  49 + super(systemContext);
  50 + this.sessionActors = new HashMap<>();
  51 + this.pendingMsgs = new HashMap<>();
  52 + this.instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress();
  53 +
  54 + systemContext.getDiscoveryService().getOtherServers().stream()
  55 + .filter(otherServer -> otherServer.getServerAddress().compareTo(instance) > 0)
  56 + .forEach(otherServer -> onCreateSessionRequest(
  57 + new RpcSessionCreateRequestMsg(UUID.randomUUID(), otherServer.getServerAddress(), null)));
  58 + }
  59 +
  60 + @Override
  61 + protected boolean process(TbActorMsg msg) {
  62 + //TODO Move everything here, to work with TbActorMsg
  63 + return false;
  64 + }
  65 +
  66 + @Override
  67 + public void onReceive(Object msg) {
  68 + if (msg instanceof ClusterAPIProtos.ClusterMessage) {
  69 + onMsg((ClusterAPIProtos.ClusterMessage) msg);
  70 + } else if (msg instanceof RpcBroadcastMsg) {
  71 + onMsg((RpcBroadcastMsg) msg);
  72 + } else if (msg instanceof RpcSessionCreateRequestMsg) {
  73 + onCreateSessionRequest((RpcSessionCreateRequestMsg) msg);
  74 + } else if (msg instanceof RpcSessionConnectedMsg) {
  75 + onSessionConnected((RpcSessionConnectedMsg) msg);
  76 + } else if (msg instanceof RpcSessionDisconnectedMsg) {
  77 + onSessionDisconnected((RpcSessionDisconnectedMsg) msg);
  78 + } else if (msg instanceof RpcSessionClosedMsg) {
  79 + onSessionClosed((RpcSessionClosedMsg) msg);
  80 + } else if (msg instanceof ClusterEventMsg) {
  81 + onClusterEvent((ClusterEventMsg) msg);
  82 + }
  83 + }
  84 +
  85 + private void onMsg(RpcBroadcastMsg msg) {
  86 + log.debug("Forwarding msg to session actors {}", msg);
  87 + sessionActors.keySet().forEach(address -> {
  88 + ClusterAPIProtos.ClusterMessage msgWithServerAddress = msg.getMsg()
  89 + .toBuilder()
  90 + .setServerAddress(ClusterAPIProtos.ServerAddress
  91 + .newBuilder()
  92 + .setHost(address.getHost())
  93 + .setPort(address.getPort())
  94 + .build())
  95 + .build();
  96 + onMsg(msgWithServerAddress);
  97 + });
  98 + pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg()));
  99 + }
  100 +
  101 + private void onMsg(ClusterAPIProtos.ClusterMessage msg) {
  102 + if (msg.hasServerAddress()) {
  103 + ServerAddress address = new ServerAddress(msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE);
  104 + SessionActorInfo session = sessionActors.get(address);
  105 + if (session != null) {
  106 + log.debug("{} Forwarding msg to session actor: {}", address, msg);
  107 + session.getActor().tell(msg, ActorRef.noSender());
  108 + } else {
  109 + log.debug("{} Storing msg to pending queue: {}", address, msg);
  110 + Queue<ClusterAPIProtos.ClusterMessage> queue = pendingMsgs.get(address);
  111 + if (queue == null) {
  112 + queue = new LinkedList<>();
  113 + pendingMsgs.put(new ServerAddress(
  114 + msg.getServerAddress().getHost(), msg.getServerAddress().getPort(), ServerType.CORE), queue);
  115 + }
  116 + queue.add(msg);
  117 + }
  118 + } else {
  119 + log.warn("Cluster msg doesn't have server address [{}]", msg);
  120 + }
  121 + }
  122 +
  123 + @Override
  124 + public void postStop() {
  125 + sessionActors.clear();
  126 + pendingMsgs.clear();
  127 + }
  128 +
  129 + private void onClusterEvent(ClusterEventMsg msg) {
  130 + ServerAddress server = msg.getServerAddress();
  131 + if (server.compareTo(instance) > 0) {
  132 + if (msg.isAdded()) {
  133 + onCreateSessionRequest(new RpcSessionCreateRequestMsg(UUID.randomUUID(), server, null));
  134 + } else {
  135 + onSessionClose(false, server);
  136 + }
  137 + }
  138 + }
  139 +
  140 + private void onSessionConnected(RpcSessionConnectedMsg msg) {
  141 + register(msg.getRemoteAddress(), msg.getId(), context().sender());
  142 + }
  143 +
  144 + private void onSessionDisconnected(RpcSessionDisconnectedMsg msg) {
  145 + boolean reconnect = msg.isClient() && isRegistered(msg.getRemoteAddress());
  146 + onSessionClose(reconnect, msg.getRemoteAddress());
  147 + }
  148 +
  149 + private void onSessionClosed(RpcSessionClosedMsg msg) {
  150 + boolean reconnect = msg.isClient() && isRegistered(msg.getRemoteAddress());
  151 + onSessionClose(reconnect, msg.getRemoteAddress());
  152 + }
  153 +
  154 + private boolean isRegistered(ServerAddress address) {
  155 + for (ServerInstance server : systemContext.getDiscoveryService().getOtherServers()) {
  156 + if (server.getServerAddress().equals(address)) {
  157 + return true;
  158 + }
  159 + }
  160 + return false;
  161 + }
  162 +
  163 + private void onSessionClose(boolean reconnect, ServerAddress remoteAddress) {
  164 + log.info("[{}] session closed. Should reconnect: {}", remoteAddress, reconnect);
  165 + SessionActorInfo sessionRef = sessionActors.get(remoteAddress);
  166 + if (sessionRef != null && context().sender() != null && context().sender().equals(sessionRef.actor)) {
  167 + context().stop(sessionRef.actor);
  168 + sessionActors.remove(remoteAddress);
  169 + pendingMsgs.remove(remoteAddress);
  170 + if (reconnect) {
  171 + onCreateSessionRequest(new RpcSessionCreateRequestMsg(sessionRef.sessionId, remoteAddress, null));
  172 + }
  173 + }
  174 + }
  175 +
  176 + private void onCreateSessionRequest(RpcSessionCreateRequestMsg msg) {
  177 + if (msg.getRemoteAddress() != null) {
  178 + if (!sessionActors.containsKey(msg.getRemoteAddress())) {
  179 + ActorRef actorRef = createSessionActor(msg);
  180 + register(msg.getRemoteAddress(), msg.getMsgUid(), actorRef);
  181 + }
  182 + } else {
  183 + createSessionActor(msg);
  184 + }
  185 + }
  186 +
  187 + private void register(ServerAddress remoteAddress, UUID uuid, ActorRef sender) {
  188 + sessionActors.put(remoteAddress, new SessionActorInfo(uuid, sender));
  189 + log.info("[{}][{}] Registering session actor.", remoteAddress, uuid);
  190 + Queue<ClusterAPIProtos.ClusterMessage> data = pendingMsgs.remove(remoteAddress);
  191 + if (data != null) {
  192 + log.info("[{}][{}] Forwarding {} pending messages.", remoteAddress, uuid, data.size());
  193 + data.forEach(msg -> sender.tell(new RpcSessionTellMsg(msg), ActorRef.noSender()));
  194 + } else {
  195 + log.info("[{}][{}] No pending messages to forward.", remoteAddress, uuid);
  196 + }
  197 + }
  198 +
  199 + private ActorRef createSessionActor(RpcSessionCreateRequestMsg msg) {
  200 + log.info("[{}] Creating session actor.", msg.getMsgUid());
  201 + ActorRef actor = context().actorOf(
  202 + Props.create(new RpcSessionActor.ActorCreator(systemContext, msg.getMsgUid()))
  203 + .withDispatcher(DefaultActorService.RPC_DISPATCHER_NAME));
  204 + actor.tell(msg, context().self());
  205 + return actor;
  206 + }
  207 +
  208 + public static class ActorCreator extends ContextBasedCreator<RpcManagerActor> {
  209 + private static final long serialVersionUID = 1L;
  210 +
  211 + public ActorCreator(ActorSystemContext context) {
  212 + super(context);
  213 + }
  214 +
  215 + @Override
  216 + public RpcManagerActor create() {
  217 + return new RpcManagerActor(context);
  218 + }
  219 + }
  220 +
  221 + @Override
  222 + public SupervisorStrategy supervisorStrategy() {
  223 + return strategy;
  224 + }
  225 +
  226 + private final SupervisorStrategy strategy = new OneForOneStrategy(3, Duration.create("1 minute"), t -> {
  227 + log.warn("Unknown failure", t);
  228 + return SupervisorStrategy.resume();
  229 + });
  230 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.rpc;
  17 +
  18 +import io.grpc.ManagedChannel;
  19 +import io.grpc.ManagedChannelBuilder;
  20 +import io.grpc.stub.StreamObserver;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.server.actors.ActorSystemContext;
  23 +import org.thingsboard.server.actors.service.ContextAwareActor;
  24 +import org.thingsboard.server.actors.service.ContextBasedCreator;
  25 +import org.thingsboard.server.common.msg.TbActorMsg;
  26 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  27 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
  28 +import org.thingsboard.server.gen.cluster.ClusterRpcServiceGrpc;
  29 +import org.thingsboard.server.service.cluster.rpc.GrpcSession;
  30 +import org.thingsboard.server.service.cluster.rpc.GrpcSessionListener;
  31 +
  32 +import java.util.UUID;
  33 +
  34 +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CONNECT_RPC_MESSAGE;
  35 +
  36 +/**
  37 + * @author Andrew Shvayka
  38 + */
  39 +@Slf4j
  40 +public class RpcSessionActor extends ContextAwareActor {
  41 +
  42 +
  43 + private final UUID sessionId;
  44 + private GrpcSession session;
  45 + private GrpcSessionListener listener;
  46 +
  47 + private RpcSessionActor(ActorSystemContext systemContext, UUID sessionId) {
  48 + super(systemContext);
  49 + this.sessionId = sessionId;
  50 + }
  51 +
  52 + @Override
  53 + protected boolean process(TbActorMsg msg) {
  54 + //TODO Move everything here, to work with TbActorMsg
  55 + return false;
  56 + }
  57 +
  58 + @Override
  59 + public void onReceive(Object msg) {
  60 + if (msg instanceof ClusterAPIProtos.ClusterMessage) {
  61 + tell((ClusterAPIProtos.ClusterMessage) msg);
  62 + } else if (msg instanceof RpcSessionCreateRequestMsg) {
  63 + initSession((RpcSessionCreateRequestMsg) msg);
  64 + }
  65 + }
  66 +
  67 + private void tell(ClusterAPIProtos.ClusterMessage msg) {
  68 + if (session != null) {
  69 + session.sendMsg(msg);
  70 + } else {
  71 + log.trace("Failed to send message due to missing session!");
  72 + }
  73 + }
  74 +
  75 + @Override
  76 + public void postStop() {
  77 + if (session != null) {
  78 + log.info("Closing session -> {}", session.getRemoteServer());
  79 + try {
  80 + session.close();
  81 + } catch (RuntimeException e) {
  82 + log.trace("Failed to close session!", e);
  83 + }
  84 + }
  85 + }
  86 +
  87 + private void initSession(RpcSessionCreateRequestMsg msg) {
  88 + log.info("[{}] Initializing session", context().self());
  89 + ServerAddress remoteServer = msg.getRemoteAddress();
  90 + listener = new BasicRpcSessionListener(systemContext, context().parent(), context().self());
  91 + if (msg.getRemoteAddress() == null) {
  92 + // Server session
  93 + session = new GrpcSession(listener);
  94 + session.setOutputStream(msg.getResponseObserver());
  95 + session.initInputStream();
  96 + session.initOutputStream();
  97 + systemContext.getRpcService().onSessionCreated(msg.getMsgUid(), session.getInputStream());
  98 + } else {
  99 + // Client session
  100 + ManagedChannel channel = ManagedChannelBuilder.forAddress(remoteServer.getHost(), remoteServer.getPort()).usePlaintext().build();
  101 + session = new GrpcSession(remoteServer, listener, channel);
  102 + session.initInputStream();
  103 +
  104 + ClusterRpcServiceGrpc.ClusterRpcServiceStub stub = ClusterRpcServiceGrpc.newStub(channel);
  105 + StreamObserver<ClusterAPIProtos.ClusterMessage> outputStream = stub.handleMsgs(session.getInputStream());
  106 +
  107 + session.setOutputStream(outputStream);
  108 + session.initOutputStream();
  109 + outputStream.onNext(toConnectMsg());
  110 + }
  111 + }
  112 +
  113 + public static class ActorCreator extends ContextBasedCreator<RpcSessionActor> {
  114 + private static final long serialVersionUID = 1L;
  115 +
  116 + private final UUID sessionId;
  117 +
  118 + public ActorCreator(ActorSystemContext context, UUID sessionId) {
  119 + super(context);
  120 + this.sessionId = sessionId;
  121 + }
  122 +
  123 + @Override
  124 + public RpcSessionActor create() {
  125 + return new RpcSessionActor(context, sessionId);
  126 + }
  127 + }
  128 +
  129 + private ClusterAPIProtos.ClusterMessage toConnectMsg() {
  130 + ServerAddress instance = systemContext.getDiscoveryService().getCurrentServer().getServerAddress();
  131 + return ClusterAPIProtos.ClusterMessage.newBuilder().setMessageType(CONNECT_RPC_MESSAGE).setServerAddress(
  132 + ClusterAPIProtos.ServerAddress.newBuilder().setHost(instance.getHost())
  133 + .setPort(instance.getPort()).build()).build();
  134 + }
  135 +}
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionClosedMsg.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/kafka/KafkaTbQueueMsgMetadata.java
@@ -13,15 +13,17 @@ @@ -13,15 +13,17 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.kafka; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 -import lombok.AllArgsConstructor;  
19 import lombok.Data; 18 import lombok.Data;
20 -import org.apache.kafka.clients.producer.RecordMetadata;  
21 -import org.thingsboard.server.queue.TbQueueMsgMetadata; 19 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
22 20
  21 +/**
  22 + * @author Andrew Shvayka
  23 + */
23 @Data 24 @Data
24 -@AllArgsConstructor  
25 -public class KafkaTbQueueMsgMetadata implements TbQueueMsgMetadata {  
26 - private RecordMetadata metadata; 25 +public final class RpcSessionClosedMsg {
  26 +
  27 + private final boolean client;
  28 + private final ServerAddress remoteAddress;
27 } 29 }
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionConnectedMsg.java renamed from application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingDecision.java
@@ -13,19 +13,19 @@ @@ -13,19 +13,19 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.service.queue.processing; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;  
20 -import org.thingsboard.server.queue.common.TbProtoQueueMsg; 19 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
21 20
22 import java.util.UUID; 21 import java.util.UUID;
23 -import java.util.concurrent.ConcurrentMap;  
24 22
  23 +/**
  24 + * @author Andrew Shvayka
  25 + */
25 @Data 26 @Data
26 -public class TbRuleEngineProcessingDecision {  
27 -  
28 - private final boolean commit;  
29 - private final ConcurrentMap<UUID, TbProtoQueueMsg<ToRuleEngineMsg>> reprocessMap; 27 +public final class RpcSessionConnectedMsg {
30 28
  29 + private final ServerAddress remoteAddress;
  30 + private final UUID id;
31 } 31 }
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionCreateRequestMsg.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/sqs/AwsSqsTbQueueMsgMetadata.java
@@ -13,16 +13,23 @@ @@ -13,16 +13,23 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.sqs; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 -import com.amazonaws.http.SdkHttpMetadata;  
19 -import lombok.AllArgsConstructor; 18 +import io.grpc.stub.StreamObserver;
20 import lombok.Data; 19 import lombok.Data;
21 -import org.thingsboard.server.queue.TbQueueMsgMetadata; 20 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  21 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
22 22
  23 +import java.util.UUID;
  24 +
  25 +/**
  26 + * @author Andrew Shvayka
  27 + */
23 @Data 28 @Data
24 -@AllArgsConstructor  
25 -public class AwsSqsTbQueueMsgMetadata implements TbQueueMsgMetadata { 29 +public final class RpcSessionCreateRequestMsg {
  30 +
  31 + private final UUID msgUid;
  32 + private final ServerAddress remoteAddress;
  33 + private final StreamObserver<ClusterAPIProtos.ClusterMessage> responseObserver;
26 34
27 - private final SdkHttpMetadata metadata;  
28 } 35 }
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionDisconnectedMsg.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/settings/TbRuleEngineQueueAckStrategyConfiguration.java
@@ -13,16 +13,17 @@ @@ -13,16 +13,17 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.settings; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
19 20
  21 +/**
  22 + * @author Andrew Shvayka
  23 + */
20 @Data 24 @Data
21 -public class TbRuleEngineQueueAckStrategyConfiguration {  
22 -  
23 - private String type;  
24 - private int retries;  
25 - private double failurePercentage;  
26 - private long pauseBetweenRetries; 25 +public final class RpcSessionDisconnectedMsg {
27 26
  27 + private final boolean client;
  28 + private final ServerAddress remoteAddress;
28 } 29 }
application/src/main/java/org/thingsboard/server/actors/rpc/RpcSessionTellMsg.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/discovery/TenantRoutingInfo.java
@@ -13,14 +13,15 @@ @@ -13,14 +13,15 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue.discovery; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.data.id.TenantId; 19 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
20 20
  21 +/**
  22 + * @author Andrew Shvayka
  23 + */
21 @Data 24 @Data
22 -public class TenantRoutingInfo {  
23 - private final TenantId tenantId;  
24 - private final boolean isolatedTbCore;  
25 - private final boolean isolatedTbRuleEngine; 25 +public final class RpcSessionTellMsg {
  26 + private final ClusterAPIProtos.ClusterMessage msg;
26 } 27 }
application/src/main/java/org/thingsboard/server/actors/rpc/SessionActorInfo.java renamed from common/queue/src/main/java/org/thingsboard/server/queue/TbQueueMsg.java
@@ -13,15 +13,18 @@ @@ -13,15 +13,18 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.queue; 16 +package org.thingsboard.server.actors.rpc;
17 17
18 -import java.util.UUID;  
19 -  
20 -public interface TbQueueMsg {  
21 -  
22 - UUID getKey(); 18 +import akka.actor.ActorRef;
  19 +import lombok.Data;
23 20
24 - TbQueueMsgHeaders getHeaders(); 21 +import java.util.UUID;
25 22
26 - byte[] getData(); 23 +/**
  24 + * @author Andrew Shvayka
  25 + */
  26 +@Data
  27 +public final class SessionActorInfo {
  28 + protected final UUID sessionId;
  29 + protected final ActorRef actor;
27 } 30 }
@@ -17,13 +17,18 @@ package org.thingsboard.server.actors.ruleChain; @@ -17,13 +17,18 @@ package org.thingsboard.server.actors.ruleChain;
17 17
18 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
19 import com.datastax.driver.core.ResultSetFuture; 19 import com.datastax.driver.core.ResultSetFuture;
  20 +import com.datastax.driver.core.utils.UUIDs;
20 import com.fasterxml.jackson.core.JsonProcessingException; 21 import com.fasterxml.jackson.core.JsonProcessingException;
21 import com.fasterxml.jackson.databind.ObjectMapper; 22 import com.fasterxml.jackson.databind.ObjectMapper;
  23 +import com.fasterxml.jackson.databind.node.ObjectNode;
22 import io.netty.channel.EventLoopGroup; 24 import io.netty.channel.EventLoopGroup;
23 -import lombok.extern.slf4j.Slf4j;  
24 import org.springframework.data.redis.core.RedisTemplate; 25 import org.springframework.data.redis.core.RedisTemplate;
  26 +import org.springframework.util.StringUtils;
25 import org.thingsboard.common.util.ListeningExecutor; 27 import org.thingsboard.common.util.ListeningExecutor;
26 import org.thingsboard.rule.engine.api.MailService; 28 import org.thingsboard.rule.engine.api.MailService;
  29 +import org.thingsboard.rule.engine.api.RuleChainTransactionService;
  30 +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
  31 +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse;
27 import org.thingsboard.rule.engine.api.RuleEngineRpcService; 32 import org.thingsboard.rule.engine.api.RuleEngineRpcService;
28 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; 33 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
29 import org.thingsboard.rule.engine.api.ScriptEngine; 34 import org.thingsboard.rule.engine.api.ScriptEngine;
@@ -35,15 +40,19 @@ import org.thingsboard.server.common.data.DataConstants; @@ -35,15 +40,19 @@ import org.thingsboard.server.common.data.DataConstants;
35 import org.thingsboard.server.common.data.Device; 40 import org.thingsboard.server.common.data.Device;
36 import org.thingsboard.server.common.data.alarm.Alarm; 41 import org.thingsboard.server.common.data.alarm.Alarm;
37 import org.thingsboard.server.common.data.asset.Asset; 42 import org.thingsboard.server.common.data.asset.Asset;
  43 +import org.thingsboard.server.common.data.id.DeviceId;
38 import org.thingsboard.server.common.data.id.EntityId; 44 import org.thingsboard.server.common.data.id.EntityId;
39 -import org.thingsboard.server.common.data.id.RuleChainId;  
40 import org.thingsboard.server.common.data.id.RuleNodeId; 45 import org.thingsboard.server.common.data.id.RuleNodeId;
41 import org.thingsboard.server.common.data.id.TenantId; 46 import org.thingsboard.server.common.data.id.TenantId;
  47 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
42 import org.thingsboard.server.common.data.rule.RuleNode; 48 import org.thingsboard.server.common.data.rule.RuleNode;
43 import org.thingsboard.server.common.msg.TbMsg; 49 import org.thingsboard.server.common.msg.TbMsg;
44 import org.thingsboard.server.common.msg.TbMsgMetaData; 50 import org.thingsboard.server.common.msg.TbMsgMetaData;
45 -import org.thingsboard.server.common.msg.queue.ServiceType;  
46 -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 51 +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
  52 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  53 +import org.thingsboard.server.common.msg.cluster.ServerType;
  54 +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
  55 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
47 import org.thingsboard.server.dao.alarm.AlarmService; 56 import org.thingsboard.server.dao.alarm.AlarmService;
48 import org.thingsboard.server.dao.asset.AssetService; 57 import org.thingsboard.server.dao.asset.AssetService;
49 import org.thingsboard.server.dao.attributes.AttributesService; 58 import org.thingsboard.server.dao.attributes.AttributesService;
@@ -58,13 +67,11 @@ import org.thingsboard.server.dao.rule.RuleChainService; @@ -58,13 +67,11 @@ import org.thingsboard.server.dao.rule.RuleChainService;
58 import org.thingsboard.server.dao.tenant.TenantService; 67 import org.thingsboard.server.dao.tenant.TenantService;
59 import org.thingsboard.server.dao.timeseries.TimeseriesService; 68 import org.thingsboard.server.dao.timeseries.TimeseriesService;
60 import org.thingsboard.server.dao.user.UserService; 69 import org.thingsboard.server.dao.user.UserService;
61 -import org.thingsboard.server.gen.transport.TransportProtos;  
62 -import org.thingsboard.server.queue.TbQueueCallback;  
63 -import org.thingsboard.server.queue.TbQueueMsgMetadata;  
64 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; 70 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
65 import scala.concurrent.duration.Duration; 71 import scala.concurrent.duration.Duration;
66 72
67 import java.util.Collections; 73 import java.util.Collections;
  74 +import java.util.Optional;
68 import java.util.Set; 75 import java.util.Set;
69 import java.util.concurrent.TimeUnit; 76 import java.util.concurrent.TimeUnit;
70 import java.util.function.Consumer; 77 import java.util.function.Consumer;
@@ -72,7 +79,6 @@ import java.util.function.Consumer; @@ -72,7 +79,6 @@ import java.util.function.Consumer;
72 /** 79 /**
73 * Created by ashvayka on 19.03.18. 80 * Created by ashvayka on 19.03.18.
74 */ 81 */
75 -@Slf4j  
76 class DefaultTbContext implements TbContext { 82 class DefaultTbContext implements TbContext {
77 83
78 public final static ObjectMapper mapper = new ObjectMapper(); 84 public final static ObjectMapper mapper = new ObjectMapper();
@@ -86,11 +92,6 @@ class DefaultTbContext implements TbContext { @@ -86,11 +92,6 @@ class DefaultTbContext implements TbContext {
86 } 92 }
87 93
88 @Override 94 @Override
89 - public void tellSuccess(TbMsg msg) {  
90 - tellNext(msg, Collections.singleton(TbRelationTypes.SUCCESS), null);  
91 - }  
92 -  
93 - @Override  
94 public void tellNext(TbMsg msg, String relationType) { 95 public void tellNext(TbMsg msg, String relationType) {
95 tellNext(msg, Collections.singleton(relationType), null); 96 tellNext(msg, Collections.singleton(relationType), null);
96 } 97 }
@@ -100,11 +101,16 @@ class DefaultTbContext implements TbContext { @@ -100,11 +101,16 @@ class DefaultTbContext implements TbContext {
100 tellNext(msg, relationTypes, null); 101 tellNext(msg, relationTypes, null);
101 } 102 }
102 103
  104 + @Override
  105 + public void tellNext(TbMsg msg, String relationType, Throwable th) {
  106 + tellNext(msg, Collections.singleton(relationType), th);
  107 + }
  108 +
103 private void tellNext(TbMsg msg, Set<String> relationTypes, Throwable th) { 109 private void tellNext(TbMsg msg, Set<String> relationTypes, Throwable th) {
104 if (nodeCtx.getSelf().isDebugMode()) { 110 if (nodeCtx.getSelf().isDebugMode()) {
105 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); 111 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
106 } 112 }
107 - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null), nodeCtx.getSelfActor()); 113 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg), nodeCtx.getSelfActor());
108 } 114 }
109 115
110 @Override 116 @Override
@@ -114,93 +120,9 @@ class DefaultTbContext implements TbContext { @@ -114,93 +120,9 @@ class DefaultTbContext implements TbContext {
114 } 120 }
115 121
116 @Override 122 @Override
117 - public void enqueue(TbMsg tbMsg, Runnable onSuccess, Consumer<Throwable> onFailure) {  
118 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());  
119 - enqueue(tpi, tbMsg, onFailure, onSuccess);  
120 - }  
121 -  
122 - @Override  
123 - public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) {  
124 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());  
125 - enqueue(tpi, tbMsg, onFailure, onSuccess);  
126 - }  
127 -  
128 - private void enqueue(TopicPartitionInfo tpi, TbMsg tbMsg, Consumer<Throwable> onFailure, Runnable onSuccess) {  
129 - TransportProtos.ToRuleEngineMsg msg = TransportProtos.ToRuleEngineMsg.newBuilder()  
130 - .setTenantIdMSB(getTenantId().getId().getMostSignificantBits())  
131 - .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())  
132 - .setTbMsg(TbMsg.toByteString(tbMsg)).build();  
133 - mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg, new SimpleTbQueueCallback(onSuccess, onFailure));  
134 - }  
135 -  
136 - @Override  
137 - public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) {  
138 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());  
139 - enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null);  
140 - }  
141 -  
142 - @Override  
143 - public void enqueueForTellNext(TbMsg tbMsg, String relationType) {  
144 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());  
145 - enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, null, null);  
146 - }  
147 -  
148 - @Override  
149 - public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes) {  
150 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());  
151 - enqueueForTellNext(tpi, tbMsg, relationTypes, null, null, null);  
152 - }  
153 -  
154 - @Override  
155 - public void enqueueForTellNext(TbMsg tbMsg, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {  
156 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());  
157 - enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);  
158 - }  
159 -  
160 - @Override  
161 - public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {  
162 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());  
163 - enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure);  
164 - }  
165 -  
166 - @Override  
167 - public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {  
168 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());  
169 - enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);  
170 - }  
171 -  
172 - @Override  
173 - public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {  
174 - TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());  
175 - enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure);  
176 - }  
177 -  
178 - private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg tbMsg, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) {  
179 - RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId();  
180 - RuleNodeId ruleNodeId = nodeCtx.getSelf().getId();  
181 - tbMsg = TbMsg.newMsg(tbMsg, ruleChainId, ruleNodeId);  
182 - TransportProtos.ToRuleEngineMsg.Builder msg = TransportProtos.ToRuleEngineMsg.newBuilder()  
183 - .setTenantIdMSB(getTenantId().getId().getMostSignificantBits())  
184 - .setTenantIdLSB(getTenantId().getId().getLeastSignificantBits())  
185 - .setTbMsg(TbMsg.toByteString(tbMsg))  
186 - .addAllRelationTypes(relationTypes);  
187 - if (failureMessage != null) {  
188 - msg.setFailureMessage(failureMessage);  
189 - }  
190 - mainCtx.getClusterService().pushMsgToRuleEngine(tpi, tbMsg.getId(), msg.build(), new SimpleTbQueueCallback(onSuccess, onFailure));  
191 - }  
192 -  
193 - @Override  
194 - public void ack(TbMsg tbMsg) {  
195 - if (nodeCtx.getSelf().isDebugMode()) {  
196 - mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "ACK", null);  
197 - }  
198 - tbMsg.getCallback().onSuccess();  
199 - }  
200 -  
201 - @Override  
202 public boolean isLocalEntity(EntityId entityId) { 123 public boolean isLocalEntity(EntityId entityId) {
203 - return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), entityId).isMyPartition(); 124 + Optional<ServerAddress> address = mainCtx.getRoutingService().resolveById(entityId);
  125 + return !address.isPresent();
204 } 126 }
205 127
206 private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) { 128 private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
@@ -212,48 +134,66 @@ class DefaultTbContext implements TbContext { @@ -212,48 +134,66 @@ class DefaultTbContext implements TbContext {
212 if (nodeCtx.getSelf().isDebugMode()) { 134 if (nodeCtx.getSelf().isDebugMode()) {
213 mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th); 135 mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, TbRelationTypes.FAILURE, th);
214 } 136 }
215 - nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE),  
216 - msg, th != null ? th.getMessage() : null), nodeCtx.getSelfActor()); 137 + nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), Collections.singleton(TbRelationTypes.FAILURE), msg), nodeCtx.getSelfActor());
217 } 138 }
218 139
  140 + @Override
219 public void updateSelf(RuleNode self) { 141 public void updateSelf(RuleNode self) {
220 nodeCtx.setSelf(self); 142 nodeCtx.setSelf(self);
221 } 143 }
222 144
223 @Override 145 @Override
224 public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) { 146 public TbMsg newMsg(String type, EntityId originator, TbMsgMetaData metaData, String data) {
225 - return TbMsg.newMsg(type, originator, metaData, data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId()); 147 + return new TbMsg(UUIDs.timeBased(), type, originator, metaData.copy(), data, nodeCtx.getSelf().getRuleChainId(), nodeCtx.getSelf().getId(), mainCtx.getQueuePartitionId());
226 } 148 }
227 149
228 @Override 150 @Override
229 public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) { 151 public TbMsg transformMsg(TbMsg origMsg, String type, EntityId originator, TbMsgMetaData metaData, String data) {
230 - return TbMsg.transformMsg(origMsg, type, originator, metaData, data); 152 + return new TbMsg(origMsg.getId(), type, originator, metaData.copy(), origMsg.getDataType(), data, origMsg.getTransactionData(), origMsg.getRuleChainId(), origMsg.getRuleNodeId(), mainCtx.getQueuePartitionId());
  153 + }
  154 +
  155 + @Override
  156 + public void sendTbMsgToRuleEngine(TbMsg msg) {
  157 + mainCtx.getActorService().onMsg(new SendToClusterMsg(msg.getOriginator(), new ServiceToRuleEngineMsg(getTenantId(), msg)));
231 } 158 }
232 159
233 public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) { 160 public TbMsg customerCreatedMsg(Customer customer, RuleNodeId ruleNodeId) {
234 - return entityCreatedMsg(customer, customer.getId(), ruleNodeId); 161 + try {
  162 + ObjectNode entityNode = mapper.valueToTree(customer);
  163 + return new TbMsg(UUIDs.timeBased(), DataConstants.ENTITY_CREATED, customer.getId(), getActionMetaData(ruleNodeId), mapper.writeValueAsString(entityNode), null, null, 0L);
  164 + } catch (JsonProcessingException | IllegalArgumentException e) {
  165 + throw new RuntimeException("Failed to process customer created msg: " + e);
  166 + }
235 } 167 }
236 168
237 public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { 169 public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) {
238 - return entityCreatedMsg(device, device.getId(), ruleNodeId); 170 + try {
  171 + ObjectNode entityNode = mapper.valueToTree(device);
  172 + return new TbMsg(UUIDs.timeBased(), DataConstants.ENTITY_CREATED, device.getId(), getActionMetaData(ruleNodeId), mapper.writeValueAsString(entityNode), null, null, 0L);
  173 + } catch (JsonProcessingException | IllegalArgumentException e) {
  174 + throw new RuntimeException("Failed to process device created msg: " + e);
  175 + }
239 } 176 }
240 177
241 public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { 178 public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) {
242 - return entityCreatedMsg(asset, asset.getId(), ruleNodeId); 179 + try {
  180 + ObjectNode entityNode = mapper.valueToTree(asset);
  181 + return new TbMsg(UUIDs.timeBased(), DataConstants.ENTITY_CREATED, asset.getId(), getActionMetaData(ruleNodeId), mapper.writeValueAsString(entityNode), null, null, 0L);
  182 + } catch (JsonProcessingException | IllegalArgumentException e) {
  183 + throw new RuntimeException("Failed to process asset created msg: " + e);
  184 + }
243 } 185 }
244 186
245 public TbMsg alarmCreatedMsg(Alarm alarm, RuleNodeId ruleNodeId) { 187 public TbMsg alarmCreatedMsg(Alarm alarm, RuleNodeId ruleNodeId) {
246 - return entityCreatedMsg(alarm, alarm.getId(), ruleNodeId);  
247 - }  
248 -  
249 - public <E, I extends EntityId> TbMsg entityCreatedMsg(E entity, I id, RuleNodeId ruleNodeId) {  
250 try { 188 try {
251 - return TbMsg.newMsg(DataConstants.ENTITY_CREATED, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); 189 + ObjectNode entityNode = mapper.valueToTree(alarm);
  190 + return new TbMsg(UUIDs.timeBased(), DataConstants.ENTITY_CREATED, alarm.getId(), getActionMetaData(ruleNodeId), mapper.writeValueAsString(entityNode), null, null, 0L);
252 } catch (JsonProcessingException | IllegalArgumentException e) { 191 } catch (JsonProcessingException | IllegalArgumentException e) {
253 - throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " created msg: " + e); 192 + throw new RuntimeException("Failed to process alarm created msg: " + e);
254 } 193 }
255 } 194 }
256 195
  196 +
257 @Override 197 @Override
258 public RuleNodeId getSelfId() { 198 public RuleNodeId getSelfId() {
259 return nodeCtx.getSelf().getId(); 199 return nodeCtx.getSelf().getId();
@@ -311,8 +251,8 @@ class DefaultTbContext implements TbContext { @@ -311,8 +251,8 @@ class DefaultTbContext implements TbContext {
311 } 251 }
312 252
313 @Override 253 @Override
314 - public String getServiceId() {  
315 - return mainCtx.getServiceInfoProvider().getServiceId(); 254 + public String getNodeId() {
  255 + return mainCtx.getNodeIdProvider().getNodeId();
316 } 256 }
317 257
318 @Override 258 @Override
@@ -381,6 +321,11 @@ class DefaultTbContext implements TbContext { @@ -381,6 +321,11 @@ class DefaultTbContext implements TbContext {
381 } 321 }
382 322
383 @Override 323 @Override
  324 + public RuleChainTransactionService getRuleChainTransactionService() {
  325 + return mainCtx.getRuleChainTransactionService();
  326 + }
  327 +
  328 + @Override
384 public EventLoopGroup getSharedEventLoop() { 329 public EventLoopGroup getSharedEventLoop() {
385 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); 330 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
386 } 331 }
@@ -396,7 +341,35 @@ class DefaultTbContext implements TbContext { @@ -396,7 +341,35 @@ class DefaultTbContext implements TbContext {
396 341
397 @Override 342 @Override
398 public RuleEngineRpcService getRpcService() { 343 public RuleEngineRpcService getRpcService() {
399 - return mainCtx.getTbRuleEngineDeviceRpcService(); 344 + return new RuleEngineRpcService() {
  345 + @Override
  346 + public void sendRpcReply(DeviceId deviceId, int requestId, String body) {
  347 + mainCtx.getDeviceRpcService().sendReplyToRpcCallFromDevice(nodeCtx.getTenantId(), deviceId, requestId, body);
  348 + }
  349 +
  350 + @Override
  351 + public void sendRpcRequest(RuleEngineDeviceRpcRequest src, Consumer<RuleEngineDeviceRpcResponse> consumer) {
  352 + ToDeviceRpcRequest request = new ToDeviceRpcRequest(src.getRequestUUID(), nodeCtx.getTenantId(), src.getDeviceId(),
  353 + src.isOneway(), src.getExpirationTime(), new ToDeviceRpcRequestBody(src.getMethod(), src.getBody()));
  354 + mainCtx.getDeviceRpcService().forwardServerSideRPCRequestToDeviceActor(request, response -> {
  355 + if (src.isRestApiCall()) {
  356 + ServerAddress requestOriginAddress;
  357 + if (!StringUtils.isEmpty(src.getOriginHost())) {
  358 + requestOriginAddress = new ServerAddress(src.getOriginHost(), src.getOriginPort(), ServerType.CORE);
  359 + } else {
  360 + requestOriginAddress = mainCtx.getRoutingService().getCurrentServer();
  361 + }
  362 + mainCtx.getDeviceRpcService().processResponseToServerSideRPCRequestFromRuleEngine(requestOriginAddress, response);
  363 + }
  364 + consumer.accept(RuleEngineDeviceRpcResponse.builder()
  365 + .deviceId(src.getDeviceId())
  366 + .requestId(src.getRequestId())
  367 + .error(response.getError())
  368 + .response(response.getResponse())
  369 + .build());
  370 + });
  371 + }
  372 + };
400 } 373 }
401 374
402 @Override 375 @Override
@@ -414,6 +387,10 @@ class DefaultTbContext implements TbContext { @@ -414,6 +387,10 @@ class DefaultTbContext implements TbContext {
414 return mainCtx.getRedisTemplate(); 387 return mainCtx.getRedisTemplate();
415 } 388 }
416 389
  390 + @Override
  391 + public String getServerAddress() {
  392 + return mainCtx.getServerAddress();
  393 + }
417 394
418 private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { 395 private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) {
419 TbMsgMetaData metaData = new TbMsgMetaData(); 396 TbMsgMetaData metaData = new TbMsgMetaData();
@@ -421,29 +398,4 @@ class DefaultTbContext implements TbContext { @@ -421,29 +398,4 @@ class DefaultTbContext implements TbContext {
421 return metaData; 398 return metaData;
422 } 399 }
423 400
424 - private class SimpleTbQueueCallback implements TbQueueCallback {  
425 - private final Runnable onSuccess;  
426 - private final Consumer<Throwable> onFailure;  
427 -  
428 - public SimpleTbQueueCallback(Runnable onSuccess, Consumer<Throwable> onFailure) {  
429 - this.onSuccess = onSuccess;  
430 - this.onFailure = onFailure;  
431 - }  
432 -  
433 - @Override  
434 - public void onSuccess(TbQueueMsgMetadata metadata) {  
435 - if (onSuccess != null) {  
436 - onSuccess.run();  
437 - }  
438 - }  
439 -  
440 - @Override  
441 - public void onFailure(Throwable t) {  
442 - if (onFailure != null) {  
443 - onFailure.accept(t);  
444 - } else {  
445 - log.debug("[{}] Failed to put item into queue", nodeCtx.getTenantId(), t);  
446 - }  
447 - }  
448 - }  
449 } 401 }
application/src/main/java/org/thingsboard/server/actors/ruleChain/RemoteToRuleChainTellNextMsg.java renamed from application/src/main/java/org/thingsboard/server/service/subscription/TbSubscription.java
@@ -13,40 +13,36 @@ @@ -13,40 +13,36 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.service.subscription; 16 +package org.thingsboard.server.actors.ruleChain;
17 17
18 -import lombok.AllArgsConstructor;  
19 import lombok.Data; 18 import lombok.Data;
20 -import org.thingsboard.server.common.data.id.EntityId; 19 +import org.thingsboard.server.common.data.id.RuleChainId;
21 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.msg.MsgType;
  22 +import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  23 +import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
22 24
23 -import java.util.Objects; 25 +import java.io.Serializable;
24 26
  27 +/**
  28 + * Created by ashvayka on 19.03.18.
  29 + */
25 @Data 30 @Data
26 -@AllArgsConstructor  
27 -public abstract class TbSubscription { 31 +final class RemoteToRuleChainTellNextMsg extends RuleNodeToRuleChainTellNextMsg implements TenantAwareMsg, RuleChainAwareMsg {
28 32
29 - private final String serviceId;  
30 - private final String sessionId;  
31 - private final int subscriptionId; 33 + private static final long serialVersionUID = 2459605482321657447L;
32 private final TenantId tenantId; 34 private final TenantId tenantId;
33 - private final EntityId entityId;  
34 - private final TbSubscriptionType type; 35 + private final RuleChainId ruleChainId;
35 36
36 - @Override  
37 - public boolean equals(Object o) {  
38 - if (this == o) return true;  
39 - if (o == null || getClass() != o.getClass()) return false;  
40 - TbSubscription that = (TbSubscription) o;  
41 - return subscriptionId == that.subscriptionId &&  
42 - sessionId.equals(that.sessionId) &&  
43 - tenantId.equals(that.tenantId) &&  
44 - entityId.equals(that.entityId) &&  
45 - type == that.type; 37 + public RemoteToRuleChainTellNextMsg(RuleNodeToRuleChainTellNextMsg original, TenantId tenantId, RuleChainId ruleChainId) {
  38 + super(original.getOriginator(), original.getRelationTypes(), original.getMsg());
  39 + this.tenantId = tenantId;
  40 + this.ruleChainId = ruleChainId;
46 } 41 }
47 42
48 @Override 43 @Override
49 - public int hashCode() {  
50 - return Objects.hash(sessionId, subscriptionId, tenantId, entityId, type); 44 + public MsgType getMsgType() {
  45 + return MsgType.REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG;
51 } 46 }
  47 +
52 } 48 }
@@ -15,25 +15,25 @@ @@ -15,25 +15,25 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
  18 +import akka.actor.ActorInitializationException;
18 import akka.actor.OneForOneStrategy; 19 import akka.actor.OneForOneStrategy;
19 import akka.actor.SupervisorStrategy; 20 import akka.actor.SupervisorStrategy;
20 import org.thingsboard.server.actors.ActorSystemContext; 21 import org.thingsboard.server.actors.ActorSystemContext;
  22 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
21 import org.thingsboard.server.actors.service.ComponentActor; 23 import org.thingsboard.server.actors.service.ComponentActor;
22 import org.thingsboard.server.actors.service.ContextBasedCreator; 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
23 import org.thingsboard.server.common.data.id.RuleChainId; 25 import org.thingsboard.server.common.data.id.RuleChainId;
24 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
25 -import org.thingsboard.server.common.data.rule.RuleChain;  
26 import org.thingsboard.server.common.msg.TbActorMsg; 27 import org.thingsboard.server.common.msg.TbActorMsg;
27 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 28 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
28 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;  
29 -import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 29 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
30 import scala.concurrent.duration.Duration; 30 import scala.concurrent.duration.Duration;
31 31
32 public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMessageProcessor> { 32 public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMessageProcessor> {
33 33
34 - private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChain ruleChain) {  
35 - super(systemContext, tenantId, ruleChain.getId());  
36 - setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChain, systemContext, 34 + private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) {
  35 + super(systemContext, tenantId, ruleChainId);
  36 + setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext,
37 context().parent(), context().self())); 37 context().parent(), context().self()));
38 } 38 }
39 39
@@ -43,17 +43,20 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe @@ -43,17 +43,20 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
43 case COMPONENT_LIFE_CYCLE_MSG: 43 case COMPONENT_LIFE_CYCLE_MSG:
44 onComponentLifecycleMsg((ComponentLifecycleMsg) msg); 44 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
45 break; 45 break;
46 - case QUEUE_TO_RULE_ENGINE_MSG:  
47 - processor.onQueueToRuleEngineMsg((QueueToRuleEngineMsg) msg); 46 + case SERVICE_TO_RULE_ENGINE_MSG:
  47 + processor.onServiceToRuleEngineMsg((ServiceToRuleEngineMsg) msg);
  48 + break;
  49 + case DEVICE_ACTOR_TO_RULE_ENGINE_MSG:
  50 + processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
48 break; 51 break;
49 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: 52 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
  53 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
50 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); 54 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
51 break; 55 break;
52 case RULE_CHAIN_TO_RULE_CHAIN_MSG: 56 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
53 processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg); 57 processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
54 break; 58 break;
55 - case PARTITION_CHANGE_MSG:  
56 - processor.onPartitionChangeMsg((PartitionChangeMsg) msg); 59 + case CLUSTER_EVENT_MSG:
57 break; 60 break;
58 case STATS_PERSIST_TICK_MSG: 61 case STATS_PERSIST_TICK_MSG:
59 onStatsPersistTick(id); 62 onStatsPersistTick(id);
@@ -68,17 +71,17 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe @@ -68,17 +71,17 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
68 private static final long serialVersionUID = 1L; 71 private static final long serialVersionUID = 1L;
69 72
70 private final TenantId tenantId; 73 private final TenantId tenantId;
71 - private final RuleChain ruleChain; 74 + private final RuleChainId ruleChainId;
72 75
73 - public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChain ruleChain) { 76 + public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId pluginId) {
74 super(context); 77 super(context);
75 this.tenantId = tenantId; 78 this.tenantId = tenantId;
76 - this.ruleChain = ruleChain; 79 + this.ruleChainId = pluginId;
77 } 80 }
78 81
79 @Override 82 @Override
80 public RuleChainActor create() { 83 public RuleChainActor create() {
81 - return new RuleChainActor(context, tenantId, ruleChain); 84 + return new RuleChainActor(context, tenantId, ruleChainId);
82 } 85 }
83 } 86 }
84 87
@@ -18,9 +18,14 @@ package org.thingsboard.server.actors.ruleChain; @@ -18,9 +18,14 @@ package org.thingsboard.server.actors.ruleChain;
18 import akka.actor.ActorContext; 18 import akka.actor.ActorContext;
19 import akka.actor.ActorRef; 19 import akka.actor.ActorRef;
20 import akka.actor.Props; 20 import akka.actor.Props;
  21 +import akka.event.LoggingAdapter;
  22 +import com.datastax.driver.core.utils.UUIDs;
  23 +
  24 +import java.util.Optional;
  25 +
21 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
22 -import org.thingsboard.rule.engine.api.TbRelationTypes;  
23 import org.thingsboard.server.actors.ActorSystemContext; 27 import org.thingsboard.server.actors.ActorSystemContext;
  28 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
24 import org.thingsboard.server.actors.service.DefaultActorService; 29 import org.thingsboard.server.actors.service.DefaultActorService;
25 import org.thingsboard.server.actors.shared.ComponentMsgProcessor; 30 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
26 import org.thingsboard.server.common.data.EntityType; 31 import org.thingsboard.server.common.data.EntityType;
@@ -34,19 +39,11 @@ import org.thingsboard.server.common.data.relation.EntityRelation; @@ -34,19 +39,11 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
34 import org.thingsboard.server.common.data.rule.RuleChain; 39 import org.thingsboard.server.common.data.rule.RuleChain;
35 import org.thingsboard.server.common.data.rule.RuleNode; 40 import org.thingsboard.server.common.data.rule.RuleNode;
36 import org.thingsboard.server.common.msg.TbMsg; 41 import org.thingsboard.server.common.msg.TbMsg;
  42 +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  43 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
37 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 44 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
38 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;  
39 -import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;  
40 -import org.thingsboard.server.common.msg.queue.RuleEngineException;  
41 -import org.thingsboard.server.common.msg.queue.RuleNodeException;  
42 -import org.thingsboard.server.common.msg.queue.ServiceType;  
43 -import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 45 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
44 import org.thingsboard.server.dao.rule.RuleChainService; 46 import org.thingsboard.server.dao.rule.RuleChainService;
45 -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg;  
46 -import org.thingsboard.server.queue.TbQueueCallback;  
47 -import org.thingsboard.server.queue.common.MultipleTbQueueTbMsgCallbackWrapper;  
48 -import org.thingsboard.server.queue.common.TbQueueTbMsgCallbackWrapper;  
49 -import org.thingsboard.server.service.queue.TbClusterService;  
50 47
51 import java.util.ArrayList; 48 import java.util.ArrayList;
52 import java.util.Collections; 49 import java.util.Collections;
@@ -62,28 +59,27 @@ import java.util.stream.Collectors; @@ -62,28 +59,27 @@ import java.util.stream.Collectors;
62 @Slf4j 59 @Slf4j
63 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> { 60 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> {
64 61
  62 + private static final long DEFAULT_CLUSTER_PARTITION = 0L;
65 private final ActorRef parent; 63 private final ActorRef parent;
66 private final ActorRef self; 64 private final ActorRef self;
67 private final Map<RuleNodeId, RuleNodeCtx> nodeActors; 65 private final Map<RuleNodeId, RuleNodeCtx> nodeActors;
68 private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes; 66 private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes;
69 private final RuleChainService service; 67 private final RuleChainService service;
70 - private final TbClusterService clusterService;  
71 - private String ruleChainName;  
72 68
73 private RuleNodeId firstId; 69 private RuleNodeId firstId;
74 private RuleNodeCtx firstNode; 70 private RuleNodeCtx firstNode;
75 private boolean started; 71 private boolean started;
  72 + private String ruleChainName;
76 73
77 - RuleChainActorMessageProcessor(TenantId tenantId, RuleChain ruleChain, ActorSystemContext systemContext 74 + RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, ActorSystemContext systemContext
78 , ActorRef parent, ActorRef self) { 75 , ActorRef parent, ActorRef self) {
79 - super(systemContext, tenantId, ruleChain.getId());  
80 - this.ruleChainName = ruleChain.getName(); 76 + super(systemContext, tenantId, ruleChainId);
81 this.parent = parent; 77 this.parent = parent;
82 this.self = self; 78 this.self = self;
83 this.nodeActors = new HashMap<>(); 79 this.nodeActors = new HashMap<>();
84 this.nodeRoutes = new HashMap<>(); 80 this.nodeRoutes = new HashMap<>();
85 this.service = systemContext.getRuleChainService(); 81 this.service = systemContext.getRuleChainService();
86 - this.clusterService = systemContext.getClusterService(); 82 + this.ruleChainName = ruleChainId.toString();
87 } 83 }
88 84
89 @Override 85 @Override
@@ -96,6 +92,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -96,6 +92,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
96 if (!started) { 92 if (!started) {
97 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 93 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
98 if (ruleChain != null) { 94 if (ruleChain != null) {
  95 + ruleChainName = ruleChain.getName();
99 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); 96 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
100 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); 97 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
101 // Creating and starting the actors; 98 // Creating and starting the actors;
@@ -155,8 +152,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -155,8 +152,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
155 } 152 }
156 153
157 @Override 154 @Override
158 - public void onPartitionChangeMsg(PartitionChangeMsg msg) {  
159 - nodeActors.values().stream().map(RuleNodeCtx::getSelfActor).forEach(actorRef -> actorRef.tell(msg, self)); 155 + public void onClusterEventMsg(ClusterEventMsg msg) {
  156 +
160 } 157 }
161 158
162 private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) { 159 private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) {
@@ -195,123 +192,100 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -195,123 +192,100 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
195 state = ComponentLifecycleState.ACTIVE; 192 state = ComponentLifecycleState.ACTIVE;
196 } 193 }
197 194
198 - void onQueueToRuleEngineMsg(QueueToRuleEngineMsg envelope) {  
199 - TbMsg msg = envelope.getTbMsg();  
200 - log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, msg.getId(), msg);  
201 - if (envelope.getRelationTypes() == null || envelope.getRelationTypes().isEmpty()) {  
202 - try {  
203 - checkActive();  
204 - RuleNodeId targetId = msg.getRuleNodeId();  
205 - RuleNodeCtx targetCtx;  
206 - if (targetId == null) {  
207 - targetCtx = firstNode;  
208 - msg = msg.copyWithRuleChainId(entityId);  
209 - } else {  
210 - targetCtx = nodeActors.get(targetId);  
211 - }  
212 - if (targetCtx != null) {  
213 - log.trace("[{}][{}] Pushing message to target rule node", entityId, targetId);  
214 - pushMsgToNode(targetCtx, msg, "");  
215 - } else {  
216 - log.trace("[{}][{}] Rule node does not exist. Probably old message", entityId, targetId);  
217 - msg.getCallback().onSuccess();  
218 - }  
219 - } catch (Exception e) {  
220 - envelope.getTbMsg().getCallback().onFailure(new RuleEngineException(e.getMessage()));  
221 - }  
222 - } else {  
223 - onTellNext(envelope.getTbMsg(), envelope.getTbMsg().getRuleNodeId(), envelope.getRelationTypes(), envelope.getFailureMessage()); 195 + void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg envelope) {
  196 + log.trace("[{}][{}] Processing message [{}]: {}", entityId, firstId, envelope.getTbMsg().getId(), envelope.getTbMsg());
  197 + checkActive();
  198 + if (firstNode != null) {
  199 + log.trace("[{}][{}] Pushing message to first rule node", entityId, firstId);
  200 + pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
224 } 201 }
225 } 202 }
226 203
227 - void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) { 204 + void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg envelope) {
228 checkActive(); 205 checkActive();
229 if (firstNode != null) { 206 if (firstNode != null) {
230 - pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());  
231 - } else {  
232 - envelope.getMsg().getCallback().onSuccess(); 207 + pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getTbMsg()), "");
233 } 208 }
234 } 209 }
235 210
236 - void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {  
237 - onTellNext(envelope.getMsg(), envelope.getOriginator(), envelope.getRelationTypes(), envelope.getFailureMessage());  
238 - }  
239 -  
240 - private void onTellNext(TbMsg msg, RuleNodeId originatorNodeId, Set<String> relationTypes, String failureMessage) {  
241 - try {  
242 - checkActive();  
243 - EntityId entityId = msg.getOriginator();  
244 - TopicPartitionInfo tpi = systemContext.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId);  
245 - List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream()  
246 - .filter(r -> contains(relationTypes, r.getType()))  
247 - .collect(Collectors.toList());  
248 - int relationsCount = relations.size();  
249 - if (relationsCount == 0) {  
250 - log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId());  
251 - if (relationTypes.contains(TbRelationTypes.FAILURE)) {  
252 - RuleNodeCtx ruleNodeCtx = nodeActors.get(originatorNodeId);  
253 - if (ruleNodeCtx != null) {  
254 - msg.getCallback().onFailure(new RuleNodeException(failureMessage, ruleChainName, ruleNodeCtx.getSelf()));  
255 - } else {  
256 - log.debug("[{}] Failure during message processing by Rule Node [{}]. Enable and see debug events for more info", entityId, originatorNodeId.getId());  
257 - msg.getCallback().onFailure(new RuleEngineException("Failure during message processing by Rule Node [" + originatorNodeId.getId().toString() + "]"));  
258 - }  
259 - } else {  
260 - msg.getCallback().onSuccess();  
261 - }  
262 - } else if (relationsCount == 1) {  
263 - for (RuleNodeRelation relation : relations) {  
264 - log.trace("[{}][{}][{}] Pushing message to single target: [{}]", tenantId, entityId, msg.getId(), relation.getOut());  
265 - pushToTarget(tpi, msg, relation.getOut(), relation.getType());  
266 - } 211 + void onRuleChainToRuleChainMsg(RuleChainToRuleChainMsg envelope) {
  212 + checkActive();
  213 + if (envelope.isEnqueue()) {
  214 + if (firstNode != null) {
  215 + pushMsgToNode(firstNode, enrichWithRuleChainId(envelope.getMsg()), envelope.getFromRelationType());
  216 + }
  217 + } else {
  218 + if (firstNode != null) {
  219 + pushMsgToNode(firstNode, envelope.getMsg(), envelope.getFromRelationType());
267 } else { 220 } else {
268 - MultipleTbQueueTbMsgCallbackWrapper callbackWrapper = new MultipleTbQueueTbMsgCallbackWrapper(relationsCount, msg.getCallback());  
269 - log.trace("[{}][{}][{}] Pushing message to multiple targets: [{}]", tenantId, entityId, msg.getId(), relations);  
270 - for (RuleNodeRelation relation : relations) {  
271 - EntityId target = relation.getOut();  
272 - putToQueue(tpi, msg, callbackWrapper, target);  
273 - } 221 +// TODO: Ack this message in Kafka
  222 +// TbMsg msg = envelope.getMsg();
  223 +// EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
  224 +// queue.ack(tenantId, envelope.getMsg(), ackId.getId(), msg.getClusterPartition());
274 } 225 }
275 - } catch (Exception e) {  
276 - msg.getCallback().onFailure(new RuleEngineException("onTellNext - " + e.getMessage()));  
277 } 226 }
278 } 227 }
279 228
280 - private void putToQueue(TopicPartitionInfo tpi, TbMsg msg, TbQueueCallback callbackWrapper, EntityId target) {  
281 - switch (target.getEntityType()) {  
282 - case RULE_NODE:  
283 - putToQueue(tpi, msg.copyWithRuleNodeId(entityId, new RuleNodeId(target.getId())), callbackWrapper);  
284 - break;  
285 - case RULE_CHAIN:  
286 - putToQueue(tpi, msg.copyWithRuleChainId(new RuleChainId(target.getId())), callbackWrapper);  
287 - break; 229 + void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
  230 + checkActive();
  231 + TbMsg msg = envelope.getMsg();
  232 + EntityId originatorEntityId = msg.getOriginator();
  233 + Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(originatorEntityId);
  234 +
  235 + if (address.isPresent()) {
  236 + onRemoteTellNext(address.get(), envelope);
  237 + } else {
  238 + onLocalTellNext(envelope);
288 } 239 }
289 } 240 }
290 241
291 - private void pushToTarget(TopicPartitionInfo tpi, TbMsg msg, EntityId target, String fromRelationType) {  
292 - if (tpi.isMyPartition()) {  
293 - switch (target.getEntityType()) {  
294 - case RULE_NODE:  
295 - pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg, fromRelationType);  
296 - break;  
297 - case RULE_CHAIN:  
298 - parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType), self);  
299 - break; 242 + private void onRemoteTellNext(ServerAddress serverAddress, RuleNodeToRuleChainTellNextMsg envelope) {
  243 + TbMsg msg = envelope.getMsg();
  244 + log.debug("Forwarding [{}] msg to remote server [{}] due to changed originator id: [{}]", msg.getId(), serverAddress, msg.getOriginator());
  245 + envelope = new RemoteToRuleChainTellNextMsg(envelope, tenantId, entityId);
  246 + systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(serverAddress, envelope));
  247 + }
  248 +
  249 + private void onLocalTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
  250 + TbMsg msg = envelope.getMsg();
  251 + RuleNodeId originatorNodeId = envelope.getOriginator();
  252 + List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream()
  253 + .filter(r -> contains(envelope.getRelationTypes(), r.getType()))
  254 + .collect(Collectors.toList());
  255 + int relationsCount = relations.size();
  256 + EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
  257 + if (relationsCount == 0) {
  258 + log.trace("[{}][{}][{}] No outbound relations to process", tenantId, entityId, msg.getId());
  259 + if (ackId != null) {
  260 +// TODO: Ack this message in Kafka
  261 +// queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
  262 + }
  263 + } else if (relationsCount == 1) {
  264 + for (RuleNodeRelation relation : relations) {
  265 + log.trace("[{}][{}][{}] Pushing message to single target: [{}]", tenantId, entityId, msg.getId(), relation.getOut());
  266 + pushToTarget(msg, relation.getOut(), relation.getType());
300 } 267 }
301 } else { 268 } else {
302 - putToQueue(tpi, msg, new TbQueueTbMsgCallbackWrapper(msg.getCallback()), target); 269 + for (RuleNodeRelation relation : relations) {
  270 + EntityId target = relation.getOut();
  271 + log.trace("[{}][{}][{}] Pushing message to multiple targets: [{}]", tenantId, entityId, msg.getId(), relation.getOut());
  272 + switch (target.getEntityType()) {
  273 + case RULE_NODE:
  274 + enqueueAndForwardMsgCopyToNode(msg, target, relation.getType());
  275 + break;
  276 + case RULE_CHAIN:
  277 + enqueueAndForwardMsgCopyToChain(msg, target, relation.getType());
  278 + break;
  279 + }
  280 + }
  281 + //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues.
  282 + if (ackId != null) {
  283 +// TODO: Ack this message in Kafka
  284 +// queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
  285 + }
303 } 286 }
304 } 287 }
305 288
306 - private void putToQueue(TopicPartitionInfo tpi, TbMsg newMsg, TbQueueCallback callbackWrapper) {  
307 - ToRuleEngineMsg toQueueMsg = ToRuleEngineMsg.newBuilder()  
308 - .setTenantIdMSB(tenantId.getId().getMostSignificantBits())  
309 - .setTenantIdLSB(tenantId.getId().getLeastSignificantBits())  
310 - .setTbMsg(TbMsg.toByteString(newMsg))  
311 - .build();  
312 - clusterService.pushMsgToRuleEngine(tpi, newMsg.getId(), toQueueMsg, callbackWrapper);  
313 - }  
314 -  
315 private boolean contains(Set<String> relationTypes, String type) { 289 private boolean contains(Set<String> relationTypes, String type) {
316 if (relationTypes == null) { 290 if (relationTypes == null) {
317 return true; 291 return true;
@@ -324,13 +298,38 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -324,13 +298,38 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
324 return false; 298 return false;
325 } 299 }
326 300
  301 + private void enqueueAndForwardMsgCopyToChain(TbMsg msg, EntityId target, String fromRelationType) {
  302 + RuleChainId targetRCId = new RuleChainId(target.getId());
  303 + TbMsg copyMsg = msg.copy(UUIDs.timeBased(), targetRCId, null, DEFAULT_CLUSTER_PARTITION);
  304 + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, copyMsg, fromRelationType, true), self);
  305 + }
  306 +
  307 + private void enqueueAndForwardMsgCopyToNode(TbMsg msg, EntityId target, String fromRelationType) {
  308 + RuleNodeId targetId = new RuleNodeId(target.getId());
  309 + RuleNodeCtx targetNodeCtx = nodeActors.get(targetId);
  310 + TbMsg copy = msg.copy(UUIDs.timeBased(), entityId, targetId, DEFAULT_CLUSTER_PARTITION);
  311 + pushMsgToNode(targetNodeCtx, copy, fromRelationType);
  312 + }
  313 +
  314 + private void pushToTarget(TbMsg msg, EntityId target, String fromRelationType) {
  315 + switch (target.getEntityType()) {
  316 + case RULE_NODE:
  317 + pushMsgToNode(nodeActors.get(new RuleNodeId(target.getId())), msg, fromRelationType);
  318 + break;
  319 + case RULE_CHAIN:
  320 + parent.tell(new RuleChainToRuleChainMsg(new RuleChainId(target.getId()), entityId, msg, fromRelationType, false), self);
  321 + break;
  322 + }
  323 + }
  324 +
327 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) { 325 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
328 if (nodeCtx != null) { 326 if (nodeCtx != null) {
329 nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self); 327 nodeCtx.getSelfActor().tell(new RuleChainToRuleNodeMsg(new DefaultTbContext(systemContext, nodeCtx), msg, fromRelationType), self);
330 - } else {  
331 - log.error("[{}][{}] RuleNodeCtx is empty", entityId, ruleChainName);  
332 - msg.getCallback().onFailure(new RuleEngineException("Rule Node CTX is empty"));  
333 } 328 }
334 } 329 }
335 330
  331 + private TbMsg enrichWithRuleChainId(TbMsg tbMsg) {
  332 + // We don't put firstNodeId because it may change over time;
  333 + return new TbMsg(tbMsg.getId(), tbMsg.getType(), tbMsg.getOriginator(), tbMsg.getMetaData().copy(), tbMsg.getData(), entityId, null, systemContext.getQueuePartitionId());
  334 + }
336 } 335 }
@@ -15,89 +15,43 @@ @@ -15,89 +15,43 @@
15 */ 15 */
16 package org.thingsboard.server.actors.ruleChain; 16 package org.thingsboard.server.actors.ruleChain;
17 17
18 -import akka.actor.ActorContext;  
19 import akka.actor.ActorRef; 18 import akka.actor.ActorRef;
20 -import akka.actor.Props;  
21 -import com.google.common.collect.BiMap;  
22 -import com.google.common.collect.HashBiMap;  
23 -import lombok.Getter;  
24 import org.thingsboard.server.actors.ActorSystemContext; 19 import org.thingsboard.server.actors.ActorSystemContext;
25 import org.thingsboard.server.actors.service.ContextAwareActor; 20 import org.thingsboard.server.actors.service.ContextAwareActor;
26 -import org.thingsboard.server.actors.service.DefaultActorService;  
27 -import org.thingsboard.server.common.data.EntityType; 21 +import org.thingsboard.server.actors.shared.rulechain.RuleChainManager;
28 import org.thingsboard.server.common.data.id.EntityId; 22 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.RuleChainId; 23 import org.thingsboard.server.common.data.id.RuleChainId;
30 -import org.thingsboard.server.common.data.id.TenantId;  
31 -import org.thingsboard.server.common.data.page.PageDataIterable;  
32 -import org.thingsboard.server.common.data.rule.RuleChain;  
33 import org.thingsboard.server.dao.rule.RuleChainService; 24 import org.thingsboard.server.dao.rule.RuleChainService;
34 25
35 -import java.util.function.Function;  
36 -  
37 /** 26 /**
38 * Created by ashvayka on 15.03.18. 27 * Created by ashvayka on 15.03.18.
39 */ 28 */
40 public abstract class RuleChainManagerActor extends ContextAwareActor { 29 public abstract class RuleChainManagerActor extends ContextAwareActor {
41 30
42 - protected final TenantId tenantId;  
43 - private final RuleChainService ruleChainService;  
44 - private final BiMap<RuleChainId, ActorRef> actors;  
45 - @Getter  
46 - protected RuleChain rootChain;  
47 - @Getter  
48 - protected ActorRef rootChainActor; 31 + protected final RuleChainManager ruleChainManager;
  32 + protected final RuleChainService ruleChainService;
49 33
50 - public RuleChainManagerActor(ActorSystemContext systemContext, TenantId tenantId) { 34 + public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager) {
51 super(systemContext); 35 super(systemContext);
52 - this.tenantId = tenantId;  
53 - this.actors = HashBiMap.create(); 36 + this.ruleChainManager = ruleChainManager;
54 this.ruleChainService = systemContext.getRuleChainService(); 37 this.ruleChainService = systemContext.getRuleChainService();
55 } 38 }
56 39
57 protected void initRuleChains() { 40 protected void initRuleChains() {
58 - for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {  
59 - RuleChainId ruleChainId = ruleChain.getId();  
60 - log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());  
61 - //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa.  
62 - ActorRef actorRef = getOrCreateActor(this.context(), ruleChainId, id -> ruleChain);  
63 - visit(ruleChain, actorRef);  
64 - log.debug("[{}|{}] Rule Chain actor created.", ruleChainId.getEntityType(), ruleChainId.getId());  
65 - }  
66 - }  
67 -  
68 - protected void visit(RuleChain entity, ActorRef actorRef) {  
69 - if (entity != null && entity.isRoot()) {  
70 - rootChain = entity;  
71 - rootChainActor = actorRef;  
72 - }  
73 - }  
74 -  
75 - public ActorRef getOrCreateActor(akka.actor.ActorContext context, RuleChainId ruleChainId) {  
76 - return getOrCreateActor(context, ruleChainId, eId -> ruleChainService.findRuleChainById(TenantId.SYS_TENANT_ID, eId));  
77 - }  
78 -  
79 - public ActorRef getOrCreateActor(akka.actor.ActorContext context, RuleChainId ruleChainId, Function<RuleChainId, RuleChain> provider) {  
80 - return actors.computeIfAbsent(ruleChainId, eId -> {  
81 - RuleChain ruleChain = provider.apply(eId);  
82 - return context.actorOf(Props.create(new RuleChainActor.ActorCreator(systemContext, tenantId, ruleChain))  
83 - .withDispatcher(DefaultActorService.TENANT_RULE_DISPATCHER_NAME), eId.toString());  
84 - }); 41 + ruleChainManager.init(this.context());
85 } 42 }
86 43
87 protected ActorRef getEntityActorRef(EntityId entityId) { 44 protected ActorRef getEntityActorRef(EntityId entityId) {
88 ActorRef target = null; 45 ActorRef target = null;
89 - if (entityId.getEntityType() == EntityType.RULE_CHAIN) {  
90 - target = getOrCreateActor(this.context(), (RuleChainId) entityId); 46 + switch (entityId.getEntityType()) {
  47 + case RULE_CHAIN:
  48 + target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId);
  49 + break;
91 } 50 }
92 return target; 51 return target;
93 } 52 }
94 53
95 protected void broadcast(Object msg) { 54 protected void broadcast(Object msg) {
96 - actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender())); 55 + ruleChainManager.broadcast(msg);
97 } 56 }
98 -  
99 - public ActorRef get(RuleChainId id) {  
100 - return actors.get(id);  
101 - }  
102 -  
103 } 57 }
@@ -32,6 +32,7 @@ public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAware @@ -32,6 +32,7 @@ public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAware
32 private final RuleChainId source; 32 private final RuleChainId source;
33 private final TbMsg msg; 33 private final TbMsg msg;
34 private final String fromRelationType; 34 private final String fromRelationType;
  35 + private final boolean enqueue;
35 36
36 @Override 37 @Override
37 public RuleChainId getRuleChainId() { 38 public RuleChainId getRuleChainId() {
@@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.id.RuleNodeId; @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
23 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.msg.TbActorMsg; 24 import org.thingsboard.server.common.msg.TbActorMsg;
25 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 25 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
26 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;  
27 26
28 public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessageProcessor> { 27 public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessageProcessor> {
29 28
@@ -54,9 +53,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa @@ -54,9 +53,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
54 case STATS_PERSIST_TICK_MSG: 53 case STATS_PERSIST_TICK_MSG:
55 onStatsPersistTick(id); 54 onStatsPersistTick(id);
56 break; 55 break;
57 - case PARTITION_CHANGE_MSG:  
58 - onClusterEventMsg((PartitionChangeMsg) msg);  
59 - break;  
60 default: 56 default:
61 return false; 57 return false;
62 } 58 }
@@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId; @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
27 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
29 import org.thingsboard.server.common.data.rule.RuleNode; 29 import org.thingsboard.server.common.data.rule.RuleNode;
30 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 30 +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
31 import org.thingsboard.server.dao.rule.RuleChainService; 31 import org.thingsboard.server.dao.rule.RuleChainService;
32 32
33 /** 33 /**
@@ -40,7 +40,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -40,7 +40,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
40 private final RuleChainService service; 40 private final RuleChainService service;
41 private RuleNode ruleNode; 41 private RuleNode ruleNode;
42 private TbNode tbNode; 42 private TbNode tbNode;
43 - private DefaultTbContext defaultCtx; 43 + private TbContext defaultCtx;
44 44
45 RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext 45 RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext
46 , ActorRef parent, ActorRef self) { 46 , ActorRef parent, ActorRef self) {
@@ -84,9 +84,9 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -84,9 +84,9 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
84 } 84 }
85 85
86 @Override 86 @Override
87 - public void onPartitionChangeMsg(PartitionChangeMsg msg) { 87 + public void onClusterEventMsg(ClusterEventMsg msg) {
88 if (tbNode != null) { 88 if (tbNode != null) {
89 - tbNode.onPartitionChangeMsg(defaultCtx, msg); 89 + tbNode.onClusterEventMsg(defaultCtx, msg);
90 } 90 }
91 } 91 }
92 92
@@ -34,7 +34,6 @@ class RuleNodeToRuleChainTellNextMsg implements TbActorMsg, Serializable { @@ -34,7 +34,6 @@ class RuleNodeToRuleChainTellNextMsg implements TbActorMsg, Serializable {
34 private final RuleNodeId originator; 34 private final RuleNodeId originator;
35 private final Set<String> relationTypes; 35 private final Set<String> relationTypes;
36 private final TbMsg msg; 36 private final TbMsg msg;
37 - private final String failureMessage;  
38 37
39 @Override 38 @Override
40 public MsgType getMsgType() { 39 public MsgType getMsgType() {
@@ -15,7 +15,24 @@ @@ -15,7 +15,24 @@
15 */ 15 */
16 package org.thingsboard.server.actors.service; 16 package org.thingsboard.server.actors.service;
17 17
18 -public interface ActorService { 18 +import org.thingsboard.server.common.data.id.DeviceId;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  22 +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
  23 +import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
  24 +import org.thingsboard.server.common.transport.SessionMsgProcessor;
  25 +import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener;
  26 +import org.thingsboard.server.service.cluster.rpc.RpcMsgListener;
19 27
  28 +public interface ActorService extends SessionMsgProcessor, RpcMsgListener, DiscoveryServiceListener {
  29 +
  30 + void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
  31 +
  32 + void onMsg(SendToClusterMsg msg);
  33 +
  34 + void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId);
  35 +
  36 + void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType);
20 37
21 } 38 }
@@ -22,7 +22,7 @@ import org.thingsboard.server.actors.stats.StatsPersistMsg; @@ -22,7 +22,7 @@ import org.thingsboard.server.actors.stats.StatsPersistMsg;
22 import org.thingsboard.server.common.data.id.EntityId; 22 import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 24 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
25 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 25 +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
26 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 26 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
27 27
28 /** 28 /**
@@ -115,9 +115,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP @@ -115,9 +115,9 @@ public abstract class ComponentActor<T extends EntityId, P extends ComponentMsgP
115 } 115 }
116 } 116 }
117 117
118 - protected void onClusterEventMsg(PartitionChangeMsg msg) { 118 + protected void onClusterEventMsg(ClusterEventMsg msg) {
119 try { 119 try {
120 - processor.onPartitionChangeMsg(msg); 120 + processor.onClusterEventMsg(msg);
121 } catch (Exception e) { 121 } catch (Exception e) {
122 logAndPersist("onClusterEventMsg", e); 122 logAndPersist("onClusterEventMsg", e);
123 } 123 }
@@ -16,14 +16,14 @@ @@ -16,14 +16,14 @@
16 package org.thingsboard.server.actors.service; 16 package org.thingsboard.server.actors.service;
17 17
18 import akka.actor.Terminated; 18 import akka.actor.Terminated;
19 -import akka.actor.UntypedAbstractActor; 19 +import akka.actor.UntypedActor;
20 import org.slf4j.Logger; 20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory; 21 import org.slf4j.LoggerFactory;
22 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
23 import org.thingsboard.server.common.msg.TbActorMsg; 23 import org.thingsboard.server.common.msg.TbActorMsg;
24 24
25 25
26 -public abstract class ContextAwareActor extends UntypedAbstractActor { 26 +public abstract class ContextAwareActor extends UntypedActor {
27 27
28 protected final Logger log = LoggerFactory.getLogger(getClass()); 28 protected final Logger log = LoggerFactory.getLogger(getClass());
29 29
@@ -19,6 +19,7 @@ import akka.actor.ActorRef; @@ -19,6 +19,7 @@ import akka.actor.ActorRef;
19 import akka.actor.ActorSystem; 19 import akka.actor.ActorSystem;
20 import akka.actor.Props; 20 import akka.actor.Props;
21 import akka.actor.Terminated; 21 import akka.actor.Terminated;
  22 +import com.google.protobuf.ByteString;
22 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.beans.factory.annotation.Value; 25 import org.springframework.beans.factory.annotation.Value;
@@ -26,20 +27,44 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -26,20 +27,44 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
26 import org.springframework.context.event.EventListener; 27 import org.springframework.context.event.EventListener;
27 import org.springframework.scheduling.annotation.Scheduled; 28 import org.springframework.scheduling.annotation.Scheduled;
28 import org.springframework.stereotype.Service; 29 import org.springframework.stereotype.Service;
  30 +import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
  31 +import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
29 import org.thingsboard.server.actors.ActorSystemContext; 32 import org.thingsboard.server.actors.ActorSystemContext;
30 import org.thingsboard.server.actors.app.AppActor; 33 import org.thingsboard.server.actors.app.AppActor;
31 import org.thingsboard.server.actors.app.AppInitMsg; 34 import org.thingsboard.server.actors.app.AppInitMsg;
  35 +import org.thingsboard.server.actors.rpc.RpcBroadcastMsg;
  36 +import org.thingsboard.server.actors.rpc.RpcManagerActor;
  37 +import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
32 import org.thingsboard.server.actors.stats.StatsActor; 38 import org.thingsboard.server.actors.stats.StatsActor;
33 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;  
34 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 39 +import org.thingsboard.server.common.data.Device;
  40 +import org.thingsboard.server.common.data.id.DeviceId;
  41 +import org.thingsboard.server.common.data.id.EntityId;
  42 +import org.thingsboard.server.common.data.id.TenantId;
  43 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  44 +import org.thingsboard.server.common.msg.TbActorMsg;
  45 +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  46 +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg;
  47 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
  48 +import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
  49 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
  50 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
  51 +import org.thingsboard.server.service.cluster.discovery.DiscoveryService;
  52 +import org.thingsboard.server.service.cluster.discovery.ServerInstance;
  53 +import org.thingsboard.server.service.cluster.rpc.ClusterRpcService;
  54 +import org.thingsboard.server.service.state.DeviceStateService;
  55 +import org.thingsboard.server.service.transport.RuleEngineStats;
35 import scala.concurrent.Await; 56 import scala.concurrent.Await;
36 import scala.concurrent.Future; 57 import scala.concurrent.Future;
37 import scala.concurrent.duration.Duration; 58 import scala.concurrent.duration.Duration;
38 59
39 import javax.annotation.PostConstruct; 60 import javax.annotation.PostConstruct;
40 import javax.annotation.PreDestroy; 61 import javax.annotation.PreDestroy;
  62 +import java.util.concurrent.Executors;
  63 +import java.util.concurrent.ScheduledExecutorService;
41 import java.util.concurrent.atomic.AtomicInteger; 64 import java.util.concurrent.atomic.AtomicInteger;
42 65
  66 +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE;
  67 +
43 @Service 68 @Service
44 @Slf4j 69 @Slf4j
45 public class DefaultActorService implements ActorService { 70 public class DefaultActorService implements ActorService {
@@ -50,14 +75,26 @@ public class DefaultActorService implements ActorService { @@ -50,14 +75,26 @@ public class DefaultActorService implements ActorService {
50 public static final String CORE_DISPATCHER_NAME = "core-dispatcher"; 75 public static final String CORE_DISPATCHER_NAME = "core-dispatcher";
51 public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher"; 76 public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher";
52 public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher"; 77 public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher";
  78 + public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher";
53 79
54 @Autowired 80 @Autowired
55 private ActorSystemContext actorContext; 81 private ActorSystemContext actorContext;
56 82
  83 + @Autowired
  84 + private ClusterRpcService rpcService;
  85 +
  86 + @Autowired
  87 + private DiscoveryService discoveryService;
  88 +
  89 + @Autowired
  90 + private DeviceStateService deviceStateService;
  91 +
57 private ActorSystem system; 92 private ActorSystem system;
58 93
59 private ActorRef appActor; 94 private ActorRef appActor;
60 95
  96 + private ActorRef rpcManagerActor;
  97 +
61 @PostConstruct 98 @PostConstruct
62 public void initActorSystem() { 99 public void initActorSystem() {
63 log.info("Initializing Actor system."); 100 log.info("Initializing Actor system.");
@@ -68,9 +105,13 @@ public class DefaultActorService implements ActorService { @@ -68,9 +105,13 @@ public class DefaultActorService implements ActorService {
68 appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor"); 105 appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor");
69 actorContext.setAppActor(appActor); 106 actorContext.setAppActor(appActor);
70 107
  108 + rpcManagerActor = system.actorOf(Props.create(new RpcManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
  109 + "rpcManagerActor");
  110 +
71 ActorRef statsActor = system.actorOf(Props.create(new StatsActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME), "statsActor"); 111 ActorRef statsActor = system.actorOf(Props.create(new StatsActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME), "statsActor");
72 actorContext.setStatsActor(statsActor); 112 actorContext.setStatsActor(statsActor);
73 113
  114 + rpcService.init(this);
74 log.info("Actor system initialized."); 115 log.info("Actor system initialized.");
75 } 116 }
76 117
@@ -80,12 +121,6 @@ public class DefaultActorService implements ActorService { @@ -80,12 +121,6 @@ public class DefaultActorService implements ActorService {
80 appActor.tell(new AppInitMsg(), ActorRef.noSender()); 121 appActor.tell(new AppInitMsg(), ActorRef.noSender());
81 } 122 }
82 123
83 - @EventListener(PartitionChangeEvent.class)  
84 - public void onApplicationEvent(PartitionChangeEvent partitionChangeEvent) {  
85 - log.info("Received partition change event.");  
86 - this.appActor.tell(new PartitionChangeMsg(partitionChangeEvent.getServiceQueueKey(), partitionChangeEvent.getPartitions()), ActorRef.noSender());  
87 - }  
88 -  
89 @PreDestroy 124 @PreDestroy
90 public void stopActorSystem() { 125 public void stopActorSystem() {
91 Future<Terminated> status = system.terminate(); 126 Future<Terminated> status = system.terminate();
@@ -97,4 +132,157 @@ public class DefaultActorService implements ActorService { @@ -97,4 +132,157 @@ public class DefaultActorService implements ActorService {
97 } 132 }
98 } 133 }
99 134
  135 + @Override
  136 + public void onMsg(SendToClusterMsg msg) {
  137 + appActor.tell(msg, ActorRef.noSender());
  138 + }
  139 +
  140 + @Override
  141 + public void onServerAdded(ServerInstance server) {
  142 + log.trace("Processing onServerAdded msg: {}", server);
  143 + broadcast(new ClusterEventMsg(server.getServerAddress(), true));
  144 + }
  145 +
  146 + @Override
  147 + public void onServerUpdated(ServerInstance server) {
  148 + //Do nothing
  149 + }
  150 +
  151 + @Override
  152 + public void onServerRemoved(ServerInstance server) {
  153 + log.trace("Processing onServerRemoved msg: {}", server);
  154 + broadcast(new ClusterEventMsg(server.getServerAddress(), false));
  155 + }
  156 +
  157 + @Override
  158 + public void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
  159 + log.trace("[{}] Processing {} state change event: {}", tenantId, entityId.getEntityType(), state);
  160 + broadcast(new ComponentLifecycleMsg(tenantId, entityId, state));
  161 + }
  162 +
  163 + @Override
  164 + public void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId) {
  165 + DeviceCredentialsUpdateNotificationMsg msg = new DeviceCredentialsUpdateNotificationMsg(tenantId, deviceId);
  166 + appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
  167 + }
  168 +
  169 + @Override
  170 + public void onDeviceNameOrTypeUpdate(TenantId tenantId, DeviceId deviceId, String deviceName, String deviceType) {
  171 + log.trace("[{}] Processing onDeviceNameOrTypeUpdate event, deviceName: {}, deviceType: {}", deviceId, deviceName, deviceType);
  172 + DeviceNameOrTypeUpdateMsg msg = new DeviceNameOrTypeUpdateMsg(tenantId, deviceId, deviceName, deviceType);
  173 + appActor.tell(new SendToClusterMsg(deviceId, msg), ActorRef.noSender());
  174 + }
  175 +
  176 + public void broadcast(ToAllNodesMsg msg) {
  177 + actorContext.getEncodingService().encode(msg);
  178 + rpcService.broadcast(new RpcBroadcastMsg(ClusterAPIProtos.ClusterMessage
  179 + .newBuilder()
  180 + .setPayload(ByteString
  181 + .copyFrom(actorContext.getEncodingService().encode(msg)))
  182 + .setMessageType(CLUSTER_ACTOR_MESSAGE)
  183 + .build()));
  184 + appActor.tell(msg, ActorRef.noSender());
  185 + }
  186 +
  187 + private void broadcast(ClusterEventMsg msg) {
  188 + this.appActor.tell(msg, ActorRef.noSender());
  189 + this.rpcManagerActor.tell(msg, ActorRef.noSender());
  190 + }
  191 +
  192 + @Value("${cluster.stats.enabled:false}")
  193 + private boolean statsEnabled;
  194 +
  195 + private final AtomicInteger sentClusterMsgs = new AtomicInteger(0);
  196 + private final AtomicInteger receivedClusterMsgs = new AtomicInteger(0);
  197 +
  198 +
  199 + @Scheduled(fixedDelayString = "${cluster.stats.print_interval_ms}")
  200 + public void printStats() {
  201 + if (statsEnabled) {
  202 + int sent = sentClusterMsgs.getAndSet(0);
  203 + int received = receivedClusterMsgs.getAndSet(0);
  204 + if (sent > 0 || received > 0) {
  205 + log.info("Cluster msgs sent [{}] received [{}]", sent, received);
  206 + }
  207 + }
  208 + }
  209 +
  210 + @Override
  211 + public void onReceivedMsg(ServerAddress source, ClusterAPIProtos.ClusterMessage msg) {
  212 + if (statsEnabled) {
  213 + receivedClusterMsgs.incrementAndGet();
  214 + }
  215 + ServerAddress serverAddress = new ServerAddress(source.getHost(), source.getPort(), source.getServerType());
  216 + if (log.isDebugEnabled()) {
  217 + log.info("Received msg [{}] from [{}]", msg.getMessageType().name(), serverAddress);
  218 + log.info("MSG: {}", msg);
  219 + }
  220 + switch (msg.getMessageType()) {
  221 + case CLUSTER_ACTOR_MESSAGE:
  222 + java.util.Optional<TbActorMsg> decodedMsg = actorContext.getEncodingService()
  223 + .decode(msg.getPayload().toByteArray());
  224 + if (decodedMsg.isPresent()) {
  225 + appActor.tell(decodedMsg.get(), ActorRef.noSender());
  226 + } else {
  227 + log.error("Error during decoding cluster proto message");
  228 + }
  229 + break;
  230 + case TO_ALL_NODES_MSG:
  231 + //TODO
  232 + break;
  233 + case CLUSTER_TELEMETRY_SUBSCRIPTION_CREATE_MESSAGE:
  234 + actorContext.getTsSubService().onNewRemoteSubscription(serverAddress, msg.getPayload().toByteArray());
  235 + break;
  236 + case CLUSTER_TELEMETRY_SUBSCRIPTION_UPDATE_MESSAGE:
  237 + actorContext.getTsSubService().onRemoteSubscriptionUpdate(serverAddress, msg.getPayload().toByteArray());
  238 + break;
  239 + case CLUSTER_TELEMETRY_SUBSCRIPTION_CLOSE_MESSAGE:
  240 + actorContext.getTsSubService().onRemoteSubscriptionClose(serverAddress, msg.getPayload().toByteArray());
  241 + break;
  242 + case CLUSTER_TELEMETRY_SESSION_CLOSE_MESSAGE:
  243 + actorContext.getTsSubService().onRemoteSessionClose(serverAddress, msg.getPayload().toByteArray());
  244 + break;
  245 + case CLUSTER_TELEMETRY_ATTR_UPDATE_MESSAGE:
  246 + actorContext.getTsSubService().onRemoteAttributesUpdate(serverAddress, msg.getPayload().toByteArray());
  247 + break;
  248 + case CLUSTER_TELEMETRY_TS_UPDATE_MESSAGE:
  249 + actorContext.getTsSubService().onRemoteTsUpdate(serverAddress, msg.getPayload().toByteArray());
  250 + break;
  251 + case CLUSTER_RPC_FROM_DEVICE_RESPONSE_MESSAGE:
  252 + actorContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromRemoteServer(serverAddress, msg.getPayload().toByteArray());
  253 + break;
  254 + case CLUSTER_DEVICE_STATE_SERVICE_MESSAGE:
  255 + actorContext.getDeviceStateService().onRemoteMsg(serverAddress, msg.getPayload().toByteArray());
  256 + break;
  257 + case CLUSTER_TRANSACTION_SERVICE_MESSAGE:
  258 + actorContext.getRuleChainTransactionService().onRemoteTransactionMsg(serverAddress, msg.getPayload().toByteArray());
  259 + break;
  260 + }
  261 + }
  262 +
  263 + @Override
  264 + public void onSendMsg(ClusterAPIProtos.ClusterMessage msg) {
  265 + if (statsEnabled) {
  266 + sentClusterMsgs.incrementAndGet();
  267 + }
  268 + rpcManagerActor.tell(msg, ActorRef.noSender());
  269 + }
  270 +
  271 + @Override
  272 + public void onRpcSessionCreateRequestMsg(RpcSessionCreateRequestMsg msg) {
  273 + if (statsEnabled) {
  274 + sentClusterMsgs.incrementAndGet();
  275 + }
  276 + rpcManagerActor.tell(msg, ActorRef.noSender());
  277 + }
  278 +
  279 + @Override
  280 + public void onBroadcastMsg(RpcBroadcastMsg msg) {
  281 + rpcManagerActor.tell(msg, ActorRef.noSender());
  282 + }
  283 +
  284 + @Override
  285 + public void onDeviceAdded(Device device) {
  286 + deviceStateService.onDeviceAdded(device);
  287 + }
100 } 288 }
@@ -16,13 +16,20 @@ @@ -16,13 +16,20 @@
16 package org.thingsboard.server.actors.shared; 16 package org.thingsboard.server.actors.shared;
17 17
18 import akka.actor.ActorContext; 18 import akka.actor.ActorContext;
  19 +import akka.event.LoggingAdapter;
  20 +import com.google.common.util.concurrent.FutureCallback;
  21 +import com.google.common.util.concurrent.Futures;
19 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
20 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
21 import org.thingsboard.server.actors.stats.StatsPersistTick; 24 import org.thingsboard.server.actors.stats.StatsPersistTick;
22 import org.thingsboard.server.common.data.id.EntityId; 25 import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.TenantId; 26 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 27 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
25 -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 28 +import org.thingsboard.server.common.msg.TbMsg;
  29 +import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  30 +
  31 +import javax.annotation.Nullable;
  32 +import java.util.function.Consumer;
26 33
27 @Slf4j 34 @Slf4j
28 public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor { 35 public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor {
@@ -43,7 +50,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract @@ -43,7 +50,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
43 50
44 public abstract void stop(ActorContext context) throws Exception; 51 public abstract void stop(ActorContext context) throws Exception;
45 52
46 - public abstract void onPartitionChangeMsg(PartitionChangeMsg msg) throws Exception; 53 + public abstract void onClusterEventMsg(ClusterEventMsg msg) throws Exception;
47 54
48 public void onCreated(ActorContext context) throws Exception { 55 public void onCreated(ActorContext context) throws Exception {
49 start(context); 56 start(context);
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.shared;
  17 +
  18 +import akka.actor.ActorContext;
  19 +import akka.actor.ActorRef;
  20 +import akka.actor.Props;
  21 +import akka.actor.UntypedActor;
  22 +import akka.japi.Creator;
  23 +import com.google.common.collect.BiMap;
  24 +import com.google.common.collect.HashBiMap;
  25 +import lombok.extern.slf4j.Slf4j;
  26 +import org.thingsboard.server.actors.ActorSystemContext;
  27 +import org.thingsboard.server.actors.service.ContextAwareActor;
  28 +import org.thingsboard.server.common.data.SearchTextBased;
  29 +import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
  31 +import org.thingsboard.server.common.data.id.UUIDBased;
  32 +import org.thingsboard.server.common.data.page.PageDataIterable;
  33 +
  34 +import java.util.HashMap;
  35 +import java.util.Map;
  36 +
  37 +/**
  38 + * Created by ashvayka on 15.03.18.
  39 + */
  40 +@Slf4j
  41 +public abstract class EntityActorsManager<T extends EntityId, A extends UntypedActor, M extends SearchTextBased<? extends UUIDBased>> {
  42 +
  43 + protected final ActorSystemContext systemContext;
  44 + protected final BiMap<T, ActorRef> actors;
  45 +
  46 + public EntityActorsManager(ActorSystemContext systemContext) {
  47 + this.systemContext = systemContext;
  48 + this.actors = HashBiMap.create();
  49 + }
  50 +
  51 + protected abstract TenantId getTenantId();
  52 +
  53 + protected abstract String getDispatcherName();
  54 +
  55 + protected abstract Creator<A> creator(T entityId);
  56 +
  57 + protected abstract PageDataIterable.FetchFunction<M> getFetchEntitiesFunction();
  58 +
  59 + public void init(ActorContext context) {
  60 + for (M entity : new PageDataIterable<>(getFetchEntitiesFunction(), ContextAwareActor.ENTITY_PACK_LIMIT)) {
  61 + T entityId = (T) entity.getId();
  62 + log.debug("[{}|{}] Creating entity actor", entityId.getEntityType(), entityId.getId());
  63 + //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa.
  64 + ActorRef actorRef = getOrCreateActor(context, entityId);
  65 + visit(entity, actorRef);
  66 + log.debug("[{}|{}] Entity actor created.", entityId.getEntityType(), entityId.getId());
  67 + }
  68 + }
  69 +
  70 + public void visit(M entity, ActorRef actorRef) {
  71 + }
  72 +
  73 + public ActorRef getOrCreateActor(ActorContext context, T entityId) {
  74 + return actors.computeIfAbsent(entityId, eId ->
  75 + context.actorOf(Props.create(creator(eId))
  76 + .withDispatcher(getDispatcherName()), eId.toString()));
  77 + }
  78 +
  79 + public void broadcast(Object msg) {
  80 + actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
  81 + }
  82 +
  83 + public void remove(T id) {
  84 + actors.remove(id);
  85 + }
  86 +
  87 +}