Commit 38ebbe257656047da3f627cda091b466166f8427
Committed by
GitHub
1 parent
99ef1cba
Revert "Merge with master. AlarmRepository.findAlarms is failing (#2663)"
This reverts commit 2c02406e.
Showing
33 changed files
with
1357 additions
and
2423 deletions
Too many changes to show.
To preserve performance only 33 of 471 files are displayed.
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 | +} | ... | ... |