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   -}
\ No newline at end of file
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   -}
\ No newline at end of file
... ... @@ -123,28 +123,3 @@ BEGIN
123 123 END LOOP;
124 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 32 import lombok.extern.slf4j.Slf4j;
33 33 import org.springframework.beans.factory.annotation.Autowired;
34 34 import org.springframework.beans.factory.annotation.Value;
  35 +import org.springframework.context.annotation.Lazy;
35 36 import org.springframework.data.redis.core.RedisTemplate;
36 37 import org.springframework.scheduling.annotation.Scheduled;
37 38 import org.springframework.stereotype.Component;
38 39 import org.thingsboard.rule.engine.api.MailService;
  40 +import org.thingsboard.rule.engine.api.RuleChainTransactionService;
39 41 import org.thingsboard.server.actors.service.ActorService;
40 42 import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
41 43 import org.thingsboard.server.common.data.DataConstants;
... ... @@ -43,11 +45,10 @@ import org.thingsboard.server.common.data.Event;
43 45 import org.thingsboard.server.common.data.id.EntityId;
44 46 import org.thingsboard.server.common.data.id.TenantId;
45 47 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
46   -import org.thingsboard.server.common.msg.TbActorMsg;
47 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 50 import org.thingsboard.server.common.msg.tools.TbRateLimits;
  51 +import org.thingsboard.server.common.transport.auth.DeviceAuthService;
51 52 import org.thingsboard.server.dao.alarm.AlarmService;
52 53 import org.thingsboard.server.dao.asset.AssetService;
53 54 import org.thingsboard.server.dao.attributes.AttributesService;
... ... @@ -64,23 +65,24 @@ import org.thingsboard.server.dao.rule.RuleChainService;
64 65 import org.thingsboard.server.dao.tenant.TenantService;
65 66 import org.thingsboard.server.dao.timeseries.TimeseriesService;
66 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 72 import org.thingsboard.server.service.component.ComponentDiscoveryService;
70 73 import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
  74 +import org.thingsboard.server.service.executors.ClusterRpcCallbackExecutorService;
71 75 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
72 76 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
73 77 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
74 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 80 import org.thingsboard.server.service.script.JsExecutorService;
79 81 import org.thingsboard.server.service.script.JsInvokeService;
80 82 import org.thingsboard.server.service.session.DeviceSessionCacheService;
81 83 import org.thingsboard.server.service.state.DeviceStateService;
82 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 87 import javax.annotation.Nullable;
86 88 import java.io.IOException;
... ... @@ -104,14 +106,13 @@ public class ActorSystemContext {
104 106 return debugPerTenantLimits;
105 107 }
106 108
107   - @Autowired
108 109 @Getter
109 110 @Setter
110   - private TbServiceInfoProvider serviceInfoProvider;
  111 + private ActorService actorService;
111 112
  113 + @Autowired
112 114 @Getter
113   - @Setter
114   - private ActorService actorService;
  115 + private DiscoveryService discoveryService;
115 116
116 117 @Autowired
117 118 @Getter
... ... @@ -120,10 +121,22 @@ public class ActorSystemContext {
120 121
121 122 @Autowired
122 123 @Getter
  124 + private ClusterRoutingService routingService;
  125 +
  126 + @Autowired
  127 + @Getter
  128 + private ClusterRpcService rpcService;
  129 +
  130 + @Autowired
  131 + @Getter
123 132 private DataDecodingEncodingService encodingService;
124 133
125 134 @Autowired
126 135 @Getter
  136 + private DeviceAuthService deviceAuthService;
  137 +
  138 + @Autowired
  139 + @Getter
127 140 private DeviceService deviceService;
128 141
129 142 @Autowired
... ... @@ -151,13 +164,6 @@ public class ActorSystemContext {
151 164 private RuleChainService ruleChainService;
152 165
153 166 @Autowired
154   - private PartitionService partitionService;
155   -
156   - @Autowired
157   - @Getter
158   - private TbClusterService clusterService;
159   -
160   - @Autowired
161 167 @Getter
162 168 private TimeseriesService tsService;
163 169
... ... @@ -191,6 +197,10 @@ public class ActorSystemContext {
191 197
192 198 @Autowired
193 199 @Getter
  200 + private DeviceRpcService deviceRpcService;
  201 +
  202 + @Autowired
  203 + @Getter
194 204 private JsInvokeService jsSandbox;
195 205
196 206 @Autowired
... ... @@ -203,6 +213,10 @@ public class ActorSystemContext {
203 213
204 214 @Autowired
205 215 @Getter
  216 + private ClusterRpcCallbackExecutorService clusterRpcCallbackExecutor;
  217 +
  218 + @Autowired
  219 + @Getter
206 220 private DbCallbackExecutorService dbCallbackExecutor;
207 221
208 222 @Autowired
... ... @@ -217,32 +231,27 @@ public class ActorSystemContext {
217 231 @Getter
218 232 private MailService mailService;
219 233
220   - //TODO: separate context for TbCore and TbRuleEngine
221   - @Autowired(required = false)
  234 + @Autowired
222 235 @Getter
223 236 private DeviceStateService deviceStateService;
224 237
225   - @Autowired(required = false)
  238 + @Autowired
226 239 @Getter
227 240 private DeviceSessionCacheService deviceSessionCacheService;
228 241
229   - @Autowired(required = false)
  242 + @Lazy
  243 + @Autowired
230 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 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 253 @Getter
245   - private TbCoreDeviceRpcService tbCoreDeviceRpcService;
  254 + private long queuePartitionId;
246 255
247 256 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
248 257 @Getter
... ... @@ -260,6 +269,10 @@ public class ActorSystemContext {
260 269 @Getter
261 270 private long queuePersistenceTimeout;
262 271
  272 + @Value("${actors.client_side_rpc.timeout}")
  273 + @Getter
  274 + private long clientSideRpcTimeout;
  275 +
263 276 @Value("${actors.rule.chain.error_persist_frequency}")
264 277 @Getter
265 278 private long ruleChainErrorPersistFrequency;
... ... @@ -321,6 +334,11 @@ public class ActorSystemContext {
321 334 @Setter
322 335 private ActorSystem actorSystem;
323 336
  337 + @Autowired
  338 + @Getter
  339 + private TbNodeIdProvider nodeIdProvider;
  340 +
  341 + @Getter
324 342 @Setter
325 343 private ActorRef appActor;
326 344
... ... @@ -347,8 +365,6 @@ public class ActorSystemContext {
347 365 config = ConfigFactory.parseResources(AKKA_CONF_FILE_NAME).withFallback(ConfigFactory.load());
348 366 }
349 367
350   -
351   -
352 368 public Scheduler getScheduler() {
353 369 return actorSystem.scheduler();
354 370 }
... ... @@ -358,7 +374,7 @@ public class ActorSystemContext {
358 374 event.setTenantId(tenantId);
359 375 event.setEntityId(entityId);
360 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 378 persistEvent(event);
363 379 }
364 380
... ... @@ -367,7 +383,7 @@ public class ActorSystemContext {
367 383 event.setTenantId(tenantId);
368 384 event.setEntityId(entityId);
369 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 387 persistEvent(event);
372 388 }
373 389
... ... @@ -381,8 +397,8 @@ public class ActorSystemContext {
381 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 402 if (e.isPresent()) {
387 403 node = node.put("success", false);
388 404 node = node.put("error", toString(e.get()));
... ... @@ -392,21 +408,12 @@ public class ActorSystemContext {
392 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 419 public void persistDebugInput(TenantId tenantId, EntityId entityId, TbMsg tbMsg, String relationType) {
... ... @@ -437,7 +444,7 @@ public class ActorSystemContext {
437 444
438 445 ObjectNode node = mapper.createObjectNode()
439 446 .put("type", type)
440   - .put("server", getServiceId())
  447 + .put("server", getServerAddress())
441 448 .put("entityId", tbMsg.getOriginator().getId().toString())
442 449 .put("entityName", tbMsg.getOriginator().getEntityType().name())
443 450 .put("msgId", tbMsg.getId().toString())
... ... @@ -497,7 +504,7 @@ public class ActorSystemContext {
497 504
498 505 ObjectNode node = mapper.createObjectNode()
499 506 //todo: what fields are needed here?
500   - .put("server", getServiceId())
  507 + .put("server", getServerAddress())
501 508 .put("message", "Reached debug mode rate limit!");
502 509
503 510 if (error != null) {
... ... @@ -523,7 +530,4 @@ public class ActorSystemContext {
523 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 20 import akka.actor.OneForOneStrategy;
21 21 import akka.actor.Props;
22 22 import akka.actor.SupervisorStrategy;
  23 +import akka.actor.SupervisorStrategy.Directive;
23 24 import akka.actor.Terminated;
  25 +import akka.event.Logging;
  26 +import akka.event.LoggingAdapter;
  27 +import akka.japi.Function;
24 28 import com.google.common.collect.BiMap;
25 29 import com.google.common.collect.HashBiMap;
  30 +import lombok.extern.slf4j.Slf4j;
26 31 import org.thingsboard.server.actors.ActorSystemContext;
27   -import org.thingsboard.server.actors.service.ContextAwareActor;
  32 +import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
28 33 import org.thingsboard.server.actors.service.ContextBasedCreator;
29 34 import org.thingsboard.server.actors.service.DefaultActorService;
  35 +import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
30 36 import org.thingsboard.server.actors.tenant.TenantActor;
31 37 import org.thingsboard.server.common.data.EntityType;
32 38 import org.thingsboard.server.common.data.Tenant;
... ... @@ -36,32 +42,29 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
36 42 import org.thingsboard.server.common.msg.MsgType;
37 43 import org.thingsboard.server.common.msg.TbActorMsg;
38 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 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 49 import org.thingsboard.server.dao.model.ModelConstants;
44 50 import org.thingsboard.server.dao.tenant.TenantService;
45   -import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
46 51 import scala.concurrent.duration.Duration;
47 52
48   -import java.util.HashSet;
  53 +import java.util.HashMap;
  54 +import java.util.Map;
49 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 59 private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
55 60 private final TenantService tenantService;
56 61 private final BiMap<TenantId, ActorRef> tenantActors;
57   - private final Set<TenantId> deletedTenants;
58 62 private boolean ruleChainsInitialized;
59 63
60 64 private AppActor(ActorSystemContext systemContext) {
61   - super(systemContext);
  65 + super(systemContext, new SystemRuleChainManager(systemContext));
62 66 this.tenantService = systemContext.getTenantService();
63 67 this.tenantActors = HashBiMap.create();
64   - this.deletedTenants = new HashSet<>();
65 68 }
66 69
67 70 @Override
... ... @@ -76,7 +79,7 @@ public class AppActor extends ContextAwareActor {
76 79 @Override
77 80 protected boolean process(TbActorMsg msg) {
78 81 if (!ruleChainsInitialized) {
79   - initTenantActors();
  82 + initRuleChainsAndTenantActors();
80 83 ruleChainsInitialized = true;
81 84 if (msg.getMsgType() != MsgType.APP_INIT_MSG) {
82 85 log.warn("Rule Chains initialized by unexpected message: {}", msg);
... ... @@ -85,14 +88,17 @@ public class AppActor extends ContextAwareActor {
85 88 switch (msg.getMsgType()) {
86 89 case APP_INIT_MSG:
87 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 95 broadcast(msg);
90 96 break;
91 97 case COMPONENT_LIFE_CYCLE_MSG:
92 98 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
93 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 102 break;
97 103 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
98 104 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
... ... @@ -100,6 +106,7 @@ public class AppActor extends ContextAwareActor {
100 106 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
101 107 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
102 108 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  109 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
103 110 onToDeviceActorMsg((TenantAwareMsg) msg);
104 111 break;
105 112 default:
... ... @@ -108,30 +115,16 @@ public class AppActor extends ContextAwareActor {
108 115 return true;
109 116 }
110 117
111   - private void initTenantActors() {
  118 + private void initRuleChainsAndTenantActors() {
112 119 log.info("Starting main system actor.");
113 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 125 log.debug("[{}] Creating tenant actor", tenant.getId());
120 126 getOrCreateTenantActor(tenant.getId());
121 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 130 log.info("Main system actor started.");
... ... @@ -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 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 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 156 protected void broadcast(Object msg) {
  157 + super.broadcast(msg);
156 158 tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
157 159 }
158 160
159 161 private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
160 162 ActorRef target = null;
161 163 if (SYSTEM_TENANT.equals(msg.getTenantId())) {
162   - log.warn("Message has system tenant id: {}", msg);
  164 + target = getEntityActorRef(msg.getEntityId());
163 165 } else {
164 166 if (msg.getEntityId().getEntityType() == EntityType.TENANT
165 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 170 if (tenantActor != null) {
171 171 log.debug("[{}] Deleting tenant actor: {}", msg.getTenantId(), tenantActor);
172 172 context().stop(tenantActor);
... ... @@ -183,22 +183,16 @@ public class AppActor extends ContextAwareActor {
183 183 }
184 184
185 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 189 private ActorRef getOrCreateTenantActor(TenantId tenantId) {
196 190 return tenantActors.computeIfAbsent(tenantId, k -> {
197   - log.info("[{}] Creating tenant actor.", tenantId);
  191 + log.debug("[{}] Creating tenant actor.", tenantId);
198 192 ActorRef tenantActor = context().actorOf(Props.create(new TenantActor.ActorCreator(systemContext, tenantId))
199 193 .withDispatcher(DefaultActorService.CORE_DISPATCHER_NAME), tenantId.toString());
200 194 context().watch(tenantActor);
201   - log.info("[{}] Created tenant actor: {}.", tenantId, tenantActor);
  195 + log.debug("[{}] Created tenant actor: {}.", tenantId, tenantActor);
202 196 return tenantActor;
203 197 });
204 198 }
... ...
... ... @@ -22,8 +22,10 @@ import org.thingsboard.server.actors.service.ContextAwareActor;
22 22 import org.thingsboard.server.common.data.id.DeviceId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 24 import org.thingsboard.server.common.msg.TbActorMsg;
  25 +import org.thingsboard.server.common.msg.timeout.DeviceActorClientSideRpcTimeoutMsg;
25 26 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
26 27 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  28 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
27 29 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
28 30
29 31 public class DeviceActor extends ContextAwareActor {
... ... @@ -47,11 +49,6 @@ public class DeviceActor extends ContextAwareActor {
47 49 }
48 50
49 51 @Override
50   - public void postStop() {
51   -
52   - }
53   -
54   - @Override
55 52 protected boolean process(TbActorMsg msg) {
56 53 switch (msg.getMsgType()) {
57 54 case TRANSPORT_TO_DEVICE_ACTOR_MSG:
... ... @@ -69,9 +66,15 @@ public class DeviceActor extends ContextAwareActor {
69 66 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
70 67 processor.processRpcRequest(context(), (ToDeviceRpcRequestActorMsg) msg);
71 68 break;
  69 + case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  70 + processor.processToServerRPCResponse(context(), (ToServerRpcResponseActorMsg) msg);
  71 + break;
72 72 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
73 73 processor.processServerSideRpcTimeout(context(), (DeviceActorServerSideRpcTimeoutMsg) msg);
74 74 break;
  75 + case DEVICE_ACTOR_CLIENT_SIDE_RPC_TIMEOUT_MSG:
  76 + processor.processClientSideRpcTimeout(context(), (DeviceActorClientSideRpcTimeoutMsg) msg);
  77 + break;
75 78 case SESSION_TIMEOUT_MSG:
76 79 processor.checkSessionsTimeout();
77 80 break;
... ...
... ... @@ -16,10 +16,13 @@
16 16 package org.thingsboard.server.actors.device;
17 17
18 18 import akka.actor.ActorContext;
  19 +import com.datastax.driver.core.utils.UUIDs;
19 20 import com.google.common.util.concurrent.FutureCallback;
20 21 import com.google.common.util.concurrent.Futures;
21 22 import com.google.common.util.concurrent.ListenableFuture;
22 23 import com.google.common.util.concurrent.MoreExecutors;
  24 +import com.google.gson.Gson;
  25 +import com.google.gson.JsonObject;
23 26 import com.google.protobuf.InvalidProtocolBufferException;
24 27 import lombok.extern.slf4j.Slf4j;
25 28 import org.apache.commons.collections.CollectionUtils;
... ... @@ -35,34 +38,38 @@ import org.thingsboard.server.common.data.kv.AttributeKey;
35 38 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
36 39 import org.thingsboard.server.common.data.kv.KvEntry;
37 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 43 import org.thingsboard.server.common.msg.TbMsgMetaData;
39   -import org.thingsboard.server.common.msg.queue.TbCallback;
40 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 47 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  48 +import org.thingsboard.server.gen.transport.TransportProtos;
42 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 51 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
45 52 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
46 53 import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
47 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 57 import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
49 58 import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
50 59 import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
51 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 61 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
55 62 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
56   -import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
57 63 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
58 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 65 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
  66 +import org.thingsboard.server.gen.transport.TransportProtos.TsKvListProto;
62 67 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
63 68 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
64 69 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
  70 +import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg;
65 71 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
  72 +import org.thingsboard.server.utils.JsonUtils;
66 73
67 74 import javax.annotation.Nullable;
68 75 import java.util.ArrayList;
... ... @@ -93,6 +100,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
93 100 private final Map<UUID, SessionInfo> attributeSubscriptions;
94 101 private final Map<UUID, SessionInfo> rpcSubscriptions;
95 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 107 private int rpcSeq = 0;
98 108 private String deviceName;
... ... @@ -107,6 +117,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
107 117 this.attributeSubscriptions = new HashMap<>();
108 118 this.rpcSubscriptions = new HashMap<>();
109 119 this.toDeviceRpcPendingMap = new HashMap<>();
  120 + this.toServerRpcPendingMap = new HashMap<>();
110 121 if (initAttributes()) {
111 122 restoreSessions();
112 123 }
... ... @@ -142,7 +153,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
142 153 Set<UUID> syncSessionSet = new HashSet<>();
143 154 rpcSubscriptions.forEach((key, value) -> {
144 155 sendToTransport(rpcRequest, key, value.getNodeId());
145   - if (SessionType.SYNC == value.getType()) {
  156 + if (TransportProtos.SessionType.SYNC == value.getType()) {
146 157 syncSessionSet.add(key);
147 158 }
148 159 });
... ... @@ -150,7 +161,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
150 161
151 162 if (request.isOneway() && sent) {
152 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 165 } else {
155 166 registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
156 167 }
... ... @@ -171,16 +182,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
171 182 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
172 183 if (requestMd != null) {
173 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 186 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
176 187 }
177 188 }
178 189
179 190 private void sendPendingRequests(ActorContext context, UUID sessionId, SessionInfoProto sessionInfo) {
180   - SessionType sessionType = getSessionType(sessionId);
  191 + TransportProtos.SessionType sessionType = getSessionType(sessionId);
181 192 if (!toDeviceRpcPendingMap.isEmpty()) {
182 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 195 log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
185 196 rpcSubscriptions.remove(sessionId);
186 197 }
... ... @@ -188,7 +199,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
188 199 log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
189 200 }
190 201 Set<Integer> sentOneWayIds = new HashSet<>();
191   - if (sessionType == SessionType.ASYNC) {
  202 + if (sessionType == TransportProtos.SessionType.ASYNC) {
192 203 toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
193 204 } else {
194 205 toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
... ... @@ -203,7 +214,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
203 214 ToDeviceRpcRequestBody body = request.getBody();
204 215 if (request.isOneway()) {
205 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 219 ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder().setRequestId(
209 220 entry.getKey()).setMethodName(body.getMethod()).setParams(body.getParams()).build();
... ... @@ -212,8 +223,8 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
212 223 }
213 224
214 225 void process(ActorContext context, TransportToDeviceActorMsgWrapper wrapper) {
  226 + boolean reportDeviceActivity = false;
215 227 TransportToDeviceActorMsg msg = wrapper.getMsg();
216   - TbCallback callback = wrapper.getCallback();
217 228 if (msg.hasSessionEvent()) {
218 229 processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent());
219 230 }
... ... @@ -223,16 +234,34 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
223 234 if (msg.hasSubscribeToRPC()) {
224 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 245 if (msg.hasGetAttributes()) {
227 246 handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes());
228 247 }
229 248 if (msg.hasToDeviceRPCCallResponse()) {
230 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 255 if (msg.hasSubscriptionInfo()) {
233 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 267 private void reportSessionOpen() {
... ... @@ -297,8 +326,67 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
297 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 392 void processAttributesUpdate(ActorContext context, DeviceAttributesEventNotificationMsg msg) {
... ... @@ -346,7 +434,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
346 434 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
347 435 boolean success = requestMd != null;
348 436 if (success) {
349   - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
  437 + systemContext.getDeviceRpcService().processResponseToServerSideRPCRequestFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
350 438 responseMsg.getPayload(), null));
351 439 } else {
352 440 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
... ... @@ -361,7 +449,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
361 449 } else {
362 450 SessionInfoMetaData sessionMD = sessions.get(sessionId);
363 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 454 sessionMD.setSubscribedToAttributes(true);
367 455 log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
... ... @@ -382,7 +470,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
382 470 } else {
383 471 SessionInfoMetaData sessionMD = sessions.get(sessionId);
384 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 475 sessionMD.setSubscribedToRPC(true);
388 476 log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
... ... @@ -406,11 +494,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
406 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 498 if (sessions.size() == 1) {
411 499 reportSessionOpen();
412 500 }
413   - systemContext.getDeviceStateService().onDeviceActivity(deviceId, System.currentTimeMillis());
414 501 dumpSessions();
415 502 } else if (msg.getEvent() == SessionEvent.CLOSED) {
416 503 log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
... ... @@ -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 515 UUID sessionId = getSessionId(sessionInfoProto);
429 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 519 sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
433 520 sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
... ... @@ -438,7 +525,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
438 525 if (subscriptionInfo.getRpcSubscription()) {
439 526 rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
440 527 }
441   - systemContext.getDeviceStateService().onDeviceActivity(deviceId, subscriptionInfo.getLastActivityTime());
442 528 dumpSessions();
443 529 }
444 530
... ... @@ -450,11 +536,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
450 536 }
451 537
452 538 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd) {
453   - ToTransportMsg msg = ToTransportMsg.newBuilder()
  539 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
454 540 .setSessionIdMSB(sessionId.getMostSignificantBits())
455 541 .setSessionIdLSB(sessionId.getLeastSignificantBits())
456 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 546 void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
... ... @@ -466,35 +552,35 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
466 552 }
467 553
468 554 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
469   - ToTransportMsg msg = ToTransportMsg.newBuilder()
  555 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
470 556 .setSessionIdMSB(sessionInfo.getSessionIdMSB())
471 557 .setSessionIdLSB(sessionInfo.getSessionIdLSB())
472 558 .setGetAttributesResponse(responseMsg).build();
473   - systemContext.getTbCoreToTransportService().process(sessionInfo.getNodeId(), msg);
  559 + systemContext.getRuleEngineTransportService().process(sessionInfo.getNodeId(), msg);
474 560 }
475 561
476 562 private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
477   - ToTransportMsg msg = ToTransportMsg.newBuilder()
  563 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
478 564 .setSessionIdMSB(sessionId.getMostSignificantBits())
479 565 .setSessionIdLSB(sessionId.getLeastSignificantBits())
480 566 .setAttributeUpdateNotification(notificationMsg).build();
481   - systemContext.getTbCoreToTransportService().process(nodeId, msg);
  567 + systemContext.getRuleEngineTransportService().process(nodeId, msg);
482 568 }
483 569
484 570 private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
485   - ToTransportMsg msg = ToTransportMsg.newBuilder()
  571 + DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder()
486 572 .setSessionIdMSB(sessionId.getMostSignificantBits())
487 573 .setSessionIdLSB(sessionId.getLeastSignificantBits())
488 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 580 .setSessionIdMSB(sessionId.getMostSignificantBits())
495 581 .setSessionIdLSB(sessionId.getLeastSignificantBits())
496 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 632
547 633 private void restoreSessions() {
548 634 log.debug("[{}] Restoring sessions from cache", deviceId);
549   - DeviceSessionsCacheEntry sessionsDump = null;
  635 + TransportProtos.DeviceSessionsCacheEntry sessionsDump = null;
550 636 try {
551   - sessionsDump = DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId));
  637 + sessionsDump = TransportProtos.DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId));
552 638 } catch (InvalidProtocolBufferException e) {
553 639 log.warn("[{}] Failed to decode device sessions from cache", deviceId);
554 640 return;
... ... @@ -557,11 +643,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
557 643 log.debug("[{}] No session information found", deviceId);
558 644 return;
559 645 }
560   - for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
  646 + for (TransportProtos.SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
561 647 SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
562 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 651 SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime());
566 652 sessions.put(sessionId, sessionMD);
567 653 if (subInfo.getAttributeSubscription()) {
... ... @@ -579,27 +665,27 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
579 665
580 666 private void dumpSessions() {
581 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 669 sessions.forEach((uuid, sessionMD) -> {
584   - if (sessionMD.getSessionInfo().getType() == SessionType.SYNC) {
  670 + if (sessionMD.getSessionInfo().getType() == TransportProtos.SessionType.SYNC) {
585 671 return;
586 672 }
587 673 SessionInfo sessionInfo = sessionMD.getSessionInfo();
588   - SubscriptionInfoProto subscriptionInfoProto = SubscriptionInfoProto.newBuilder()
  674 + TransportProtos.SubscriptionInfoProto subscriptionInfoProto = TransportProtos.SubscriptionInfoProto.newBuilder()
589 675 .setLastActivityTime(sessionMD.getLastActivityTime())
590 676 .setAttributeSubscription(sessionMD.isSubscribedToAttributes())
591 677 .setRpcSubscription(sessionMD.isSubscribedToRPC()).build();
592   - SessionInfoProto sessionInfoProto = SessionInfoProto.newBuilder()
  678 + TransportProtos.SessionInfoProto sessionInfoProto = TransportProtos.SessionInfoProto.newBuilder()
593 679 .setSessionIdMSB(uuid.getMostSignificantBits())
594 680 .setSessionIdLSB(uuid.getLeastSignificantBits())
595 681 .setNodeId(sessionInfo.getNodeId()).build();
596   - sessionsList.add(SessionSubscriptionInfoProto.newBuilder()
  682 + sessionsList.add(TransportProtos.SessionSubscriptionInfoProto.newBuilder()
597 683 .setSessionInfo(sessionInfoProto)
598 684 .setSubscriptionInfo(subscriptionInfoProto).build());
599 685 log.debug("[{}] Dumping session: {}", deviceId, sessionMD);
600 686 });
601 687 systemContext.getDeviceSessionCacheService()
602   - .put(deviceId, DeviceSessionsCacheEntry.newBuilder()
  688 + .put(deviceId, TransportProtos.DeviceSessionsCacheEntry.newBuilder()
603 689 .addAllSessions(sessionsList).build().toByteArray());
604 690 }
605 691
... ... @@ -620,5 +706,4 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
620 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 13 * See the License for the specific language governing permissions and
14 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 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 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 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 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.queue.settings;
  16 +package org.thingsboard.server.actors.rpc;
17 17
18 18 import lombok.Data;
  19 +import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
19 20
  21 +/**
  22 + * @author Andrew Shvayka
  23 + */
20 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 13 * See the License for the specific language governing permissions and
14 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 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 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 13 * See the License for the specific language governing permissions and
14 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 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 21 import java.util.UUID;
23   -import java.util.concurrent.ConcurrentMap;
24 22
  23 +/**
  24 + * @author Andrew Shvayka
  25 + */
25 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 13 * See the License for the specific language governing permissions and
14 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 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 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 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.queue.settings;
  16 +package org.thingsboard.server.actors.rpc;
17 17
18 18 import lombok.Data;
  19 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
19 20
  21 +/**
  22 + * @author Andrew Shvayka
  23 + */
20 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 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.queue.discovery;
  16 +package org.thingsboard.server.actors.rpc;
17 17
18 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 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 13 * See the License for the specific language governing permissions and
14 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 17
18 18 import akka.actor.ActorRef;
19 19 import com.datastax.driver.core.ResultSetFuture;
  20 +import com.datastax.driver.core.utils.UUIDs;
20 21 import com.fasterxml.jackson.core.JsonProcessingException;
21 22 import com.fasterxml.jackson.databind.ObjectMapper;
  23 +import com.fasterxml.jackson.databind.node.ObjectNode;
22 24 import io.netty.channel.EventLoopGroup;
23   -import lombok.extern.slf4j.Slf4j;
24 25 import org.springframework.data.redis.core.RedisTemplate;
  26 +import org.springframework.util.StringUtils;
25 27 import org.thingsboard.common.util.ListeningExecutor;
26 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 32 import org.thingsboard.rule.engine.api.RuleEngineRpcService;
28 33 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
29 34 import org.thingsboard.rule.engine.api.ScriptEngine;
... ... @@ -35,15 +40,19 @@ import org.thingsboard.server.common.data.DataConstants;
35 40 import org.thingsboard.server.common.data.Device;
36 41 import org.thingsboard.server.common.data.alarm.Alarm;
37 42 import org.thingsboard.server.common.data.asset.Asset;
  43 +import org.thingsboard.server.common.data.id.DeviceId;
38 44 import org.thingsboard.server.common.data.id.EntityId;
39   -import org.thingsboard.server.common.data.id.RuleChainId;
40 45 import org.thingsboard.server.common.data.id.RuleNodeId;
41 46 import org.thingsboard.server.common.data.id.TenantId;
  47 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
42 48 import org.thingsboard.server.common.data.rule.RuleNode;
43 49 import org.thingsboard.server.common.msg.TbMsg;
44 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 56 import org.thingsboard.server.dao.alarm.AlarmService;
48 57 import org.thingsboard.server.dao.asset.AssetService;
49 58 import org.thingsboard.server.dao.attributes.AttributesService;
... ... @@ -58,13 +67,11 @@ import org.thingsboard.server.dao.rule.RuleChainService;
58 67 import org.thingsboard.server.dao.tenant.TenantService;
59 68 import org.thingsboard.server.dao.timeseries.TimeseriesService;
60 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 70 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
65 71 import scala.concurrent.duration.Duration;
66 72
67 73 import java.util.Collections;
  74 +import java.util.Optional;
68 75 import java.util.Set;
69 76 import java.util.concurrent.TimeUnit;
70 77 import java.util.function.Consumer;
... ... @@ -72,7 +79,6 @@ import java.util.function.Consumer;
72 79 /**
73 80 * Created by ashvayka on 19.03.18.
74 81 */
75   -@Slf4j
76 82 class DefaultTbContext implements TbContext {
77 83
78 84 public final static ObjectMapper mapper = new ObjectMapper();
... ... @@ -86,11 +92,6 @@ class DefaultTbContext implements TbContext {
86 92 }
87 93
88 94 @Override
89   - public void tellSuccess(TbMsg msg) {
90   - tellNext(msg, Collections.singleton(TbRelationTypes.SUCCESS), null);
91   - }
92   -
93   - @Override
94 95 public void tellNext(TbMsg msg, String relationType) {
95 96 tellNext(msg, Collections.singleton(relationType), null);
96 97 }
... ... @@ -100,11 +101,16 @@ class DefaultTbContext implements TbContext {
100 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 109 private void tellNext(TbMsg msg, Set<String> relationTypes, Throwable th) {
104 110 if (nodeCtx.getSelf().isDebugMode()) {
105 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 116 @Override
... ... @@ -114,93 +120,9 @@ class DefaultTbContext implements TbContext {
114 120 }
115 121
116 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 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 128 private void scheduleMsgWithDelay(Object msg, long delayInMs, ActorRef target) {
... ... @@ -212,48 +134,66 @@ class DefaultTbContext implements TbContext {
212 134 if (nodeCtx.getSelf().isDebugMode()) {
213 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 141 public void updateSelf(RuleNode self) {
220 142 nodeCtx.setSelf(self);
221 143 }
222 144
223 145 @Override
224 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 150 @Override
229 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 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 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 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 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 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 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 197 @Override
258 198 public RuleNodeId getSelfId() {
259 199 return nodeCtx.getSelf().getId();
... ... @@ -311,8 +251,8 @@ class DefaultTbContext implements TbContext {
311 251 }
312 252
313 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 258 @Override
... ... @@ -381,6 +321,11 @@ class DefaultTbContext implements TbContext {
381 321 }
382 322
383 323 @Override
  324 + public RuleChainTransactionService getRuleChainTransactionService() {
  325 + return mainCtx.getRuleChainTransactionService();
  326 + }
  327 +
  328 + @Override
384 329 public EventLoopGroup getSharedEventLoop() {
385 330 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
386 331 }
... ... @@ -396,7 +341,35 @@ class DefaultTbContext implements TbContext {
396 341
397 342 @Override
398 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 375 @Override
... ... @@ -414,6 +387,10 @@ class DefaultTbContext implements TbContext {
414 387 return mainCtx.getRedisTemplate();
415 388 }
416 389
  390 + @Override
  391 + public String getServerAddress() {
  392 + return mainCtx.getServerAddress();
  393 + }
417 394
418 395 private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) {
419 396 TbMsgMetaData metaData = new TbMsgMetaData();
... ... @@ -421,29 +398,4 @@ class DefaultTbContext implements TbContext {
421 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 13 * See the License for the specific language governing permissions and
14 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 18 import lombok.Data;
20   -import org.thingsboard.server.common.data.id.EntityId;
  19 +import org.thingsboard.server.common.data.id.RuleChainId;
21 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 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 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 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 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
  18 +import akka.actor.ActorInitializationException;
18 19 import akka.actor.OneForOneStrategy;
19 20 import akka.actor.SupervisorStrategy;
20 21 import org.thingsboard.server.actors.ActorSystemContext;
  22 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
21 23 import org.thingsboard.server.actors.service.ComponentActor;
22 24 import org.thingsboard.server.actors.service.ContextBasedCreator;
23 25 import org.thingsboard.server.common.data.id.RuleChainId;
24 26 import org.thingsboard.server.common.data.id.TenantId;
25   -import org.thingsboard.server.common.data.rule.RuleChain;
26 27 import org.thingsboard.server.common.msg.TbActorMsg;
27 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 30 import scala.concurrent.duration.Duration;
31 31
32 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 37 context().parent(), context().self()));
38 38 }
39 39
... ... @@ -43,17 +43,20 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
43 43 case COMPONENT_LIFE_CYCLE_MSG:
44 44 onComponentLifecycleMsg((ComponentLifecycleMsg) msg);
45 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 51 break;
49 52 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
  53 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
50 54 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
51 55 break;
52 56 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
53 57 processor.onRuleChainToRuleChainMsg((RuleChainToRuleChainMsg) msg);
54 58 break;
55   - case PARTITION_CHANGE_MSG:
56   - processor.onPartitionChangeMsg((PartitionChangeMsg) msg);
  59 + case CLUSTER_EVENT_MSG:
57 60 break;
58 61 case STATS_PERSIST_TICK_MSG:
59 62 onStatsPersistTick(id);
... ... @@ -68,17 +71,17 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
68 71 private static final long serialVersionUID = 1L;
69 72
70 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 77 super(context);
75 78 this.tenantId = tenantId;
76   - this.ruleChain = ruleChain;
  79 + this.ruleChainId = pluginId;
77 80 }
78 81
79 82 @Override
80 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 18 import akka.actor.ActorContext;
19 19 import akka.actor.ActorRef;
20 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 26 import lombok.extern.slf4j.Slf4j;
22   -import org.thingsboard.rule.engine.api.TbRelationTypes;
23 27 import org.thingsboard.server.actors.ActorSystemContext;
  28 +import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
24 29 import org.thingsboard.server.actors.service.DefaultActorService;
25 30 import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
26 31 import org.thingsboard.server.common.data.EntityType;
... ... @@ -34,19 +39,11 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
34 39 import org.thingsboard.server.common.data.rule.RuleChain;
35 40 import org.thingsboard.server.common.data.rule.RuleNode;
36 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 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 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 48 import java.util.ArrayList;
52 49 import java.util.Collections;
... ... @@ -62,28 +59,27 @@ import java.util.stream.Collectors;
62 59 @Slf4j
63 60 public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> {
64 61
  62 + private static final long DEFAULT_CLUSTER_PARTITION = 0L;
65 63 private final ActorRef parent;
66 64 private final ActorRef self;
67 65 private final Map<RuleNodeId, RuleNodeCtx> nodeActors;
68 66 private final Map<RuleNodeId, List<RuleNodeRelation>> nodeRoutes;
69 67 private final RuleChainService service;
70   - private final TbClusterService clusterService;
71   - private String ruleChainName;
72 68
73 69 private RuleNodeId firstId;
74 70 private RuleNodeCtx firstNode;
75 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 75 , ActorRef parent, ActorRef self) {
79   - super(systemContext, tenantId, ruleChain.getId());
80   - this.ruleChainName = ruleChain.getName();
  76 + super(systemContext, tenantId, ruleChainId);
81 77 this.parent = parent;
82 78 this.self = self;
83 79 this.nodeActors = new HashMap<>();
84 80 this.nodeRoutes = new HashMap<>();
85 81 this.service = systemContext.getRuleChainService();
86   - this.clusterService = systemContext.getClusterService();
  82 + this.ruleChainName = ruleChainId.toString();
87 83 }
88 84
89 85 @Override
... ... @@ -96,6 +92,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
96 92 if (!started) {
97 93 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
98 94 if (ruleChain != null) {
  95 + ruleChainName = ruleChain.getName();
99 96 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
100 97 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
101 98 // Creating and starting the actors;
... ... @@ -155,8 +152,8 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
155 152 }
156 153
157 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 159 private ActorRef createRuleNodeActor(ActorContext context, RuleNode ruleNode) {
... ... @@ -195,123 +192,100 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
195 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 205 checkActive();
229 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 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 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 289 private boolean contains(Set<String> relationTypes, String type) {
316 290 if (relationTypes == null) {
317 291 return true;
... ... @@ -324,13 +298,38 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
324 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 325 private void pushMsgToNode(RuleNodeCtx nodeCtx, TbMsg msg, String fromRelationType) {
328 326 if (nodeCtx != null) {
329 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 15 */
16 16 package org.thingsboard.server.actors.ruleChain;
17 17
18   -import akka.actor.ActorContext;
19 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 19 import org.thingsboard.server.actors.ActorSystemContext;
25 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 22 import org.thingsboard.server.common.data.id.EntityId;
29 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 24 import org.thingsboard.server.dao.rule.RuleChainService;
34 25
35   -import java.util.function.Function;
36   -
37 26 /**
38 27 * Created by ashvayka on 15.03.18.
39 28 */
40 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 35 super(systemContext);
52   - this.tenantId = tenantId;
53   - this.actors = HashBiMap.create();
  36 + this.ruleChainManager = ruleChainManager;
54 37 this.ruleChainService = systemContext.getRuleChainService();
55 38 }
56 39
57 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 44 protected ActorRef getEntityActorRef(EntityId entityId) {
88 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 51 return target;
93 52 }
94 53
95 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 32 private final RuleChainId source;
33 33 private final TbMsg msg;
34 34 private final String fromRelationType;
  35 + private final boolean enqueue;
35 36
36 37 @Override
37 38 public RuleChainId getRuleChainId() {
... ...
... ... @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 24 import org.thingsboard.server.common.msg.TbActorMsg;
25 25 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
26   -import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
27 26
28 27 public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessageProcessor> {
29 28
... ... @@ -54,9 +53,6 @@ public class RuleNodeActor extends ComponentActor<RuleNodeId, RuleNodeActorMessa
54 53 case STATS_PERSIST_TICK_MSG:
55 54 onStatsPersistTick(id);
56 55 break;
57   - case PARTITION_CHANGE_MSG:
58   - onClusterEventMsg((PartitionChangeMsg) msg);
59   - break;
60 56 default:
61 57 return false;
62 58 }
... ...
... ... @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
27 27 import org.thingsboard.server.common.data.id.TenantId;
28 28 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
29 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 31 import org.thingsboard.server.dao.rule.RuleChainService;
32 32
33 33 /**
... ... @@ -40,7 +40,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
40 40 private final RuleChainService service;
41 41 private RuleNode ruleNode;
42 42 private TbNode tbNode;
43   - private DefaultTbContext defaultCtx;
  43 + private TbContext defaultCtx;
44 44
45 45 RuleNodeActorMessageProcessor(TenantId tenantId, RuleChainId ruleChainId, RuleNodeId ruleNodeId, ActorSystemContext systemContext
46 46 , ActorRef parent, ActorRef self) {
... ... @@ -84,9 +84,9 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
84 84 }
85 85
86 86 @Override
87   - public void onPartitionChangeMsg(PartitionChangeMsg msg) {
  87 + public void onClusterEventMsg(ClusterEventMsg msg) {
88 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 34 private final RuleNodeId originator;
35 35 private final Set<String> relationTypes;
36 36 private final TbMsg msg;
37   - private final String failureMessage;
38 37
39 38 @Override
40 39 public MsgType getMsgType() {
... ...
... ... @@ -15,7 +15,24 @@
15 15 */
16 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 22 import org.thingsboard.server.common.data.id.EntityId;
23 23 import org.thingsboard.server.common.data.id.TenantId;
24 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 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 115 }
116 116 }
117 117
118   - protected void onClusterEventMsg(PartitionChangeMsg msg) {
  118 + protected void onClusterEventMsg(ClusterEventMsg msg) {
119 119 try {
120   - processor.onPartitionChangeMsg(msg);
  120 + processor.onClusterEventMsg(msg);
121 121 } catch (Exception e) {
122 122 logAndPersist("onClusterEventMsg", e);
123 123 }
... ...
... ... @@ -16,14 +16,14 @@
16 16 package org.thingsboard.server.actors.service;
17 17
18 18 import akka.actor.Terminated;
19   -import akka.actor.UntypedAbstractActor;
  19 +import akka.actor.UntypedActor;
20 20 import org.slf4j.Logger;
21 21 import org.slf4j.LoggerFactory;
22 22 import org.thingsboard.server.actors.ActorSystemContext;
23 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 28 protected final Logger log = LoggerFactory.getLogger(getClass());
29 29
... ...
... ... @@ -19,6 +19,7 @@ import akka.actor.ActorRef;
19 19 import akka.actor.ActorSystem;
20 20 import akka.actor.Props;
21 21 import akka.actor.Terminated;
  22 +import com.google.protobuf.ByteString;
22 23 import lombok.extern.slf4j.Slf4j;
23 24 import org.springframework.beans.factory.annotation.Autowired;
24 25 import org.springframework.beans.factory.annotation.Value;
... ... @@ -26,20 +27,44 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
26 27 import org.springframework.context.event.EventListener;
27 28 import org.springframework.scheduling.annotation.Scheduled;
28 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 32 import org.thingsboard.server.actors.ActorSystemContext;
30 33 import org.thingsboard.server.actors.app.AppActor;
31 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 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 56 import scala.concurrent.Await;
36 57 import scala.concurrent.Future;
37 58 import scala.concurrent.duration.Duration;
38 59
39 60 import javax.annotation.PostConstruct;
40 61 import javax.annotation.PreDestroy;
  62 +import java.util.concurrent.Executors;
  63 +import java.util.concurrent.ScheduledExecutorService;
41 64 import java.util.concurrent.atomic.AtomicInteger;
42 65
  66 +import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CLUSTER_ACTOR_MESSAGE;
  67 +
43 68 @Service
44 69 @Slf4j
45 70 public class DefaultActorService implements ActorService {
... ... @@ -50,14 +75,26 @@ public class DefaultActorService implements ActorService {
50 75 public static final String CORE_DISPATCHER_NAME = "core-dispatcher";
51 76 public static final String SYSTEM_RULE_DISPATCHER_NAME = "system-rule-dispatcher";
52 77 public static final String TENANT_RULE_DISPATCHER_NAME = "rule-dispatcher";
  78 + public static final String RPC_DISPATCHER_NAME = "rpc-dispatcher";
53 79
54 80 @Autowired
55 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 92 private ActorSystem system;
58 93
59 94 private ActorRef appActor;
60 95
  96 + private ActorRef rpcManagerActor;
  97 +
61 98 @PostConstruct
62 99 public void initActorSystem() {
63 100 log.info("Initializing Actor system.");
... ... @@ -68,9 +105,13 @@ public class DefaultActorService implements ActorService {
68 105 appActor = system.actorOf(Props.create(new AppActor.ActorCreator(actorContext)).withDispatcher(APP_DISPATCHER_NAME), "appActor");
69 106 actorContext.setAppActor(appActor);
70 107
  108 + rpcManagerActor = system.actorOf(Props.create(new RpcManagerActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME),
  109 + "rpcManagerActor");
  110 +
71 111 ActorRef statsActor = system.actorOf(Props.create(new StatsActor.ActorCreator(actorContext)).withDispatcher(CORE_DISPATCHER_NAME), "statsActor");
72 112 actorContext.setStatsActor(statsActor);
73 113
  114 + rpcService.init(this);
74 115 log.info("Actor system initialized.");
75 116 }
76 117
... ... @@ -80,12 +121,6 @@ public class DefaultActorService implements ActorService {
80 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 124 @PreDestroy
90 125 public void stopActorSystem() {
91 126 Future<Terminated> status = system.terminate();
... ... @@ -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 16 package org.thingsboard.server.actors.shared;
17 17
18 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 22 import lombok.extern.slf4j.Slf4j;
20 23 import org.thingsboard.server.actors.ActorSystemContext;
21 24 import org.thingsboard.server.actors.stats.StatsPersistTick;
22 25 import org.thingsboard.server.common.data.id.EntityId;
23 26 import org.thingsboard.server.common.data.id.TenantId;
24 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 34 @Slf4j
28 35 public abstract class ComponentMsgProcessor<T extends EntityId> extends AbstractContextAwareMsgProcessor {
... ... @@ -43,7 +50,7 @@ public abstract class ComponentMsgProcessor<T extends EntityId> extends Abstract
43 50
44 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 55 public void onCreated(ActorContext context) throws Exception {
49 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 +}
... ...