Commit f07d9c64404eaa77de8f530558fdebce157cc063
Committed by
GitHub
Merge pull request #2667 from thingsboard/revert-2663-develop/3.0-merge
Revert "Merge with master. AlarmRepository.findAlarms is failing"
Showing
33 changed files
with
1357 additions
and
2423 deletions
Too many changes to show.
To preserve performance only 33 of 471 files are displayed.
application/src/main/data/json/demo/dashboards/gateways.json
deleted
100644 → 0
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 |
application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
deleted
100644 → 0
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 | } | ... | ... |
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
... | ... | @@ -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 | +} | ... | ... |