Commit 38ebbe257656047da3f627cda091b466166f8427

Authored by Andrew Shvayka
Committed by GitHub
1 parent 99ef1cba

Revert "Merge with master. AlarmRepository.findAlarms is failing (#2663)"

This reverts commit 2c02406e.
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 +}
... ...