Commit 9317281f573a965c4a1b90af696514716425f4f0

Authored by Artem Babak
2 parents cbe5a264 0c3d1556

Merge remote-tracking branch 'thingsboard/master' into edge/cleanups-fixes

Showing 53 changed files with 1789 additions and 1561 deletions

Too many changes to show.

To preserve performance only 53 of 613 files are displayed.

@@ -33,15 +33,15 @@ @@ -33,15 +33,15 @@
33 "enableSearch": true, 33 "enableSearch": true,
34 "displayPagination": true, 34 "displayPagination": true,
35 "defaultPageSize": 10, 35 "defaultPageSize": 10,
36 - "defaultSortOrder": "entityLabel",  
37 - "displayEntityName": false, 36 + "defaultSortOrder": "entityName",
  37 + "displayEntityName": true,
38 "displayEntityType": false, 38 "displayEntityType": false,
39 "enableSelectColumnDisplay": false, 39 "enableSelectColumnDisplay": false,
40 "enableStickyHeader": true, 40 "enableStickyHeader": true,
41 "enableStickyAction": false, 41 "enableStickyAction": false,
42 "entitiesTitle": "Devices", 42 "entitiesTitle": "Devices",
43 - "displayEntityLabel": true,  
44 - "entityLabelColumnTitle": "Device" 43 + "displayEntityLabel": false,
  44 + "entityNameColumnTitle": "Device"
45 }, 45 },
46 "title": "New Entities table", 46 "title": "New Entities table",
47 "dropShadow": true, 47 "dropShadow": true,
@@ -828,15 +828,15 @@ @@ -828,15 +828,15 @@
828 "enableSearch": true, 828 "enableSearch": true,
829 "displayPagination": true, 829 "displayPagination": true,
830 "defaultPageSize": 10, 830 "defaultPageSize": 10,
831 - "defaultSortOrder": "entityLabel",  
832 - "displayEntityName": false, 831 + "defaultSortOrder": "entityName",
  832 + "displayEntityName": true,
833 "displayEntityType": false, 833 "displayEntityType": false,
834 "enableSelectColumnDisplay": false, 834 "enableSelectColumnDisplay": false,
835 "enableStickyHeader": true, 835 "enableStickyHeader": true,
836 "enableStickyAction": true, 836 "enableStickyAction": true,
837 "entitiesTitle": "Devices", 837 "entitiesTitle": "Devices",
838 - "displayEntityLabel": true,  
839 - "entityLabelColumnTitle": "Device" 838 + "displayEntityLabel": false,
  839 + "entityNameColumnTitle": "Device"
840 }, 840 },
841 "title": "New Entities table", 841 "title": "New Entities table",
842 "dropShadow": true, 842 "dropShadow": true,
@@ -1125,15 +1125,15 @@ @@ -1125,15 +1125,15 @@
1125 "enableSearch": true, 1125 "enableSearch": true,
1126 "displayPagination": true, 1126 "displayPagination": true,
1127 "defaultPageSize": 10, 1127 "defaultPageSize": 10,
1128 - "defaultSortOrder": "entityLabel",  
1129 - "displayEntityName": false, 1128 + "defaultSortOrder": "entityName",
  1129 + "displayEntityName": true,
1130 "displayEntityType": false, 1130 "displayEntityType": false,
1131 "enableSelectColumnDisplay": false, 1131 "enableSelectColumnDisplay": false,
1132 "enableStickyHeader": true, 1132 "enableStickyHeader": true,
1133 "enableStickyAction": true, 1133 "enableStickyAction": true,
1134 "entitiesTitle": "Devices", 1134 "entitiesTitle": "Devices",
1135 - "displayEntityLabel": true,  
1136 - "entityLabelColumnTitle": "Device" 1135 + "displayEntityLabel": false,
  1136 + "entityNameColumnTitle": "Device"
1137 }, 1137 },
1138 "title": "New Entities table", 1138 "title": "New Entities table",
1139 "dropShadow": true, 1139 "dropShadow": true,
@@ -1422,15 +1422,15 @@ @@ -1422,15 +1422,15 @@
1422 "enableSearch": true, 1422 "enableSearch": true,
1423 "displayPagination": true, 1423 "displayPagination": true,
1424 "defaultPageSize": 10, 1424 "defaultPageSize": 10,
1425 - "defaultSortOrder": "entityLabel",  
1426 - "displayEntityName": false, 1425 + "defaultSortOrder": "entityName",
  1426 + "displayEntityName": true,
1427 "displayEntityType": false, 1427 "displayEntityType": false,
1428 "enableSelectColumnDisplay": false, 1428 "enableSelectColumnDisplay": false,
1429 "enableStickyHeader": true, 1429 "enableStickyHeader": true,
1430 "enableStickyAction": true, 1430 "enableStickyAction": true,
1431 "entitiesTitle": "Devices", 1431 "entitiesTitle": "Devices",
1432 - "displayEntityLabel": true,  
1433 - "entityLabelColumnTitle": "Device" 1432 + "displayEntityLabel": false,
  1433 + "entityNameColumnTitle": "Device"
1434 }, 1434 },
1435 "title": "New Entities table", 1435 "title": "New Entities table",
1436 "dropShadow": true, 1436 "dropShadow": true,
@@ -1719,15 +1719,15 @@ @@ -1719,15 +1719,15 @@
1719 "enableSearch": true, 1719 "enableSearch": true,
1720 "displayPagination": true, 1720 "displayPagination": true,
1721 "defaultPageSize": 10, 1721 "defaultPageSize": 10,
1722 - "defaultSortOrder": "entityLabel",  
1723 - "displayEntityName": false, 1722 + "defaultSortOrder": "entityName",
  1723 + "displayEntityName": true,
1724 "displayEntityType": false, 1724 "displayEntityType": false,
1725 "enableSelectColumnDisplay": false, 1725 "enableSelectColumnDisplay": false,
1726 "enableStickyHeader": true, 1726 "enableStickyHeader": true,
1727 "enableStickyAction": true, 1727 "enableStickyAction": true,
1728 "entitiesTitle": "Devices", 1728 "entitiesTitle": "Devices",
1729 - "displayEntityLabel": true,  
1730 - "entityLabelColumnTitle": "Device" 1729 + "displayEntityLabel": false,
  1730 + "entityNameColumnTitle": "Device"
1731 }, 1731 },
1732 "title": "New Entities table", 1732 "title": "New Entities table",
1733 "dropShadow": true, 1733 "dropShadow": true,
@@ -33,15 +33,15 @@ @@ -33,15 +33,15 @@
33 "enableSearch": true, 33 "enableSearch": true,
34 "displayPagination": true, 34 "displayPagination": true,
35 "defaultPageSize": 10, 35 "defaultPageSize": 10,
36 - "defaultSortOrder": "entityLabel",  
37 - "displayEntityName": false, 36 + "defaultSortOrder": "entityName",
  37 + "displayEntityName": true,
38 "displayEntityType": false, 38 "displayEntityType": false,
39 "enableSelectColumnDisplay": false, 39 "enableSelectColumnDisplay": false,
40 "enableStickyHeader": true, 40 "enableStickyHeader": true,
41 "enableStickyAction": false, 41 "enableStickyAction": false,
42 "entitiesTitle": "Devices", 42 "entitiesTitle": "Devices",
43 - "displayEntityLabel": true,  
44 - "entityLabelColumnTitle": "Device" 43 + "displayEntityLabel": false,
  44 + "entityNameColumnTitle": "Device"
45 }, 45 },
46 "title": "New Entities table", 46 "title": "New Entities table",
47 "dropShadow": true, 47 "dropShadow": true,
@@ -828,15 +828,15 @@ @@ -828,15 +828,15 @@
828 "enableSearch": true, 828 "enableSearch": true,
829 "displayPagination": true, 829 "displayPagination": true,
830 "defaultPageSize": 10, 830 "defaultPageSize": 10,
831 - "defaultSortOrder": "entityLabel",  
832 - "displayEntityName": false, 831 + "defaultSortOrder": "entityName",
  832 + "displayEntityName": true,
833 "displayEntityType": false, 833 "displayEntityType": false,
834 "enableSelectColumnDisplay": false, 834 "enableSelectColumnDisplay": false,
835 "enableStickyHeader": true, 835 "enableStickyHeader": true,
836 "enableStickyAction": true, 836 "enableStickyAction": true,
837 "entitiesTitle": "Devices", 837 "entitiesTitle": "Devices",
838 - "displayEntityLabel": true,  
839 - "entityLabelColumnTitle": "Device" 838 + "displayEntityLabel": false,
  839 + "entityNameColumnTitle": "Device"
840 }, 840 },
841 "title": "New Entities table", 841 "title": "New Entities table",
842 "dropShadow": true, 842 "dropShadow": true,
@@ -1125,15 +1125,15 @@ @@ -1125,15 +1125,15 @@
1125 "enableSearch": true, 1125 "enableSearch": true,
1126 "displayPagination": true, 1126 "displayPagination": true,
1127 "defaultPageSize": 10, 1127 "defaultPageSize": 10,
1128 - "defaultSortOrder": "entityLabel",  
1129 - "displayEntityName": false, 1128 + "defaultSortOrder": "entityName",
  1129 + "displayEntityName": true,
1130 "displayEntityType": false, 1130 "displayEntityType": false,
1131 "enableSelectColumnDisplay": false, 1131 "enableSelectColumnDisplay": false,
1132 "enableStickyHeader": true, 1132 "enableStickyHeader": true,
1133 "enableStickyAction": true, 1133 "enableStickyAction": true,
1134 "entitiesTitle": "Devices", 1134 "entitiesTitle": "Devices",
1135 - "displayEntityLabel": true,  
1136 - "entityLabelColumnTitle": "Device" 1135 + "displayEntityLabel": false,
  1136 + "entityNameColumnTitle": "Device"
1137 }, 1137 },
1138 "title": "New Entities table", 1138 "title": "New Entities table",
1139 "dropShadow": true, 1139 "dropShadow": true,
@@ -1422,15 +1422,15 @@ @@ -1422,15 +1422,15 @@
1422 "enableSearch": true, 1422 "enableSearch": true,
1423 "displayPagination": true, 1423 "displayPagination": true,
1424 "defaultPageSize": 10, 1424 "defaultPageSize": 10,
1425 - "defaultSortOrder": "entityLabel",  
1426 - "displayEntityName": false, 1425 + "defaultSortOrder": "entityName",
  1426 + "displayEntityName": true,
1427 "displayEntityType": false, 1427 "displayEntityType": false,
1428 "enableSelectColumnDisplay": false, 1428 "enableSelectColumnDisplay": false,
1429 "enableStickyHeader": true, 1429 "enableStickyHeader": true,
1430 "enableStickyAction": true, 1430 "enableStickyAction": true,
1431 "entitiesTitle": "Devices", 1431 "entitiesTitle": "Devices",
1432 - "displayEntityLabel": true,  
1433 - "entityLabelColumnTitle": "Device" 1432 + "displayEntityLabel": false,
  1433 + "entityNameColumnTitle": "Device"
1434 }, 1434 },
1435 "title": "New Entities table", 1435 "title": "New Entities table",
1436 "dropShadow": true, 1436 "dropShadow": true,
@@ -1719,15 +1719,15 @@ @@ -1719,15 +1719,15 @@
1719 "enableSearch": true, 1719 "enableSearch": true,
1720 "displayPagination": true, 1720 "displayPagination": true,
1721 "defaultPageSize": 10, 1721 "defaultPageSize": 10,
1722 - "defaultSortOrder": "entityLabel",  
1723 - "displayEntityName": false, 1722 + "defaultSortOrder": "entityName",
  1723 + "displayEntityName": true,
1724 "displayEntityType": false, 1724 "displayEntityType": false,
1725 "enableSelectColumnDisplay": false, 1725 "enableSelectColumnDisplay": false,
1726 "enableStickyHeader": true, 1726 "enableStickyHeader": true,
1727 "enableStickyAction": true, 1727 "enableStickyAction": true,
1728 "entitiesTitle": "Devices", 1728 "entitiesTitle": "Devices",
1729 - "displayEntityLabel": true,  
1730 - "entityLabelColumnTitle": "Device" 1729 + "displayEntityLabel": false,
  1730 + "entityNameColumnTitle": "Device"
1731 }, 1731 },
1732 "title": "New Entities table", 1732 "title": "New Entities table",
1733 "dropShadow": true, 1733 "dropShadow": true,
1 -{  
2 - "ruleChain": {  
3 - "additionalInfo": null,  
4 - "name": "Edge Root Rule Chain",  
5 - "type": "EDGE",  
6 - "firstRuleNodeId": null,  
7 - "root": true,  
8 - "debugMode": false,  
9 - "configuration": null  
10 - },  
11 - "metadata": {  
12 - "firstNodeIndex": 0,  
13 - "nodes": [  
14 - {  
15 - "additionalInfo": {  
16 - "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",  
17 - "layoutX": 203,  
18 - "layoutY": 259  
19 - },  
20 - "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",  
21 - "name": "Device Profile Node",  
22 - "debugMode": false,  
23 - "configuration": {  
24 - "persistAlarmRulesState": false,  
25 - "fetchAlarmRulesStateOnStart": false  
26 - }  
27 - },  
28 - {  
29 - "additionalInfo": {  
30 - "layoutX": 823,  
31 - "layoutY": 157  
32 - },  
33 - "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",  
34 - "name": "Save Timeseries",  
35 - "debugMode": false,  
36 - "configuration": {  
37 - "defaultTTL": 0  
38 - }  
39 - },  
40 - {  
41 - "additionalInfo": {  
42 - "layoutX": 824,  
43 - "layoutY": 52  
44 - },  
45 - "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",  
46 - "name": "Save Client Attributes",  
47 - "debugMode": false,  
48 - "configuration": {  
49 - "scope": "CLIENT_SCOPE"  
50 - }  
51 - },  
52 - {  
53 - "additionalInfo": {  
54 - "layoutX": 347,  
55 - "layoutY": 149  
56 - },  
57 - "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",  
58 - "name": "Message Type Switch",  
59 - "debugMode": false,  
60 - "configuration": {  
61 - "version": 0  
62 - }  
63 - },  
64 - {  
65 - "additionalInfo": {  
66 - "layoutX": 825,  
67 - "layoutY": 266  
68 - },  
69 - "type": "org.thingsboard.rule.engine.action.TbLogNode",  
70 - "name": "Log RPC from Device",  
71 - "debugMode": false,  
72 - "configuration": {  
73 - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"  
74 - }  
75 - },  
76 - {  
77 - "additionalInfo": {  
78 - "layoutX": 824,  
79 - "layoutY": 378  
80 - },  
81 - "type": "org.thingsboard.rule.engine.action.TbLogNode",  
82 - "name": "Log Other",  
83 - "debugMode": false,  
84 - "configuration": {  
85 - "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"  
86 - }  
87 - },  
88 - {  
89 - "additionalInfo": {  
90 - "layoutX": 824,  
91 - "layoutY": 466  
92 - },  
93 - "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",  
94 - "name": "RPC Call Request",  
95 - "debugMode": false,  
96 - "configuration": {  
97 - "timeoutInSeconds": 60  
98 - }  
99 - },  
100 - {  
101 - "additionalInfo": {  
102 - "layoutX": 1134,  
103 - "layoutY": 132  
104 - },  
105 - "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",  
106 - "name": "Push to cloud",  
107 - "debugMode": false,  
108 - "configuration": {  
109 - "version": 0  
110 - }  
111 - }  
112 - ],  
113 - "connections": [  
114 - {  
115 - "fromIndex": 0,  
116 - "toIndex": 3,  
117 - "type": "Success"  
118 - },  
119 - {  
120 - "fromIndex": 1,  
121 - "toIndex": 7,  
122 - "type": "Success"  
123 - },  
124 - {  
125 - "fromIndex": 2,  
126 - "toIndex": 7,  
127 - "type": "Success"  
128 - },  
129 - {  
130 - "fromIndex": 3,  
131 - "toIndex": 6,  
132 - "type": "RPC Request to Device"  
133 - },  
134 - {  
135 - "fromIndex": 3,  
136 - "toIndex": 5,  
137 - "type": "Other"  
138 - },  
139 - {  
140 - "fromIndex": 3,  
141 - "toIndex": 2,  
142 - "type": "Post attributes"  
143 - },  
144 - {  
145 - "fromIndex": 3,  
146 - "toIndex": 1,  
147 - "type": "Post telemetry"  
148 - },  
149 - {  
150 - "fromIndex": 3,  
151 - "toIndex": 4,  
152 - "type": "RPC Request from Device"  
153 - },  
154 - {  
155 - "fromIndex": 4,  
156 - "toIndex": 7,  
157 - "type": "Success"  
158 - }  
159 - ],  
160 - "ruleChainConnections": null  
161 - }  
162 -}  
@@ -18,8 +18,8 @@ @@ -18,8 +18,8 @@
18 "resources": [], 18 "resources": [],
19 "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>", 19 "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>",
20 "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", 20 "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n",
21 - "controllerScript": "var requestTimeout = 500;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}",  
22 - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\"\n ]\n}", 21 + "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' <method> [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}",
  22 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"requestPersistent\": {\n \"title\": \"RPC request persistent\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"requestPersistent\"\n ]\n}",
23 "dataKeySettingsSchema": "{}\n", 23 "dataKeySettingsSchema": "{}\n",
24 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" 24 "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"
25 } 25 }
@@ -14,8 +14,8 @@ @@ -14,8 +14,8 @@
14 { 14 {
15 "additionalInfo": { 15 "additionalInfo": {
16 "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", 16 "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
17 - "layoutX": 203,  
18 - "layoutY": 259 17 + "layoutX": 187,
  18 + "layoutY": 468
19 }, 19 },
20 "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", 20 "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
21 "name": "Device Profile Node", 21 "name": "Device Profile Node",
@@ -99,14 +99,14 @@ @@ -99,14 +99,14 @@
99 }, 99 },
100 { 100 {
101 "additionalInfo": { 101 "additionalInfo": {
102 - "layoutX": 1134,  
103 - "layoutY": 132 102 + "layoutX": 1129,
  103 + "layoutY": 52
104 }, 104 },
105 "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", 105 "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
106 "name": "Push to cloud", 106 "name": "Push to cloud",
107 "debugMode": false, 107 "debugMode": false,
108 "configuration": { 108 "configuration": {
109 - "version": 0 109 + "scope": "SERVER_SCOPE"
110 } 110 }
111 } 111 }
112 ], 112 ],
@@ -152,6 +152,11 @@ @@ -152,6 +152,11 @@
152 "type": "RPC Request from Device" 152 "type": "RPC Request from Device"
153 }, 153 },
154 { 154 {
  155 + "fromIndex": 3,
  156 + "toIndex": 7,
  157 + "type": "Attributes Updated"
  158 + },
  159 + {
155 "fromIndex": 4, 160 "fromIndex": 4,
156 "toIndex": 7, 161 "toIndex": 7,
157 "type": "Success" 162 "type": "Success"
@@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar @@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar
18 LANGUAGE plpgsql AS 18 LANGUAGE plpgsql AS
19 $$ 19 $$
20 DECLARE 20 DECLARE
21 - max_tenant_ttl bigint;  
22 - max_customer_ttl bigint;  
23 - max_ttl bigint;  
24 - date timestamp;  
25 - partition_by_max_ttl_date varchar;  
26 - partition_month varchar;  
27 - partition_day varchar;  
28 - partition_year varchar;  
29 - partition varchar;  
30 - partition_to_delete varchar;  
31 - 21 + max_tenant_ttl bigint;
  22 + max_customer_ttl bigint;
  23 + max_ttl bigint;
  24 + date timestamp;
  25 + partition_by_max_ttl_date varchar;
  26 + partition_by_max_ttl_month varchar;
  27 + partition_by_max_ttl_day varchar;
  28 + partition_by_max_ttl_year varchar;
  29 + partition varchar;
  30 + partition_year integer;
  31 + partition_month integer;
  32 + partition_day integer;
32 33
33 BEGIN 34 BEGIN
34 SELECT max(attribute_kv.long_v) 35 SELECT max(attribute_kv.long_v)
@@ -45,53 +46,138 @@ BEGIN @@ -45,53 +46,138 @@ BEGIN
45 if max_ttl IS NOT NULL AND max_ttl > 0 THEN 46 if max_ttl IS NOT NULL AND max_ttl > 0 THEN
46 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); 47 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl);
47 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); 48 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date);
  49 + RAISE NOTICE 'Date by max ttl: %', date;
48 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; 50 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date;
49 IF partition_by_max_ttl_date IS NOT NULL THEN 51 IF partition_by_max_ttl_date IS NOT NULL THEN
50 CASE 52 CASE
51 WHEN partition_type = 'DAYS' THEN 53 WHEN partition_type = 'DAYS' THEN
52 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);  
53 - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);  
54 - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); 54 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  55 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
  56 + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
55 WHEN partition_type = 'MONTHS' THEN 57 WHEN partition_type = 'MONTHS' THEN
56 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);  
57 - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); 58 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  59 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
58 ELSE 60 ELSE
59 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); 61 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
60 END CASE; 62 END CASE;
61 - FOR partition IN SELECT tablename  
62 - FROM pg_tables  
63 - WHERE schemaname = 'public'  
64 - AND tablename like 'ts_kv_' || '%'  
65 - AND tablename != 'ts_kv_latest'  
66 - AND tablename != 'ts_kv_dictionary'  
67 - AND tablename != 'ts_kv_indefinite'  
68 - LOOP  
69 - IF partition != partition_by_max_ttl_date THEN  
70 - IF partition_year IS NOT NULL THEN  
71 - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN  
72 - partition_to_delete := partition;  
73 - ELSE  
74 - IF partition_month IS NOT NULL THEN  
75 - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN  
76 - partition_to_delete := partition; 63 + IF partition_by_max_ttl_year IS NULL THEN
  64 + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!';
  65 + ELSE
  66 + IF partition_type = 'YEARS' THEN
  67 + FOR partition IN SELECT tablename
  68 + FROM pg_tables
  69 + WHERE schemaname = 'public'
  70 + AND tablename like 'ts_kv_' || '%'
  71 + AND tablename != 'ts_kv_latest'
  72 + AND tablename != 'ts_kv_dictionary'
  73 + AND tablename != 'ts_kv_indefinite'
  74 + AND tablename != partition_by_max_ttl_date
  75 + LOOP
  76 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  77 + IF partition_year < partition_by_max_ttl_year::integer THEN
  78 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  79 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  80 + deleted := deleted + 1;
  81 + END IF;
  82 + END LOOP;
  83 + ELSE
  84 + IF partition_type = 'MONTHS' THEN
  85 + IF partition_by_max_ttl_month IS NULL THEN
  86 + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!';
  87 + ELSE
  88 + FOR partition IN SELECT tablename
  89 + FROM pg_tables
  90 + WHERE schemaname = 'public'
  91 + AND tablename like 'ts_kv_' || '%'
  92 + AND tablename != 'ts_kv_latest'
  93 + AND tablename != 'ts_kv_dictionary'
  94 + AND tablename != 'ts_kv_indefinite'
  95 + AND tablename != partition_by_max_ttl_date
  96 + LOOP
  97 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  98 + IF partition_year > partition_by_max_ttl_year::integer THEN
  99 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  100 + CONTINUE;
77 ELSE 101 ELSE
78 - IF partition_day IS NOT NULL THEN  
79 - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN  
80 - partition_to_delete := partition; 102 + IF partition_year < partition_by_max_ttl_year::integer THEN
  103 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  104 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  105 + deleted := deleted + 1;
  106 + ELSE
  107 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  108 + IF partition_year = partition_by_max_ttl_year::integer THEN
  109 + IF partition_month >= partition_by_max_ttl_month::integer THEN
  110 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  111 + CONTINUE;
  112 + ELSE
  113 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  114 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  115 + deleted := deleted + 1;
  116 + END IF;
81 END IF; 117 END IF;
82 END IF; 118 END IF;
83 END IF; 119 END IF;
  120 + END LOOP;
  121 + END IF;
  122 + ELSE
  123 + IF partition_type = 'DAYS' THEN
  124 + IF partition_by_max_ttl_month IS NULL THEN
  125 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!';
  126 + ELSE
  127 + IF partition_by_max_ttl_day IS NULL THEN
  128 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!';
  129 + ELSE
  130 + FOR partition IN SELECT tablename
  131 + FROM pg_tables
  132 + WHERE schemaname = 'public'
  133 + AND tablename like 'ts_kv_' || '%'
  134 + AND tablename != 'ts_kv_latest'
  135 + AND tablename != 'ts_kv_dictionary'
  136 + AND tablename != 'ts_kv_indefinite'
  137 + AND tablename != partition_by_max_ttl_date
  138 + LOOP
  139 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  140 + IF partition_year > partition_by_max_ttl_year::integer THEN
  141 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  142 + CONTINUE;
  143 + ELSE
  144 + IF partition_year < partition_by_max_ttl_year::integer THEN
  145 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  146 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  147 + deleted := deleted + 1;
  148 + ELSE
  149 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  150 + IF partition_month > partition_by_max_ttl_month::integer THEN
  151 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  152 + CONTINUE;
  153 + ELSE
  154 + IF partition_month < partition_by_max_ttl_month::integer THEN
  155 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  156 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  157 + deleted := deleted + 1;
  158 + ELSE
  159 + partition_day := SPLIT_PART(partition, '_', 5)::integer;
  160 + IF partition_day >= partition_by_max_ttl_day::integer THEN
  161 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  162 + CONTINUE;
  163 + ELSE
  164 + IF partition_day < partition_by_max_ttl_day::integer THEN
  165 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  166 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  167 + deleted := deleted + 1;
  168 + END IF;
  169 + END IF;
  170 + END IF;
  171 + END IF;
  172 + END IF;
  173 + END IF;
  174 + END LOOP;
84 END IF; 175 END IF;
85 END IF; 176 END IF;
86 END IF; 177 END IF;
87 - IF partition_to_delete IS NOT NULL THEN  
88 - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete;  
89 - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete);  
90 - partition_to_delete := NULL;  
91 - deleted := deleted + 1;  
92 - END IF;  
93 END IF; 178 END IF;
94 - END LOOP; 179 + END IF;
  180 + END IF;
95 END IF; 181 END IF;
96 END IF; 182 END IF;
97 END 183 END
@@ -107,8 +193,6 @@ BEGIN @@ -107,8 +193,6 @@ BEGIN
107 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); 193 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM');
108 WHEN partition_type = 'YEARS' THEN 194 WHEN partition_type = 'YEARS' THEN
109 partition := 'ts_kv_' || to_char(date, 'yyyy'); 195 partition := 'ts_kv_' || to_char(date, 'yyyy');
110 - WHEN partition_type = 'INDEFINITE' THEN  
111 - partition := NULL;  
112 ELSE 196 ELSE
113 partition := NULL; 197 partition := NULL;
114 END CASE; 198 END CASE;
@@ -197,3 +197,17 @@ $$; @@ -197,3 +197,17 @@ $$;
197 ALTER TABLE api_usage_state 197 ALTER TABLE api_usage_state
198 ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32); 198 ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32);
199 UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL; 199 UPDATE api_usage_state SET alarm_exec = 'ENABLED' WHERE alarm_exec IS NULL;
  200 +
  201 +CREATE TABLE IF NOT EXISTS rpc (
  202 + id uuid NOT NULL CONSTRAINT rpc_pkey PRIMARY KEY,
  203 + created_time bigint NOT NULL,
  204 + tenant_id uuid NOT NULL,
  205 + device_id uuid NOT NULL,
  206 + expiration_time bigint NOT NULL,
  207 + request varchar(10000000) NOT NULL,
  208 + response varchar(10000000),
  209 + status varchar(255) NOT NULL
  210 +);
  211 +
  212 +CREATE INDEX IF NOT EXISTS idx_rpc_tenant_id_device_id ON rpc(tenant_id, device_id);
  213 +
  1 +--
  2 +-- Copyright © 2016-2021 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 +
  17 +-- PROCEDURE: public.cleanup_events_by_ttl(bigint, bigint, bigint)
  18 +
  19 +DROP PROCEDURE IF EXISTS public.cleanup_events_by_ttl(bigint, bigint, bigint);
  20 +
  21 +CREATE OR REPLACE PROCEDURE public.cleanup_events_by_ttl(
  22 + ttl bigint,
  23 + debug_ttl bigint,
  24 + INOUT deleted bigint)
  25 +LANGUAGE 'plpgsql'
  26 +AS $BODY$
  27 +DECLARE
  28 + ttl_ts bigint;
  29 + debug_ttl_ts bigint;
  30 + ttl_deleted_count bigint DEFAULT 0;
  31 + debug_ttl_deleted_count bigint DEFAULT 0;
  32 +BEGIN
  33 + IF ttl > 0 THEN
  34 + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
  35 +
  36 + DELETE FROM event
  37 + WHERE ts < ttl_ts
  38 + AND NOT event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION');
  39 +
  40 + GET DIAGNOSTICS ttl_deleted_count = ROW_COUNT;
  41 + END IF;
  42 +
  43 + IF debug_ttl > 0 THEN
  44 + debug_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - debug_ttl::bigint * 1000)::bigint;
  45 +
  46 + DELETE FROM event
  47 + WHERE ts < debug_ttl_ts
  48 + AND event_type IN ('DEBUG_RULE_NODE', 'DEBUG_RULE_CHAIN', 'DEBUG_CONVERTER', 'DEBUG_INTEGRATION');
  49 +
  50 + GET DIAGNOSTICS debug_ttl_deleted_count = ROW_COUNT;
  51 + END IF;
  52 +
  53 + RAISE NOTICE 'Events removed by ttl: %', ttl_deleted_count;
  54 + RAISE NOTICE 'Debug Events removed by ttl: %', debug_ttl_deleted_count;
  55 + deleted := ttl_deleted_count + debug_ttl_deleted_count;
  56 +END
  57 +$BODY$;
  58 +
  59 +
  60 +-- Index: idx_event_ts
  61 +
  62 +DROP INDEX IF EXISTS public.idx_event_ts;
  63 +
  64 +-- Hint: add CONCURRENTLY to CREATE INDEX query in case of more then 1 million records or during live update
  65 +-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_ts
  66 +CREATE INDEX IF NOT EXISTS idx_event_ts
  67 + ON public.event
  68 + (ts DESC NULLS LAST)
  69 + WITH (FILLFACTOR=95);
  70 +
  71 +COMMENT ON INDEX public.idx_event_ts
  72 + IS 'This index helps to delete events by TTL using timestamp';
  73 +
  74 +
  75 +-- Index: idx_event_tenant_entity_type_entity_event_type_created_time_des
  76 +
  77 +DROP INDEX IF EXISTS public.idx_event_tenant_entity_type_entity_event_type_created_time_des;
  78 +
  79 +-- CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des
  80 +CREATE INDEX IF NOT EXISTS idx_event_tenant_entity_type_entity_event_type_created_time_des
  81 + ON public.event
  82 + (tenant_id ASC, entity_type ASC, entity_id ASC, event_type ASC, created_time DESC NULLS LAST)
  83 + WITH (FILLFACTOR=95);
  84 +
  85 +COMMENT ON INDEX public.idx_event_tenant_entity_type_entity_event_type_created_time_des
  86 + IS 'This index helps to open latest events on UI fast';
  87 +
  88 +-- Index: idx_event_type_entity_id
  89 +-- Description: replaced with more suitable idx_event_tenant_entity_type_entity_event_type_created_time_des
  90 +DROP INDEX IF EXISTS public.idx_event_type_entity_id;
@@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService; @@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService;
65 import org.thingsboard.server.dao.resource.ResourceService; 65 import org.thingsboard.server.dao.resource.ResourceService;
66 import org.thingsboard.server.dao.rule.RuleChainService; 66 import org.thingsboard.server.dao.rule.RuleChainService;
67 import org.thingsboard.server.dao.rule.RuleNodeStateService; 67 import org.thingsboard.server.dao.rule.RuleNodeStateService;
  68 +import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
68 import org.thingsboard.server.dao.tenant.TenantProfileService; 69 import org.thingsboard.server.dao.tenant.TenantProfileService;
69 import org.thingsboard.server.dao.tenant.TenantService; 70 import org.thingsboard.server.dao.tenant.TenantService;
70 import org.thingsboard.server.dao.timeseries.TimeseriesService; 71 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService; @@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
80 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 81 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
81 import org.thingsboard.server.service.mail.MailExecutorService; 82 import org.thingsboard.server.service.mail.MailExecutorService;
82 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 83 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
83 -import org.thingsboard.server.dao.tenant.TbTenantProfileCache;  
84 import org.thingsboard.server.service.queue.TbClusterService; 84 import org.thingsboard.server.service.queue.TbClusterService;
85 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 85 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
  86 +import org.thingsboard.server.service.rpc.TbRpcService;
86 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 87 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
87 import org.thingsboard.server.service.script.JsExecutorService; 88 import org.thingsboard.server.service.script.JsExecutorService;
88 import org.thingsboard.server.service.script.JsInvokeService; 89 import org.thingsboard.server.service.script.JsInvokeService;
@@ -303,23 +304,33 @@ public class ActorSystemContext { @@ -303,23 +304,33 @@ public class ActorSystemContext {
303 304
304 @Lazy 305 @Lazy
305 @Autowired(required = false) 306 @Autowired(required = false)
306 - @Getter private EdgeService edgeService; 307 + @Getter
  308 + private EdgeService edgeService;
307 309
308 @Lazy 310 @Lazy
309 @Autowired(required = false) 311 @Autowired(required = false)
310 - @Getter private EdgeEventService edgeEventService; 312 + @Getter
  313 + private EdgeEventService edgeEventService;
311 314
312 @Lazy 315 @Lazy
313 @Autowired(required = false) 316 @Autowired(required = false)
314 - @Getter private EdgeRpcService edgeRpcService; 317 + @Getter
  318 + private EdgeRpcService edgeRpcService;
315 319
316 @Lazy 320 @Lazy
317 @Autowired(required = false) 321 @Autowired(required = false)
318 - @Getter private ResourceService resourceService; 322 + @Getter
  323 + private ResourceService resourceService;
319 324
320 @Lazy 325 @Lazy
321 @Autowired(required = false) 326 @Autowired(required = false)
322 - @Getter private OtaPackageService otaPackageService; 327 + @Getter
  328 + private OtaPackageService otaPackageService;
  329 +
  330 + @Lazy
  331 + @Autowired(required = false)
  332 + @Getter
  333 + private TbRpcService tbRpcService;
323 334
324 @Value("${actors.session.max_concurrent_sessions_per_device:1}") 335 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
325 @Getter 336 @Getter
@@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor { @@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor {
46 super.init(ctx); 46 super.init(ctx);
47 log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); 47 log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId);
48 try { 48 try {
49 - processor.initSessionTimeout(ctx); 49 + processor.init(ctx);
50 log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); 50 log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId);
51 } catch (Exception e) { 51 } catch (Exception e) {
52 log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e); 52 log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e);
@@ -23,6 +23,8 @@ import com.google.common.util.concurrent.MoreExecutors; @@ -23,6 +23,8 @@ import com.google.common.util.concurrent.MoreExecutors;
23 import com.google.protobuf.InvalidProtocolBufferException; 23 import com.google.protobuf.InvalidProtocolBufferException;
24 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
25 import org.apache.commons.collections.CollectionUtils; 25 import org.apache.commons.collections.CollectionUtils;
  26 +import org.thingsboard.common.util.JacksonUtil;
  27 +import org.thingsboard.common.util.LinkedHashMapRemoveEldest;
26 import org.thingsboard.rule.engine.api.RpcError; 28 import org.thingsboard.rule.engine.api.RpcError;
27 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 29 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
28 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 30 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
@@ -38,12 +40,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -38,12 +40,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
38 import org.thingsboard.server.common.data.edge.EdgeEventType; 40 import org.thingsboard.server.common.data.edge.EdgeEventType;
39 import org.thingsboard.server.common.data.id.DeviceId; 41 import org.thingsboard.server.common.data.id.DeviceId;
40 import org.thingsboard.server.common.data.id.EdgeId; 42 import org.thingsboard.server.common.data.id.EdgeId;
  43 +import org.thingsboard.server.common.data.id.RpcId;
41 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
42 import org.thingsboard.server.common.data.kv.AttributeKey; 45 import org.thingsboard.server.common.data.kv.AttributeKey;
43 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 46 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
44 import org.thingsboard.server.common.data.kv.KvEntry; 47 import org.thingsboard.server.common.data.kv.KvEntry;
  48 +import org.thingsboard.server.common.data.page.PageData;
  49 +import org.thingsboard.server.common.data.page.PageLink;
45 import org.thingsboard.server.common.data.relation.EntityRelation; 50 import org.thingsboard.server.common.data.relation.EntityRelation;
46 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 51 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  52 +import org.thingsboard.server.common.data.rpc.Rpc;
  53 +import org.thingsboard.server.common.data.rpc.RpcStatus;
47 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 54 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
48 import org.thingsboard.server.common.data.security.DeviceCredentials; 55 import org.thingsboard.server.common.data.security.DeviceCredentials;
49 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 56 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@@ -52,8 +59,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; @@ -52,8 +59,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
52 import org.thingsboard.server.common.msg.queue.TbCallback; 59 import org.thingsboard.server.common.msg.queue.TbCallback;
53 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 60 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
54 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 61 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
55 -import org.thingsboard.server.gen.transport.TransportProtos;  
56 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; 62 import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
  63 +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
57 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; 64 import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
58 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; 65 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
59 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; 66 import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
@@ -68,10 +75,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; @@ -68,10 +75,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
68 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; 75 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg;
69 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; 76 import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg;
70 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; 77 import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto;
  78 +import org.thingsboard.server.gen.transport.TransportProtos.ToDevicePersistedRpcResponseMsg;
71 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; 79 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
72 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; 80 import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg;
73 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; 81 import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
74 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; 82 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg;
  83 +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
75 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; 84 import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg;
76 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 85 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
77 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 86 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
@@ -88,6 +97,8 @@ import java.util.HashSet; @@ -88,6 +97,8 @@ import java.util.HashSet;
88 import java.util.LinkedHashMap; 97 import java.util.LinkedHashMap;
89 import java.util.List; 98 import java.util.List;
90 import java.util.Map; 99 import java.util.Map;
  100 +import java.util.Objects;
  101 +import java.util.Optional;
91 import java.util.Set; 102 import java.util.Set;
92 import java.util.UUID; 103 import java.util.UUID;
93 import java.util.function.Consumer; 104 import java.util.function.Consumer;
@@ -102,7 +113,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -102,7 +113,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
102 113
103 final TenantId tenantId; 114 final TenantId tenantId;
104 final DeviceId deviceId; 115 final DeviceId deviceId;
105 - private final Map<UUID, SessionInfoMetaData> sessions; 116 + final LinkedHashMapRemoveEldest<UUID, SessionInfoMetaData> sessions;
106 private final Map<UUID, SessionInfo> attributeSubscriptions; 117 private final Map<UUID, SessionInfo> attributeSubscriptions;
107 private final Map<UUID, SessionInfo> rpcSubscriptions; 118 private final Map<UUID, SessionInfo> rpcSubscriptions;
108 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; 119 private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
@@ -117,16 +128,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -117,16 +128,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
117 super(systemContext); 128 super(systemContext);
118 this.tenantId = tenantId; 129 this.tenantId = tenantId;
119 this.deviceId = deviceId; 130 this.deviceId = deviceId;
120 - this.sessions = new LinkedHashMap<>();  
121 this.attributeSubscriptions = new HashMap<>(); 131 this.attributeSubscriptions = new HashMap<>();
122 this.rpcSubscriptions = new HashMap<>(); 132 this.rpcSubscriptions = new HashMap<>();
123 this.toDeviceRpcPendingMap = new HashMap<>(); 133 this.toDeviceRpcPendingMap = new HashMap<>();
  134 + this.sessions = new LinkedHashMapRemoveEldest<>(systemContext.getMaxConcurrentSessionsPerDevice(), this::notifyTransportAboutClosedSessionMaxSessionsLimit);
124 if (initAttributes()) { 135 if (initAttributes()) {
125 restoreSessions(); 136 restoreSessions();
126 } 137 }
127 } 138 }
128 139
129 - private boolean initAttributes() { 140 + boolean initAttributes() {
130 Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId); 141 Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId);
131 if (device != null) { 142 if (device != null) {
132 this.deviceName = device.getName(); 143 this.deviceName = device.getName();
@@ -162,20 +173,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -162,20 +173,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
162 173
163 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { 174 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
164 ToDeviceRpcRequest request = msg.getMsg(); 175 ToDeviceRpcRequest request = msg.getMsg();
165 - ToDeviceRpcRequestBody body = request.getBody();  
166 - ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder()  
167 - .setRequestId(rpcSeq++)  
168 - .setMethodName(body.getMethod())  
169 - .setParams(body.getParams())  
170 - .setExpirationTime(request.getExpirationTime())  
171 - .setRequestIdMSB(request.getId().getMostSignificantBits())  
172 - .setRequestIdLSB(request.getId().getLeastSignificantBits())  
173 - .build(); 176 + ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request);
174 177
175 long timeout = request.getExpirationTime() - System.currentTimeMillis(); 178 long timeout = request.getExpirationTime() - System.currentTimeMillis();
  179 + boolean persisted = request.isPersisted();
  180 +
176 if (timeout <= 0) { 181 if (timeout <= 0) {
177 log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime()); 182 log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());
  183 + if (persisted) {
  184 + createRpc(request, RpcStatus.TIMEOUT);
  185 + }
178 return; 186 return;
  187 + } else if (persisted) {
  188 + createRpc(request, RpcStatus.QUEUED);
179 } 189 }
180 190
181 boolean sent; 191 boolean sent;
@@ -192,11 +202,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -192,11 +202,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
192 syncSessionSet.add(key); 202 syncSessionSet.add(key);
193 } 203 }
194 }); 204 });
195 - log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions); 205 + log.trace("46) Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);
196 syncSessionSet.forEach(rpcSubscriptions::remove); 206 syncSessionSet.forEach(rpcSubscriptions::remove);
197 } 207 }
198 208
199 - if (request.isOneway() && sent) { 209 + if (persisted) {
  210 + ObjectNode response = JacksonUtil.newObjectNode();
  211 + response.put("rpcId", request.getId().toString());
  212 + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null));
  213 + }
  214 +
  215 + if (!persisted && request.isOneway() && sent) {
200 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 216 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
201 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); 217 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
202 } else { 218 } else {
@@ -209,6 +225,31 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -209,6 +225,31 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
209 } 225 }
210 } 226 }
211 227
  228 + private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) {
  229 + Rpc rpc = new Rpc(new RpcId(request.getId()));
  230 + rpc.setCreatedTime(System.currentTimeMillis());
  231 + rpc.setTenantId(tenantId);
  232 + rpc.setDeviceId(deviceId);
  233 + rpc.setExpirationTime(request.getExpirationTime());
  234 + rpc.setRequest(JacksonUtil.valueToTree(request));
  235 + rpc.setStatus(status);
  236 + return systemContext.getTbRpcService().save(tenantId, rpc);
  237 + }
  238 +
  239 + private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) {
  240 + ToDeviceRpcRequestBody body = request.getBody();
  241 + return ToDeviceRpcRequestMsg.newBuilder()
  242 + .setRequestId(rpcSeq++)
  243 + .setMethodName(body.getMethod())
  244 + .setParams(body.getParams())
  245 + .setExpirationTime(request.getExpirationTime())
  246 + .setRequestIdMSB(request.getId().getMostSignificantBits())
  247 + .setRequestIdLSB(request.getId().getLeastSignificantBits())
  248 + .setOneway(request.isOneway())
  249 + .setPersisted(request.isPersisted())
  250 + .build();
  251 + }
  252 +
212 void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) { 253 void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
213 log.debug("[{}] Processing rpc command response from edge session", deviceId); 254 log.debug("[{}] Processing rpc command response from edge session", deviceId);
214 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); 255 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
@@ -230,6 +271,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -230,6 +271,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
230 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); 271 ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
231 if (requestMd != null) { 272 if (requestMd != null) {
232 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId()); 273 log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
  274 + if (requestMd.getMsg().getMsg().isPersisted()) {
  275 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.TIMEOUT, null);
  276 + }
233 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 277 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
234 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); 278 null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
235 } 279 }
@@ -253,7 +297,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -253,7 +297,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
253 toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds)); 297 toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, sessionInfo.getNodeId(), sentOneWayIds));
254 } 298 }
255 299
256 - sentOneWayIds.forEach(toDeviceRpcPendingMap::remove); 300 + sentOneWayIds.stream().filter(id -> !toDeviceRpcPendingMap.get(id).getMsg().getMsg().isPersisted()).forEach(toDeviceRpcPendingMap::remove);
257 } 301 }
258 302
259 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) { 303 private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
@@ -271,7 +315,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -271,7 +315,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
271 .setExpirationTime(request.getExpirationTime()) 315 .setExpirationTime(request.getExpirationTime())
272 .setRequestIdMSB(request.getId().getMostSignificantBits()) 316 .setRequestIdMSB(request.getId().getMostSignificantBits())
273 .setRequestIdLSB(request.getId().getLeastSignificantBits()) 317 .setRequestIdLSB(request.getId().getLeastSignificantBits())
  318 + .setOneway(request.isOneway())
  319 + .setPersisted(request.isPersisted())
274 .build(); 320 .build();
  321 +
275 sendToTransport(rpcRequest, sessionId, nodeId); 322 sendToTransport(rpcRequest, sessionId, nodeId);
276 }; 323 };
277 } 324 }
@@ -279,31 +326,39 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -279,31 +326,39 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
279 void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) { 326 void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {
280 TransportToDeviceActorMsg msg = wrapper.getMsg(); 327 TransportToDeviceActorMsg msg = wrapper.getMsg();
281 TbCallback callback = wrapper.getCallback(); 328 TbCallback callback = wrapper.getCallback();
  329 + var sessionInfo = msg.getSessionInfo();
  330 +
282 if (msg.hasSessionEvent()) { 331 if (msg.hasSessionEvent()) {
283 - processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); 332 + processSessionStateMsgs(sessionInfo, msg.getSessionEvent());
284 } 333 }
285 if (msg.hasSubscribeToAttributes()) { 334 if (msg.hasSubscribeToAttributes()) {
286 - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes()); 335 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes());
287 } 336 }
288 if (msg.hasSubscribeToRPC()) { 337 if (msg.hasSubscribeToRPC()) {
289 - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToRPC()); 338 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC());
  339 + }
  340 + if (msg.hasSendPendingRPC()) {
  341 + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo);
290 } 342 }
291 if (msg.hasGetAttributes()) { 343 if (msg.hasGetAttributes()) {
292 - handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); 344 + handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes());
293 } 345 }
294 if (msg.hasToDeviceRPCCallResponse()) { 346 if (msg.hasToDeviceRPCCallResponse()) {
295 - processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); 347 + processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse());
296 } 348 }
297 if (msg.hasSubscriptionInfo()) { 349 if (msg.hasSubscriptionInfo()) {
298 - handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); 350 + handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo());
299 } 351 }
300 if (msg.hasClaimDevice()) { 352 if (msg.hasClaimDevice()) {
301 - handleClaimDeviceMsg(context, msg.getSessionInfo(), msg.getClaimDevice()); 353 + handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice());
  354 + }
  355 + if (msg.hasPersistedRpcResponseMsg()) {
  356 + processPersistedRpcResponses(context, sessionInfo, msg.getPersistedRpcResponseMsg());
302 } 357 }
303 callback.onSuccess(); 358 callback.onSuccess();
304 } 359 }
305 360
306 - private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg) { 361 + private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
307 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); 362 DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
308 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()); 363 systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
309 } 364 }
@@ -442,11 +497,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -442,11 +497,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
442 if (success) { 497 if (success) {
443 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), 498 systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),
444 responseMsg.getPayload(), null)); 499 responseMsg.getPayload(), null));
  500 + if (requestMd.getMsg().getMsg().isPersisted()) {
  501 + systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.SUCCESSFUL, JacksonUtil.toJsonNode(responseMsg.getPayload()));
  502 + }
445 } else { 503 } else {
446 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId()); 504 log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
447 } 505 }
448 } 506 }
449 507
  508 + private void processPersistedRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDevicePersistedRpcResponseMsg responseMsg) {
  509 + UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB());
  510 + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), RpcStatus.valueOf(responseMsg.getStatus()), null);
  511 + }
  512 +
450 private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { 513 private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {
451 UUID sessionId = getSessionId(sessionInfo); 514 UUID sessionId = getSessionId(sessionInfo);
452 if (subscribeCmd.getUnsubscribe()) { 515 if (subscribeCmd.getUnsubscribe()) {
@@ -488,18 +551,14 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -488,18 +551,14 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
488 551
489 private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { 552 private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
490 UUID sessionId = getSessionId(sessionInfo); 553 UUID sessionId = getSessionId(sessionInfo);
  554 + Objects.requireNonNull(sessionId);
491 if (msg.getEvent() == SessionEvent.OPEN) { 555 if (msg.getEvent() == SessionEvent.OPEN) {
492 if (sessions.containsKey(sessionId)) { 556 if (sessions.containsKey(sessionId)) {
493 log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId); 557 log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
494 return; 558 return;
495 } 559 }
496 - log.debug("[{}] Processing new session [{}]", deviceId, sessionId);  
497 - if (sessions.size() >= systemContext.getMaxConcurrentSessionsPerDevice()) {  
498 - UUID sessionIdToRemove = sessions.keySet().stream().findFirst().orElse(null);  
499 - if (sessionIdToRemove != null) {  
500 - notifyTransportAboutClosedSession(sessionIdToRemove, sessions.remove(sessionIdToRemove), "max concurrent sessions limit reached per device!");  
501 - }  
502 - } 560 + log.info("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());
  561 +
503 sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId()))); 562 sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
504 if (sessions.size() == 1) { 563 if (sessions.size() == 1) {
505 reportSessionOpen(); 564 reportSessionOpen();
@@ -520,8 +579,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -520,8 +579,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
520 579
521 private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { 580 private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) {
522 UUID sessionId = getSessionId(sessionInfoProto); 581 UUID sessionId = getSessionId(sessionInfoProto);
  582 + Objects.requireNonNull(sessionId);
  583 +
523 SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId, 584 SessionInfoMetaData sessionMD = sessions.computeIfAbsent(sessionId,
524 - id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), 0L)); 585 + id -> new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId()), subscriptionInfo.getLastActivityTime()));
525 586
526 sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime()); 587 sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
527 sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription()); 588 sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
@@ -551,6 +612,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -551,6 +612,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
551 } 612 }
552 } 613 }
553 614
  615 + private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) {
  616 + notifyTransportAboutClosedSession(sessionId, sessionMd, "max concurrent sessions limit reached per device!");
  617 + }
  618 +
  619 +
554 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) { 620 private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) {
555 SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto 621 SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto
556 .newBuilder() 622 .newBuilder()
@@ -565,7 +631,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -565,7 +631,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
565 631
566 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) { 632 void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
567 log.info("2) LwM2Mtype: "); 633 log.info("2) LwM2Mtype: ");
568 - TransportProtos.ToTransportUpdateCredentialsProto.Builder notification = TransportProtos.ToTransportUpdateCredentialsProto.newBuilder(); 634 + ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder();
569 notification.addCredentialsId(deviceCredentials.getCredentialsId()); 635 notification.addCredentialsId(deviceCredentials.getCredentialsId());
570 notification.addCredentialsValue(deviceCredentials.getCredentialsValue()); 636 notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
571 ToTransportMsg msg = ToTransportMsg.newBuilder() 637 ToTransportMsg msg = ToTransportMsg.newBuilder()
@@ -640,7 +706,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -640,7 +706,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
640 ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent); 706 ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
641 Futures.addCallback(future, new FutureCallback<EdgeEvent>() { 707 Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
642 @Override 708 @Override
643 - public void onSuccess( EdgeEvent result) { 709 + public void onSuccess(EdgeEvent result) {
644 systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId); 710 systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
645 } 711 }
646 712
@@ -697,7 +763,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -697,7 +763,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
697 return builder.build(); 763 return builder.build();
698 } 764 }
699 765
700 - private void restoreSessions() { 766 + void restoreSessions() {
701 log.debug("[{}] Restoring sessions from cache", deviceId); 767 log.debug("[{}] Restoring sessions from cache", deviceId);
702 DeviceSessionsCacheEntry sessionsDump = null; 768 DeviceSessionsCacheEntry sessionsDump = null;
703 try { 769 try {
@@ -710,6 +776,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -710,6 +776,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
710 log.debug("[{}] No session information found", deviceId); 776 log.debug("[{}] No session information found", deviceId);
711 return; 777 return;
712 } 778 }
  779 + // TODO: Take latest max allowed sessions size from cache
713 for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { 780 for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {
714 SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo(); 781 SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
715 UUID sessionId = getSessionId(sessionInfoProto); 782 UUID sessionId = getSessionId(sessionInfoProto);
@@ -756,11 +823,30 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -756,11 +823,30 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
756 .addAllSessions(sessionsList).build().toByteArray()); 823 .addAllSessions(sessionsList).build().toByteArray());
757 } 824 }
758 825
759 - void initSessionTimeout(TbActorCtx ctx) { 826 + void init(TbActorCtx ctx) {
760 schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout()); 827 schedulePeriodicMsgWithDelay(ctx, SessionTimeoutCheckMsg.instance(), systemContext.getSessionReportTimeout(), systemContext.getSessionReportTimeout());
  828 + PageLink pageLink = new PageLink(1024);
  829 + PageData<Rpc> pageData;
  830 + do {
  831 + pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink);
  832 + pageData.getData().forEach(rpc -> {
  833 + ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class);
  834 + long timeout = rpc.getExpirationTime() - System.currentTimeMillis();
  835 + if (timeout <= 0) {
  836 + rpc.setStatus(RpcStatus.TIMEOUT);
  837 + systemContext.getTbRpcService().save(tenantId, rpc);
  838 + } else {
  839 + registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout);
  840 + }
  841 + });
  842 + if (pageData.hasNext()) {
  843 + pageLink = pageLink.nextPageLink();
  844 + }
  845 + } while (pageData.hasNext());
761 } 846 }
762 847
763 void checkSessionsTimeout() { 848 void checkSessionsTimeout() {
  849 + log.debug("[{}] checkSessionsTimeout started. Size before check {}", deviceId, sessions.size());
764 long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout(); 850 long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout();
765 Map<UUID, SessionInfoMetaData> sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); 851 Map<UUID, SessionInfoMetaData> sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
766 sessionsToRemove.forEach((sessionId, sessionMD) -> { 852 sessionsToRemove.forEach((sessionId, sessionMD) -> {
@@ -772,6 +858,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -772,6 +858,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
772 if (!sessionsToRemove.isEmpty()) { 858 if (!sessionsToRemove.isEmpty()) {
773 dumpSessions(); 859 dumpSessions();
774 } 860 }
  861 + log.debug("[{}] checkSessionsTimeout finished. Size after check {}", deviceId, sessions.size());
775 } 862 }
776 863
777 } 864 }
@@ -25,5 +25,4 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; @@ -25,5 +25,4 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType;
25 public class SessionInfo { 25 public class SessionInfo {
26 private final SessionType type; 26 private final SessionType type;
27 private final String nodeId; 27 private final String nodeId;
28 - private long lastActivityTime;  
29 } 28 }
@@ -245,11 +245,11 @@ public class TenantActor extends RuleChainManagerActor { @@ -245,11 +245,11 @@ public class TenantActor extends RuleChainManagerActor {
245 EdgeId edgeId = new EdgeId(msg.getEntityId().getId()); 245 EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
246 EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService(); 246 EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
247 if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { 247 if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
248 - edgeRpcService.deleteEdge(edgeId); 248 + edgeRpcService.deleteEdge(tenantId, edgeId);
249 } else { 249 } else {
250 Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId); 250 Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
251 if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) { 251 if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
252 - edgeRpcService.updateEdge(edge); 252 + edgeRpcService.updateEdge(tenantId, edge);
253 } 253 }
254 } 254 }
255 } else if (isRuleEngineForCurrentTenant) { 255 } else if (isRuleEngineForCurrentTenant) {
@@ -277,7 +277,7 @@ public class TenantActor extends RuleChainManagerActor { @@ -277,7 +277,7 @@ public class TenantActor extends RuleChainManagerActor {
277 277
278 private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) { 278 private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) {
279 log.trace("[{}] onToEdgeSessionMsg [{}]", msg.getTenantId(), msg); 279 log.trace("[{}] onToEdgeSessionMsg [{}]", msg.getTenantId(), msg);
280 - systemContext.getEdgeRpcService().onEdgeEvent(msg.getEdgeId()); 280 + systemContext.getEdgeRpcService().onEdgeEvent(tenantId, msg.getEdgeId());
281 } 281 }
282 282
283 public static class ActorCreator extends ContextBasedCreator { 283 public static class ActorCreator extends ContextBasedCreator {
@@ -135,7 +135,7 @@ public class AuthController extends BaseController { @@ -135,7 +135,7 @@ public class AuthController extends BaseController {
135 } 135 }
136 } 136 }
137 137
138 - @RequestMapping(value = "/noauth/activate", params = { "activateToken" }, method = RequestMethod.GET) 138 + @RequestMapping(value = "/noauth/activate", params = {"activateToken"}, method = RequestMethod.GET)
139 public ResponseEntity<String> checkActivateToken( 139 public ResponseEntity<String> checkActivateToken(
140 @RequestParam(value = "activateToken") String activateToken) { 140 @RequestParam(value = "activateToken") String activateToken) {
141 HttpHeaders headers = new HttpHeaders(); 141 HttpHeaders headers = new HttpHeaders();
@@ -159,7 +159,7 @@ public class AuthController extends BaseController { @@ -159,7 +159,7 @@ public class AuthController extends BaseController {
159 159
160 @RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST) 160 @RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST)
161 @ResponseStatus(value = HttpStatus.OK) 161 @ResponseStatus(value = HttpStatus.OK)
162 - public void requestResetPasswordByEmail ( 162 + public void requestResetPasswordByEmail(
163 @RequestBody JsonNode resetPasswordByEmailRequest, 163 @RequestBody JsonNode resetPasswordByEmailRequest,
164 HttpServletRequest request) throws ThingsboardException { 164 HttpServletRequest request) throws ThingsboardException {
165 try { 165 try {
@@ -170,13 +170,13 @@ public class AuthController extends BaseController { @@ -170,13 +170,13 @@ public class AuthController extends BaseController {
170 String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl, 170 String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
171 userCredentials.getResetToken()); 171 userCredentials.getResetToken());
172 172
173 - mailService.sendResetPasswordEmail(resetUrl, email); 173 + mailService.sendResetPasswordEmailAsync(resetUrl, email);
174 } catch (Exception e) { 174 } catch (Exception e) {
175 - throw handleException(e); 175 + log.warn("Error occurred: {}", e.getMessage());
176 } 176 }
177 } 177 }
178 178
179 - @RequestMapping(value = "/noauth/resetPassword", params = { "resetToken" }, method = RequestMethod.GET) 179 + @RequestMapping(value = "/noauth/resetPassword", params = {"resetToken"}, method = RequestMethod.GET)
180 public ResponseEntity<String> checkResetToken( 180 public ResponseEntity<String> checkResetToken(
181 @RequestParam(value = "resetToken") String resetToken) { 181 @RequestParam(value = "resetToken") String resetToken) {
182 HttpHeaders headers = new HttpHeaders(); 182 HttpHeaders headers = new HttpHeaders();
@@ -37,12 +37,12 @@ import org.thingsboard.server.common.data.EdgeUtils; @@ -37,12 +37,12 @@ import org.thingsboard.server.common.data.EdgeUtils;
37 import org.thingsboard.server.common.data.EntityType; 37 import org.thingsboard.server.common.data.EntityType;
38 import org.thingsboard.server.common.data.EntityView; 38 import org.thingsboard.server.common.data.EntityView;
39 import org.thingsboard.server.common.data.EntityViewInfo; 39 import org.thingsboard.server.common.data.EntityViewInfo;
40 -import org.thingsboard.server.common.data.OtaPackage;  
41 -import org.thingsboard.server.common.data.OtaPackageInfo;  
42 import org.thingsboard.server.common.data.HasName; 40 import org.thingsboard.server.common.data.HasName;
43 import org.thingsboard.server.common.data.HasTenantId; 41 import org.thingsboard.server.common.data.HasTenantId;
44 -import org.thingsboard.server.common.data.TbResourceInfo; 42 +import org.thingsboard.server.common.data.OtaPackage;
  43 +import org.thingsboard.server.common.data.OtaPackageInfo;
45 import org.thingsboard.server.common.data.TbResource; 44 import org.thingsboard.server.common.data.TbResource;
  45 +import org.thingsboard.server.common.data.TbResourceInfo;
46 import org.thingsboard.server.common.data.Tenant; 46 import org.thingsboard.server.common.data.Tenant;
47 import org.thingsboard.server.common.data.TenantInfo; 47 import org.thingsboard.server.common.data.TenantInfo;
48 import org.thingsboard.server.common.data.TenantProfile; 48 import org.thingsboard.server.common.data.TenantProfile;
@@ -69,20 +69,23 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -69,20 +69,23 @@ import org.thingsboard.server.common.data.id.EntityId;
69 import org.thingsboard.server.common.data.id.EntityIdFactory; 69 import org.thingsboard.server.common.data.id.EntityIdFactory;
70 import org.thingsboard.server.common.data.id.EntityViewId; 70 import org.thingsboard.server.common.data.id.EntityViewId;
71 import org.thingsboard.server.common.data.id.OtaPackageId; 71 import org.thingsboard.server.common.data.id.OtaPackageId;
72 -import org.thingsboard.server.common.data.id.TbResourceId; 72 +import org.thingsboard.server.common.data.id.RpcId;
73 import org.thingsboard.server.common.data.id.RuleChainId; 73 import org.thingsboard.server.common.data.id.RuleChainId;
74 import org.thingsboard.server.common.data.id.RuleNodeId; 74 import org.thingsboard.server.common.data.id.RuleNodeId;
  75 +import org.thingsboard.server.common.data.id.TbResourceId;
75 import org.thingsboard.server.common.data.id.TenantId; 76 import org.thingsboard.server.common.data.id.TenantId;
76 import org.thingsboard.server.common.data.id.TenantProfileId; 77 import org.thingsboard.server.common.data.id.TenantProfileId;
77 import org.thingsboard.server.common.data.id.UserId; 78 import org.thingsboard.server.common.data.id.UserId;
78 import org.thingsboard.server.common.data.id.WidgetTypeId; 79 import org.thingsboard.server.common.data.id.WidgetTypeId;
79 import org.thingsboard.server.common.data.id.WidgetsBundleId; 80 import org.thingsboard.server.common.data.id.WidgetsBundleId;
  81 +import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId;
80 import org.thingsboard.server.common.data.page.PageLink; 82 import org.thingsboard.server.common.data.page.PageLink;
81 import org.thingsboard.server.common.data.page.SortOrder; 83 import org.thingsboard.server.common.data.page.SortOrder;
82 import org.thingsboard.server.common.data.page.TimePageLink; 84 import org.thingsboard.server.common.data.page.TimePageLink;
83 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 85 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
84 import org.thingsboard.server.common.data.plugin.ComponentType; 86 import org.thingsboard.server.common.data.plugin.ComponentType;
85 import org.thingsboard.server.common.data.relation.EntityRelation; 87 import org.thingsboard.server.common.data.relation.EntityRelation;
  88 +import org.thingsboard.server.common.data.rpc.Rpc;
86 import org.thingsboard.server.common.data.rule.RuleChain; 89 import org.thingsboard.server.common.data.rule.RuleChain;
87 import org.thingsboard.server.common.data.rule.RuleChainType; 90 import org.thingsboard.server.common.data.rule.RuleChainType;
88 import org.thingsboard.server.common.data.rule.RuleNode; 91 import org.thingsboard.server.common.data.rule.RuleNode;
@@ -101,11 +104,12 @@ import org.thingsboard.server.dao.edge.EdgeService; @@ -101,11 +104,12 @@ import org.thingsboard.server.dao.edge.EdgeService;
101 import org.thingsboard.server.dao.entityview.EntityViewService; 104 import org.thingsboard.server.dao.entityview.EntityViewService;
102 import org.thingsboard.server.dao.exception.DataValidationException; 105 import org.thingsboard.server.dao.exception.DataValidationException;
103 import org.thingsboard.server.dao.exception.IncorrectParameterException; 106 import org.thingsboard.server.dao.exception.IncorrectParameterException;
104 -import org.thingsboard.server.dao.ota.OtaPackageService;  
105 import org.thingsboard.server.dao.model.ModelConstants; 107 import org.thingsboard.server.dao.model.ModelConstants;
106 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 108 import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
107 import org.thingsboard.server.dao.oauth2.OAuth2Service; 109 import org.thingsboard.server.dao.oauth2.OAuth2Service;
  110 +import org.thingsboard.server.dao.ota.OtaPackageService;
108 import org.thingsboard.server.dao.relation.RelationService; 111 import org.thingsboard.server.dao.relation.RelationService;
  112 +import org.thingsboard.server.dao.rpc.RpcService;
109 import org.thingsboard.server.dao.rule.RuleChainService; 113 import org.thingsboard.server.dao.rule.RuleChainService;
110 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 114 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
111 import org.thingsboard.server.dao.tenant.TenantProfileService; 115 import org.thingsboard.server.dao.tenant.TenantProfileService;
@@ -120,11 +124,10 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider; @@ -120,11 +124,10 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
120 import org.thingsboard.server.queue.util.TbCoreComponent; 124 import org.thingsboard.server.queue.util.TbCoreComponent;
121 import org.thingsboard.server.service.action.RuleEngineEntityActionService; 125 import org.thingsboard.server.service.action.RuleEngineEntityActionService;
122 import org.thingsboard.server.service.component.ComponentDiscoveryService; 126 import org.thingsboard.server.service.component.ComponentDiscoveryService;
123 -import org.thingsboard.server.service.ota.OtaPackageStateService;  
124 import org.thingsboard.server.service.edge.EdgeNotificationService; 127 import org.thingsboard.server.service.edge.EdgeNotificationService;
125 -import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;  
126 -import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; 128 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
127 import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository; 129 import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository;
  130 +import org.thingsboard.server.service.ota.OtaPackageStateService;
128 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 131 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
129 import org.thingsboard.server.service.queue.TbClusterService; 132 import org.thingsboard.server.service.queue.TbClusterService;
130 import org.thingsboard.server.service.resource.TbResourceService; 133 import org.thingsboard.server.service.resource.TbResourceService;
@@ -138,6 +141,8 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; @@ -138,6 +141,8 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
138 141
139 import javax.mail.MessagingException; 142 import javax.mail.MessagingException;
140 import javax.servlet.http.HttpServletResponse; 143 import javax.servlet.http.HttpServletResponse;
  144 +import java.util.ArrayList;
  145 +import java.util.Collections;
141 import java.util.List; 146 import java.util.List;
142 import java.util.Optional; 147 import java.util.Optional;
143 import java.util.Set; 148 import java.util.Set;
@@ -153,6 +158,8 @@ public abstract class BaseController { @@ -153,6 +158,8 @@ public abstract class BaseController {
153 protected static final String DEFAULT_DASHBOARD = "defaultDashboardId"; 158 protected static final String DEFAULT_DASHBOARD = "defaultDashboardId";
154 protected static final String HOME_DASHBOARD = "homeDashboardId"; 159 protected static final String HOME_DASHBOARD = "homeDashboardId";
155 160
  161 + private static final int DEFAULT_PAGE_SIZE = 1000;
  162 +
156 private static final ObjectMapper json = new ObjectMapper(); 163 private static final ObjectMapper json = new ObjectMapper();
157 164
158 @Autowired 165 @Autowired
@@ -246,6 +253,9 @@ public abstract class BaseController { @@ -246,6 +253,9 @@ public abstract class BaseController {
246 protected OtaPackageStateService otaPackageStateService; 253 protected OtaPackageStateService otaPackageStateService;
247 254
248 @Autowired 255 @Autowired
  256 + protected RpcService rpcService;
  257 +
  258 + @Autowired
249 protected TbQueueProducerProvider producerProvider; 259 protected TbQueueProducerProvider producerProvider;
250 260
251 @Autowired 261 @Autowired
@@ -264,10 +274,7 @@ public abstract class BaseController { @@ -264,10 +274,7 @@ public abstract class BaseController {
264 protected EdgeNotificationService edgeNotificationService; 274 protected EdgeNotificationService edgeNotificationService;
265 275
266 @Autowired(required = false) 276 @Autowired(required = false)
267 - protected SyncEdgeService syncEdgeService;  
268 -  
269 - @Autowired(required = false)  
270 - protected EdgeGrpcService edgeGrpcService; 277 + protected EdgeRpcService edgeGrpcService;
271 278
272 @Autowired 279 @Autowired
273 protected RuleEngineEntityActionService ruleEngineEntityActionService; 280 protected RuleEngineEntityActionService ruleEngineEntityActionService;
@@ -786,6 +793,18 @@ public abstract class BaseController { @@ -786,6 +793,18 @@ public abstract class BaseController {
786 } 793 }
787 } 794 }
788 795
  796 + Rpc checkRpcId(RpcId rpcId, Operation operation) throws ThingsboardException {
  797 + try {
  798 + validateId(rpcId, "Incorrect rpcId " + rpcId);
  799 + Rpc rpc = rpcService.findById(getCurrentUser().getTenantId(), rpcId);
  800 + checkNotNull(rpc);
  801 + accessControlService.checkPermission(getCurrentUser(), Resource.RPC, operation, rpcId, rpc);
  802 + return rpc;
  803 + } catch (Exception e) {
  804 + throw handleException(e, false);
  805 + }
  806 + }
  807 +
789 @SuppressWarnings("unchecked") 808 @SuppressWarnings("unchecked")
790 protected <I extends EntityId> I emptyId(EntityType entityType) { 809 protected <I extends EntityId> I emptyId(EntityType entityType) {
791 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); 810 return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
@@ -899,11 +918,14 @@ public abstract class BaseController { @@ -899,11 +918,14 @@ public abstract class BaseController {
899 if (!edgesEnabled) { 918 if (!edgesEnabled) {
900 return null; 919 return null;
901 } 920 }
902 - List<EdgeId> result = null;  
903 - try {  
904 - result = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId).get();  
905 - } catch (Exception e) {  
906 - log.error("[{}] can't find related edge ids for entity [{}]", tenantId, entityId, e); 921 + if (EntityType.EDGE.equals(entityId.getEntityType())) {
  922 + return Collections.singletonList(new EdgeId(entityId.getId()));
  923 + }
  924 + PageDataIterableByTenantIdEntityId<EdgeId> relatedEdgeIdsIterator =
  925 + new PageDataIterableByTenantIdEntityId<>(edgeService::findRelatedEdgeIdsByEntityId, tenantId, entityId, DEFAULT_PAGE_SIZE);
  926 + List<EdgeId> result = new ArrayList<>();
  927 + for(EdgeId edgeId : relatedEdgeIdsIterator) {
  928 + result.add(edgeId);
907 } 929 }
908 return result; 930 return result;
909 } 931 }
@@ -782,15 +782,17 @@ public class DeviceController extends BaseController { @@ -782,15 +782,17 @@ public class DeviceController extends BaseController {
782 } 782 }
783 783
784 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 784 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
785 - @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET) 785 + @RequestMapping(value = "/devices/count/{otaPackageType}/{deviceProfileId}", method = RequestMethod.GET)
786 @ResponseBody 786 @ResponseBody
787 - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,  
788 - @RequestParam String deviceProfileId) throws ThingsboardException { 787 + public Long countByDeviceProfileAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType,
  788 + @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException {
789 checkParameter("OtaPackageType", otaPackageType); 789 checkParameter("OtaPackageType", otaPackageType);
790 checkParameter("DeviceProfileId", deviceProfileId); 790 checkParameter("DeviceProfileId", deviceProfileId);
791 try { 791 try {
792 return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( 792 return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(
793 - getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType)); 793 + getTenantId(),
  794 + new DeviceProfileId(UUID.fromString(deviceProfileId)),
  795 + OtaPackageType.valueOf(otaPackageType));
794 } catch (Exception e) { 796 } catch (Exception e) {
795 throw handleException(e); 797 throw handleException(e);
796 } 798 }
@@ -49,7 +49,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; @@ -49,7 +49,6 @@ import org.thingsboard.server.dao.exception.DataValidationException;
49 import org.thingsboard.server.dao.exception.IncorrectParameterException; 49 import org.thingsboard.server.dao.exception.IncorrectParameterException;
50 import org.thingsboard.server.dao.model.ModelConstants; 50 import org.thingsboard.server.dao.model.ModelConstants;
51 import org.thingsboard.server.queue.util.TbCoreComponent; 51 import org.thingsboard.server.queue.util.TbCoreComponent;
52 -import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession;  
53 import org.thingsboard.server.service.security.model.SecurityUser; 52 import org.thingsboard.server.service.security.model.SecurityUser;
54 import org.thingsboard.server.service.security.permission.Operation; 53 import org.thingsboard.server.service.security.permission.Operation;
55 import org.thingsboard.server.service.security.permission.Resource; 54 import org.thingsboard.server.service.security.permission.Resource;
@@ -128,7 +127,7 @@ public class EdgeController extends BaseController { @@ -128,7 +127,7 @@ public class EdgeController extends BaseController {
128 accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, 127 accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
129 edge.getId(), edge); 128 edge.getId(), edge);
130 129
131 - Edge savedEdge = checkNotNull(edgeService.saveEdge(edge)); 130 + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true));
132 131
133 if (created) { 132 if (created) {
134 ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId()); 133 ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId());
@@ -536,9 +535,7 @@ public class EdgeController extends BaseController { @@ -536,9 +535,7 @@ public class EdgeController extends BaseController {
536 edgeId = checkNotNull(edgeId); 535 edgeId = checkNotNull(edgeId);
537 SecurityUser user = getCurrentUser(); 536 SecurityUser user = getCurrentUser();
538 TenantId tenantId = user.getTenantId(); 537 TenantId tenantId = user.getTenantId();
539 - EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(tenantId, edgeId);  
540 - Edge edge = session.getEdge();  
541 - syncEdgeService.sync(tenantId, edge); 538 + edgeGrpcService.startSyncProcess(tenantId, edgeId);
542 } else { 539 } else {
543 throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); 540 throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
544 } 541 }
@@ -32,12 +32,13 @@ import org.springframework.web.multipart.MultipartFile; @@ -32,12 +32,13 @@ import org.springframework.web.multipart.MultipartFile;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.OtaPackage; 33 import org.thingsboard.server.common.data.OtaPackage;
34 import org.thingsboard.server.common.data.OtaPackageInfo; 34 import org.thingsboard.server.common.data.OtaPackageInfo;
  35 +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
35 import org.thingsboard.server.common.data.audit.ActionType; 36 import org.thingsboard.server.common.data.audit.ActionType;
36 import org.thingsboard.server.common.data.exception.ThingsboardException; 37 import org.thingsboard.server.common.data.exception.ThingsboardException;
37 -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;  
38 -import org.thingsboard.server.common.data.ota.OtaPackageType;  
39 import org.thingsboard.server.common.data.id.DeviceProfileId; 38 import org.thingsboard.server.common.data.id.DeviceProfileId;
40 import org.thingsboard.server.common.data.id.OtaPackageId; 39 import org.thingsboard.server.common.data.id.OtaPackageId;
  40 +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
  41 +import org.thingsboard.server.common.data.ota.OtaPackageType;
41 import org.thingsboard.server.common.data.page.PageData; 42 import org.thingsboard.server.common.data.page.PageData;
42 import org.thingsboard.server.common.data.page.PageLink; 43 import org.thingsboard.server.common.data.page.PageLink;
43 import org.thingsboard.server.queue.util.TbCoreComponent; 44 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -109,12 +110,12 @@ public class OtaPackageController extends BaseController { @@ -109,12 +110,12 @@ public class OtaPackageController extends BaseController {
109 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 110 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
110 @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) 111 @RequestMapping(value = "/otaPackage", method = RequestMethod.POST)
111 @ResponseBody 112 @ResponseBody
112 - public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException { 113 + public OtaPackageInfo saveOtaPackageInfo(@RequestBody SaveOtaPackageInfoRequest otaPackageInfo) throws ThingsboardException {
113 boolean created = otaPackageInfo.getId() == null; 114 boolean created = otaPackageInfo.getId() == null;
114 try { 115 try {
115 otaPackageInfo.setTenantId(getTenantId()); 116 otaPackageInfo.setTenantId(getTenantId());
116 checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE); 117 checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE);
117 - OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(otaPackageInfo); 118 + OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(otaPackageInfo), otaPackageInfo.isUsesUrl());
118 logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo, 119 logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo,
119 null, created ? ActionType.ADDED : ActionType.UPDATED, null); 120 null, created ? ActionType.ADDED : ActionType.UPDATED, null);
120 return savedOtaPackageInfo; 121 return savedOtaPackageInfo;
@@ -29,17 +29,24 @@ import org.springframework.web.bind.annotation.PathVariable; @@ -29,17 +29,24 @@ import org.springframework.web.bind.annotation.PathVariable;
29 import org.springframework.web.bind.annotation.RequestBody; 29 import org.springframework.web.bind.annotation.RequestBody;
30 import org.springframework.web.bind.annotation.RequestMapping; 30 import org.springframework.web.bind.annotation.RequestMapping;
31 import org.springframework.web.bind.annotation.RequestMethod; 31 import org.springframework.web.bind.annotation.RequestMethod;
  32 +import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 33 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
34 import org.springframework.web.context.request.async.DeferredResult; 35 import org.springframework.web.context.request.async.DeferredResult;
35 import org.thingsboard.rule.engine.api.RpcError; 36 import org.thingsboard.rule.engine.api.RpcError;
  37 +import org.thingsboard.server.common.data.DataConstants;
36 import org.thingsboard.server.common.data.audit.ActionType; 38 import org.thingsboard.server.common.data.audit.ActionType;
37 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 39 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 40 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 import org.thingsboard.server.common.data.id.DeviceId; 41 import org.thingsboard.server.common.data.id.DeviceId;
40 import org.thingsboard.server.common.data.id.EntityId; 42 import org.thingsboard.server.common.data.id.EntityId;
  43 +import org.thingsboard.server.common.data.id.RpcId;
41 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
42 import org.thingsboard.server.common.data.id.UUIDBased; 45 import org.thingsboard.server.common.data.id.UUIDBased;
  46 +import org.thingsboard.server.common.data.page.PageData;
  47 +import org.thingsboard.server.common.data.page.PageLink;
  48 +import org.thingsboard.server.common.data.rpc.Rpc;
  49 +import org.thingsboard.server.common.data.rpc.RpcStatus;
43 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 50 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
44 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 51 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
45 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -93,6 +100,52 @@ public class RpcController extends BaseController { @@ -93,6 +100,52 @@ public class RpcController extends BaseController {
93 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody); 100 return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody);
94 } 101 }
95 102
  103 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  104 + @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
  105 + @ResponseBody
  106 + public Rpc getPersistedRpc(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  107 + checkParameter("RpcId", strRpc);
  108 + try {
  109 + RpcId rpcId = new RpcId(UUID.fromString(strRpc));
  110 + return checkRpcId(rpcId, Operation.READ);
  111 + } catch (Exception e) {
  112 + throw handleException(e);
  113 + }
  114 + }
  115 +
  116 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  117 + @RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
  118 + @ResponseBody
  119 + public PageData<Rpc> getPersistedRpcByDevice(@PathVariable("deviceId") String strDeviceId,
  120 + @RequestParam int pageSize,
  121 + @RequestParam int page,
  122 + @RequestParam RpcStatus rpcStatus,
  123 + @RequestParam(required = false) String textSearch,
  124 + @RequestParam(required = false) String sortProperty,
  125 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  126 + checkParameter("DeviceId", strDeviceId);
  127 + try {
  128 + TenantId tenantId = getCurrentUser().getTenantId();
  129 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  130 + DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
  131 + return checkNotNull(rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink));
  132 + } catch (Exception e) {
  133 + throw handleException(e);
  134 + }
  135 + }
  136 +
  137 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  138 + @RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
  139 + @ResponseBody
  140 + public void deleteResource(@PathVariable("rpcId") String strRpc) throws ThingsboardException {
  141 + checkParameter("RpcId", strRpc);
  142 + try {
  143 + rpcService.deleteRpc(getTenantId(), new RpcId(UUID.fromString(strRpc)));
  144 + } catch (Exception e) {
  145 + throw handleException(e);
  146 + }
  147 + }
  148 +
96 private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { 149 private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException {
97 try { 150 try {
98 JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); 151 JsonNode rpcRequestBody = jsonMapper.readTree(requestBody);
@@ -103,6 +156,7 @@ public class RpcController extends BaseController { @@ -103,6 +156,7 @@ public class RpcController extends BaseController {
103 long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout; 156 long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout;
104 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); 157 long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout);
105 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID(); 158 UUID rpcRequestUUID = rpcRequestBody.has("requestUUID") ? UUID.fromString(rpcRequestBody.get("requestUUID").asText()) : UUID.randomUUID();
  159 + boolean persisted = rpcRequestBody.has(DataConstants.PERSISTENT) && rpcRequestBody.get(DataConstants.PERSISTENT).asBoolean();
106 accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() { 160 accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() {
107 @Override 161 @Override
108 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { 162 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
@@ -111,7 +165,8 @@ public class RpcController extends BaseController { @@ -111,7 +165,8 @@ public class RpcController extends BaseController {
111 deviceId, 165 deviceId,
112 oneWay, 166 oneWay,
113 expTime, 167 expTime,
114 - body 168 + body,
  169 + persisted
115 ); 170 );
116 deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser); 171 deviceRpcService.processRestApiRpcRequest(rpcRequest, fromDeviceRpcResponse -> reply(new LocalRequestMetaData(rpcRequest, currentUser, result), fromDeviceRpcResponse), currentUser);
117 } 172 }
@@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.id.RuleChainId; @@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
50 import org.thingsboard.server.common.data.id.RuleNodeId; 50 import org.thingsboard.server.common.data.id.RuleNodeId;
51 import org.thingsboard.server.common.data.id.TenantId; 51 import org.thingsboard.server.common.data.id.TenantId;
52 import org.thingsboard.server.common.data.page.PageData; 52 import org.thingsboard.server.common.data.page.PageData;
  53 +import org.thingsboard.server.common.data.page.PageDataIterableByTenant;
53 import org.thingsboard.server.common.data.page.PageLink; 54 import org.thingsboard.server.common.data.page.PageLink;
54 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 55 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
55 import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; 56 import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
@@ -70,10 +71,12 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; @@ -70,10 +71,12 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
70 import org.thingsboard.server.service.security.permission.Operation; 71 import org.thingsboard.server.service.security.permission.Operation;
71 import org.thingsboard.server.service.security.permission.Resource; 72 import org.thingsboard.server.service.security.permission.Resource;
72 73
  74 +import java.util.ArrayList;
73 import java.util.List; 75 import java.util.List;
74 import java.util.Map; 76 import java.util.Map;
75 import java.util.Set; 77 import java.util.Set;
76 import java.util.concurrent.ConcurrentMap; 78 import java.util.concurrent.ConcurrentMap;
  79 +import java.util.concurrent.TimeUnit;
77 import java.util.stream.Collectors; 80 import java.util.stream.Collectors;
78 81
79 @Slf4j 82 @Slf4j
@@ -85,7 +88,10 @@ public class RuleChainController extends BaseController { @@ -85,7 +88,10 @@ public class RuleChainController extends BaseController {
85 public static final String RULE_CHAIN_ID = "ruleChainId"; 88 public static final String RULE_CHAIN_ID = "ruleChainId";
86 public static final String RULE_NODE_ID = "ruleNodeId"; 89 public static final String RULE_NODE_ID = "ruleNodeId";
87 90
  91 + private static final int DEFAULT_PAGE_SIZE = 1000;
  92 +
88 private static final ObjectMapper objectMapper = new ObjectMapper(); 93 private static final ObjectMapper objectMapper = new ObjectMapper();
  94 + public static final int TIMEOUT = 20;
89 95
90 @Autowired 96 @Autowired
91 private InstallScripts installScripts; 97 private InstallScripts installScripts;
@@ -388,25 +394,25 @@ public class RuleChainController extends BaseController { @@ -388,25 +394,25 @@ public class RuleChainController extends BaseController {
388 TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); 394 TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data);
389 switch (scriptType) { 395 switch (scriptType) {
390 case "update": 396 case "update":
391 - output = msgToOutput(engine.executeUpdate(inMsg)); 397 + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
392 break; 398 break;
393 case "generate": 399 case "generate":
394 - output = msgToOutput(engine.executeGenerate(inMsg)); 400 + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
395 break; 401 break;
396 case "filter": 402 case "filter":
397 - boolean result = engine.executeFilter(inMsg); 403 + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
398 output = Boolean.toString(result); 404 output = Boolean.toString(result);
399 break; 405 break;
400 case "switch": 406 case "switch":
401 - Set<String> states = engine.executeSwitch(inMsg); 407 + Set<String> states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
402 output = objectMapper.writeValueAsString(states); 408 output = objectMapper.writeValueAsString(states);
403 break; 409 break;
404 case "json": 410 case "json":
405 - JsonNode json = engine.executeJson(inMsg); 411 + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
406 output = objectMapper.writeValueAsString(json); 412 output = objectMapper.writeValueAsString(json);
407 break; 413 break;
408 case "string": 414 case "string":
409 - output = engine.executeToString(inMsg); 415 + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
410 break; 416 break;
411 default: 417 default:
412 throw new IllegalArgumentException("Unsupported script type: " + scriptType); 418 throw new IllegalArgumentException("Unsupported script type: " + scriptType);
@@ -477,7 +483,7 @@ public class RuleChainController extends BaseController { @@ -477,7 +483,7 @@ public class RuleChainController extends BaseController {
477 return objectMapper.writeValueAsString(resultNode); 483 return objectMapper.writeValueAsString(resultNode);
478 } 484 }
479 485
480 - private JsonNode convertMsgToOut(TbMsg msg) throws Exception{ 486 + private JsonNode convertMsgToOut(TbMsg msg) throws Exception {
481 ObjectNode msgData = objectMapper.createObjectNode(); 487 ObjectNode msgData = objectMapper.createObjectNode();
482 if (!StringUtils.isEmpty(msg.getData())) { 488 if (!StringUtils.isEmpty(msg.getData())) {
483 msgData.set("msg", objectMapper.readTree(msg.getData())); 489 msgData.set("msg", objectMapper.readTree(msg.getData()));
@@ -632,13 +638,20 @@ public class RuleChainController extends BaseController { @@ -632,13 +638,20 @@ public class RuleChainController extends BaseController {
632 } 638 }
633 } 639 }
634 640
  641 + // TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign
635 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 642 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
636 @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET) 643 @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET)
637 @ResponseBody 644 @ResponseBody
638 public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException { 645 public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException {
639 try { 646 try {
640 TenantId tenantId = getCurrentUser().getTenantId(); 647 TenantId tenantId = getCurrentUser().getTenantId();
641 - return checkNotNull(ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId)).get(); 648 + List<RuleChain> result = new ArrayList<>();
  649 + PageDataIterableByTenant<RuleChain> autoAssignRuleChainsIterator =
  650 + new PageDataIterableByTenant<>(ruleChainService::findAutoAssignToEdgeRuleChainsByTenantId, tenantId, DEFAULT_PAGE_SIZE);
  651 + for (RuleChain ruleChain : autoAssignRuleChainsIterator) {
  652 + result.add(ruleChain);
  653 + }
  654 + return checkNotNull(result);
642 } catch (Exception e) { 655 } catch (Exception e) {
643 throw handleException(e); 656 throw handleException(e);
644 } 657 }
@@ -95,9 +95,7 @@ public class TenantController extends BaseController { @@ -95,9 +95,7 @@ public class TenantController extends BaseController {
95 tenant = checkNotNull(tenantService.saveTenant(tenant)); 95 tenant = checkNotNull(tenantService.saveTenant(tenant));
96 if (newTenant) { 96 if (newTenant) {
97 installScripts.createDefaultRuleChains(tenant.getId()); 97 installScripts.createDefaultRuleChains(tenant.getId());
98 - if (edgesEnabled) {  
99 - installScripts.createDefaultEdgeRuleChains(tenant.getId());  
100 - } 98 + installScripts.createDefaultEdgeRuleChains(tenant.getId());
101 } 99 }
102 tenantProfileCache.evict(tenant.getId()); 100 tenantProfileCache.evict(tenant.getId());
103 tbClusterService.onTenantChange(tenant, null); 101 tbClusterService.onTenantChange(tenant, null);
@@ -25,9 +25,12 @@ import org.springframework.security.authentication.BadCredentialsException; @@ -25,9 +25,12 @@ import org.springframework.security.authentication.BadCredentialsException;
25 import org.springframework.security.authentication.DisabledException; 25 import org.springframework.security.authentication.DisabledException;
26 import org.springframework.security.authentication.LockedException; 26 import org.springframework.security.authentication.LockedException;
27 import org.springframework.security.core.AuthenticationException; 27 import org.springframework.security.core.AuthenticationException;
  28 +import org.springframework.security.core.userdetails.UsernameNotFoundException;
28 import org.springframework.security.web.access.AccessDeniedHandler; 29 import org.springframework.security.web.access.AccessDeniedHandler;
29 -import org.springframework.stereotype.Component; 30 +import org.springframework.web.bind.annotation.ExceptionHandler;
  31 +import org.springframework.web.bind.annotation.RestControllerAdvice;
30 import org.springframework.web.client.HttpClientErrorException; 32 import org.springframework.web.client.HttpClientErrorException;
  33 +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
31 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 34 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
32 import org.thingsboard.server.common.data.exception.ThingsboardException; 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
33 import org.thingsboard.server.common.msg.tools.TbRateLimitsException; 36 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@@ -40,14 +43,15 @@ import javax.servlet.http.HttpServletRequest; @@ -40,14 +43,15 @@ import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse; 43 import javax.servlet.http.HttpServletResponse;
41 import java.io.IOException; 44 import java.io.IOException;
42 45
43 -@Component  
44 @Slf4j 46 @Slf4j
45 -public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { 47 +@RestControllerAdvice
  48 +public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler {
46 49
47 @Autowired 50 @Autowired
48 private ObjectMapper mapper; 51 private ObjectMapper mapper;
49 52
50 @Override 53 @Override
  54 + @ExceptionHandler(AccessDeniedException.class)
51 public void handle(HttpServletRequest request, HttpServletResponse response, 55 public void handle(HttpServletRequest request, HttpServletResponse response,
52 AccessDeniedException accessDeniedException) throws IOException, 56 AccessDeniedException accessDeniedException) throws IOException,
53 ServletException { 57 ServletException {
@@ -60,6 +64,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -60,6 +64,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
60 } 64 }
61 } 65 }
62 66
  67 + @ExceptionHandler(Exception.class)
63 public void handle(Exception exception, HttpServletResponse response) { 68 public void handle(Exception exception, HttpServletResponse response) {
64 log.debug("Processing exception {}", exception.getMessage(), exception); 69 log.debug("Processing exception {}", exception.getMessage(), exception);
65 if (!response.isCommitted()) { 70 if (!response.isCommitted()) {
@@ -148,7 +153,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -148,7 +153,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
148 153
149 private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException { 154 private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException {
150 response.setStatus(HttpStatus.UNAUTHORIZED.value()); 155 response.setStatus(HttpStatus.UNAUTHORIZED.value());
151 - if (authenticationException instanceof BadCredentialsException) { 156 + if (authenticationException instanceof BadCredentialsException || authenticationException instanceof UsernameNotFoundException) {
152 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); 157 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
153 } else if (authenticationException instanceof DisabledException) { 158 } else if (authenticationException instanceof DisabledException) {
154 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); 159 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
@@ -159,7 +164,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -159,7 +164,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
159 } else if (authenticationException instanceof AuthMethodNotSupportedException) { 164 } else if (authenticationException instanceof AuthMethodNotSupportedException) {
160 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); 165 mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
161 } else if (authenticationException instanceof UserPasswordExpiredException) { 166 } else if (authenticationException instanceof UserPasswordExpiredException) {
162 - UserPasswordExpiredException expiredException = (UserPasswordExpiredException)authenticationException; 167 + UserPasswordExpiredException expiredException = (UserPasswordExpiredException) authenticationException;
163 String resetToken = expiredException.getResetToken(); 168 String resetToken = expiredException.getResetToken();
164 mapper.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken)); 169 mapper.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken));
165 } else { 170 } else {
@@ -15,9 +15,7 @@ @@ -15,9 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.edge; 16 package org.thingsboard.server.service.edge;
17 17
18 -import com.fasterxml.jackson.core.JsonProcessingException;  
19 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
20 -import com.fasterxml.jackson.databind.ObjectMapper;  
21 import com.google.common.util.concurrent.FutureCallback; 19 import com.google.common.util.concurrent.FutureCallback;
22 import com.google.common.util.concurrent.Futures; 20 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.ListenableFuture; 21 import com.google.common.util.concurrent.ListenableFuture;
@@ -25,45 +23,30 @@ import lombok.extern.slf4j.Slf4j; @@ -25,45 +23,30 @@ import lombok.extern.slf4j.Slf4j;
25 import org.checkerframework.checker.nullness.qual.Nullable; 23 import org.checkerframework.checker.nullness.qual.Nullable;
26 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
28 -import org.thingsboard.server.common.data.EdgeUtils;  
29 -import org.thingsboard.server.common.data.EntityType;  
30 -import org.thingsboard.server.common.data.User;  
31 -import org.thingsboard.server.common.data.alarm.Alarm;  
32 import org.thingsboard.server.common.data.edge.Edge; 26 import org.thingsboard.server.common.data.edge.Edge;
33 import org.thingsboard.server.common.data.edge.EdgeEvent; 27 import org.thingsboard.server.common.data.edge.EdgeEvent;
34 import org.thingsboard.server.common.data.edge.EdgeEventActionType; 28 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
35 import org.thingsboard.server.common.data.edge.EdgeEventType; 29 import org.thingsboard.server.common.data.edge.EdgeEventType;
36 -import org.thingsboard.server.common.data.id.AlarmId;  
37 -import org.thingsboard.server.common.data.id.CustomerId;  
38 import org.thingsboard.server.common.data.id.EdgeId; 30 import org.thingsboard.server.common.data.id.EdgeId;
39 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
40 -import org.thingsboard.server.common.data.id.EntityIdFactory;  
41 import org.thingsboard.server.common.data.id.RuleChainId; 32 import org.thingsboard.server.common.data.id.RuleChainId;
42 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
43 -import org.thingsboard.server.common.data.page.PageData;  
44 -import org.thingsboard.server.common.data.page.PageLink;  
45 -import org.thingsboard.server.common.data.page.TimePageLink;  
46 -import org.thingsboard.server.common.data.relation.EntityRelation;  
47 -import org.thingsboard.server.common.data.rule.RuleChain;  
48 -import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;  
49 import org.thingsboard.server.common.msg.queue.TbCallback; 34 import org.thingsboard.server.common.msg.queue.TbCallback;
50 -import org.thingsboard.server.dao.alarm.AlarmService;  
51 import org.thingsboard.server.dao.edge.EdgeEventService; 35 import org.thingsboard.server.dao.edge.EdgeEventService;
52 import org.thingsboard.server.dao.edge.EdgeService; 36 import org.thingsboard.server.dao.edge.EdgeService;
53 -import org.thingsboard.server.dao.rule.RuleChainService;  
54 -import org.thingsboard.server.dao.user.UserService;  
55 import org.thingsboard.server.gen.transport.TransportProtos; 37 import org.thingsboard.server.gen.transport.TransportProtos;
56 import org.thingsboard.server.queue.util.TbCoreComponent; 38 import org.thingsboard.server.queue.util.TbCoreComponent;
  39 +import org.thingsboard.server.service.edge.rpc.processor.AlarmEdgeProcessor;
  40 +import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
  41 +import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor;
  42 +import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
  43 +import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
57 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 44 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
58 import org.thingsboard.server.service.queue.TbClusterService; 45 import org.thingsboard.server.service.queue.TbClusterService;
59 46
60 import javax.annotation.PostConstruct; 47 import javax.annotation.PostConstruct;
61 import javax.annotation.PreDestroy; 48 import javax.annotation.PreDestroy;
62 import java.io.IOException; 49 import java.io.IOException;
63 -import java.util.ArrayList;  
64 -import java.util.HashSet;  
65 -import java.util.List;  
66 -import java.util.Set;  
67 import java.util.UUID; 50 import java.util.UUID;
68 import java.util.concurrent.ExecutorService; 51 import java.util.concurrent.ExecutorService;
69 import java.util.concurrent.Executors; 52 import java.util.concurrent.Executors;
@@ -73,30 +56,32 @@ import java.util.concurrent.Executors; @@ -73,30 +56,32 @@ import java.util.concurrent.Executors;
73 @Slf4j 56 @Slf4j
74 public class DefaultEdgeNotificationService implements EdgeNotificationService { 57 public class DefaultEdgeNotificationService implements EdgeNotificationService {
75 58
76 - private static final ObjectMapper mapper = new ObjectMapper(); 59 + @Autowired
  60 + private EdgeService edgeService;
77 61
78 - private static final int DEFAULT_LIMIT = 100; 62 + @Autowired
  63 + private EdgeEventService edgeEventService;
79 64
80 @Autowired 65 @Autowired
81 - private EdgeService edgeService; 66 + private TbClusterService clusterService;
82 67
83 @Autowired 68 @Autowired
84 - private AlarmService alarmService; 69 + private DbCallbackExecutorService dbCallbackExecutorService;
85 70
86 @Autowired 71 @Autowired
87 - private UserService userService; 72 + private EdgeProcessor edgeProcessor;
88 73
89 @Autowired 74 @Autowired
90 - private RuleChainService ruleChainService; 75 + private EntityEdgeProcessor entityProcessor;
91 76
92 @Autowired 77 @Autowired
93 - private EdgeEventService edgeEventService; 78 + private AlarmEdgeProcessor alarmProcessor;
94 79
95 @Autowired 80 @Autowired
96 - private TbClusterService clusterService; 81 + private RelationEdgeProcessor relationProcessor;
97 82
98 @Autowired 83 @Autowired
99 - private DbCallbackExecutorService dbCallbackExecutorService; 84 + private CustomerEdgeProcessor customerProcessor;
100 85
101 private ExecutorService tsCallBackExecutor; 86 private ExecutorService tsCallBackExecutor;
102 87
@@ -115,7 +100,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -115,7 +100,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
115 @Override 100 @Override
116 public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { 101 public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
117 edge.setRootRuleChainId(ruleChainId); 102 edge.setRootRuleChainId(ruleChainId);
118 - Edge savedEdge = edgeService.saveEdge(edge); 103 + Edge savedEdge = edgeService.saveEdge(edge, true);
119 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null); 104 saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null);
120 return savedEdge; 105 return savedEdge;
121 } 106 }
@@ -160,7 +145,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -160,7 +145,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
160 EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); 145 EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
161 switch (type) { 146 switch (type) {
162 case EDGE: 147 case EDGE:
163 - processEdge(tenantId, edgeNotificationMsg); 148 + edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg);
164 break; 149 break;
165 case USER: 150 case USER:
166 case ASSET: 151 case ASSET:
@@ -169,20 +154,20 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -169,20 +154,20 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
169 case ENTITY_VIEW: 154 case ENTITY_VIEW:
170 case DASHBOARD: 155 case DASHBOARD:
171 case RULE_CHAIN: 156 case RULE_CHAIN:
172 - processEntity(tenantId, edgeNotificationMsg); 157 + entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
173 break; 158 break;
174 case CUSTOMER: 159 case CUSTOMER:
175 - processCustomer(tenantId, edgeNotificationMsg); 160 + customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg);
176 break; 161 break;
177 case WIDGETS_BUNDLE: 162 case WIDGETS_BUNDLE:
178 case WIDGET_TYPE: 163 case WIDGET_TYPE:
179 - processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg); 164 + entityProcessor.processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg);
180 break; 165 break;
181 case ALARM: 166 case ALARM:
182 - processAlarm(tenantId, edgeNotificationMsg); 167 + alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg);
183 break; 168 break;
184 case RELATION: 169 case RELATION:
185 - processRelation(tenantId, edgeNotificationMsg); 170 + relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg);
186 break; 171 break;
187 default: 172 default:
188 log.debug("Edge event type [{}] is not designed to be pushed to edge", type); 173 log.debug("Edge event type [{}] is not designed to be pushed to edge", type);
@@ -195,311 +180,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { @@ -195,311 +180,6 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
195 } 180 }
196 } 181 }
197 182
198 - private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {  
199 - try {  
200 - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());  
201 - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));  
202 - ListenableFuture<Edge> edgeFuture;  
203 - switch (actionType) {  
204 - case ASSIGNED_TO_CUSTOMER:  
205 - CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);  
206 - edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);  
207 - Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {  
208 - @Override  
209 - public void onSuccess(@Nullable Edge edge) {  
210 - if (edge != null && !customerId.isNullUid()) {  
211 - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);  
212 - PageLink pageLink = new PageLink(DEFAULT_LIMIT);  
213 - PageData<User> pageData;  
214 - do {  
215 - pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);  
216 - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {  
217 - log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());  
218 - for (User user : pageData.getData()) {  
219 - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);  
220 - }  
221 - if (pageData.hasNext()) {  
222 - pageLink = pageLink.nextPageLink();  
223 - }  
224 - }  
225 - } while (pageData != null && pageData.hasNext());  
226 - }  
227 - }  
228 -  
229 - @Override  
230 - public void onFailure(Throwable t) {  
231 - log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);  
232 - }  
233 - }, dbCallbackExecutorService);  
234 - break;  
235 - case UNASSIGNED_FROM_CUSTOMER:  
236 - CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);  
237 - edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);  
238 - Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {  
239 - @Override  
240 - public void onSuccess(@Nullable Edge edge) {  
241 - if (edge != null && !customerIdToDelete.isNullUid()) {  
242 - saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);  
243 - }  
244 - }  
245 -  
246 - @Override  
247 - public void onFailure(Throwable t) {  
248 - log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);  
249 - }  
250 - }, dbCallbackExecutorService);  
251 - break;  
252 - }  
253 - } catch (Exception e) {  
254 - log.error("Exception during processing edge event", e);  
255 - }  
256 - }  
257 -  
258 - private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {  
259 - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());  
260 - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());  
261 - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));  
262 - switch (actionType) {  
263 - case ADDED:  
264 - case UPDATED:  
265 - case DELETED:  
266 - processActionForAllEdges(tenantId, type, actionType, entityId);  
267 - break;  
268 - }  
269 - }  
270 -  
271 - private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {  
272 - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());  
273 - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());  
274 - UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());  
275 - CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());  
276 - switch (actionType) {  
277 - case UPDATED:  
278 - PageLink pageLink = new PageLink(DEFAULT_LIMIT);  
279 - PageData<Edge> pageData;  
280 - do {  
281 - pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);  
282 - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {  
283 - for (Edge edge : pageData.getData()) {  
284 - saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);  
285 - }  
286 - if (pageData.hasNext()) {  
287 - pageLink = pageLink.nextPageLink();  
288 - }  
289 - }  
290 - } while (pageData != null && pageData.hasNext());  
291 - break;  
292 - case DELETED:  
293 - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));  
294 - saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);  
295 - break;  
296 - }  
297 - }  
298 -  
299 - private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {  
300 - EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());  
301 - EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());  
302 - EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,  
303 - new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));  
304 - EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));  
305 - ListenableFuture<List<EdgeId>> edgeIdsFuture;  
306 - switch (actionType) {  
307 - case ADDED: // used only for USER entity  
308 - case UPDATED:  
309 - case CREDENTIALS_UPDATED:  
310 - edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);  
311 - Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {  
312 - @Override  
313 - public void onSuccess(@Nullable List<EdgeId> edgeIds) {  
314 - if (edgeIds != null && !edgeIds.isEmpty()) {  
315 - for (EdgeId edgeId : edgeIds) {  
316 - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);  
317 - }  
318 - }  
319 - }  
320 - @Override  
321 - public void onFailure(Throwable throwable) {  
322 - log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);  
323 - }  
324 - }, dbCallbackExecutorService);  
325 - break;  
326 - case ASSIGNED_TO_CUSTOMER:  
327 - case UNASSIGNED_FROM_CUSTOMER:  
328 - edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);  
329 - Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {  
330 - @Override  
331 - public void onSuccess(@Nullable List<EdgeId> edgeIds) {  
332 - if (edgeIds != null && !edgeIds.isEmpty()) {  
333 - for (EdgeId edgeId : edgeIds) {  
334 - try {  
335 - CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);  
336 - ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId);  
337 - Futures.addCallback(future, new FutureCallback<Edge>() {  
338 - @Override  
339 - public void onSuccess(@Nullable Edge edge) {  
340 - if (edge != null && edge.getCustomerId() != null &&  
341 - !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {  
342 - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);  
343 - }  
344 - }  
345 - @Override  
346 - public void onFailure(Throwable throwable) {  
347 - log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable);  
348 - }  
349 - }, dbCallbackExecutorService);  
350 - } catch (Exception e) {  
351 - log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);  
352 - }  
353 - }  
354 - }  
355 - }  
356 -  
357 - @Override  
358 - public void onFailure(Throwable throwable) {  
359 - log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);  
360 - }  
361 - }, dbCallbackExecutorService);  
362 - break;  
363 - case DELETED:  
364 - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);  
365 - break;  
366 - case ASSIGNED_TO_EDGE:  
367 - case UNASSIGNED_FROM_EDGE:  
368 - saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);  
369 - if (type.equals(EdgeEventType.RULE_CHAIN)) {  
370 - updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);  
371 - }  
372 - break;  
373 - }  
374 - }  
375 -  
376 - private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {  
377 - PageLink pageLink = new PageLink(DEFAULT_LIMIT);  
378 - PageData<RuleChain> pageData;  
379 - do {  
380 - pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);  
381 - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {  
382 - for (RuleChain ruleChain : pageData.getData()) {  
383 - if (!ruleChain.getId().equals(processingRuleChainId)) {  
384 - List<RuleChainConnectionInfo> connectionInfos =  
385 - ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();  
386 - if (connectionInfos != null && !connectionInfos.isEmpty()) {  
387 - for (RuleChainConnectionInfo connectionInfo : connectionInfos) {  
388 - if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {  
389 - saveEdgeEvent(tenantId,  
390 - edgeId,  
391 - EdgeEventType.RULE_CHAIN_METADATA,  
392 - EdgeEventActionType.UPDATED,  
393 - ruleChain.getId(),  
394 - null);  
395 - }  
396 - }  
397 - }  
398 - }  
399 - }  
400 - if (pageData.hasNext()) {  
401 - pageLink = pageLink.nextPageLink();  
402 - }  
403 - }  
404 - } while (pageData != null && pageData.hasNext());  
405 - }  
406 -  
407 - private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {  
408 - AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));  
409 - ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);  
410 - Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {  
411 - @Override  
412 - public void onSuccess(@Nullable Alarm alarm) {  
413 - if (alarm != null) {  
414 - EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());  
415 - if (type != null) {  
416 - ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());  
417 - Futures.addCallback(relatedEdgeIdsByEntityIdFuture, new FutureCallback<List<EdgeId>>() {  
418 - @Override  
419 - public void onSuccess(@Nullable List<EdgeId> relatedEdgeIdsByEntityId) {  
420 - if (relatedEdgeIdsByEntityId != null) {  
421 - for (EdgeId edgeId : relatedEdgeIdsByEntityId) {  
422 - saveEdgeEvent(tenantId,  
423 - edgeId,  
424 - EdgeEventType.ALARM,  
425 - EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),  
426 - alarmId,  
427 - null);  
428 - }  
429 - }  
430 - }  
431 -  
432 - @Override  
433 - public void onFailure(Throwable t) {  
434 - log.warn("[{}] can't find related edge ids by entity id [{}]", tenantId.getId(), alarm.getOriginator(), t);  
435 - }  
436 - }, dbCallbackExecutorService);  
437 - }  
438 - }  
439 - }  
440 -  
441 - @Override  
442 - public void onFailure(Throwable t) {  
443 - log.warn("[{}] can't find alarm by id [{}]", tenantId.getId(), alarmId.getId(), t);  
444 - }  
445 - }, dbCallbackExecutorService);  
446 - }  
447 -  
448 - private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {  
449 - EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);  
450 - if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&  
451 - !relation.getTo().getEntityType().equals(EntityType.EDGE)) {  
452 - List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();  
453 - futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));  
454 - futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));  
455 - ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);  
456 - Futures.addCallback(combinedFuture, new FutureCallback<List<List<EdgeId>>>() {  
457 - @Override  
458 - public void onSuccess(@Nullable List<List<EdgeId>> listOfListsEdgeIds) {  
459 - Set<EdgeId> uniqueEdgeIds = new HashSet<>();  
460 - if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {  
461 - for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {  
462 - if (listOfListsEdgeId != null) {  
463 - uniqueEdgeIds.addAll(listOfListsEdgeId);  
464 - }  
465 - }  
466 - }  
467 - if (!uniqueEdgeIds.isEmpty()) {  
468 - for (EdgeId edgeId : uniqueEdgeIds) {  
469 - saveEdgeEvent(tenantId,  
470 - edgeId,  
471 - EdgeEventType.RELATION,  
472 - EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),  
473 - null,  
474 - mapper.valueToTree(relation));  
475 - }  
476 - }  
477 - }  
478 -  
479 - @Override  
480 - public void onFailure(Throwable t) {  
481 - log.warn("[{}] can't find related edge ids by relation to id [{}] and relation from id [{}]" ,  
482 - tenantId.getId(), relation.getTo().getId(), relation.getFrom().getId(), t);  
483 - }  
484 - }, dbCallbackExecutorService);  
485 - }  
486 - }  
487 -  
488 - private void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {  
489 - PageLink pageLink = new PageLink(DEFAULT_LIMIT);  
490 - PageData<Edge> pageData;  
491 - do {  
492 - pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);  
493 - if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {  
494 - for (Edge edge : pageData.getData()) {  
495 - saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);  
496 - }  
497 - if (pageData.hasNext()) {  
498 - pageLink = pageLink.nextPageLink();  
499 - }  
500 - }  
501 - } while (pageData != null && pageData.hasNext());  
502 - }  
503 } 183 }
504 184
505 185
@@ -16,218 +16,130 @@ @@ -16,218 +16,130 @@
16 package org.thingsboard.server.service.edge; 16 package org.thingsboard.server.service.edge;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import lombok.Getter;  
20 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.beans.factory.annotation.Autowired;
21 import org.springframework.context.annotation.Lazy; 20 import org.springframework.context.annotation.Lazy;
22 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
23 -import org.thingsboard.server.actors.service.ActorService;  
24 -import org.thingsboard.server.dao.alarm.AlarmService;  
25 import org.thingsboard.server.dao.asset.AssetService; 22 import org.thingsboard.server.dao.asset.AssetService;
26 import org.thingsboard.server.dao.attributes.AttributesService; 23 import org.thingsboard.server.dao.attributes.AttributesService;
27 -import org.thingsboard.server.dao.customer.CustomerService;  
28 import org.thingsboard.server.dao.dashboard.DashboardService; 24 import org.thingsboard.server.dao.dashboard.DashboardService;
29 -import org.thingsboard.server.dao.device.DeviceCredentialsService;  
30 import org.thingsboard.server.dao.device.DeviceProfileService; 25 import org.thingsboard.server.dao.device.DeviceProfileService;
31 -import org.thingsboard.server.dao.device.DeviceService;  
32 import org.thingsboard.server.dao.edge.EdgeEventService; 26 import org.thingsboard.server.dao.edge.EdgeEventService;
33 import org.thingsboard.server.dao.edge.EdgeService; 27 import org.thingsboard.server.dao.edge.EdgeService;
34 -import org.thingsboard.server.dao.entityview.EntityViewService;  
35 -import org.thingsboard.server.dao.relation.RelationService;  
36 import org.thingsboard.server.dao.rule.RuleChainService; 28 import org.thingsboard.server.dao.rule.RuleChainService;
  29 +import org.thingsboard.server.dao.settings.AdminSettingsService;
37 import org.thingsboard.server.dao.user.UserService; 30 import org.thingsboard.server.dao.user.UserService;
38 -import org.thingsboard.server.dao.widget.WidgetTypeService;  
39 import org.thingsboard.server.dao.widget.WidgetsBundleService; 31 import org.thingsboard.server.dao.widget.WidgetsBundleService;
40 -import org.thingsboard.server.queue.discovery.PartitionService;  
41 import org.thingsboard.server.queue.util.TbCoreComponent; 32 import org.thingsboard.server.queue.util.TbCoreComponent;
42 import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings; 33 import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
43 -import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;  
44 -import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;  
45 -import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;  
46 -import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;  
47 -import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;  
48 -import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;  
49 -import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;  
50 -import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;  
51 -import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;  
52 -import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;  
53 -import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;  
54 -import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;  
55 -import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;  
56 -import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;  
57 -import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;  
58 -import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor;  
59 -import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor;  
60 -import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor;  
61 -import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor; 34 +import org.thingsboard.server.service.edge.rpc.processor.AdminSettingsEdgeProcessor;
  35 +import org.thingsboard.server.service.edge.rpc.processor.AlarmEdgeProcessor;
  36 +import org.thingsboard.server.service.edge.rpc.processor.AssetEdgeProcessor;
  37 +import org.thingsboard.server.service.edge.rpc.processor.CustomerEdgeProcessor;
  38 +import org.thingsboard.server.service.edge.rpc.processor.DashboardEdgeProcessor;
  39 +import org.thingsboard.server.service.edge.rpc.processor.DeviceEdgeProcessor;
  40 +import org.thingsboard.server.service.edge.rpc.processor.DeviceProfileEdgeProcessor;
  41 +import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
  42 +import org.thingsboard.server.service.edge.rpc.processor.EntityViewEdgeProcessor;
  43 +import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
  44 +import org.thingsboard.server.service.edge.rpc.processor.RuleChainEdgeProcessor;
  45 +import org.thingsboard.server.service.edge.rpc.processor.TelemetryEdgeProcessor;
  46 +import org.thingsboard.server.service.edge.rpc.processor.UserEdgeProcessor;
  47 +import org.thingsboard.server.service.edge.rpc.processor.WidgetBundleEdgeProcessor;
  48 +import org.thingsboard.server.service.edge.rpc.processor.WidgetTypeEdgeProcessor;
  49 +import org.thingsboard.server.service.edge.rpc.sync.EdgeRequestsService;
62 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 50 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
63 -import org.thingsboard.server.service.queue.TbClusterService;  
64 -import org.thingsboard.server.service.state.DeviceStateService; 51 +import org.thingsboard.server.service.executors.GrpcCallbackExecutorService;
65 52
66 @Component 53 @Component
67 @TbCoreComponent 54 @TbCoreComponent
68 @Data 55 @Data
  56 +@Lazy
69 public class EdgeContextComponent { 57 public class EdgeContextComponent {
70 58
71 - @Lazy  
72 @Autowired 59 @Autowired
73 private EdgeService edgeService; 60 private EdgeService edgeService;
74 61
75 @Autowired 62 @Autowired
76 - private PartitionService partitionService;  
77 -  
78 - @Lazy  
79 - @Autowired  
80 private EdgeEventService edgeEventService; 63 private EdgeEventService edgeEventService;
81 64
82 - @Lazy  
83 @Autowired 65 @Autowired
84 - private AssetService assetService; 66 + private AdminSettingsService adminSettingsService;
85 67
86 - @Lazy  
87 @Autowired 68 @Autowired
88 - private DeviceService deviceService; 69 + private AssetService assetService;
89 70
90 - @Lazy  
91 @Autowired 71 @Autowired
92 private DeviceProfileService deviceProfileService; 72 private DeviceProfileService deviceProfileService;
93 73
94 - @Lazy  
95 - @Autowired  
96 - private DeviceCredentialsService deviceCredentialsService;  
97 -  
98 - @Lazy  
99 - @Autowired  
100 - private EntityViewService entityViewService;  
101 -  
102 - @Lazy  
103 @Autowired 74 @Autowired
104 private AttributesService attributesService; 75 private AttributesService attributesService;
105 76
106 - @Lazy  
107 - @Autowired  
108 - private CustomerService customerService;  
109 -  
110 - @Lazy  
111 - @Autowired  
112 - private RelationService relationService;  
113 -  
114 - @Lazy  
115 - @Autowired  
116 - private AlarmService alarmService;  
117 -  
118 - @Lazy  
119 @Autowired 77 @Autowired
120 private DashboardService dashboardService; 78 private DashboardService dashboardService;
121 79
122 - @Lazy  
123 @Autowired 80 @Autowired
124 private RuleChainService ruleChainService; 81 private RuleChainService ruleChainService;
125 82
126 - @Lazy  
127 @Autowired 83 @Autowired
128 private UserService userService; 84 private UserService userService;
129 85
130 - @Lazy  
131 - @Autowired  
132 - private ActorService actorService;  
133 -  
134 - @Lazy  
135 @Autowired 86 @Autowired
136 private WidgetsBundleService widgetsBundleService; 87 private WidgetsBundleService widgetsBundleService;
137 88
138 - @Lazy  
139 - @Autowired  
140 - private WidgetTypeService widgetTypeService;  
141 -  
142 - @Lazy  
143 - @Autowired  
144 - private DeviceStateService deviceStateService;  
145 -  
146 - @Lazy  
147 - @Autowired  
148 - private TbClusterService tbClusterService;  
149 -  
150 - @Lazy  
151 @Autowired 89 @Autowired
152 - private SyncEdgeService syncEdgeService; 90 + private EdgeRequestsService edgeRequestsService;
153 91
154 - @Lazy  
155 @Autowired 92 @Autowired
156 - private RuleChainMsgConstructor ruleChainMsgConstructor; 93 + private AlarmEdgeProcessor alarmProcessor;
157 94
158 - @Lazy  
159 @Autowired 95 @Autowired
160 - private AlarmMsgConstructor alarmMsgConstructor; 96 + private DeviceProfileEdgeProcessor deviceProfileProcessor;
161 97
162 - @Lazy  
163 @Autowired 98 @Autowired
164 - private DeviceMsgConstructor deviceMsgConstructor; 99 + private DeviceEdgeProcessor deviceProcessor;
165 100
166 - @Lazy  
167 @Autowired 101 @Autowired
168 - private DeviceProfileMsgConstructor deviceProfileMsgConstructor; 102 + private EntityEdgeProcessor entityProcessor;
169 103
170 - @Lazy  
171 @Autowired 104 @Autowired
172 - private AssetMsgConstructor assetMsgConstructor; 105 + private AssetEdgeProcessor assetProcessor;
173 106
174 - @Lazy  
175 @Autowired 107 @Autowired
176 - private EntityViewMsgConstructor entityViewMsgConstructor; 108 + private EntityViewEdgeProcessor entityViewProcessor;
177 109
178 - @Lazy  
179 @Autowired 110 @Autowired
180 - private DashboardMsgConstructor dashboardMsgConstructor; 111 + private UserEdgeProcessor userProcessor;
181 112
182 - @Lazy  
183 @Autowired 113 @Autowired
184 - private CustomerMsgConstructor customerMsgConstructor; 114 + private RelationEdgeProcessor relationProcessor;
185 115
186 - @Lazy  
187 @Autowired 116 @Autowired
188 - private UserMsgConstructor userMsgConstructor; 117 + private TelemetryEdgeProcessor telemetryProcessor;
189 118
190 - @Lazy  
191 @Autowired 119 @Autowired
192 - private RelationMsgConstructor relationMsgConstructor; 120 + private DashboardEdgeProcessor dashboardProcessor;
193 121
194 - @Lazy  
195 @Autowired 122 @Autowired
196 - private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; 123 + private RuleChainEdgeProcessor ruleChainProcessor;
197 124
198 - @Lazy  
199 @Autowired 125 @Autowired
200 - private WidgetTypeMsgConstructor widgetTypeMsgConstructor; 126 + private CustomerEdgeProcessor customerProcessor;
201 127
202 - @Lazy  
203 @Autowired 128 @Autowired
204 - private AdminSettingsMsgConstructor adminSettingsMsgConstructor; 129 + private WidgetBundleEdgeProcessor widgetBundleProcessor;
205 130
206 - @Lazy  
207 @Autowired 131 @Autowired
208 - private EntityDataMsgConstructor entityDataMsgConstructor; 132 + private WidgetTypeEdgeProcessor widgetTypeProcessor;
209 133
210 - @Lazy  
211 @Autowired 134 @Autowired
212 - private AlarmProcessor alarmProcessor; 135 + private AdminSettingsEdgeProcessor adminSettingsProcessor;
213 136
214 - @Lazy  
215 - @Autowired  
216 - private DeviceProcessor deviceProcessor;  
217 -  
218 - @Lazy  
219 - @Autowired  
220 - private RelationProcessor relationProcessor;  
221 -  
222 - @Lazy  
223 - @Autowired  
224 - private TelemetryProcessor telemetryProcessor;  
225 -  
226 - @Lazy  
227 @Autowired 137 @Autowired
228 private EdgeEventStorageSettings edgeEventStorageSettings; 138 private EdgeEventStorageSettings edgeEventStorageSettings;
229 139
230 @Autowired 140 @Autowired
231 - @Getter  
232 private DbCallbackExecutorService dbCallbackExecutor; 141 private DbCallbackExecutorService dbCallbackExecutor;
  142 +
  143 + @Autowired
  144 + private GrpcCallbackExecutorService grpcCallbackExecutorService;
233 } 145 }
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  20 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  21 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  22 +import org.thingsboard.server.common.data.id.EdgeId;
  23 +import org.thingsboard.server.common.data.id.EntityId;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +
  26 +public final class EdgeEventUtils {
  27 +
  28 + private EdgeEventUtils() {
  29 + }
  30 +
  31 + public static EdgeEvent constructEdgeEvent(TenantId tenantId,
  32 + EdgeId edgeId,
  33 + EdgeEventType type,
  34 + EdgeEventActionType action,
  35 + EntityId entityId,
  36 + JsonNode body) {
  37 + EdgeEvent edgeEvent = new EdgeEvent();
  38 + edgeEvent.setTenantId(tenantId);
  39 + edgeEvent.setEdgeId(edgeId);
  40 + edgeEvent.setType(type);
  41 + edgeEvent.setAction(action);
  42 + if (entityId != null) {
  43 + edgeEvent.setEntityId(entityId.getId());
  44 + }
  45 + edgeEvent.setBody(body);
  46 + return edgeEvent;
  47 + }
  48 +}
@@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
16 package org.thingsboard.server.service.edge.rpc; 16 package org.thingsboard.server.service.edge.rpc;
17 17
18 import com.fasterxml.jackson.databind.ObjectMapper; 18 import com.fasterxml.jackson.databind.ObjectMapper;
19 -import com.google.common.io.Resources;  
20 import com.google.common.util.concurrent.FutureCallback; 19 import com.google.common.util.concurrent.FutureCallback;
  20 +import com.google.common.util.concurrent.Futures;
21 import io.grpc.Server; 21 import io.grpc.Server;
22 import io.grpc.netty.NettyServerBuilder; 22 import io.grpc.netty.NettyServerBuilder;
23 import io.grpc.stub.StreamObserver; 23 import io.grpc.stub.StreamObserver;
@@ -28,16 +28,16 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -28,16 +28,16 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
28 import org.springframework.stereotype.Service; 28 import org.springframework.stereotype.Service;
29 import org.thingsboard.common.util.ThingsBoardThreadFactory; 29 import org.thingsboard.common.util.ThingsBoardThreadFactory;
30 import org.thingsboard.server.common.data.DataConstants; 30 import org.thingsboard.server.common.data.DataConstants;
31 -import org.thingsboard.server.common.data.Tenant; 31 +import org.thingsboard.server.common.data.ResourceUtils;
32 import org.thingsboard.server.common.data.edge.Edge; 32 import org.thingsboard.server.common.data.edge.Edge;
33 import org.thingsboard.server.common.data.id.EdgeId; 33 import org.thingsboard.server.common.data.id.EdgeId;
34 import org.thingsboard.server.common.data.id.TenantId; 34 import org.thingsboard.server.common.data.id.TenantId;
35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 35 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
36 import org.thingsboard.server.common.data.kv.BooleanDataEntry; 36 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
37 import org.thingsboard.server.common.data.kv.LongDataEntry; 37 import org.thingsboard.server.common.data.kv.LongDataEntry;
38 -import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;  
39 -import org.thingsboard.server.gen.edge.RequestMsg;  
40 -import org.thingsboard.server.gen.edge.ResponseMsg; 38 +import org.thingsboard.server.gen.edge.v1.EdgeRpcServiceGrpc;
  39 +import org.thingsboard.server.gen.edge.v1.RequestMsg;
  40 +import org.thingsboard.server.gen.edge.v1.ResponseMsg;
41 import org.thingsboard.server.queue.util.TbCoreComponent; 41 import org.thingsboard.server.queue.util.TbCoreComponent;
42 import org.thingsboard.server.service.edge.EdgeContextComponent; 42 import org.thingsboard.server.service.edge.EdgeContextComponent;
43 import org.thingsboard.server.service.state.DefaultDeviceStateService; 43 import org.thingsboard.server.service.state.DefaultDeviceStateService;
@@ -46,9 +46,10 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; @@ -46,9 +46,10 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
46 import javax.annotation.Nullable; 46 import javax.annotation.Nullable;
47 import javax.annotation.PostConstruct; 47 import javax.annotation.PostConstruct;
48 import javax.annotation.PreDestroy; 48 import javax.annotation.PreDestroy;
49 -import java.io.File;  
50 import java.io.IOException; 49 import java.io.IOException;
  50 +import java.io.InputStream;
51 import java.util.Collections; 51 import java.util.Collections;
  52 +import java.util.HashMap;
52 import java.util.Map; 53 import java.util.Map;
53 import java.util.UUID; 54 import java.util.UUID;
54 import java.util.concurrent.ConcurrentHashMap; 55 import java.util.concurrent.ConcurrentHashMap;
@@ -57,6 +58,8 @@ import java.util.concurrent.Executors; @@ -57,6 +58,8 @@ import java.util.concurrent.Executors;
57 import java.util.concurrent.ScheduledExecutorService; 58 import java.util.concurrent.ScheduledExecutorService;
58 import java.util.concurrent.ScheduledFuture; 59 import java.util.concurrent.ScheduledFuture;
59 import java.util.concurrent.TimeUnit; 60 import java.util.concurrent.TimeUnit;
  61 +import java.util.concurrent.locks.Lock;
  62 +import java.util.concurrent.locks.ReentrantLock;
60 63
61 @Service 64 @Service
62 @Slf4j 65 @Slf4j
@@ -65,7 +68,8 @@ import java.util.concurrent.TimeUnit; @@ -65,7 +68,8 @@ import java.util.concurrent.TimeUnit;
65 public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService { 68 public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService {
66 69
67 private final ConcurrentMap<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>(); 70 private final ConcurrentMap<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>();
68 - private final ConcurrentMap<EdgeId, Boolean> sessionNewEvents = new ConcurrentHashMap<>(); 71 + private final ConcurrentMap<EdgeId, Lock> sessionNewEventsLocks = new ConcurrentHashMap<>();
  72 + private final Map<EdgeId, Boolean> sessionNewEvents = new HashMap<>();
69 private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>(); 73 private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>();
70 private static final ObjectMapper mapper = new ObjectMapper(); 74 private static final ObjectMapper mapper = new ObjectMapper();
71 75
@@ -81,10 +85,15 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @@ -81,10 +85,15 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
81 private boolean persistToTelemetry; 85 private boolean persistToTelemetry;
82 @Value("${edges.rpc.client_max_keep_alive_time_sec}") 86 @Value("${edges.rpc.client_max_keep_alive_time_sec}")
83 private int clientMaxKeepAliveTimeSec; 87 private int clientMaxKeepAliveTimeSec;
  88 + @Value("${edges.rpc.max_inbound_message_size:4194304}")
  89 + private int maxInboundMessageSize;
84 90
85 @Value("${edges.scheduler_pool_size}") 91 @Value("${edges.scheduler_pool_size}")
86 private int schedulerPoolSize; 92 private int schedulerPoolSize;
87 93
  94 + @Value("${edges.send_scheduler_pool_size}")
  95 + private int sendSchedulerPoolSize;
  96 +
88 @Autowired 97 @Autowired
89 private EdgeContextComponent ctx; 98 private EdgeContextComponent ctx;
90 99
@@ -93,19 +102,22 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @@ -93,19 +102,22 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
93 102
94 private Server server; 103 private Server server;
95 104
96 - private ScheduledExecutorService scheduler; 105 + private ScheduledExecutorService edgeEventProcessingExecutorService;
  106 +
  107 + private ScheduledExecutorService sendDownlinkExecutorService;
97 108
98 @PostConstruct 109 @PostConstruct
99 public void init() { 110 public void init() {
100 log.info("Initializing Edge RPC service!"); 111 log.info("Initializing Edge RPC service!");
101 NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort) 112 NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort)
102 .permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS) 113 .permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS)
  114 + .maxInboundMessageSize(maxInboundMessageSize)
103 .addService(this); 115 .addService(this);
104 if (sslEnabled) { 116 if (sslEnabled) {
105 try { 117 try {
106 - File certFile = new File(Resources.getResource(certFileResource).toURI());  
107 - File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI());  
108 - builder.useTransportSecurity(certFile, privateKeyFile); 118 + InputStream certFileIs = ResourceUtils.getInputStream(this, certFileResource);
  119 + InputStream privateKeyFileIs = ResourceUtils.getInputStream(this, privateKeyResource);
  120 + builder.useTransportSecurity(certFileIs, privateKeyFileIs);
109 } catch (Exception e) { 121 } catch (Exception e) {
110 log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e); 122 log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e);
111 throw new RuntimeException("Unable to set up SSL context!", e); 123 throw new RuntimeException("Unable to set up SSL context!", e);
@@ -119,7 +131,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @@ -119,7 +131,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
119 log.error("Failed to start Edge RPC server!", e); 131 log.error("Failed to start Edge RPC server!", e);
120 throw new RuntimeException("Failed to start Edge RPC server!"); 132 throw new RuntimeException("Failed to start Edge RPC server!");
121 } 133 }
122 - this.scheduler = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler")); 134 + this.edgeEventProcessingExecutorService = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler"));
  135 + this.sendDownlinkExecutorService = Executors.newScheduledThreadPool(sendSchedulerPoolSize, ThingsBoardThreadFactory.forName("edge-send-scheduler"));
123 log.info("Edge RPC service initialized!"); 136 log.info("Edge RPC service initialized!");
124 } 137 }
125 138
@@ -136,62 +149,84 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @@ -136,62 +149,84 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
136 sessionEdgeEventChecks.remove(edgeId); 149 sessionEdgeEventChecks.remove(edgeId);
137 } 150 }
138 } 151 }
139 - if (scheduler != null) {  
140 - scheduler.shutdownNow(); 152 + if (edgeEventProcessingExecutorService != null) {
  153 + edgeEventProcessingExecutorService.shutdownNow();
  154 + }
  155 + if (sendDownlinkExecutorService != null) {
  156 + sendDownlinkExecutorService.shutdownNow();
141 } 157 }
142 } 158 }
143 159
144 @Override 160 @Override
145 public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) { 161 public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) {
146 - return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream(); 162 + return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper, sendDownlinkExecutorService).getInputStream();
147 } 163 }
148 164
149 @Override 165 @Override
150 - public void updateEdge(Edge edge) { 166 + public void updateEdge(TenantId tenantId, Edge edge) {
151 EdgeGrpcSession session = sessions.get(edge.getId()); 167 EdgeGrpcSession session = sessions.get(edge.getId());
152 if (session != null && session.isConnected()) { 168 if (session != null && session.isConnected()) {
153 - log.debug("[{}] Updating configuration for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId()); 169 + log.debug("[{}] Updating configuration for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
154 session.onConfigurationUpdate(edge); 170 session.onConfigurationUpdate(edge);
155 } else { 171 } else {
156 - log.debug("[{}] Session doesn't exist for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId()); 172 + log.debug("[{}] Session doesn't exist for edge [{}] [{}]", tenantId, edge.getName(), edge.getId());
157 } 173 }
158 } 174 }
159 175
160 @Override 176 @Override
161 - public void deleteEdge(EdgeId edgeId) { 177 + public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
162 EdgeGrpcSession session = sessions.get(edgeId); 178 EdgeGrpcSession session = sessions.get(edgeId);
163 if (session != null && session.isConnected()) { 179 if (session != null && session.isConnected()) {
164 - log.info("Closing and removing session for edge [{}]", edgeId); 180 + log.info("[{}] Closing and removing session for edge [{}]", tenantId, edgeId);
165 session.close(); 181 session.close();
166 sessions.remove(edgeId); 182 sessions.remove(edgeId);
167 - sessionNewEvents.remove(edgeId); 183 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  184 + newEventLock.lock();
  185 + try {
  186 + sessionNewEvents.remove(edgeId);
  187 + } finally {
  188 + newEventLock.unlock();
  189 + }
168 cancelScheduleEdgeEventsCheck(edgeId); 190 cancelScheduleEdgeEventsCheck(edgeId);
169 } 191 }
170 } 192 }
171 193
172 @Override 194 @Override
173 - public void onEdgeEvent(EdgeId edgeId) {  
174 - log.trace("[{}] onEdgeEvent", edgeId.getId());  
175 - if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {  
176 - log.trace("[{}] set session new events flag to true", edgeId.getId());  
177 - sessionNewEvents.put(edgeId, true); 195 + public void onEdgeEvent(TenantId tenantId, EdgeId edgeId) {
  196 + log.trace("[{}] onEdgeEvent [{}]", tenantId, edgeId.getId());
  197 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  198 + newEventLock.lock();
  199 + try {
  200 + if (Boolean.FALSE.equals(sessionNewEvents.get(edgeId))) {
  201 + log.trace("[{}] set session new events flag to true [{}]", tenantId, edgeId.getId());
  202 + sessionNewEvents.put(edgeId, true);
  203 + }
  204 + } finally {
  205 + newEventLock.unlock();
178 } 206 }
179 } 207 }
180 208
181 private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { 209 private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) {
182 log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId); 210 log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId);
183 sessions.put(edgeId, edgeGrpcSession); 211 sessions.put(edgeId, edgeGrpcSession);
184 - sessionNewEvents.put(edgeId, false); 212 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  213 + newEventLock.lock();
  214 + try {
  215 + sessionNewEvents.put(edgeId, true);
  216 + } finally {
  217 + newEventLock.unlock();
  218 + }
185 save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); 219 save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
186 save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis()); 220 save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis());
187 cancelScheduleEdgeEventsCheck(edgeId); 221 cancelScheduleEdgeEventsCheck(edgeId);
188 scheduleEdgeEventsCheck(edgeGrpcSession); 222 scheduleEdgeEventsCheck(edgeGrpcSession);
189 } 223 }
190 224
191 - public EdgeGrpcSession getEdgeGrpcSessionById(TenantId tenantId, EdgeId edgeId) { 225 + @Override
  226 + public void startSyncProcess(TenantId tenantId, EdgeId edgeId) {
192 EdgeGrpcSession session = sessions.get(edgeId); 227 EdgeGrpcSession session = sessions.get(edgeId);
193 if (session != null && session.isConnected()) { 228 if (session != null && session.isConnected()) {
194 - return session; 229 + session.startSyncProcess(tenantId, edgeId);
195 } else { 230 } else {
196 log.error("[{}] Edge is not connected [{}]", tenantId, edgeId); 231 log.error("[{}] Edge is not connected [{}]", tenantId, edgeId);
197 throw new RuntimeException("Edge is not connected"); 232 throw new RuntimeException("Edge is not connected");
@@ -202,19 +237,37 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @@ -202,19 +237,37 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
202 EdgeId edgeId = session.getEdge().getId(); 237 EdgeId edgeId = session.getEdge().getId();
203 UUID tenantId = session.getEdge().getTenantId().getId(); 238 UUID tenantId = session.getEdge().getTenantId().getId();
204 if (sessions.containsKey(edgeId)) { 239 if (sessions.containsKey(edgeId)) {
205 - ScheduledFuture<?> schedule = scheduler.schedule(() -> { 240 + ScheduledFuture<?> edgeEventCheckTask = edgeEventProcessingExecutorService.schedule(() -> {
206 try { 241 try {
207 - if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {  
208 - log.trace("[{}] Set session new events flag to false", edgeId.getId());  
209 - sessionNewEvents.put(edgeId, false);  
210 - session.processEdgeEvents(); 242 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  243 + newEventLock.lock();
  244 + try {
  245 + if (Boolean.TRUE.equals(sessionNewEvents.get(edgeId))) {
  246 + log.trace("[{}] Set session new events flag to false", edgeId.getId());
  247 + sessionNewEvents.put(edgeId, false);
  248 + Futures.addCallback(session.processEdgeEvents(), new FutureCallback<>() {
  249 + @Override
  250 + public void onSuccess(Void result) {
  251 + scheduleEdgeEventsCheck(session);
  252 + }
  253 +
  254 + @Override
  255 + public void onFailure(Throwable t) {
  256 + log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), t);
  257 + scheduleEdgeEventsCheck(session);
  258 + }
  259 + }, ctx.getGrpcCallbackExecutorService());
  260 + } else {
  261 + scheduleEdgeEventsCheck(session);
  262 + }
  263 + } finally {
  264 + newEventLock.unlock();
211 } 265 }
212 } catch (Exception e) { 266 } catch (Exception e) {
213 log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e); 267 log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e);
214 } 268 }
215 - scheduleEdgeEventsCheck(session);  
216 }, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS); 269 }, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS);
217 - sessionEdgeEventChecks.put(edgeId, schedule); 270 + sessionEdgeEventChecks.put(edgeId, edgeEventCheckTask);
218 log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId()); 271 log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId());
219 } else { 272 } else {
220 log.debug("[{}] Session was removed and edge event check schedule must not be started [{}]", 273 log.debug("[{}] Session was removed and edge event check schedule must not be started [{}]",
@@ -236,7 +289,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i @@ -236,7 +289,13 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i
236 private void onEdgeDisconnect(EdgeId edgeId) { 289 private void onEdgeDisconnect(EdgeId edgeId) {
237 log.info("[{}] edge disconnected!", edgeId); 290 log.info("[{}] edge disconnected!", edgeId);
238 sessions.remove(edgeId); 291 sessions.remove(edgeId);
239 - sessionNewEvents.remove(edgeId); 292 + final Lock newEventLock = sessionNewEventsLocks.computeIfAbsent(edgeId, id -> new ReentrantLock());
  293 + newEventLock.lock();
  294 + try {
  295 + sessionNewEvents.remove(edgeId);
  296 + } finally {
  297 + newEventLock.unlock();
  298 + }
240 save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); 299 save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
241 save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis()); 300 save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis());
242 cancelScheduleEdgeEventsCheck(edgeId); 301 cancelScheduleEdgeEventsCheck(edgeId);
@@ -16,111 +16,76 @@ @@ -16,111 +16,76 @@
16 package org.thingsboard.server.service.edge.rpc; 16 package org.thingsboard.server.service.edge.rpc;
17 17
18 import com.datastax.oss.driver.api.core.uuid.Uuids; 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
19 -import com.fasterxml.jackson.core.JsonProcessingException;  
20 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
21 -import com.fasterxml.jackson.databind.node.ObjectNode;  
22 import com.google.common.util.concurrent.FutureCallback; 20 import com.google.common.util.concurrent.FutureCallback;
23 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
25 -import com.google.common.util.concurrent.MoreExecutors;  
26 -import com.google.gson.JsonElement; 23 +import com.google.common.util.concurrent.SettableFuture;
27 import io.grpc.stub.StreamObserver; 24 import io.grpc.stub.StreamObserver;
28 import lombok.Data; 25 import lombok.Data;
29 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
30 import org.checkerframework.checker.nullness.qual.Nullable; 27 import org.checkerframework.checker.nullness.qual.Nullable;
31 -import org.thingsboard.server.common.data.AdminSettings;  
32 -import org.thingsboard.server.common.data.Customer;  
33 -import org.thingsboard.server.common.data.Dashboard; 28 +import org.thingsboard.common.util.JacksonUtil;
34 import org.thingsboard.server.common.data.DataConstants; 29 import org.thingsboard.server.common.data.DataConstants;
35 -import org.thingsboard.server.common.data.Device;  
36 -import org.thingsboard.server.common.data.DeviceProfile;  
37 -import org.thingsboard.server.common.data.EntityView;  
38 -import org.thingsboard.server.common.data.HasCustomerId;  
39 -import org.thingsboard.server.common.data.User;  
40 -import org.thingsboard.server.common.data.alarm.Alarm;  
41 -import org.thingsboard.server.common.data.asset.Asset; 30 +import org.thingsboard.server.common.data.EdgeUtils;
42 import org.thingsboard.server.common.data.edge.Edge; 31 import org.thingsboard.server.common.data.edge.Edge;
43 import org.thingsboard.server.common.data.edge.EdgeEvent; 32 import org.thingsboard.server.common.data.edge.EdgeEvent;
44 import org.thingsboard.server.common.data.edge.EdgeEventActionType; 33 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
45 -import org.thingsboard.server.common.data.edge.EdgeEventType;  
46 -import org.thingsboard.server.common.data.id.AlarmId;  
47 -import org.thingsboard.server.common.data.id.AssetId;  
48 -import org.thingsboard.server.common.data.id.CustomerId;  
49 -import org.thingsboard.server.common.data.id.DashboardId;  
50 -import org.thingsboard.server.common.data.id.DeviceId;  
51 -import org.thingsboard.server.common.data.id.DeviceProfileId;  
52 import org.thingsboard.server.common.data.id.EdgeId; 34 import org.thingsboard.server.common.data.id.EdgeId;
53 -import org.thingsboard.server.common.data.id.EntityId;  
54 -import org.thingsboard.server.common.data.id.EntityViewId;  
55 -import org.thingsboard.server.common.data.id.RuleChainId;  
56 import org.thingsboard.server.common.data.id.TenantId; 35 import org.thingsboard.server.common.data.id.TenantId;
57 -import org.thingsboard.server.common.data.id.UserId;  
58 -import org.thingsboard.server.common.data.id.WidgetTypeId;  
59 -import org.thingsboard.server.common.data.id.WidgetsBundleId;  
60 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 36 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
61 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 37 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
62 import org.thingsboard.server.common.data.kv.LongDataEntry; 38 import org.thingsboard.server.common.data.kv.LongDataEntry;
63 import org.thingsboard.server.common.data.page.PageData; 39 import org.thingsboard.server.common.data.page.PageData;
64 -import org.thingsboard.server.common.data.page.SortOrder;  
65 -import org.thingsboard.server.common.data.page.TimePageLink;  
66 -import org.thingsboard.server.common.data.relation.EntityRelation;  
67 -import org.thingsboard.server.common.data.rule.RuleChain;  
68 -import org.thingsboard.server.common.data.rule.RuleChainMetaData;  
69 -import org.thingsboard.server.common.data.security.DeviceCredentials;  
70 -import org.thingsboard.server.common.data.security.UserCredentials;  
71 -import org.thingsboard.server.common.data.widget.WidgetType;  
72 -import org.thingsboard.server.common.data.widget.WidgetsBundle;  
73 -import org.thingsboard.server.common.transport.util.JsonUtils;  
74 -import org.thingsboard.common.util.JacksonUtil;  
75 -import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;  
76 -import org.thingsboard.server.gen.edge.AlarmUpdateMsg;  
77 -import org.thingsboard.server.gen.edge.AssetUpdateMsg;  
78 -import org.thingsboard.server.gen.edge.AttributesRequestMsg;  
79 -import org.thingsboard.server.gen.edge.ConnectRequestMsg;  
80 -import org.thingsboard.server.gen.edge.ConnectResponseCode;  
81 -import org.thingsboard.server.gen.edge.ConnectResponseMsg;  
82 -import org.thingsboard.server.gen.edge.CustomerUpdateMsg;  
83 -import org.thingsboard.server.gen.edge.DashboardUpdateMsg;  
84 -import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;  
85 -import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;  
86 -import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;  
87 -import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;  
88 -import org.thingsboard.server.gen.edge.DeviceUpdateMsg;  
89 -import org.thingsboard.server.gen.edge.DownlinkMsg;  
90 -import org.thingsboard.server.gen.edge.DownlinkResponseMsg;  
91 -import org.thingsboard.server.gen.edge.EdgeConfiguration;  
92 -import org.thingsboard.server.gen.edge.EdgeUpdateMsg;  
93 -import org.thingsboard.server.gen.edge.EntityDataProto;  
94 -import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;  
95 -import org.thingsboard.server.gen.edge.RelationRequestMsg;  
96 -import org.thingsboard.server.gen.edge.RelationUpdateMsg;  
97 -import org.thingsboard.server.gen.edge.RequestMsg;  
98 -import org.thingsboard.server.gen.edge.RequestMsgType;  
99 -import org.thingsboard.server.gen.edge.ResponseMsg;  
100 -import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;  
101 -import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;  
102 -import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;  
103 -import org.thingsboard.server.gen.edge.UpdateMsgType;  
104 -import org.thingsboard.server.gen.edge.UplinkMsg;  
105 -import org.thingsboard.server.gen.edge.UplinkResponseMsg;  
106 -import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;  
107 -import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;  
108 -import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;  
109 -import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; 40 +import org.thingsboard.server.common.data.page.PageLink;
  41 +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
  42 +import org.thingsboard.server.gen.edge.v1.AttributesRequestMsg;
  43 +import org.thingsboard.server.gen.edge.v1.ConnectRequestMsg;
  44 +import org.thingsboard.server.gen.edge.v1.ConnectResponseCode;
  45 +import org.thingsboard.server.gen.edge.v1.ConnectResponseMsg;
  46 +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
  47 +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
  48 +import org.thingsboard.server.gen.edge.v1.DeviceProfileDevicesRequestMsg;
  49 +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
  50 +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
  51 +import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
  52 +import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
  53 +import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
  54 +import org.thingsboard.server.gen.edge.v1.EdgeUpdateMsg;
  55 +import org.thingsboard.server.gen.edge.v1.EntityDataProto;
  56 +import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg;
  57 +import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
  58 +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
  59 +import org.thingsboard.server.gen.edge.v1.RequestMsg;
  60 +import org.thingsboard.server.gen.edge.v1.RequestMsgType;
  61 +import org.thingsboard.server.gen.edge.v1.ResponseMsg;
  62 +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataRequestMsg;
  63 +import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
  64 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  65 +import org.thingsboard.server.gen.edge.v1.UplinkMsg;
  66 +import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
  67 +import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
  68 +import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
110 import org.thingsboard.server.service.edge.EdgeContextComponent; 69 import org.thingsboard.server.service.edge.EdgeContextComponent;
  70 +import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
  71 +import org.thingsboard.server.service.edge.rpc.fetch.GeneralEdgeEventFetcher;
111 72
112 import java.io.Closeable; 73 import java.io.Closeable;
113 import java.util.ArrayList; 74 import java.util.ArrayList;
114 import java.util.Collections; 75 import java.util.Collections;
  76 +import java.util.LinkedHashMap;
115 import java.util.List; 77 import java.util.List;
  78 +import java.util.Map;
  79 +import java.util.Objects;
116 import java.util.Optional; 80 import java.util.Optional;
117 import java.util.UUID; 81 import java.util.UUID;
118 -import java.util.concurrent.CountDownLatch;  
119 -import java.util.concurrent.ExecutionException; 82 +import java.util.concurrent.ScheduledExecutorService;
  83 +import java.util.concurrent.ScheduledFuture;
120 import java.util.concurrent.TimeUnit; 84 import java.util.concurrent.TimeUnit;
121 import java.util.concurrent.locks.ReentrantLock; 85 import java.util.concurrent.locks.ReentrantLock;
122 import java.util.function.BiConsumer; 86 import java.util.function.BiConsumer;
123 import java.util.function.Consumer; 87 import java.util.function.Consumer;
  88 +import java.util.stream.Collectors;
124 89
125 @Slf4j 90 @Slf4j
126 @Data 91 @Data
@@ -135,27 +100,31 @@ public final class EdgeGrpcSession implements Closeable { @@ -135,27 +100,31 @@ public final class EdgeGrpcSession implements Closeable {
135 private final Consumer<EdgeId> sessionCloseListener; 100 private final Consumer<EdgeId> sessionCloseListener;
136 private final ObjectMapper mapper; 101 private final ObjectMapper mapper;
137 102
  103 + private final EdgeSessionState sessionState = new EdgeSessionState();
  104 +
138 private EdgeContextComponent ctx; 105 private EdgeContextComponent ctx;
139 private Edge edge; 106 private Edge edge;
140 private StreamObserver<RequestMsg> inputStream; 107 private StreamObserver<RequestMsg> inputStream;
141 private StreamObserver<ResponseMsg> outputStream; 108 private StreamObserver<ResponseMsg> outputStream;
142 private boolean connected; 109 private boolean connected;
  110 + private boolean syncCompleted;
143 111
144 - private CountDownLatch latch; 112 + private ScheduledExecutorService sendDownlinkExecutorService;
145 113
146 EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver<ResponseMsg> outputStream, BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener, 114 EdgeGrpcSession(EdgeContextComponent ctx, StreamObserver<ResponseMsg> outputStream, BiConsumer<EdgeId, EdgeGrpcSession> sessionOpenListener,
147 - Consumer<EdgeId> sessionCloseListener, ObjectMapper mapper) { 115 + Consumer<EdgeId> sessionCloseListener, ObjectMapper mapper, ScheduledExecutorService sendDownlinkExecutorService) {
148 this.sessionId = UUID.randomUUID(); 116 this.sessionId = UUID.randomUUID();
149 this.ctx = ctx; 117 this.ctx = ctx;
150 this.outputStream = outputStream; 118 this.outputStream = outputStream;
151 this.sessionOpenListener = sessionOpenListener; 119 this.sessionOpenListener = sessionOpenListener;
152 this.sessionCloseListener = sessionCloseListener; 120 this.sessionCloseListener = sessionCloseListener;
153 this.mapper = mapper; 121 this.mapper = mapper;
  122 + this.sendDownlinkExecutorService = sendDownlinkExecutorService;
154 initInputStream(); 123 initInputStream();
155 } 124 }
156 125
157 private void initInputStream() { 126 private void initInputStream() {
158 - this.inputStream = new StreamObserver<RequestMsg>() { 127 + this.inputStream = new StreamObserver<>() {
159 @Override 128 @Override
160 public void onNext(RequestMsg requestMsg) { 129 public void onNext(RequestMsg requestMsg) {
161 if (!connected && requestMsg.getMsgType().equals(RequestMsgType.CONNECT_RPC_MESSAGE)) { 130 if (!connected && requestMsg.getMsgType().equals(RequestMsgType.CONNECT_RPC_MESSAGE)) {
@@ -169,15 +138,21 @@ public final class EdgeGrpcSession implements Closeable { @@ -169,15 +138,21 @@ public final class EdgeGrpcSession implements Closeable {
169 connected = true; 138 connected = true;
170 } 139 }
171 } 140 }
172 - if (connected && requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) {  
173 - ctx.getSyncEdgeService().sync(edge.getTenantId(), edge);  
174 - }  
175 if (connected) { 141 if (connected) {
176 - if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasUplinkMsg()) {  
177 - onUplinkMsg(requestMsg.getUplinkMsg()); 142 + if (requestMsg.getMsgType().equals(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)) {
  143 + if (requestMsg.hasSyncRequestMsg() && requestMsg.getSyncRequestMsg().getSyncRequired()) {
  144 + startSyncProcess(edge.getTenantId(), edge.getId());
  145 + } else {
  146 + syncCompleted = true;
  147 + }
178 } 148 }
179 - if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE) && requestMsg.hasDownlinkResponseMsg()) {  
180 - onDownlinkResponse(requestMsg.getDownlinkResponseMsg()); 149 + if (requestMsg.getMsgType().equals(RequestMsgType.UPLINK_RPC_MESSAGE)) {
  150 + if (requestMsg.hasUplinkMsg()) {
  151 + onUplinkMsg(requestMsg.getUplinkMsg());
  152 + }
  153 + if (requestMsg.hasDownlinkResponseMsg()) {
  154 + onDownlinkResponse(requestMsg.getDownlinkResponseMsg());
  155 + }
181 } 156 }
182 } 157 }
183 } 158 }
@@ -198,21 +173,65 @@ public final class EdgeGrpcSession implements Closeable { @@ -198,21 +173,65 @@ public final class EdgeGrpcSession implements Closeable {
198 if (edge != null) { 173 if (edge != null) {
199 try { 174 try {
200 sessionCloseListener.accept(edge.getId()); 175 sessionCloseListener.accept(edge.getId());
201 - } catch (Exception ignored) {} 176 + } catch (Exception ignored) {
  177 + }
202 } 178 }
203 try { 179 try {
204 outputStream.onCompleted(); 180 outputStream.onCompleted();
205 - } catch (Exception ignored) {} 181 + } catch (Exception ignored) {
  182 + }
206 } 183 }
207 }; 184 };
208 } 185 }
209 186
  187 + public void startSyncProcess(TenantId tenantId, EdgeId edgeId) {
  188 + log.trace("[{}][{}] Staring edge sync process", tenantId, edgeId);
  189 + syncCompleted = false;
  190 + doSync(new EdgeSyncCursor(ctx, edge));
  191 + }
  192 +
  193 + private void doSync(EdgeSyncCursor cursor) {
  194 + if (cursor.hasNext()) {
  195 + log.info("[{}][{}] starting sync process, cursor current idx = {}", edge.getTenantId(), edge.getId(), cursor.getCurrentIdx());
  196 + ListenableFuture<UUID> uuidListenableFuture = startProcessingEdgeEvents(cursor.getNext());
  197 + Futures.addCallback(uuidListenableFuture, new FutureCallback<>() {
  198 + @Override
  199 + public void onSuccess(@Nullable UUID result) {
  200 + doSync(cursor);
  201 + }
  202 +
  203 + @Override
  204 + public void onFailure(Throwable t) {
  205 + log.error("[{}][{}] Exception during sync process", edge.getTenantId(), edge.getId(), t);
  206 + }
  207 + }, ctx.getGrpcCallbackExecutorService());
  208 + } else {
  209 + DownlinkMsg syncCompleteDownlinkMsg = DownlinkMsg.newBuilder()
  210 + .setDownlinkMsgId(EdgeUtils.nextPositiveInt())
  211 + .setSyncCompletedMsg(SyncCompletedMsg.newBuilder().build())
  212 + .build();
  213 + Futures.addCallback(sendDownlinkMsgsPack(Collections.singletonList(syncCompleteDownlinkMsg)), new FutureCallback<Void>() {
  214 + @Override
  215 + public void onSuccess(Void result) {
  216 + syncCompleted = true;
  217 + }
  218 +
  219 + @Override
  220 + public void onFailure(Throwable t) {
  221 + log.error("[{}][{}] Exception during sending sync complete", edge.getTenantId(), edge.getId(), t);
  222 + }
  223 + }, ctx.getGrpcCallbackExecutorService());
  224 + }
  225 + }
  226 +
210 private void onUplinkMsg(UplinkMsg uplinkMsg) { 227 private void onUplinkMsg(UplinkMsg uplinkMsg) {
211 ListenableFuture<List<Void>> future = processUplinkMsg(uplinkMsg); 228 ListenableFuture<List<Void>> future = processUplinkMsg(uplinkMsg);
212 - Futures.addCallback(future, new FutureCallback<List<Void>>() { 229 + Futures.addCallback(future, new FutureCallback<>() {
213 @Override 230 @Override
214 public void onSuccess(@Nullable List<Void> result) { 231 public void onSuccess(@Nullable List<Void> result) {
215 - UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(true).build(); 232 + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder()
  233 + .setUplinkMsgId(uplinkMsg.getUplinkMsgId())
  234 + .setSuccess(true).build();
216 sendDownlinkMsg(ResponseMsg.newBuilder() 235 sendDownlinkMsg(ResponseMsg.newBuilder()
217 .setUplinkResponseMsg(uplinkResponseMsg) 236 .setUplinkResponseMsg(uplinkResponseMsg)
218 .build()); 237 .build());
@@ -220,22 +239,32 @@ public final class EdgeGrpcSession implements Closeable { @@ -220,22 +239,32 @@ public final class EdgeGrpcSession implements Closeable {
220 239
221 @Override 240 @Override
222 public void onFailure(Throwable t) { 241 public void onFailure(Throwable t) {
223 - UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build(); 242 + String errorMsg = t.getMessage() != null ? t.getMessage() : "";
  243 + UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder()
  244 + .setUplinkMsgId(uplinkMsg.getUplinkMsgId())
  245 + .setSuccess(false).setErrorMsg(errorMsg).build();
224 sendDownlinkMsg(ResponseMsg.newBuilder() 246 sendDownlinkMsg(ResponseMsg.newBuilder()
225 .setUplinkResponseMsg(uplinkResponseMsg) 247 .setUplinkResponseMsg(uplinkResponseMsg)
226 .build()); 248 .build());
227 } 249 }
228 - }, MoreExecutors.directExecutor()); 250 + }, ctx.getGrpcCallbackExecutorService());
229 } 251 }
230 252
231 private void onDownlinkResponse(DownlinkResponseMsg msg) { 253 private void onDownlinkResponse(DownlinkResponseMsg msg) {
232 try { 254 try {
233 if (msg.getSuccess()) { 255 if (msg.getSuccess()) {
  256 + sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId());
234 log.debug("[{}] Msg has been processed successfully! {}", edge.getRoutingKey(), msg); 257 log.debug("[{}] Msg has been processed successfully! {}", edge.getRoutingKey(), msg);
235 } else { 258 } else {
236 log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg()); 259 log.error("[{}] Msg processing failed! Error msg: {}", edge.getRoutingKey(), msg.getErrorMsg());
237 } 260 }
238 - latch.countDown(); 261 + if (sessionState.getPendingMsgsMap().isEmpty()) {
  262 + log.debug("[{}] Pending msgs map is empty. Stopping current iteration {}", edge.getRoutingKey(), msg);
  263 + if (sessionState.getScheduledSendDownlinkTask() != null) {
  264 + sessionState.getScheduledSendDownlinkTask().cancel(false);
  265 + }
  266 + sessionState.getSendDownlinkMsgsFuture().set(null);
  267 + }
239 } catch (Exception e) { 268 } catch (Exception e) {
240 log.error("[{}] Can't process downlink response message [{}]", this.sessionId, msg, e); 269 log.error("[{}] Can't process downlink response message [{}]", this.sessionId, msg, e);
241 } 270 }
@@ -244,8 +273,8 @@ public final class EdgeGrpcSession implements Closeable { @@ -244,8 +273,8 @@ public final class EdgeGrpcSession implements Closeable {
244 private void sendDownlinkMsg(ResponseMsg downlinkMsg) { 273 private void sendDownlinkMsg(ResponseMsg downlinkMsg) {
245 log.trace("[{}] Sending downlink msg [{}]", this.sessionId, downlinkMsg); 274 log.trace("[{}] Sending downlink msg [{}]", this.sessionId, downlinkMsg);
246 if (isConnected()) { 275 if (isConnected()) {
  276 + downlinkMsgLock.lock();
247 try { 277 try {
248 - downlinkMsgLock.lock();  
249 outputStream.onNext(downlinkMsg); 278 outputStream.onNext(downlinkMsg);
250 } catch (Exception e) { 279 } catch (Exception e) {
251 log.error("[{}] Failed to send downlink message [{}]", this.sessionId, downlinkMsg, e); 280 log.error("[{}] Failed to send downlink message [{}]", this.sessionId, downlinkMsg, e);
@@ -269,149 +298,192 @@ public final class EdgeGrpcSession implements Closeable { @@ -269,149 +298,192 @@ public final class EdgeGrpcSession implements Closeable {
269 sendDownlinkMsg(edgeConfigMsg); 298 sendDownlinkMsg(edgeConfigMsg);
270 } 299 }
271 300
272 - void processEdgeEvents() throws ExecutionException, InterruptedException {  
273 - log.trace("[{}] processHandleMessages started", this.sessionId);  
274 - if (isConnected()) { 301 + ListenableFuture<Void> processEdgeEvents() throws Exception {
  302 + SettableFuture<Void> result = SettableFuture.create();
  303 + log.trace("[{}] starting processing edge events", this.sessionId);
  304 + if (isConnected() && isSyncCompleted()) {
275 Long queueStartTs = getQueueStartTs().get(); 305 Long queueStartTs = getQueueStartTs().get();
276 - TimePageLink pageLink = new TimePageLink(  
277 - ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(),  
278 - 0,  
279 - null,  
280 - new SortOrder("createdTime", SortOrder.Direction.ASC), 306 + GeneralEdgeEventFetcher fetcher = new GeneralEdgeEventFetcher(
281 queueStartTs, 307 queueStartTs,
282 - null);  
283 - PageData<EdgeEvent> pageData;  
284 - UUID ifOffset = null;  
285 - boolean success = true;  
286 - do {  
287 - pageData = ctx.getEdgeEventService().findEdgeEvents(edge.getTenantId(), edge.getId(), pageLink, true);  
288 - if (isConnected() && !pageData.getData().isEmpty()) {  
289 - log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());  
290 - List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData());  
291 - log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, downlinkMsgsPack.size());  
292 -  
293 - latch = new CountDownLatch(downlinkMsgsPack.size());  
294 - for (DownlinkMsg downlinkMsg : downlinkMsgsPack) {  
295 - sendDownlinkMsg(ResponseMsg.newBuilder()  
296 - .setDownlinkMsg(downlinkMsg)  
297 - .build()); 308 + ctx.getEdgeEventService());
  309 + ListenableFuture<UUID> ifOffsetFuture = startProcessingEdgeEvents(fetcher);
  310 + Futures.addCallback(ifOffsetFuture, new FutureCallback<>() {
  311 + @Override
  312 + public void onSuccess(@Nullable UUID ifOffset) {
  313 + if (ifOffset != null) {
  314 + Long newStartTs = Uuids.unixTimestamp(ifOffset);
  315 + ListenableFuture<List<Void>> updateFuture = updateQueueStartTs(newStartTs);
  316 + Futures.addCallback(updateFuture, new FutureCallback<>() {
  317 + @Override
  318 + public void onSuccess(@Nullable List<Void> list) {
  319 + log.debug("[{}] queue offset was updated [{}][{}]", sessionId, ifOffset, newStartTs);
  320 + result.set(null);
  321 + }
  322 +
  323 + @Override
  324 + public void onFailure(Throwable t) {
  325 + log.error("[{}] Failed to update queue offset [{}]", sessionId, ifOffset, t);
  326 + result.setException(t);
  327 + }
  328 + }, ctx.getGrpcCallbackExecutorService());
  329 + } else {
  330 + log.trace("[{}] ifOffset is null. Skipping iteration without db update", sessionId);
  331 + result.set(null);
298 } 332 }
  333 + }
299 334
300 - ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId();  
301 -  
302 - success = latch.await(10, TimeUnit.SECONDS);  
303 - if (!success) {  
304 - log.warn("[{}] Failed to deliver the batch: {}", this.sessionId, downlinkMsgsPack);  
305 - } 335 + @Override
  336 + public void onFailure(Throwable t) {
  337 + log.error("[{}] Failed to process events", sessionId, t);
  338 + result.setException(t);
306 } 339 }
307 - if (isConnected() && (!success || pageData.hasNext())) {  
308 - try {  
309 - Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches());  
310 - } catch (InterruptedException e) {  
311 - log.error("[{}] Error during sleep between batches", this.sessionId, e);  
312 - }  
313 - if (success) {  
314 - pageLink = pageLink.nextPageLink(); 340 + }, ctx.getGrpcCallbackExecutorService());
  341 + } else {
  342 + log.trace("[{}] edge is not connected or sync is not completed. Skipping iteration", sessionId);
  343 + result.set(null);
  344 + }
  345 + return result;
  346 + }
  347 +
  348 + private ListenableFuture<UUID> startProcessingEdgeEvents(EdgeEventFetcher fetcher) {
  349 + SettableFuture<UUID> result = SettableFuture.create();
  350 + PageLink pageLink = fetcher.getPageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount());
  351 + processEdgeEvents(fetcher, pageLink, result);
  352 + return result;
  353 + }
  354 +
  355 + private void processEdgeEvents(EdgeEventFetcher fetcher, PageLink pageLink, SettableFuture<UUID> result) {
  356 + try {
  357 + PageData<EdgeEvent> pageData = fetcher.fetchEdgeEvents(edge.getTenantId(), edge, pageLink);
  358 + if (isConnected() && !pageData.getData().isEmpty()) {
  359 + log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size());
  360 + List<DownlinkMsg> downlinkMsgsPack = convertToDownlinkMsgsPack(pageData.getData());
  361 + Futures.addCallback(sendDownlinkMsgsPack(downlinkMsgsPack), new FutureCallback<Void>() {
  362 + @Override
  363 + public void onSuccess(@Nullable Void tmp) {
  364 + if (isConnected() && pageData.hasNext()) {
  365 + processEdgeEvents(fetcher, pageLink.nextPageLink(), result);
  366 + } else {
  367 + UUID ifOffset = pageData.getData().get(pageData.getData().size() - 1).getUuidId();
  368 + result.set(ifOffset);
  369 + }
315 } 370 }
316 - }  
317 - } while (isConnected() && (!success || pageData.hasNext()));  
318 371
319 - if (ifOffset != null) {  
320 - Long newStartTs = Uuids.unixTimestamp(ifOffset);  
321 - updateQueueStartTs(newStartTs); 372 + @Override
  373 + public void onFailure(Throwable t) {
  374 + log.error("[{}] Failed to send downlink msgs pack", sessionId, t);
  375 + result.setException(t);
  376 + }
  377 + }, ctx.getGrpcCallbackExecutorService());
  378 + } else {
  379 + log.trace("[{}] no event(s) found. Stop processing edge events", this.sessionId);
  380 + result.set(null);
322 } 381 }
  382 + } catch (Exception e) {
  383 + log.error("[{}] Failed to fetch edge events", this.sessionId, e);
  384 + result.setException(e);
323 } 385 }
324 - log.trace("[{}] processHandleMessages finished", this.sessionId);  
325 } 386 }
326 387
327 - private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {  
328 - List<DownlinkMsg> result = new ArrayList<>();  
329 - for (EdgeEvent edgeEvent : edgeEvents) {  
330 - log.trace("[{}] Processing edge event [{}]", this.sessionId, edgeEvent); 388 + private ListenableFuture<Void> sendDownlinkMsgsPack(List<DownlinkMsg> downlinkMsgsPack) {
  389 + if (sessionState.getSendDownlinkMsgsFuture() != null && !sessionState.getSendDownlinkMsgsFuture().isDone()) {
  390 + String erroMsg = "[" + this.sessionId + "] Previous send downdlink future was not properly completed, stopping it now";
  391 + log.error(erroMsg);
  392 + sessionState.getSendDownlinkMsgsFuture().setException(new RuntimeException(erroMsg));
  393 + }
  394 + sessionState.setSendDownlinkMsgsFuture(SettableFuture.create());
  395 + sessionState.getPendingMsgsMap().clear();
  396 + downlinkMsgsPack.forEach(msg -> sessionState.getPendingMsgsMap().put(msg.getDownlinkMsgId(), msg));
  397 + scheduleDownlinkMsgsPackSend(true);
  398 + return sessionState.getSendDownlinkMsgsFuture();
  399 + }
  400 +
  401 + private void scheduleDownlinkMsgsPackSend(boolean firstRun) {
  402 + Runnable sendDownlinkMsgsTask = () -> {
331 try { 403 try {
332 - DownlinkMsg downlinkMsg = null;  
333 - switch (edgeEvent.getAction()) {  
334 - case UPDATED:  
335 - case ADDED:  
336 - case DELETED:  
337 - case ASSIGNED_TO_EDGE:  
338 - case UNASSIGNED_FROM_EDGE:  
339 - case ALARM_ACK:  
340 - case ALARM_CLEAR:  
341 - case CREDENTIALS_UPDATED:  
342 - case RELATION_ADD_OR_UPDATE:  
343 - case RELATION_DELETED:  
344 - case ASSIGNED_TO_CUSTOMER:  
345 - case UNASSIGNED_FROM_CUSTOMER:  
346 - downlinkMsg = processEntityMessage(edgeEvent, edgeEvent.getAction());  
347 - break;  
348 - case ATTRIBUTES_UPDATED:  
349 - case POST_ATTRIBUTES:  
350 - case ATTRIBUTES_DELETED:  
351 - case TIMESERIES_UPDATED:  
352 - downlinkMsg = processTelemetryMessage(edgeEvent);  
353 - break;  
354 - case CREDENTIALS_REQUEST:  
355 - downlinkMsg = processCredentialsRequestMessage(edgeEvent);  
356 - break;  
357 - case ENTITY_MERGE_REQUEST:  
358 - downlinkMsg = processEntityMergeRequestMessage(edgeEvent);  
359 - break;  
360 - case RPC_CALL:  
361 - downlinkMsg = processRpcCallMsg(edgeEvent);  
362 - break;  
363 - }  
364 - if (downlinkMsg != null) {  
365 - result.add(downlinkMsg); 404 + if (isConnected() && sessionState.getPendingMsgsMap().values().size() > 0) {
  405 + if (!firstRun) {
  406 + log.warn("[{}] Failed to deliver the batch: {}", this.sessionId, sessionState.getPendingMsgsMap().values());
  407 + }
  408 + log.trace("[{}] [{}] downlink msg(s) are going to be send.", this.sessionId, sessionState.getPendingMsgsMap().values().size());
  409 + List<DownlinkMsg> copy = new ArrayList<>(sessionState.getPendingMsgsMap().values());
  410 + for (DownlinkMsg downlinkMsg : copy) {
  411 + sendDownlinkMsg(ResponseMsg.newBuilder()
  412 + .setDownlinkMsg(downlinkMsg)
  413 + .build());
  414 + }
  415 + scheduleDownlinkMsgsPackSend(false);
  416 + } else {
  417 + sessionState.getSendDownlinkMsgsFuture().set(null);
366 } 418 }
367 } catch (Exception e) { 419 } catch (Exception e) {
368 - log.error("Exception during processing records from queue", e); 420 + sessionState.getSendDownlinkMsgsFuture().setException(e);
369 } 421 }
  422 + };
  423 +
  424 + if (firstRun) {
  425 + sendDownlinkExecutorService.submit(sendDownlinkMsgsTask);
  426 + } else {
  427 + sessionState.setScheduledSendDownlinkTask(
  428 + sendDownlinkExecutorService.schedule(
  429 + sendDownlinkMsgsTask,
  430 + ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches(),
  431 + TimeUnit.MILLISECONDS)
  432 + );
370 } 433 }
371 - return result; 434 +
372 } 435 }
373 436
374 - private DownlinkMsg processEntityMergeRequestMessage(EdgeEvent edgeEvent) { 437 + private DownlinkMsg convertToDownlinkMsg(EdgeEvent edgeEvent) {
  438 + log.trace("[{}][{}] converting edge event to downlink msg [{}]", edge.getTenantId(), this.sessionId, edgeEvent);
375 DownlinkMsg downlinkMsg = null; 439 DownlinkMsg downlinkMsg = null;
376 - if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {  
377 - DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());  
378 - Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), deviceId);  
379 - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device);  
380 - String conflictName = null;  
381 - if(edgeEvent.getBody() != null) {  
382 - conflictName = edgeEvent.getBody().get("conflictName").asText(); 440 + try {
  441 + switch (edgeEvent.getAction()) {
  442 + case UPDATED:
  443 + case ADDED:
  444 + case DELETED:
  445 + case ASSIGNED_TO_EDGE:
  446 + case UNASSIGNED_FROM_EDGE:
  447 + case ALARM_ACK:
  448 + case ALARM_CLEAR:
  449 + case CREDENTIALS_UPDATED:
  450 + case RELATION_ADD_OR_UPDATE:
  451 + case RELATION_DELETED:
  452 + case ASSIGNED_TO_CUSTOMER:
  453 + case UNASSIGNED_FROM_CUSTOMER:
  454 + downlinkMsg = processEntityMessage(edgeEvent, edgeEvent.getAction());
  455 + log.trace("[{}][{}] entity message processed [{}]", edgeEvent.getTenantId(), this.sessionId, downlinkMsg);
  456 + break;
  457 + case ATTRIBUTES_UPDATED:
  458 + case POST_ATTRIBUTES:
  459 + case ATTRIBUTES_DELETED:
  460 + case TIMESERIES_UPDATED:
  461 + downlinkMsg = ctx.getTelemetryProcessor().processTelemetryMessageToEdge(edgeEvent);
  462 + break;
  463 + case CREDENTIALS_REQUEST:
  464 + downlinkMsg = ctx.getEntityProcessor().processCredentialsRequestMessageToEdge(edgeEvent);
  465 + break;
  466 + case ENTITY_MERGE_REQUEST:
  467 + downlinkMsg = ctx.getEntityProcessor().processEntityMergeRequestMessageToEdge(edge, edgeEvent);
  468 + break;
  469 + case RPC_CALL:
  470 + downlinkMsg = ctx.getDeviceProcessor().processRpcCallMsgToEdge(edgeEvent);
  471 + break;
  472 + default:
  473 + log.warn("[{}][{}] Unsupported action type [{}]", edge.getTenantId(), this.sessionId, edgeEvent.getAction());
383 } 474 }
384 - DeviceUpdateMsg d = ctx.getDeviceMsgConstructor()  
385 - .constructDeviceUpdatedMsg(UpdateMsgType.ENTITY_MERGE_RPC_MESSAGE, device, customerId, conflictName);  
386 - downlinkMsg = DownlinkMsg.newBuilder()  
387 - .addAllDeviceUpdateMsg(Collections.singletonList(d))  
388 - .build(); 475 + } catch (Exception e) {
  476 + log.error("[{}][{}] Exception during converting edge event to downlink msg", edge.getTenantId(), this.sessionId, e);
389 } 477 }
390 return downlinkMsg; 478 return downlinkMsg;
391 } 479 }
392 480
393 - private DownlinkMsg processRpcCallMsg(EdgeEvent edgeEvent) {  
394 - log.trace("Executing processRpcCall, edgeEvent [{}]", edgeEvent);  
395 - DeviceRpcCallMsg deviceRpcCallMsg =  
396 - ctx.getDeviceMsgConstructor().constructDeviceRpcCallMsg(edgeEvent.getEntityId(), edgeEvent.getBody());  
397 - return DownlinkMsg.newBuilder()  
398 - .addAllDeviceRpcCallMsg(Collections.singletonList(deviceRpcCallMsg))  
399 - .build();  
400 - }  
401 -  
402 - private DownlinkMsg processCredentialsRequestMessage(EdgeEvent edgeEvent) {  
403 - DownlinkMsg downlinkMsg = null;  
404 - if (EdgeEventType.DEVICE.equals(edgeEvent.getType())) {  
405 - DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());  
406 - DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = DeviceCredentialsRequestMsg.newBuilder()  
407 - .setDeviceIdMSB(deviceId.getId().getMostSignificantBits())  
408 - .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits())  
409 - .build();  
410 - DownlinkMsg.Builder builder = DownlinkMsg.newBuilder()  
411 - .addAllDeviceCredentialsRequestMsg(Collections.singletonList(deviceCredentialsRequestMsg));  
412 - downlinkMsg = builder.build();  
413 - }  
414 - return downlinkMsg; 481 + private List<DownlinkMsg> convertToDownlinkMsgsPack(List<EdgeEvent> edgeEvents) {
  482 + return edgeEvents
  483 + .stream()
  484 + .map(this::convertToDownlinkMsg)
  485 + .filter(Objects::nonNull)
  486 + .collect(Collectors.toList());
415 } 487 }
416 488
417 private ListenableFuture<Long> getQueueStartTs() { 489 private ListenableFuture<Long> getQueueStartTs() {
@@ -424,52 +496,14 @@ public final class EdgeGrpcSession implements Closeable { @@ -424,52 +496,14 @@ public final class EdgeGrpcSession implements Closeable {
424 } else { 496 } else {
425 return 0L; 497 return 0L;
426 } 498 }
427 - }, ctx.getDbCallbackExecutor()); 499 + }, ctx.getGrpcCallbackExecutorService());
428 } 500 }
429 501
430 - private void updateQueueStartTs(Long newStartTs) { 502 + private ListenableFuture<List<Void>> updateQueueStartTs(Long newStartTs) {
431 log.trace("[{}] updating QueueStartTs [{}][{}]", this.sessionId, edge.getId(), newStartTs); 503 log.trace("[{}] updating QueueStartTs [{}][{}]", this.sessionId, edge.getId(), newStartTs);
432 newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1 504 newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1
433 List<AttributeKvEntry> attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); 505 List<AttributeKvEntry> attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis()));
434 - ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);  
435 - }  
436 -  
437 - private DownlinkMsg processTelemetryMessage(EdgeEvent edgeEvent) {  
438 - log.trace("[{}] Executing processTelemetryMessage, edgeEvent [{}]", this.sessionId, edgeEvent);  
439 - EntityId entityId = null;  
440 - switch (edgeEvent.getType()) {  
441 - case DEVICE:  
442 - entityId = new DeviceId(edgeEvent.getEntityId());  
443 - break;  
444 - case ASSET:  
445 - entityId = new AssetId(edgeEvent.getEntityId());  
446 - break;  
447 - case ENTITY_VIEW:  
448 - entityId = new EntityViewId(edgeEvent.getEntityId());  
449 - break;  
450 - case DASHBOARD:  
451 - entityId = new DashboardId(edgeEvent.getEntityId());  
452 - break;  
453 - case TENANT:  
454 - entityId = new TenantId(edgeEvent.getEntityId());  
455 - break;  
456 - case CUSTOMER:  
457 - entityId = new CustomerId(edgeEvent.getEntityId());  
458 - break;  
459 - case EDGE:  
460 - entityId = new EdgeId(edgeEvent.getEntityId());  
461 - break;  
462 - }  
463 - DownlinkMsg downlinkMsg = null;  
464 - if (entityId != null) {  
465 - log.debug("[{}] Sending telemetry data msg, entityId [{}], body [{}]", this.sessionId, edgeEvent.getEntityId(), edgeEvent.getBody());  
466 - try {  
467 - downlinkMsg = constructEntityDataProtoMsg(entityId, edgeEvent.getAction(), JsonUtils.parse(mapper.writeValueAsString(edgeEvent.getBody())));  
468 - } catch (Exception e) {  
469 - log.warn("[{}] Can't send telemetry data msg, entityId [{}], body [{}]", this.sessionId, edgeEvent.getEntityId(), edgeEvent.getBody(), e);  
470 - }  
471 - }  
472 - return downlinkMsg; 506 + return ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes);
473 } 507 }
474 508
475 private DownlinkMsg processEntityMessage(EdgeEvent edgeEvent, EdgeEventActionType action) { 509 private DownlinkMsg processEntityMessage(EdgeEvent edgeEvent, EdgeEventActionType action) {
@@ -477,411 +511,39 @@ public final class EdgeGrpcSession implements Closeable { @@ -477,411 +511,39 @@ public final class EdgeGrpcSession implements Closeable {
477 log.trace("Executing processEntityMessage, edgeEvent [{}], action [{}], msgType [{}]", edgeEvent, action, msgType); 511 log.trace("Executing processEntityMessage, edgeEvent [{}], action [{}], msgType [{}]", edgeEvent, action, msgType);
478 switch (edgeEvent.getType()) { 512 switch (edgeEvent.getType()) {
479 case DEVICE: 513 case DEVICE:
480 - return processDevice(edgeEvent, msgType, action); 514 + return ctx.getDeviceProcessor().processDeviceToEdge(edge, edgeEvent, msgType, action);
481 case DEVICE_PROFILE: 515 case DEVICE_PROFILE:
482 - return processDeviceProfile(edgeEvent, msgType, action); 516 + return ctx.getDeviceProfileProcessor().processDeviceProfileToEdge(edgeEvent, msgType, action);
483 case ASSET: 517 case ASSET:
484 - return processAsset(edgeEvent, msgType, action); 518 + return ctx.getAssetProcessor().processAssetToEdge(edge, edgeEvent, msgType, action);
485 case ENTITY_VIEW: 519 case ENTITY_VIEW:
486 - return processEntityView(edgeEvent, msgType, action); 520 + return ctx.getEntityViewProcessor().processEntityViewToEdge(edge, edgeEvent, msgType, action);
487 case DASHBOARD: 521 case DASHBOARD:
488 - return processDashboard(edgeEvent, msgType, action); 522 + return ctx.getDashboardProcessor().processDashboardToEdge(edge, edgeEvent, msgType, action);
489 case CUSTOMER: 523 case CUSTOMER:
490 - return processCustomer(edgeEvent, msgType, action); 524 + return ctx.getCustomerProcessor().processCustomerToEdge(edgeEvent, msgType, action);
491 case RULE_CHAIN: 525 case RULE_CHAIN:
492 - return processRuleChain(edgeEvent, msgType, action); 526 + return ctx.getRuleChainProcessor().processRuleChainToEdge(edge, edgeEvent, msgType, action);
493 case RULE_CHAIN_METADATA: 527 case RULE_CHAIN_METADATA:
494 - return processRuleChainMetadata(edgeEvent, msgType); 528 + return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType);
495 case ALARM: 529 case ALARM:
496 - return processAlarm(edgeEvent, msgType); 530 + return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType);
497 case USER: 531 case USER:
498 - return processUser(edgeEvent, msgType, action); 532 + return ctx.getUserProcessor().processUserToEdge(edge, edgeEvent, msgType, action);
499 case RELATION: 533 case RELATION:
500 - return processRelation(edgeEvent, msgType); 534 + return ctx.getRelationProcessor().processRelationToEdge(edgeEvent, msgType);
501 case WIDGETS_BUNDLE: 535 case WIDGETS_BUNDLE:
502 - return processWidgetsBundle(edgeEvent, msgType, action); 536 + return ctx.getWidgetBundleProcessor().processWidgetsBundleToEdge(edgeEvent, msgType, action);
503 case WIDGET_TYPE: 537 case WIDGET_TYPE:
504 - return processWidgetType(edgeEvent, msgType, action); 538 + return ctx.getWidgetTypeProcessor().processWidgetTypeToEdge(edgeEvent, msgType, action);
505 case ADMIN_SETTINGS: 539 case ADMIN_SETTINGS:
506 - return processAdminSettings(edgeEvent); 540 + return ctx.getAdminSettingsProcessor().processAdminSettingsToEdge(edgeEvent);
507 default: 541 default:
508 log.warn("Unsupported edge event type [{}]", edgeEvent); 542 log.warn("Unsupported edge event type [{}]", edgeEvent);
509 return null; 543 return null;
510 } 544 }
511 } 545 }
512 546
513 - private DownlinkMsg processDevice(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {  
514 - DeviceId deviceId = new DeviceId(edgeEvent.getEntityId());  
515 - DownlinkMsg downlinkMsg = null;  
516 - switch (edgeEdgeEventActionType) {  
517 - case ADDED:  
518 - case UPDATED:  
519 - case ASSIGNED_TO_EDGE:  
520 - case ASSIGNED_TO_CUSTOMER:  
521 - case UNASSIGNED_FROM_CUSTOMER:  
522 - Device device = ctx.getDeviceService().findDeviceById(edgeEvent.getTenantId(), deviceId);  
523 - if (device != null) {  
524 - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(device);  
525 - DeviceUpdateMsg deviceUpdateMsg =  
526 - ctx.getDeviceMsgConstructor().constructDeviceUpdatedMsg(msgType, device, customerId, null);  
527 - downlinkMsg = DownlinkMsg.newBuilder()  
528 - .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))  
529 - .build();  
530 - }  
531 - break;  
532 - case DELETED:  
533 - case UNASSIGNED_FROM_EDGE:  
534 - DeviceUpdateMsg deviceUpdateMsg =  
535 - ctx.getDeviceMsgConstructor().constructDeviceDeleteMsg(deviceId);  
536 - downlinkMsg = DownlinkMsg.newBuilder()  
537 - .addAllDeviceUpdateMsg(Collections.singletonList(deviceUpdateMsg))  
538 - .build();  
539 - break;  
540 - case CREDENTIALS_UPDATED:  
541 - DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), deviceId);  
542 - if (deviceCredentials != null) {  
543 - DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg =  
544 - ctx.getDeviceMsgConstructor().constructDeviceCredentialsUpdatedMsg(deviceCredentials);  
545 - downlinkMsg = DownlinkMsg.newBuilder()  
546 - .addAllDeviceCredentialsUpdateMsg(Collections.singletonList(deviceCredentialsUpdateMsg))  
547 - .build();  
548 - }  
549 - break;  
550 - }  
551 - log.trace("[{}] device processed [{}]", this.sessionId, downlinkMsg);  
552 - return downlinkMsg;  
553 - }  
554 -  
555 - private DownlinkMsg processDeviceProfile(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {  
556 - DeviceProfileId deviceProfileId = new DeviceProfileId(edgeEvent.getEntityId());  
557 - DownlinkMsg downlinkMsg = null;  
558 - switch (action) {  
559 - case ADDED:  
560 - case UPDATED:  
561 - DeviceProfile deviceProfile = ctx.getDeviceProfileService().findDeviceProfileById(edgeEvent.getTenantId(), deviceProfileId);  
562 - if (deviceProfile != null) {  
563 - DeviceProfileUpdateMsg deviceProfileUpdateMsg =  
564 - ctx.getDeviceProfileMsgConstructor().constructDeviceProfileUpdatedMsg(msgType, deviceProfile);  
565 - downlinkMsg = DownlinkMsg.newBuilder()  
566 - .addAllDeviceProfileUpdateMsg(Collections.singletonList(deviceProfileUpdateMsg))  
567 - .build();  
568 - }  
569 - break;  
570 - case DELETED:  
571 - DeviceProfileUpdateMsg deviceProfileUpdateMsg =  
572 - ctx.getDeviceProfileMsgConstructor().constructDeviceProfileDeleteMsg(deviceProfileId);  
573 - downlinkMsg = DownlinkMsg.newBuilder()  
574 - .addAllDeviceProfileUpdateMsg(Collections.singletonList(deviceProfileUpdateMsg))  
575 - .build();  
576 - break;  
577 - }  
578 - log.trace("[{}] device profile processed [{}]", this.sessionId, downlinkMsg);  
579 - return downlinkMsg;  
580 - }  
581 -  
582 - private DownlinkMsg processAsset(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {  
583 - AssetId assetId = new AssetId(edgeEvent.getEntityId());  
584 - DownlinkMsg downlinkMsg = null;  
585 - switch (action) {  
586 - case ADDED:  
587 - case UPDATED:  
588 - case ASSIGNED_TO_EDGE:  
589 - case ASSIGNED_TO_CUSTOMER:  
590 - case UNASSIGNED_FROM_CUSTOMER:  
591 - Asset asset = ctx.getAssetService().findAssetById(edgeEvent.getTenantId(), assetId);  
592 - if (asset != null) {  
593 - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(asset);  
594 - AssetUpdateMsg assetUpdateMsg =  
595 - ctx.getAssetMsgConstructor().constructAssetUpdatedMsg(msgType, asset, customerId);  
596 - downlinkMsg = DownlinkMsg.newBuilder()  
597 - .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg))  
598 - .build();  
599 - }  
600 - break;  
601 - case DELETED:  
602 - case UNASSIGNED_FROM_EDGE:  
603 - AssetUpdateMsg assetUpdateMsg =  
604 - ctx.getAssetMsgConstructor().constructAssetDeleteMsg(assetId);  
605 - downlinkMsg = DownlinkMsg.newBuilder()  
606 - .addAllAssetUpdateMsg(Collections.singletonList(assetUpdateMsg))  
607 - .build();  
608 - break;  
609 - }  
610 - log.trace("[{}] asset processed [{}]", this.sessionId, downlinkMsg);  
611 - return downlinkMsg;  
612 - }  
613 -  
614 - private DownlinkMsg processEntityView(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {  
615 - EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId());  
616 - DownlinkMsg downlinkMsg = null;  
617 - switch (action) {  
618 - case ADDED:  
619 - case UPDATED:  
620 - case ASSIGNED_TO_EDGE:  
621 - case ASSIGNED_TO_CUSTOMER:  
622 - case UNASSIGNED_FROM_CUSTOMER:  
623 - EntityView entityView = ctx.getEntityViewService().findEntityViewById(edgeEvent.getTenantId(), entityViewId);  
624 - if (entityView != null) {  
625 - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(entityView);  
626 - EntityViewUpdateMsg entityViewUpdateMsg =  
627 - ctx.getEntityViewMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView, customerId);  
628 - downlinkMsg = DownlinkMsg.newBuilder()  
629 - .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg))  
630 - .build();  
631 - }  
632 - break;  
633 - case DELETED:  
634 - case UNASSIGNED_FROM_EDGE:  
635 - EntityViewUpdateMsg entityViewUpdateMsg =  
636 - ctx.getEntityViewMsgConstructor().constructEntityViewDeleteMsg(entityViewId);  
637 - downlinkMsg = DownlinkMsg.newBuilder()  
638 - .addAllEntityViewUpdateMsg(Collections.singletonList(entityViewUpdateMsg))  
639 - .build();  
640 - break;  
641 - }  
642 - log.trace("[{}] entity view processed [{}]", this.sessionId, downlinkMsg);  
643 - return downlinkMsg;  
644 - }  
645 -  
646 - private DownlinkMsg processDashboard(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {  
647 - DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId());  
648 - DownlinkMsg downlinkMsg = null;  
649 - switch (action) {  
650 - case ADDED:  
651 - case UPDATED:  
652 - case ASSIGNED_TO_EDGE:  
653 - case ASSIGNED_TO_CUSTOMER:  
654 - case UNASSIGNED_FROM_CUSTOMER:  
655 - Dashboard dashboard = ctx.getDashboardService().findDashboardById(edgeEvent.getTenantId(), dashboardId);  
656 - if (dashboard != null) {  
657 - CustomerId customerId = null;  
658 - if (!edge.getCustomerId().isNullUid() && dashboard.isAssignedToCustomer(edge.getCustomerId())) {  
659 - customerId = edge.getCustomerId();  
660 - }  
661 - DashboardUpdateMsg dashboardUpdateMsg =  
662 - ctx.getDashboardMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard, customerId);  
663 - downlinkMsg = DownlinkMsg.newBuilder()  
664 - .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg))  
665 - .build();  
666 - }  
667 - break;  
668 - case DELETED:  
669 - case UNASSIGNED_FROM_EDGE:  
670 - DashboardUpdateMsg dashboardUpdateMsg =  
671 - ctx.getDashboardMsgConstructor().constructDashboardDeleteMsg(dashboardId);  
672 - downlinkMsg = DownlinkMsg.newBuilder()  
673 - .addAllDashboardUpdateMsg(Collections.singletonList(dashboardUpdateMsg))  
674 - .build();  
675 - break;  
676 - }  
677 - log.trace("[{}] dashboard processed [{}]", this.sessionId, downlinkMsg);  
678 - return downlinkMsg;  
679 - }  
680 -  
681 - private DownlinkMsg processCustomer(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {  
682 - CustomerId customerId = new CustomerId(edgeEvent.getEntityId());  
683 - DownlinkMsg downlinkMsg = null;  
684 - switch (action) {  
685 - case ADDED:  
686 - case UPDATED:  
687 - Customer customer = ctx.getCustomerService().findCustomerById(edgeEvent.getTenantId(), customerId);  
688 - if (customer != null) {  
689 - CustomerUpdateMsg customerUpdateMsg =  
690 - ctx.getCustomerMsgConstructor().constructCustomerUpdatedMsg(msgType, customer);  
691 - downlinkMsg = DownlinkMsg.newBuilder()  
692 - .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg))  
693 - .build();  
694 - }  
695 - break;  
696 - case DELETED:  
697 - CustomerUpdateMsg customerUpdateMsg =  
698 - ctx.getCustomerMsgConstructor().constructCustomerDeleteMsg(customerId);  
699 - downlinkMsg = DownlinkMsg.newBuilder()  
700 - .addAllCustomerUpdateMsg(Collections.singletonList(customerUpdateMsg))  
701 - .build();  
702 - break;  
703 - }  
704 - log.trace("[{}] customer processed [{}]", this.sessionId, downlinkMsg);  
705 - return downlinkMsg;  
706 - }  
707 -  
708 - private DownlinkMsg processRuleChain(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {  
709 - RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());  
710 - DownlinkMsg downlinkMsg = null;  
711 - switch (action) {  
712 - case ADDED:  
713 - case UPDATED:  
714 - case ASSIGNED_TO_EDGE:  
715 - RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId);  
716 - if (ruleChain != null) {  
717 - RuleChainUpdateMsg ruleChainUpdateMsg =  
718 - ctx.getRuleChainMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain);  
719 - downlinkMsg = DownlinkMsg.newBuilder()  
720 - .addAllRuleChainUpdateMsg(Collections.singletonList(ruleChainUpdateMsg))  
721 - .build();  
722 - }  
723 - break;  
724 - case DELETED:  
725 - case UNASSIGNED_FROM_EDGE:  
726 - downlinkMsg = DownlinkMsg.newBuilder()  
727 - .addAllRuleChainUpdateMsg(Collections.singletonList(ctx.getRuleChainMsgConstructor().constructRuleChainDeleteMsg(ruleChainId)))  
728 - .build();  
729 - break;  
730 - }  
731 - log.trace("[{}] rule chain processed [{}]", this.sessionId, downlinkMsg);  
732 - return downlinkMsg;  
733 - }  
734 -  
735 - private DownlinkMsg processRuleChainMetadata(EdgeEvent edgeEvent, UpdateMsgType msgType) {  
736 - RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId());  
737 - RuleChain ruleChain = ctx.getRuleChainService().findRuleChainById(edgeEvent.getTenantId(), ruleChainId);  
738 - DownlinkMsg downlinkMsg = null;  
739 - if (ruleChain != null) {  
740 - RuleChainMetaData ruleChainMetaData = ctx.getRuleChainService().loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId);  
741 - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =  
742 - ctx.getRuleChainMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData);  
743 - if (ruleChainMetadataUpdateMsg != null) {  
744 - downlinkMsg = DownlinkMsg.newBuilder()  
745 - .addAllRuleChainMetadataUpdateMsg(Collections.singletonList(ruleChainMetadataUpdateMsg))  
746 - .build();  
747 - }  
748 - }  
749 - log.trace("[{}] rule chain metadata processed [{}]", this.sessionId, downlinkMsg);  
750 - return downlinkMsg;  
751 - }  
752 -  
753 - private DownlinkMsg processUser(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {  
754 - UserId userId = new UserId(edgeEvent.getEntityId());  
755 - DownlinkMsg downlinkMsg = null;  
756 - switch (edgeEdgeEventActionType) {  
757 - case ADDED:  
758 - case UPDATED:  
759 - User user = ctx.getUserService().findUserById(edgeEvent.getTenantId(), userId);  
760 - if (user != null) {  
761 - CustomerId customerId = getCustomerIdIfEdgeAssignedToCustomer(user);  
762 - downlinkMsg = DownlinkMsg.newBuilder()  
763 - .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserMsgConstructor().constructUserUpdatedMsg(msgType, user, customerId)))  
764 - .build();  
765 - }  
766 - break;  
767 - case DELETED:  
768 - downlinkMsg = DownlinkMsg.newBuilder()  
769 - .addAllUserUpdateMsg(Collections.singletonList(ctx.getUserMsgConstructor().constructUserDeleteMsg(userId)))  
770 - .build();  
771 - break;  
772 - case CREDENTIALS_UPDATED:  
773 - UserCredentials userCredentialsByUserId = ctx.getUserService().findUserCredentialsByUserId(edge.getTenantId(), userId);  
774 - if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {  
775 - UserCredentialsUpdateMsg userCredentialsUpdateMsg =  
776 - ctx.getUserMsgConstructor().constructUserCredentialsUpdatedMsg(userCredentialsByUserId);  
777 - downlinkMsg = DownlinkMsg.newBuilder()  
778 - .addAllUserCredentialsUpdateMsg(Collections.singletonList(userCredentialsUpdateMsg))  
779 - .build();  
780 - }  
781 - }  
782 - log.trace("[{}] user processed [{}]", this.sessionId, downlinkMsg);  
783 - return downlinkMsg;  
784 - }  
785 -  
786 - private CustomerId getCustomerIdIfEdgeAssignedToCustomer(HasCustomerId hasCustomerIdEntity) {  
787 - if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(hasCustomerIdEntity.getCustomerId())) {  
788 - return edge.getCustomerId();  
789 - } else {  
790 - return null;  
791 - }  
792 - }  
793 -  
794 - private DownlinkMsg processRelation(EdgeEvent edgeEvent, UpdateMsgType msgType) {  
795 - EntityRelation entityRelation = mapper.convertValue(edgeEvent.getBody(), EntityRelation.class);  
796 - RelationUpdateMsg r = ctx.getRelationMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation);  
797 - DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()  
798 - .addAllRelationUpdateMsg(Collections.singletonList(r))  
799 - .build();  
800 - log.trace("[{}] relation processed [{}]", this.sessionId, downlinkMsg);  
801 - return downlinkMsg;  
802 - }  
803 -  
804 - private DownlinkMsg processAlarm(EdgeEvent edgeEvent, UpdateMsgType msgType) {  
805 - DownlinkMsg downlinkMsg = null;  
806 - try {  
807 - AlarmId alarmId = new AlarmId(edgeEvent.getEntityId());  
808 - Alarm alarm = ctx.getAlarmService().findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId).get();  
809 - if (alarm != null) {  
810 - downlinkMsg = DownlinkMsg.newBuilder()  
811 - .addAllAlarmUpdateMsg(Collections.singletonList(ctx.getAlarmMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)))  
812 - .build();  
813 - }  
814 - } catch (Exception e) {  
815 - log.error("Can't process alarm msg [{}] [{}]", edgeEvent, msgType, e);  
816 - }  
817 - log.trace("[{}] alarm processed [{}]", this.sessionId, downlinkMsg);  
818 - return downlinkMsg;  
819 - }  
820 -  
821 - private DownlinkMsg processWidgetsBundle(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {  
822 - WidgetsBundleId widgetsBundleId = new WidgetsBundleId(edgeEvent.getEntityId());  
823 - DownlinkMsg downlinkMsg = null;  
824 - switch (edgeEdgeEventActionType) {  
825 - case ADDED:  
826 - case UPDATED:  
827 - WidgetsBundle widgetsBundle = ctx.getWidgetsBundleService().findWidgetsBundleById(edgeEvent.getTenantId(), widgetsBundleId);  
828 - if (widgetsBundle != null) {  
829 - WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =  
830 - ctx.getWidgetsBundleMsgConstructor().constructWidgetsBundleUpdateMsg(msgType, widgetsBundle);  
831 - downlinkMsg = DownlinkMsg.newBuilder()  
832 - .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg))  
833 - .build();  
834 - }  
835 - break;  
836 - case DELETED:  
837 - WidgetsBundleUpdateMsg widgetsBundleUpdateMsg =  
838 - ctx.getWidgetsBundleMsgConstructor().constructWidgetsBundleDeleteMsg(widgetsBundleId);  
839 - downlinkMsg = DownlinkMsg.newBuilder()  
840 - .addAllWidgetsBundleUpdateMsg(Collections.singletonList(widgetsBundleUpdateMsg))  
841 - .build();  
842 - break;  
843 - }  
844 - log.trace("[{}] widget bundle processed [{}]", this.sessionId, downlinkMsg);  
845 - return downlinkMsg;  
846 - }  
847 -  
848 - private DownlinkMsg processWidgetType(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType edgeEdgeEventActionType) {  
849 - WidgetTypeId widgetTypeId = new WidgetTypeId(edgeEvent.getEntityId());  
850 - DownlinkMsg downlinkMsg = null;  
851 - switch (edgeEdgeEventActionType) {  
852 - case ADDED:  
853 - case UPDATED:  
854 - WidgetType widgetType = ctx.getWidgetTypeService().findWidgetTypeById(edgeEvent.getTenantId(), widgetTypeId);  
855 - if (widgetType != null) {  
856 - WidgetTypeUpdateMsg widgetTypeUpdateMsg =  
857 - ctx.getWidgetTypeMsgConstructor().constructWidgetTypeUpdateMsg(msgType, widgetType);  
858 - downlinkMsg = DownlinkMsg.newBuilder()  
859 - .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg))  
860 - .build();  
861 - }  
862 - break;  
863 - case DELETED:  
864 - WidgetTypeUpdateMsg widgetTypeUpdateMsg =  
865 - ctx.getWidgetTypeMsgConstructor().constructWidgetTypeDeleteMsg(widgetTypeId);  
866 - downlinkMsg = DownlinkMsg.newBuilder()  
867 - .addAllWidgetTypeUpdateMsg(Collections.singletonList(widgetTypeUpdateMsg))  
868 - .build();  
869 - break;  
870 - }  
871 - log.trace("[{}] widget type processed [{}]", this.sessionId, downlinkMsg);  
872 - return downlinkMsg;  
873 - }  
874 -  
875 - private DownlinkMsg processAdminSettings(EdgeEvent edgeEvent) {  
876 - AdminSettings adminSettings = mapper.convertValue(edgeEvent.getBody(), AdminSettings.class);  
877 - AdminSettingsUpdateMsg t = ctx.getAdminSettingsMsgConstructor().constructAdminSettingsUpdateMsg(adminSettings);  
878 - DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()  
879 - .addAllAdminSettingsUpdateMsg(Collections.singletonList(t))  
880 - .build();  
881 - log.trace("[{}] admin settings processed [{}]", this.sessionId, downlinkMsg);  
882 - return downlinkMsg;  
883 - }  
884 -  
885 private UpdateMsgType getResponseMsgType(EdgeEventActionType actionType) { 547 private UpdateMsgType getResponseMsgType(EdgeEventActionType actionType) {
886 switch (actionType) { 548 switch (actionType) {
887 case UPDATED: 549 case UPDATED:
@@ -906,71 +568,77 @@ public final class EdgeGrpcSession implements Closeable { @@ -906,71 +568,77 @@ public final class EdgeGrpcSession implements Closeable {
906 } 568 }
907 } 569 }
908 570
909 - private DownlinkMsg constructEntityDataProtoMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) {  
910 - EntityDataProto entityDataProto = ctx.getEntityDataMsgConstructor().constructEntityDataMsg(entityId, actionType, entityData);  
911 - DownlinkMsg downlinkMsg = DownlinkMsg.newBuilder()  
912 - .addAllEntityData(Collections.singletonList(entityDataProto))  
913 - .build();  
914 - log.trace("[{}] entity data proto processed [{}]", this.sessionId, downlinkMsg);  
915 - return downlinkMsg;  
916 - }  
917 -  
918 private ListenableFuture<List<Void>> processUplinkMsg(UplinkMsg uplinkMsg) { 571 private ListenableFuture<List<Void>> processUplinkMsg(UplinkMsg uplinkMsg) {
919 List<ListenableFuture<Void>> result = new ArrayList<>(); 572 List<ListenableFuture<Void>> result = new ArrayList<>();
920 try { 573 try {
921 if (uplinkMsg.getEntityDataCount() > 0) { 574 if (uplinkMsg.getEntityDataCount() > 0) {
922 for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { 575 for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) {
923 - result.addAll(ctx.getTelemetryProcessor().onTelemetryUpdate(edge.getTenantId(), edge.getCustomerId(), entityData)); 576 + result.addAll(ctx.getTelemetryProcessor().processTelemetryFromEdge(edge.getTenantId(), edge.getCustomerId(), entityData));
924 } 577 }
925 } 578 }
926 if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { 579 if (uplinkMsg.getDeviceUpdateMsgCount() > 0) {
927 for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { 580 for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) {
928 - result.add(ctx.getDeviceProcessor().onDeviceUpdate(edge.getTenantId(), edge, deviceUpdateMsg)); 581 + result.add(ctx.getDeviceProcessor().processDeviceFromEdge(edge.getTenantId(), edge, deviceUpdateMsg));
929 } 582 }
930 } 583 }
931 if (uplinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) { 584 if (uplinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) {
932 for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) { 585 for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg : uplinkMsg.getDeviceCredentialsUpdateMsgList()) {
933 - result.add(ctx.getDeviceProcessor().onDeviceCredentialsUpdate(edge.getTenantId(), deviceCredentialsUpdateMsg)); 586 + result.add(ctx.getDeviceProcessor().processDeviceCredentialsFromEdge(edge.getTenantId(), deviceCredentialsUpdateMsg));
934 } 587 }
935 } 588 }
936 if (uplinkMsg.getAlarmUpdateMsgCount() > 0) { 589 if (uplinkMsg.getAlarmUpdateMsgCount() > 0) {
937 for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) { 590 for (AlarmUpdateMsg alarmUpdateMsg : uplinkMsg.getAlarmUpdateMsgList()) {
938 - result.add(ctx.getAlarmProcessor().onAlarmUpdate(edge.getTenantId(), alarmUpdateMsg)); 591 + result.add(ctx.getAlarmProcessor().processAlarmFromEdge(edge.getTenantId(), alarmUpdateMsg));
939 } 592 }
940 } 593 }
941 if (uplinkMsg.getRelationUpdateMsgCount() > 0) { 594 if (uplinkMsg.getRelationUpdateMsgCount() > 0) {
942 for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) { 595 for (RelationUpdateMsg relationUpdateMsg : uplinkMsg.getRelationUpdateMsgList()) {
943 - result.add(ctx.getRelationProcessor().onRelationUpdate(edge.getTenantId(), relationUpdateMsg)); 596 + result.add(ctx.getRelationProcessor().processRelationFromEdge(edge.getTenantId(), relationUpdateMsg));
944 } 597 }
945 } 598 }
946 if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) { 599 if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) {
947 for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) { 600 for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) {
948 - result.add(ctx.getSyncEdgeService().processRuleChainMetadataRequestMsg(edge.getTenantId(), edge, ruleChainMetadataRequestMsg)); 601 + result.add(ctx.getEdgeRequestsService().processRuleChainMetadataRequestMsg(edge.getTenantId(), edge, ruleChainMetadataRequestMsg));
949 } 602 }
950 } 603 }
951 if (uplinkMsg.getAttributesRequestMsgCount() > 0) { 604 if (uplinkMsg.getAttributesRequestMsgCount() > 0) {
952 for (AttributesRequestMsg attributesRequestMsg : uplinkMsg.getAttributesRequestMsgList()) { 605 for (AttributesRequestMsg attributesRequestMsg : uplinkMsg.getAttributesRequestMsgList()) {
953 - result.add(ctx.getSyncEdgeService().processAttributesRequestMsg(edge.getTenantId(), edge, attributesRequestMsg)); 606 + result.add(ctx.getEdgeRequestsService().processAttributesRequestMsg(edge.getTenantId(), edge, attributesRequestMsg));
954 } 607 }
955 } 608 }
956 if (uplinkMsg.getRelationRequestMsgCount() > 0) { 609 if (uplinkMsg.getRelationRequestMsgCount() > 0) {
957 for (RelationRequestMsg relationRequestMsg : uplinkMsg.getRelationRequestMsgList()) { 610 for (RelationRequestMsg relationRequestMsg : uplinkMsg.getRelationRequestMsgList()) {
958 - result.add(ctx.getSyncEdgeService().processRelationRequestMsg(edge.getTenantId(), edge, relationRequestMsg)); 611 + result.add(ctx.getEdgeRequestsService().processRelationRequestMsg(edge.getTenantId(), edge, relationRequestMsg));
959 } 612 }
960 } 613 }
961 if (uplinkMsg.getUserCredentialsRequestMsgCount() > 0) { 614 if (uplinkMsg.getUserCredentialsRequestMsgCount() > 0) {
962 for (UserCredentialsRequestMsg userCredentialsRequestMsg : uplinkMsg.getUserCredentialsRequestMsgList()) { 615 for (UserCredentialsRequestMsg userCredentialsRequestMsg : uplinkMsg.getUserCredentialsRequestMsgList()) {
963 - result.add(ctx.getSyncEdgeService().processUserCredentialsRequestMsg(edge.getTenantId(), edge, userCredentialsRequestMsg)); 616 + result.add(ctx.getEdgeRequestsService().processUserCredentialsRequestMsg(edge.getTenantId(), edge, userCredentialsRequestMsg));
964 } 617 }
965 } 618 }
966 if (uplinkMsg.getDeviceCredentialsRequestMsgCount() > 0) { 619 if (uplinkMsg.getDeviceCredentialsRequestMsgCount() > 0) {
967 for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg : uplinkMsg.getDeviceCredentialsRequestMsgList()) { 620 for (DeviceCredentialsRequestMsg deviceCredentialsRequestMsg : uplinkMsg.getDeviceCredentialsRequestMsgList()) {
968 - result.add(ctx.getSyncEdgeService().processDeviceCredentialsRequestMsg(edge.getTenantId(), edge, deviceCredentialsRequestMsg)); 621 + result.add(ctx.getEdgeRequestsService().processDeviceCredentialsRequestMsg(edge.getTenantId(), edge, deviceCredentialsRequestMsg));
969 } 622 }
970 } 623 }
971 if (uplinkMsg.getDeviceRpcCallMsgCount() > 0) { 624 if (uplinkMsg.getDeviceRpcCallMsgCount() > 0) {
972 for (DeviceRpcCallMsg deviceRpcCallMsg : uplinkMsg.getDeviceRpcCallMsgList()) { 625 for (DeviceRpcCallMsg deviceRpcCallMsg : uplinkMsg.getDeviceRpcCallMsgList()) {
973 - result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseMsg(edge.getTenantId(), deviceRpcCallMsg)); 626 + result.add(ctx.getDeviceProcessor().processDeviceRpcCallResponseFromEdge(edge.getTenantId(), deviceRpcCallMsg));
  627 + }
  628 + }
  629 + if (uplinkMsg.getDeviceProfileDevicesRequestMsgCount() > 0) {
  630 + for (DeviceProfileDevicesRequestMsg deviceProfileDevicesRequestMsg : uplinkMsg.getDeviceProfileDevicesRequestMsgList()) {
  631 + result.add(ctx.getEdgeRequestsService().processDeviceProfileDevicesRequestMsg(edge.getTenantId(), edge, deviceProfileDevicesRequestMsg));
  632 + }
  633 + }
  634 + if (uplinkMsg.getWidgetBundleTypesRequestMsgCount() > 0) {
  635 + for (WidgetBundleTypesRequestMsg widgetBundleTypesRequestMsg : uplinkMsg.getWidgetBundleTypesRequestMsgList()) {
  636 + result.add(ctx.getEdgeRequestsService().processWidgetBundleTypesRequestMsg(edge.getTenantId(), edge, widgetBundleTypesRequestMsg));
  637 + }
  638 + }
  639 + if (uplinkMsg.getEntityViewsRequestMsgCount() > 0) {
  640 + for (EntityViewsRequestMsg entityViewRequestMsg : uplinkMsg.getEntityViewsRequestMsgList()) {
  641 + result.add(ctx.getEdgeRequestsService().processEntityViewsRequestMsg(edge.getTenantId(), edge, entityViewRequestMsg));
974 } 642 }
975 } 643 }
976 } catch (Exception e) { 644 } catch (Exception e) {
@@ -1026,7 +694,7 @@ public final class EdgeGrpcSession implements Closeable { @@ -1026,7 +694,7 @@ public final class EdgeGrpcSession implements Closeable {
1026 .setCloudType("CE"); 694 .setCloudType("CE");
1027 if (edge.getCustomerId() != null) { 695 if (edge.getCustomerId() != null) {
1028 builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits()) 696 builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits())
1029 - .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits()); 697 + .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits());
1030 } 698 }
1031 return builder 699 return builder
1032 .build(); 700 .build();
@@ -17,12 +17,15 @@ package org.thingsboard.server.service.edge.rpc; @@ -17,12 +17,15 @@ package org.thingsboard.server.service.edge.rpc;
17 17
18 import org.thingsboard.server.common.data.edge.Edge; 18 import org.thingsboard.server.common.data.edge.Edge;
19 import org.thingsboard.server.common.data.id.EdgeId; 19 import org.thingsboard.server.common.data.id.EdgeId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
20 21
21 public interface EdgeRpcService { 22 public interface EdgeRpcService {
22 23
23 - void updateEdge(Edge edge); 24 + void updateEdge(TenantId tenantId, Edge edge);
24 25
25 - void deleteEdge(EdgeId edgeId); 26 + void deleteEdge(TenantId tenantId, EdgeId edgeId);
26 27
27 - void onEdgeEvent(EdgeId edgeId); 28 + void onEdgeEvent(TenantId tenantId, EdgeId edgeId);
  29 +
  30 + void startSyncProcess(TenantId tenantId, EdgeId edgeId);
28 } 31 }
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc;
  17 +
  18 +import com.google.common.util.concurrent.SettableFuture;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
  21 +
  22 +import java.util.LinkedHashMap;
  23 +import java.util.Map;
  24 +import java.util.concurrent.ScheduledFuture;
  25 +
  26 +@Data
  27 +public class EdgeSessionState {
  28 +
  29 + private final Map<Integer, DownlinkMsg> pendingMsgsMap = new LinkedHashMap<>();
  30 + private SettableFuture<Void> sendDownlinkMsgsFuture;
  31 + private ScheduledFuture<?> scheduledSendDownlinkTask;
  32 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc;
  17 +
  18 +import org.thingsboard.server.common.data.edge.Edge;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.service.edge.EdgeContextComponent;
  21 +import org.thingsboard.server.service.edge.rpc.fetch.AdminSettingsEdgeEventFetcher;
  22 +import org.thingsboard.server.service.edge.rpc.fetch.AssetsEdgeEventFetcher;
  23 +import org.thingsboard.server.service.edge.rpc.fetch.CustomerEdgeEventFetcher;
  24 +import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetcher;
  25 +import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher;
  26 +import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher;
  27 +import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
  28 +import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
  29 +import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher;
  30 +import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher;
  31 +import org.thingsboard.server.service.edge.rpc.fetch.TenantWidgetsBundlesEdgeEventFetcher;
  32 +
  33 +import java.util.LinkedList;
  34 +import java.util.List;
  35 +import java.util.NoSuchElementException;
  36 +
  37 +public class EdgeSyncCursor {
  38 +
  39 + List<EdgeEventFetcher> fetchers = new LinkedList<>();
  40 +
  41 + int currentIdx = 0;
  42 +
  43 + public EdgeSyncCursor(EdgeContextComponent ctx, Edge edge) {
  44 + fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
  45 + fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
  46 + fetchers.add(new DeviceProfilesEdgeEventFetcher(ctx.getDeviceProfileService()));
  47 + fetchers.add(new RuleChainsEdgeEventFetcher(ctx.getRuleChainService()));
  48 + fetchers.add(new TenantAdminUsersEdgeEventFetcher(ctx.getUserService()));
  49 + if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
  50 + fetchers.add(new CustomerEdgeEventFetcher());
  51 + fetchers.add(new CustomerUsersEdgeEventFetcher(ctx.getUserService(), edge.getCustomerId()));
  52 + }
  53 + fetchers.add(new AdminSettingsEdgeEventFetcher(ctx.getAdminSettingsService()));
  54 + fetchers.add(new AssetsEdgeEventFetcher(ctx.getAssetService()));
  55 + fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
  56 + }
  57 +
  58 + public boolean hasNext() {
  59 + return fetchers.size() > currentIdx;
  60 + }
  61 +
  62 + public EdgeEventFetcher getNext() {
  63 + if (!hasNext()) {
  64 + throw new NoSuchElementException();
  65 + }
  66 + EdgeEventFetcher edgeEventFetcher = fetchers.get(currentIdx);
  67 + currentIdx++;
  68 + return edgeEventFetcher;
  69 + }
  70 +
  71 + public int getCurrentIdx() {
  72 + return currentIdx;
  73 + }
  74 +}
@@ -18,7 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor; @@ -18,7 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor;
18 import org.springframework.stereotype.Component; 18 import org.springframework.stereotype.Component;
19 import org.thingsboard.server.common.data.AdminSettings; 19 import org.thingsboard.server.common.data.AdminSettings;
20 import org.thingsboard.common.util.JacksonUtil; 20 import org.thingsboard.common.util.JacksonUtil;
21 -import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg; 21 +import org.thingsboard.server.gen.edge.v1.AdminSettingsUpdateMsg;
22 import org.thingsboard.server.queue.util.TbCoreComponent; 22 import org.thingsboard.server.queue.util.TbCoreComponent;
23 23
24 @Component 24 @Component
@@ -26,8 +26,8 @@ import org.thingsboard.server.dao.asset.AssetService; @@ -26,8 +26,8 @@ import org.thingsboard.server.dao.asset.AssetService;
26 import org.thingsboard.server.dao.device.DeviceService; 26 import org.thingsboard.server.dao.device.DeviceService;
27 import org.thingsboard.server.dao.entityview.EntityViewService; 27 import org.thingsboard.server.dao.entityview.EntityViewService;
28 import org.thingsboard.common.util.JacksonUtil; 28 import org.thingsboard.common.util.JacksonUtil;
29 -import org.thingsboard.server.gen.edge.AlarmUpdateMsg;  
30 -import org.thingsboard.server.gen.edge.UpdateMsgType; 29 +import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
  30 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
31 import org.thingsboard.server.queue.util.TbCoreComponent; 31 import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
33 @Component 33 @Component
@@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.asset.Asset; @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.asset.Asset;
20 import org.thingsboard.server.common.data.id.AssetId; 20 import org.thingsboard.server.common.data.id.AssetId;
21 import org.thingsboard.server.common.data.id.CustomerId; 21 import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.common.util.JacksonUtil; 22 import org.thingsboard.common.util.JacksonUtil;
23 -import org.thingsboard.server.gen.edge.AssetUpdateMsg;  
24 -import org.thingsboard.server.gen.edge.UpdateMsgType; 23 +import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
  24 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 import org.thingsboard.server.queue.util.TbCoreComponent; 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 @Component 27 @Component
@@ -19,8 +19,8 @@ import org.springframework.stereotype.Component; @@ -19,8 +19,8 @@ import org.springframework.stereotype.Component;
19 import org.thingsboard.server.common.data.Customer; 19 import org.thingsboard.server.common.data.Customer;
20 import org.thingsboard.server.common.data.id.CustomerId; 20 import org.thingsboard.server.common.data.id.CustomerId;
21 import org.thingsboard.common.util.JacksonUtil; 21 import org.thingsboard.common.util.JacksonUtil;
22 -import org.thingsboard.server.gen.edge.CustomerUpdateMsg;  
23 -import org.thingsboard.server.gen.edge.UpdateMsgType; 22 +import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
  23 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
24 import org.thingsboard.server.queue.util.TbCoreComponent; 24 import org.thingsboard.server.queue.util.TbCoreComponent;
25 25
26 @Component 26 @Component
@@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.Dashboard; @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.Dashboard;
20 import org.thingsboard.server.common.data.id.CustomerId; 20 import org.thingsboard.server.common.data.id.CustomerId;
21 import org.thingsboard.server.common.data.id.DashboardId; 21 import org.thingsboard.server.common.data.id.DashboardId;
22 import org.thingsboard.common.util.JacksonUtil; 22 import org.thingsboard.common.util.JacksonUtil;
23 -import org.thingsboard.server.gen.edge.DashboardUpdateMsg;  
24 -import org.thingsboard.server.gen.edge.UpdateMsgType; 23 +import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
  24 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
25 import org.thingsboard.server.queue.util.TbCoreComponent; 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 @Component 27 @Component
@@ -23,11 +23,11 @@ import org.thingsboard.server.common.data.Device; @@ -23,11 +23,11 @@ import org.thingsboard.server.common.data.Device;
23 import org.thingsboard.server.common.data.id.CustomerId; 23 import org.thingsboard.server.common.data.id.CustomerId;
24 import org.thingsboard.server.common.data.id.DeviceId; 24 import org.thingsboard.server.common.data.id.DeviceId;
25 import org.thingsboard.server.common.data.security.DeviceCredentials; 25 import org.thingsboard.server.common.data.security.DeviceCredentials;
26 -import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;  
27 -import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;  
28 -import org.thingsboard.server.gen.edge.DeviceUpdateMsg;  
29 -import org.thingsboard.server.gen.edge.RpcRequestMsg;  
30 -import org.thingsboard.server.gen.edge.UpdateMsgType; 26 +import org.thingsboard.server.gen.edge.v1.DeviceCredentialsUpdateMsg;
  27 +import org.thingsboard.server.gen.edge.v1.DeviceRpcCallMsg;
  28 +import org.thingsboard.server.gen.edge.v1.DeviceUpdateMsg;
  29 +import org.thingsboard.server.gen.edge.v1.RpcRequestMsg;
  30 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
31 import org.thingsboard.server.queue.util.TbCoreComponent; 31 import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
33 import java.util.UUID; 33 import java.util.UUID;
@@ -21,8 +21,8 @@ import org.springframework.stereotype.Component; @@ -21,8 +21,8 @@ import org.springframework.stereotype.Component;
21 import org.thingsboard.server.common.data.DeviceProfile; 21 import org.thingsboard.server.common.data.DeviceProfile;
22 import org.thingsboard.server.common.data.id.DeviceProfileId; 22 import org.thingsboard.server.common.data.id.DeviceProfileId;
23 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 23 import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
24 -import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg;  
25 -import org.thingsboard.server.gen.edge.UpdateMsgType; 24 +import org.thingsboard.server.gen.edge.v1.DeviceProfileUpdateMsg;
  25 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
26 import org.thingsboard.server.queue.util.TbCoreComponent; 26 import org.thingsboard.server.queue.util.TbCoreComponent;
27 27
28 @Component 28 @Component
@@ -41,7 +41,7 @@ public class DeviceProfileMsgConstructor { @@ -41,7 +41,7 @@ public class DeviceProfileMsgConstructor {
41 .setDefault(deviceProfile.isDefault()) 41 .setDefault(deviceProfile.isDefault())
42 .setType(deviceProfile.getType().name()) 42 .setType(deviceProfile.getType().name())
43 .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData()))); 43 .setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData())));
44 - // TODO: voba - should this be always null at the moment?? 44 + // TODO: @voba - add possibility to setup edge rule chain as device profile default
45 // if (deviceProfile.getDefaultRuleChainId() != null) { 45 // if (deviceProfile.getDefaultRuleChainId() != null) {
46 // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits()) 46 // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits())
47 // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits()); 47 // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits());
@@ -25,8 +25,8 @@ import org.springframework.stereotype.Component; @@ -25,8 +25,8 @@ import org.springframework.stereotype.Component;
25 import org.thingsboard.server.common.data.edge.EdgeEventActionType; 25 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
26 import org.thingsboard.server.common.data.id.EntityId; 26 import org.thingsboard.server.common.data.id.EntityId;
27 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 27 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
28 -import org.thingsboard.server.gen.edge.AttributeDeleteMsg;  
29 -import org.thingsboard.server.gen.edge.EntityDataProto; 28 +import org.thingsboard.server.gen.edge.v1.AttributeDeleteMsg;
  29 +import org.thingsboard.server.gen.edge.v1.EntityDataProto;
30 import org.thingsboard.server.gen.transport.TransportProtos; 30 import org.thingsboard.server.gen.transport.TransportProtos;
31 import org.thingsboard.server.queue.util.TbCoreComponent; 31 import org.thingsboard.server.queue.util.TbCoreComponent;
32 32
@@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.EntityView; @@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.EntityView;
20 import org.thingsboard.server.common.data.id.CustomerId; 20 import org.thingsboard.server.common.data.id.CustomerId;
21 import org.thingsboard.server.common.data.id.EntityViewId; 21 import org.thingsboard.server.common.data.id.EntityViewId;
22 import org.thingsboard.common.util.JacksonUtil; 22 import org.thingsboard.common.util.JacksonUtil;
23 -import org.thingsboard.server.gen.edge.EdgeEntityType;  
24 -import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;  
25 -import org.thingsboard.server.gen.edge.UpdateMsgType; 23 +import org.thingsboard.server.gen.edge.v1.EdgeEntityType;
  24 +import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
  25 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
26 import org.thingsboard.server.queue.util.TbCoreComponent; 26 import org.thingsboard.server.queue.util.TbCoreComponent;
27 27
28 @Component 28 @Component
@@ -18,8 +18,8 @@ package org.thingsboard.server.service.edge.rpc.constructor; @@ -18,8 +18,8 @@ package org.thingsboard.server.service.edge.rpc.constructor;
18 import org.springframework.stereotype.Component; 18 import org.springframework.stereotype.Component;
19 import org.thingsboard.server.common.data.relation.EntityRelation; 19 import org.thingsboard.server.common.data.relation.EntityRelation;
20 import org.thingsboard.common.util.JacksonUtil; 20 import org.thingsboard.common.util.JacksonUtil;
21 -import org.thingsboard.server.gen.edge.RelationUpdateMsg;  
22 -import org.thingsboard.server.gen.edge.UpdateMsgType; 21 +import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
  22 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
23 import org.thingsboard.server.queue.util.TbCoreComponent; 23 import org.thingsboard.server.queue.util.TbCoreComponent;
24 24
25 @Component 25 @Component
@@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; @@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
26 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 26 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
27 import org.thingsboard.server.common.data.rule.RuleNode; 27 import org.thingsboard.server.common.data.rule.RuleNode;
28 import org.thingsboard.common.util.JacksonUtil; 28 import org.thingsboard.common.util.JacksonUtil;
29 -import org.thingsboard.server.gen.edge.NodeConnectionInfoProto;  
30 -import org.thingsboard.server.gen.edge.RuleChainConnectionInfoProto;  
31 -import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;  
32 -import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;  
33 -import org.thingsboard.server.gen.edge.RuleNodeProto;  
34 -import org.thingsboard.server.gen.edge.UpdateMsgType; 29 +import org.thingsboard.server.gen.edge.v1.NodeConnectionInfoProto;
  30 +import org.thingsboard.server.gen.edge.v1.RuleChainConnectionInfoProto;
  31 +import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
  32 +import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
  33 +import org.thingsboard.server.gen.edge.v1.RuleNodeProto;
  34 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
35 import org.thingsboard.server.queue.util.TbCoreComponent; 35 import org.thingsboard.server.queue.util.TbCoreComponent;
36 36
37 import java.util.ArrayList; 37 import java.util.ArrayList;
@@ -21,9 +21,9 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -21,9 +21,9 @@ import org.thingsboard.server.common.data.id.CustomerId;
21 import org.thingsboard.server.common.data.id.UserId; 21 import org.thingsboard.server.common.data.id.UserId;
22 import org.thingsboard.server.common.data.security.UserCredentials; 22 import org.thingsboard.server.common.data.security.UserCredentials;
23 import org.thingsboard.common.util.JacksonUtil; 23 import org.thingsboard.common.util.JacksonUtil;
24 -import org.thingsboard.server.gen.edge.UpdateMsgType;  
25 -import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;  
26 -import org.thingsboard.server.gen.edge.UserUpdateMsg; 24 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  25 +import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
  26 +import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
27 import org.thingsboard.server.queue.util.TbCoreComponent; 27 import org.thingsboard.server.queue.util.TbCoreComponent;
28 28
29 @Component 29 @Component
@@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.id.TenantId;
20 import org.thingsboard.server.common.data.id.WidgetTypeId; 20 import org.thingsboard.server.common.data.id.WidgetTypeId;
21 import org.thingsboard.server.common.data.widget.WidgetType; 21 import org.thingsboard.server.common.data.widget.WidgetType;
22 import org.thingsboard.common.util.JacksonUtil; 22 import org.thingsboard.common.util.JacksonUtil;
23 -import org.thingsboard.server.gen.edge.UpdateMsgType;  
24 -import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg; 23 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  24 +import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
25 import org.thingsboard.server.queue.util.TbCoreComponent; 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 @Component 27 @Component
@@ -20,8 +20,8 @@ import org.springframework.stereotype.Component; @@ -20,8 +20,8 @@ import org.springframework.stereotype.Component;
20 import org.thingsboard.server.common.data.id.TenantId; 20 import org.thingsboard.server.common.data.id.TenantId;
21 import org.thingsboard.server.common.data.id.WidgetsBundleId; 21 import org.thingsboard.server.common.data.id.WidgetsBundleId;
22 import org.thingsboard.server.common.data.widget.WidgetsBundle; 22 import org.thingsboard.server.common.data.widget.WidgetsBundle;
23 -import org.thingsboard.server.gen.edge.UpdateMsgType;  
24 -import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; 23 +import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
  24 +import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
25 import org.thingsboard.server.queue.util.TbCoreComponent; 25 import org.thingsboard.server.queue.util.TbCoreComponent;
26 26
27 import java.nio.charset.StandardCharsets; 27 import java.nio.charset.StandardCharsets;
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import com.datastax.oss.driver.api.core.uuid.Uuids;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import com.fasterxml.jackson.databind.ObjectMapper;
  21 +import com.fasterxml.jackson.databind.node.ObjectNode;
  22 +import lombok.AllArgsConstructor;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.apache.commons.io.FileUtils;
  25 +import org.apache.commons.lang3.StringUtils;
  26 +import org.apache.commons.lang3.text.WordUtils;
  27 +import org.springframework.core.io.DefaultResourceLoader;
  28 +import org.thingsboard.server.common.data.AdminSettings;
  29 +import org.thingsboard.server.common.data.edge.Edge;
  30 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  31 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  32 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  33 +import org.thingsboard.server.common.data.id.AdminSettingsId;
  34 +import org.thingsboard.server.common.data.id.TenantId;
  35 +import org.thingsboard.server.common.data.page.PageData;
  36 +import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.dao.settings.AdminSettingsService;
  38 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  39 +
  40 +import java.io.File;
  41 +import java.nio.charset.StandardCharsets;
  42 +import java.util.ArrayList;
  43 +import java.util.HashMap;
  44 +import java.util.List;
  45 +import java.util.Map;
  46 +import java.util.regex.Matcher;
  47 +import java.util.regex.Pattern;
  48 +
  49 +@AllArgsConstructor
  50 +@Slf4j
  51 +public class AdminSettingsEdgeEventFetcher implements EdgeEventFetcher {
  52 +
  53 + private static final ObjectMapper mapper = new ObjectMapper();
  54 +
  55 + private final AdminSettingsService adminSettingsService;
  56 +
  57 + @Override
  58 + public PageLink getPageLink(int pageSize) {
  59 + return null;
  60 + }
  61 +
  62 + @Override
  63 + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) throws Exception {
  64 + List<EdgeEvent> result = new ArrayList<>();
  65 +
  66 + AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
  67 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  68 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)));
  69 +
  70 + AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue());
  71 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  72 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)));
  73 +
  74 + AdminSettings systemMailTemplates = loadMailTemplates();
  75 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  76 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)));
  77 +
  78 + AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue());
  79 + result.add(EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS,
  80 + EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)));
  81 +
  82 + // @voba - returns PageData object to be in sync with other fetchers
  83 + return new PageData<>(result, 1, result.size(), false);
  84 + }
  85 +
  86 + private AdminSettings loadMailTemplates() throws Exception {
  87 + Map<String, Object> mailTemplates = new HashMap<>();
  88 + Pattern startPattern = Pattern.compile("<div class=\"content\".*?>");
  89 + Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>");
  90 + File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles();
  91 + for (File file : files) {
  92 + Map<String, String> mailTemplate = new HashMap<>();
  93 + String name = validateName(file.getName());
  94 + String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
  95 + Matcher start = startPattern.matcher(stringTemplate);
  96 + Matcher end = endPattern.matcher(stringTemplate);
  97 + if (start.find() && end.find()) {
  98 + String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", "");
  99 + String subject = StringUtils.substringBetween(body, "<h2>", "</h2>");
  100 + mailTemplate.put("subject", subject);
  101 + mailTemplate.put("body", body);
  102 + mailTemplates.put(name, mailTemplate);
  103 + } else {
  104 + log.error("Can't load mail template from file {}", file.getName());
  105 + }
  106 + }
  107 + AdminSettings adminSettings = new AdminSettings();
  108 + adminSettings.setId(new AdminSettingsId(Uuids.timeBased()));
  109 + adminSettings.setKey("mailTemplates");
  110 + adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class));
  111 + return adminSettings;
  112 + }
  113 +
  114 + private String validateName(String name) throws Exception {
  115 + StringBuilder nameBuilder = new StringBuilder();
  116 + name = name.replace(".vm", "");
  117 + String[] nameParts = name.split("\\.");
  118 + if (nameParts.length >= 1) {
  119 + nameBuilder.append(nameParts[0]);
  120 + for (int i = 1; i < nameParts.length; i++) {
  121 + String word = WordUtils.capitalize(nameParts[i]);
  122 + nameBuilder.append(word);
  123 + }
  124 + return nameBuilder.toString();
  125 + } else {
  126 + throw new Exception("Error during filename validation");
  127 + }
  128 + }
  129 +
  130 + private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) {
  131 + AdminSettings tenantMailSettings = new AdminSettings();
  132 + jsonValue.put("useSystemMailSettings", true);
  133 + tenantMailSettings.setJsonValue(jsonValue);
  134 + tenantMailSettings.setKey(key);
  135 + return tenantMailSettings;
  136 + }
  137 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.asset.Asset;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.asset.AssetService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@AllArgsConstructor
  32 +@Slf4j
  33 +public class AssetsEdgeEventFetcher extends BasePageableEdgeEventFetcher<Asset> {
  34 +
  35 + private final AssetService assetService;
  36 +
  37 + @Override
  38 + PageData<Asset> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return assetService.findAssetsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, Asset asset) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET,
  45 + EdgeEventActionType.ADDED, asset.getId(), null);
  46 + }
  47 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.BaseData;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.EntityId;
  26 +import org.thingsboard.server.common.data.id.EventId;
  27 +import org.thingsboard.server.common.data.id.HasId;
  28 +import org.thingsboard.server.common.data.id.HasUUID;
  29 +import org.thingsboard.server.common.data.id.IdBased;
  30 +import org.thingsboard.server.common.data.id.RuleChainId;
  31 +import org.thingsboard.server.common.data.id.TenantId;
  32 +import org.thingsboard.server.common.data.id.UUIDBased;
  33 +import org.thingsboard.server.common.data.page.PageData;
  34 +import org.thingsboard.server.common.data.page.PageLink;
  35 +import org.thingsboard.server.common.data.rule.RuleChain;
  36 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  37 +
  38 +import java.util.ArrayList;
  39 +import java.util.List;
  40 +
  41 +@Slf4j
  42 +public abstract class BasePageableEdgeEventFetcher<T> implements EdgeEventFetcher {
  43 +
  44 + @Override
  45 + public PageLink getPageLink(int pageSize) {
  46 + return new PageLink(pageSize);
  47 + }
  48 +
  49 + @Override
  50 + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
  51 + log.trace("[{}] start fetching edge events [{}]", tenantId, edge.getId());
  52 + PageData<T> pageData = fetchPageData(tenantId, edge, pageLink);
  53 + List<EdgeEvent> result = new ArrayList<>();
  54 + if (!pageData.getData().isEmpty()) {
  55 + for (T entity : pageData.getData()) {
  56 + result.add(constructEdgeEvent(tenantId, edge, entity));
  57 + }
  58 + }
  59 + return new PageData<>(result, pageData.getTotalPages(), pageData.getTotalElements(), pageData.hasNext());
  60 + }
  61 +
  62 + abstract PageData<T> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink);
  63 +
  64 + abstract EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, T entity);
  65 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.User;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.user.UserService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@Slf4j
  32 +@AllArgsConstructor
  33 +public abstract class BaseUsersEdgeEventFetcher extends BasePageableEdgeEventFetcher<User> {
  34 +
  35 + protected final UserService userService;
  36 +
  37 + @Override
  38 + PageData<User> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return findUsers(tenantId, pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, User user) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER,
  45 + EdgeEventActionType.ADDED, user.getId(), null);
  46 + }
  47 +
  48 + protected abstract PageData<User> findUsers(TenantId tenantId, PageLink pageLink);
  49 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.edge.Edge;
  21 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  22 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.page.PageLink;
  27 +import org.thingsboard.server.common.data.widget.WidgetsBundle;
  28 +import org.thingsboard.server.dao.widget.WidgetsBundleService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@Slf4j
  32 +@AllArgsConstructor
  33 +public abstract class BaseWidgetsBundlesEdgeEventFetcher extends BasePageableEdgeEventFetcher<WidgetsBundle> {
  34 +
  35 + protected final WidgetsBundleService widgetsBundleService;
  36 +
  37 + @Override
  38 + PageData<WidgetsBundle> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return findWidgetsBundles(tenantId, pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, WidgetsBundle widgetsBundle) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE,
  45 + EdgeEventActionType.ADDED, widgetsBundle.getId(), null);
  46 + }
  47 +
  48 + protected abstract PageData<WidgetsBundle> findWidgetsBundles(TenantId tenantId, PageLink pageLink);
  49 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.edge.Edge;
  21 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  22 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.page.PageLink;
  27 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  28 +
  29 +import java.util.ArrayList;
  30 +import java.util.List;
  31 +
  32 +@AllArgsConstructor
  33 +@Slf4j
  34 +public class CustomerEdgeEventFetcher implements EdgeEventFetcher {
  35 +
  36 + @Override
  37 + public PageLink getPageLink(int pageSize) {
  38 + return null;
  39 + }
  40 +
  41 + @Override
  42 + public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
  43 + List<EdgeEvent> result = new ArrayList<>();
  44 + result.add(EdgeEventUtils.constructEdgeEvent(edge.getTenantId(), edge.getId(),
  45 + EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null));
  46 + // @voba - returns PageData object to be in sync with other fetchers
  47 + return new PageData<>(result, 1, result.size(), false);
  48 + }
  49 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import org.thingsboard.server.common.data.User;
  19 +import org.thingsboard.server.common.data.edge.Edge;
  20 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  21 +import org.thingsboard.server.common.data.id.CustomerId;
  22 +import org.thingsboard.server.common.data.id.TenantId;
  23 +import org.thingsboard.server.common.data.page.PageData;
  24 +import org.thingsboard.server.common.data.page.PageLink;
  25 +import org.thingsboard.server.dao.user.UserService;
  26 +
  27 +public class CustomerUsersEdgeEventFetcher extends BaseUsersEdgeEventFetcher {
  28 +
  29 + private final CustomerId customerId;
  30 +
  31 + public CustomerUsersEdgeEventFetcher(UserService userService, CustomerId customerId) {
  32 + super(userService);
  33 + this.customerId = customerId;
  34 + }
  35 +
  36 + @Override
  37 + protected PageData<User> findUsers(TenantId tenantId, PageLink pageLink) {
  38 + return userService.findCustomerUsers(tenantId, customerId, pageLink);
  39 + }
  40 +
  41 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.DashboardInfo;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.dashboard.DashboardService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@AllArgsConstructor
  32 +@Slf4j
  33 +public class DashboardsEdgeEventFetcher extends BasePageableEdgeEventFetcher<DashboardInfo> {
  34 +
  35 + private final DashboardService dashboardService;
  36 +
  37 + @Override
  38 + PageData<DashboardInfo> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, DashboardInfo dashboardInfo) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DASHBOARD,
  45 + EdgeEventActionType.ADDED, dashboardInfo.getId(), null);
  46 + }
  47 +}
  1 +/**
  2 + * Copyright © 2016-2021 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.service.edge.rpc.fetch;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.thingsboard.server.common.data.DeviceProfile;
  21 +import org.thingsboard.server.common.data.edge.Edge;
  22 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  23 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  24 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.page.PageLink;
  28 +import org.thingsboard.server.dao.device.DeviceProfileService;
  29 +import org.thingsboard.server.service.edge.rpc.EdgeEventUtils;
  30 +
  31 +@AllArgsConstructor
  32 +@Slf4j
  33 +public class DeviceProfilesEdgeEventFetcher extends BasePageableEdgeEventFetcher<DeviceProfile> {
  34 +
  35 + private final DeviceProfileService deviceProfileService;
  36 +
  37 + @Override
  38 + PageData<DeviceProfile> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
  39 + return deviceProfileService.findDeviceProfiles(tenantId, pageLink);
  40 + }
  41 +
  42 + @Override
  43 + EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, DeviceProfile deviceProfile) {
  44 + return EdgeEventUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE,
  45 + EdgeEventActionType.ADDED, deviceProfile.getId(), null);
  46 + }
  47 +}