Commit 9317281f573a965c4a1b90af696514716425f4f0
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 | 33 | "enableSearch": true, |
34 | 34 | "displayPagination": true, |
35 | 35 | "defaultPageSize": 10, |
36 | - "defaultSortOrder": "entityLabel", | |
37 | - "displayEntityName": false, | |
36 | + "defaultSortOrder": "entityName", | |
37 | + "displayEntityName": true, | |
38 | 38 | "displayEntityType": false, |
39 | 39 | "enableSelectColumnDisplay": false, |
40 | 40 | "enableStickyHeader": true, |
41 | 41 | "enableStickyAction": false, |
42 | 42 | "entitiesTitle": "Devices", |
43 | - "displayEntityLabel": true, | |
44 | - "entityLabelColumnTitle": "Device" | |
43 | + "displayEntityLabel": false, | |
44 | + "entityNameColumnTitle": "Device" | |
45 | 45 | }, |
46 | 46 | "title": "New Entities table", |
47 | 47 | "dropShadow": true, |
... | ... | @@ -828,15 +828,15 @@ |
828 | 828 | "enableSearch": true, |
829 | 829 | "displayPagination": true, |
830 | 830 | "defaultPageSize": 10, |
831 | - "defaultSortOrder": "entityLabel", | |
832 | - "displayEntityName": false, | |
831 | + "defaultSortOrder": "entityName", | |
832 | + "displayEntityName": true, | |
833 | 833 | "displayEntityType": false, |
834 | 834 | "enableSelectColumnDisplay": false, |
835 | 835 | "enableStickyHeader": true, |
836 | 836 | "enableStickyAction": true, |
837 | 837 | "entitiesTitle": "Devices", |
838 | - "displayEntityLabel": true, | |
839 | - "entityLabelColumnTitle": "Device" | |
838 | + "displayEntityLabel": false, | |
839 | + "entityNameColumnTitle": "Device" | |
840 | 840 | }, |
841 | 841 | "title": "New Entities table", |
842 | 842 | "dropShadow": true, |
... | ... | @@ -1125,15 +1125,15 @@ |
1125 | 1125 | "enableSearch": true, |
1126 | 1126 | "displayPagination": true, |
1127 | 1127 | "defaultPageSize": 10, |
1128 | - "defaultSortOrder": "entityLabel", | |
1129 | - "displayEntityName": false, | |
1128 | + "defaultSortOrder": "entityName", | |
1129 | + "displayEntityName": true, | |
1130 | 1130 | "displayEntityType": false, |
1131 | 1131 | "enableSelectColumnDisplay": false, |
1132 | 1132 | "enableStickyHeader": true, |
1133 | 1133 | "enableStickyAction": true, |
1134 | 1134 | "entitiesTitle": "Devices", |
1135 | - "displayEntityLabel": true, | |
1136 | - "entityLabelColumnTitle": "Device" | |
1135 | + "displayEntityLabel": false, | |
1136 | + "entityNameColumnTitle": "Device" | |
1137 | 1137 | }, |
1138 | 1138 | "title": "New Entities table", |
1139 | 1139 | "dropShadow": true, |
... | ... | @@ -1422,15 +1422,15 @@ |
1422 | 1422 | "enableSearch": true, |
1423 | 1423 | "displayPagination": true, |
1424 | 1424 | "defaultPageSize": 10, |
1425 | - "defaultSortOrder": "entityLabel", | |
1426 | - "displayEntityName": false, | |
1425 | + "defaultSortOrder": "entityName", | |
1426 | + "displayEntityName": true, | |
1427 | 1427 | "displayEntityType": false, |
1428 | 1428 | "enableSelectColumnDisplay": false, |
1429 | 1429 | "enableStickyHeader": true, |
1430 | 1430 | "enableStickyAction": true, |
1431 | 1431 | "entitiesTitle": "Devices", |
1432 | - "displayEntityLabel": true, | |
1433 | - "entityLabelColumnTitle": "Device" | |
1432 | + "displayEntityLabel": false, | |
1433 | + "entityNameColumnTitle": "Device" | |
1434 | 1434 | }, |
1435 | 1435 | "title": "New Entities table", |
1436 | 1436 | "dropShadow": true, |
... | ... | @@ -1719,15 +1719,15 @@ |
1719 | 1719 | "enableSearch": true, |
1720 | 1720 | "displayPagination": true, |
1721 | 1721 | "defaultPageSize": 10, |
1722 | - "defaultSortOrder": "entityLabel", | |
1723 | - "displayEntityName": false, | |
1722 | + "defaultSortOrder": "entityName", | |
1723 | + "displayEntityName": true, | |
1724 | 1724 | "displayEntityType": false, |
1725 | 1725 | "enableSelectColumnDisplay": false, |
1726 | 1726 | "enableStickyHeader": true, |
1727 | 1727 | "enableStickyAction": true, |
1728 | 1728 | "entitiesTitle": "Devices", |
1729 | - "displayEntityLabel": true, | |
1730 | - "entityLabelColumnTitle": "Device" | |
1729 | + "displayEntityLabel": false, | |
1730 | + "entityNameColumnTitle": "Device" | |
1731 | 1731 | }, |
1732 | 1732 | "title": "New Entities table", |
1733 | 1733 | "dropShadow": true, | ... | ... |
... | ... | @@ -33,15 +33,15 @@ |
33 | 33 | "enableSearch": true, |
34 | 34 | "displayPagination": true, |
35 | 35 | "defaultPageSize": 10, |
36 | - "defaultSortOrder": "entityLabel", | |
37 | - "displayEntityName": false, | |
36 | + "defaultSortOrder": "entityName", | |
37 | + "displayEntityName": true, | |
38 | 38 | "displayEntityType": false, |
39 | 39 | "enableSelectColumnDisplay": false, |
40 | 40 | "enableStickyHeader": true, |
41 | 41 | "enableStickyAction": false, |
42 | 42 | "entitiesTitle": "Devices", |
43 | - "displayEntityLabel": true, | |
44 | - "entityLabelColumnTitle": "Device" | |
43 | + "displayEntityLabel": false, | |
44 | + "entityNameColumnTitle": "Device" | |
45 | 45 | }, |
46 | 46 | "title": "New Entities table", |
47 | 47 | "dropShadow": true, |
... | ... | @@ -828,15 +828,15 @@ |
828 | 828 | "enableSearch": true, |
829 | 829 | "displayPagination": true, |
830 | 830 | "defaultPageSize": 10, |
831 | - "defaultSortOrder": "entityLabel", | |
832 | - "displayEntityName": false, | |
831 | + "defaultSortOrder": "entityName", | |
832 | + "displayEntityName": true, | |
833 | 833 | "displayEntityType": false, |
834 | 834 | "enableSelectColumnDisplay": false, |
835 | 835 | "enableStickyHeader": true, |
836 | 836 | "enableStickyAction": true, |
837 | 837 | "entitiesTitle": "Devices", |
838 | - "displayEntityLabel": true, | |
839 | - "entityLabelColumnTitle": "Device" | |
838 | + "displayEntityLabel": false, | |
839 | + "entityNameColumnTitle": "Device" | |
840 | 840 | }, |
841 | 841 | "title": "New Entities table", |
842 | 842 | "dropShadow": true, |
... | ... | @@ -1125,15 +1125,15 @@ |
1125 | 1125 | "enableSearch": true, |
1126 | 1126 | "displayPagination": true, |
1127 | 1127 | "defaultPageSize": 10, |
1128 | - "defaultSortOrder": "entityLabel", | |
1129 | - "displayEntityName": false, | |
1128 | + "defaultSortOrder": "entityName", | |
1129 | + "displayEntityName": true, | |
1130 | 1130 | "displayEntityType": false, |
1131 | 1131 | "enableSelectColumnDisplay": false, |
1132 | 1132 | "enableStickyHeader": true, |
1133 | 1133 | "enableStickyAction": true, |
1134 | 1134 | "entitiesTitle": "Devices", |
1135 | - "displayEntityLabel": true, | |
1136 | - "entityLabelColumnTitle": "Device" | |
1135 | + "displayEntityLabel": false, | |
1136 | + "entityNameColumnTitle": "Device" | |
1137 | 1137 | }, |
1138 | 1138 | "title": "New Entities table", |
1139 | 1139 | "dropShadow": true, |
... | ... | @@ -1422,15 +1422,15 @@ |
1422 | 1422 | "enableSearch": true, |
1423 | 1423 | "displayPagination": true, |
1424 | 1424 | "defaultPageSize": 10, |
1425 | - "defaultSortOrder": "entityLabel", | |
1426 | - "displayEntityName": false, | |
1425 | + "defaultSortOrder": "entityName", | |
1426 | + "displayEntityName": true, | |
1427 | 1427 | "displayEntityType": false, |
1428 | 1428 | "enableSelectColumnDisplay": false, |
1429 | 1429 | "enableStickyHeader": true, |
1430 | 1430 | "enableStickyAction": true, |
1431 | 1431 | "entitiesTitle": "Devices", |
1432 | - "displayEntityLabel": true, | |
1433 | - "entityLabelColumnTitle": "Device" | |
1432 | + "displayEntityLabel": false, | |
1433 | + "entityNameColumnTitle": "Device" | |
1434 | 1434 | }, |
1435 | 1435 | "title": "New Entities table", |
1436 | 1436 | "dropShadow": true, |
... | ... | @@ -1719,15 +1719,15 @@ |
1719 | 1719 | "enableSearch": true, |
1720 | 1720 | "displayPagination": true, |
1721 | 1721 | "defaultPageSize": 10, |
1722 | - "defaultSortOrder": "entityLabel", | |
1723 | - "displayEntityName": false, | |
1722 | + "defaultSortOrder": "entityName", | |
1723 | + "displayEntityName": true, | |
1724 | 1724 | "displayEntityType": false, |
1725 | 1725 | "enableSelectColumnDisplay": false, |
1726 | 1726 | "enableStickyHeader": true, |
1727 | 1727 | "enableStickyAction": true, |
1728 | 1728 | "entitiesTitle": "Devices", |
1729 | - "displayEntityLabel": true, | |
1730 | - "entityLabelColumnTitle": "Device" | |
1729 | + "displayEntityLabel": false, | |
1730 | + "entityNameColumnTitle": "Device" | |
1731 | 1731 | }, |
1732 | 1732 | "title": "New Entities table", |
1733 | 1733 | "dropShadow": true, | ... | ... |
application/src/main/data/json/demo/edge_management/rule_chains/edge_root_rule_chain.json
deleted
100644 → 0
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 | -} | |
\ No newline at end of file |
... | ... | @@ -18,8 +18,8 @@ |
18 | 18 | "resources": [], |
19 | 19 | "templateHtml": "<div style=\"height: 100%; overflow-y: auto;\" id=\"device-terminal\"></div>", |
20 | 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 | 23 | "dataKeySettingsSchema": "{}\n", |
24 | 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 | 14 | { |
15 | 15 | "additionalInfo": { |
16 | 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 | 20 | "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", |
21 | 21 | "name": "Device Profile Node", |
... | ... | @@ -99,14 +99,14 @@ |
99 | 99 | }, |
100 | 100 | { |
101 | 101 | "additionalInfo": { |
102 | - "layoutX": 1134, | |
103 | - "layoutY": 132 | |
102 | + "layoutX": 1129, | |
103 | + "layoutY": 52 | |
104 | 104 | }, |
105 | 105 | "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", |
106 | 106 | "name": "Push to cloud", |
107 | 107 | "debugMode": false, |
108 | 108 | "configuration": { |
109 | - "version": 0 | |
109 | + "scope": "SERVER_SCOPE" | |
110 | 110 | } |
111 | 111 | } |
112 | 112 | ], |
... | ... | @@ -152,6 +152,11 @@ |
152 | 152 | "type": "RPC Request from Device" |
153 | 153 | }, |
154 | 154 | { |
155 | + "fromIndex": 3, | |
156 | + "toIndex": 7, | |
157 | + "type": "Attributes Updated" | |
158 | + }, | |
159 | + { | |
155 | 160 | "fromIndex": 4, |
156 | 161 | "toIndex": 7, |
157 | 162 | "type": "Success" | ... | ... |
... | ... | @@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar |
18 | 18 | LANGUAGE plpgsql AS |
19 | 19 | $$ |
20 | 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 | 34 | BEGIN |
34 | 35 | SELECT max(attribute_kv.long_v) |
... | ... | @@ -45,53 +46,138 @@ BEGIN |
45 | 46 | if max_ttl IS NOT NULL AND max_ttl > 0 THEN |
46 | 47 | date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); |
47 | 48 | partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); |
49 | + RAISE NOTICE 'Date by max ttl: %', date; | |
48 | 50 | RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; |
49 | 51 | IF partition_by_max_ttl_date IS NOT NULL THEN |
50 | 52 | CASE |
51 | 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 | 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 | 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 | 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 | 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 | 117 | END IF; |
82 | 118 | END IF; |
83 | 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 | 175 | END IF; |
85 | 176 | END IF; |
86 | 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 | 178 | END IF; |
94 | - END LOOP; | |
179 | + END IF; | |
180 | + END IF; | |
95 | 181 | END IF; |
96 | 182 | END IF; |
97 | 183 | END |
... | ... | @@ -107,8 +193,6 @@ BEGIN |
107 | 193 | partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); |
108 | 194 | WHEN partition_type = 'YEARS' THEN |
109 | 195 | partition := 'ts_kv_' || to_char(date, 'yyyy'); |
110 | - WHEN partition_type = 'INDEFINITE' THEN | |
111 | - partition := NULL; | |
112 | 196 | ELSE |
113 | 197 | partition := NULL; |
114 | 198 | END CASE; | ... | ... |
... | ... | @@ -197,3 +197,17 @@ $$; |
197 | 197 | ALTER TABLE api_usage_state |
198 | 198 | ADD COLUMN IF NOT EXISTS alarm_exec VARCHAR(32); |
199 | 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; | |
\ No newline at end of file | ... | ... |
... | ... | @@ -65,6 +65,7 @@ import org.thingsboard.server.dao.relation.RelationService; |
65 | 65 | import org.thingsboard.server.dao.resource.ResourceService; |
66 | 66 | import org.thingsboard.server.dao.rule.RuleChainService; |
67 | 67 | import org.thingsboard.server.dao.rule.RuleNodeStateService; |
68 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
68 | 69 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
69 | 70 | import org.thingsboard.server.dao.tenant.TenantService; |
70 | 71 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
... | ... | @@ -80,9 +81,9 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService; |
80 | 81 | import org.thingsboard.server.service.executors.SharedEventLoopGroupService; |
81 | 82 | import org.thingsboard.server.service.mail.MailExecutorService; |
82 | 83 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
83 | -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
84 | 84 | import org.thingsboard.server.service.queue.TbClusterService; |
85 | 85 | import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; |
86 | +import org.thingsboard.server.service.rpc.TbRpcService; | |
86 | 87 | import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; |
87 | 88 | import org.thingsboard.server.service.script.JsExecutorService; |
88 | 89 | import org.thingsboard.server.service.script.JsInvokeService; |
... | ... | @@ -303,23 +304,33 @@ public class ActorSystemContext { |
303 | 304 | |
304 | 305 | @Lazy |
305 | 306 | @Autowired(required = false) |
306 | - @Getter private EdgeService edgeService; | |
307 | + @Getter | |
308 | + private EdgeService edgeService; | |
307 | 309 | |
308 | 310 | @Lazy |
309 | 311 | @Autowired(required = false) |
310 | - @Getter private EdgeEventService edgeEventService; | |
312 | + @Getter | |
313 | + private EdgeEventService edgeEventService; | |
311 | 314 | |
312 | 315 | @Lazy |
313 | 316 | @Autowired(required = false) |
314 | - @Getter private EdgeRpcService edgeRpcService; | |
317 | + @Getter | |
318 | + private EdgeRpcService edgeRpcService; | |
315 | 319 | |
316 | 320 | @Lazy |
317 | 321 | @Autowired(required = false) |
318 | - @Getter private ResourceService resourceService; | |
322 | + @Getter | |
323 | + private ResourceService resourceService; | |
319 | 324 | |
320 | 325 | @Lazy |
321 | 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 | 335 | @Value("${actors.session.max_concurrent_sessions_per_device:1}") |
325 | 336 | @Getter | ... | ... |
... | ... | @@ -46,7 +46,7 @@ public class DeviceActor extends ContextAwareActor { |
46 | 46 | super.init(ctx); |
47 | 47 | log.debug("[{}][{}] Starting device actor.", processor.tenantId, processor.deviceId); |
48 | 48 | try { |
49 | - processor.initSessionTimeout(ctx); | |
49 | + processor.init(ctx); | |
50 | 50 | log.debug("[{}][{}] Device actor started.", processor.tenantId, processor.deviceId); |
51 | 51 | } catch (Exception e) { |
52 | 52 | log.warn("[{}][{}] Unknown failure", processor.tenantId, processor.deviceId, e); | ... | ... |
... | ... | @@ -23,6 +23,8 @@ import com.google.common.util.concurrent.MoreExecutors; |
23 | 23 | import com.google.protobuf.InvalidProtocolBufferException; |
24 | 24 | import lombok.extern.slf4j.Slf4j; |
25 | 25 | import org.apache.commons.collections.CollectionUtils; |
26 | +import org.thingsboard.common.util.JacksonUtil; | |
27 | +import org.thingsboard.common.util.LinkedHashMapRemoveEldest; | |
26 | 28 | import org.thingsboard.rule.engine.api.RpcError; |
27 | 29 | import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; |
28 | 30 | import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; |
... | ... | @@ -38,12 +40,17 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
38 | 40 | import org.thingsboard.server.common.data.edge.EdgeEventType; |
39 | 41 | import org.thingsboard.server.common.data.id.DeviceId; |
40 | 42 | import org.thingsboard.server.common.data.id.EdgeId; |
43 | +import org.thingsboard.server.common.data.id.RpcId; | |
41 | 44 | import org.thingsboard.server.common.data.id.TenantId; |
42 | 45 | import org.thingsboard.server.common.data.kv.AttributeKey; |
43 | 46 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
44 | 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 | 50 | import org.thingsboard.server.common.data.relation.EntityRelation; |
46 | 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 | 54 | import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; |
48 | 55 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
49 | 56 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
... | ... | @@ -52,8 +59,8 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; |
52 | 59 | import org.thingsboard.server.common.msg.queue.TbCallback; |
53 | 60 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; |
54 | 61 | import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; |
55 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
56 | 62 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
63 | +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; | |
57 | 64 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry; |
58 | 65 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; |
59 | 66 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
... | ... | @@ -68,10 +75,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionType; |
68 | 75 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; |
69 | 76 | import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; |
70 | 77 | import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; |
78 | +import org.thingsboard.server.gen.transport.TransportProtos.ToDevicePersistedRpcResponseMsg; | |
71 | 79 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; |
72 | 80 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; |
73 | 81 | import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; |
74 | 82 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
83 | +import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; | |
75 | 84 | import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; |
76 | 85 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; |
77 | 86 | import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; |
... | ... | @@ -88,6 +97,8 @@ import java.util.HashSet; |
88 | 97 | import java.util.LinkedHashMap; |
89 | 98 | import java.util.List; |
90 | 99 | import java.util.Map; |
100 | +import java.util.Objects; | |
101 | +import java.util.Optional; | |
91 | 102 | import java.util.Set; |
92 | 103 | import java.util.UUID; |
93 | 104 | import java.util.function.Consumer; |
... | ... | @@ -102,7 +113,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
102 | 113 | |
103 | 114 | final TenantId tenantId; |
104 | 115 | final DeviceId deviceId; |
105 | - private final Map<UUID, SessionInfoMetaData> sessions; | |
116 | + final LinkedHashMapRemoveEldest<UUID, SessionInfoMetaData> sessions; | |
106 | 117 | private final Map<UUID, SessionInfo> attributeSubscriptions; |
107 | 118 | private final Map<UUID, SessionInfo> rpcSubscriptions; |
108 | 119 | private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap; |
... | ... | @@ -117,16 +128,16 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
117 | 128 | super(systemContext); |
118 | 129 | this.tenantId = tenantId; |
119 | 130 | this.deviceId = deviceId; |
120 | - this.sessions = new LinkedHashMap<>(); | |
121 | 131 | this.attributeSubscriptions = new HashMap<>(); |
122 | 132 | this.rpcSubscriptions = new HashMap<>(); |
123 | 133 | this.toDeviceRpcPendingMap = new HashMap<>(); |
134 | + this.sessions = new LinkedHashMapRemoveEldest<>(systemContext.getMaxConcurrentSessionsPerDevice(), this::notifyTransportAboutClosedSessionMaxSessionsLimit); | |
124 | 135 | if (initAttributes()) { |
125 | 136 | restoreSessions(); |
126 | 137 | } |
127 | 138 | } |
128 | 139 | |
129 | - private boolean initAttributes() { | |
140 | + boolean initAttributes() { | |
130 | 141 | Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId); |
131 | 142 | if (device != null) { |
132 | 143 | this.deviceName = device.getName(); |
... | ... | @@ -162,20 +173,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
162 | 173 | |
163 | 174 | void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { |
164 | 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 | 178 | long timeout = request.getExpirationTime() - System.currentTimeMillis(); |
179 | + boolean persisted = request.isPersisted(); | |
180 | + | |
176 | 181 | if (timeout <= 0) { |
177 | 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 | 186 | return; |
187 | + } else if (persisted) { | |
188 | + createRpc(request, RpcStatus.QUEUED); | |
179 | 189 | } |
180 | 190 | |
181 | 191 | boolean sent; |
... | ... | @@ -192,11 +202,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
192 | 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 | 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 | 216 | log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); |
201 | 217 | systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null)); |
202 | 218 | } else { |
... | ... | @@ -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 | 253 | void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) { |
213 | 254 | log.debug("[{}] Processing rpc command response from edge session", deviceId); |
214 | 255 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId()); |
... | ... | @@ -230,6 +271,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
230 | 271 | ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId()); |
231 | 272 | if (requestMd != null) { |
232 | 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 | 277 | systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
234 | 278 | null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION)); |
235 | 279 | } |
... | ... | @@ -253,7 +297,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
253 | 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 | 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 | 315 | .setExpirationTime(request.getExpirationTime()) |
272 | 316 | .setRequestIdMSB(request.getId().getMostSignificantBits()) |
273 | 317 | .setRequestIdLSB(request.getId().getLeastSignificantBits()) |
318 | + .setOneway(request.isOneway()) | |
319 | + .setPersisted(request.isPersisted()) | |
274 | 320 | .build(); |
321 | + | |
275 | 322 | sendToTransport(rpcRequest, sessionId, nodeId); |
276 | 323 | }; |
277 | 324 | } |
... | ... | @@ -279,31 +326,39 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
279 | 326 | void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) { |
280 | 327 | TransportToDeviceActorMsg msg = wrapper.getMsg(); |
281 | 328 | TbCallback callback = wrapper.getCallback(); |
329 | + var sessionInfo = msg.getSessionInfo(); | |
330 | + | |
282 | 331 | if (msg.hasSessionEvent()) { |
283 | - processSessionStateMsgs(msg.getSessionInfo(), msg.getSessionEvent()); | |
332 | + processSessionStateMsgs(sessionInfo, msg.getSessionEvent()); | |
284 | 333 | } |
285 | 334 | if (msg.hasSubscribeToAttributes()) { |
286 | - processSubscriptionCommands(context, msg.getSessionInfo(), msg.getSubscribeToAttributes()); | |
335 | + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes()); | |
287 | 336 | } |
288 | 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 | 343 | if (msg.hasGetAttributes()) { |
292 | - handleGetAttributesRequest(context, msg.getSessionInfo(), msg.getGetAttributes()); | |
344 | + handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes()); | |
293 | 345 | } |
294 | 346 | if (msg.hasToDeviceRPCCallResponse()) { |
295 | - processRpcResponses(context, msg.getSessionInfo(), msg.getToDeviceRPCCallResponse()); | |
347 | + processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse()); | |
296 | 348 | } |
297 | 349 | if (msg.hasSubscriptionInfo()) { |
298 | - handleSessionActivity(context, msg.getSessionInfo(), msg.getSubscriptionInfo()); | |
350 | + handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo()); | |
299 | 351 | } |
300 | 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 | 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 | 362 | DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); |
308 | 363 | systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()); |
309 | 364 | } |
... | ... | @@ -442,11 +497,19 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
442 | 497 | if (success) { |
443 | 498 | systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), |
444 | 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 | 503 | } else { |
446 | 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 | 513 | private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) { |
451 | 514 | UUID sessionId = getSessionId(sessionInfo); |
452 | 515 | if (subscribeCmd.getUnsubscribe()) { |
... | ... | @@ -488,18 +551,14 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
488 | 551 | |
489 | 552 | private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) { |
490 | 553 | UUID sessionId = getSessionId(sessionInfo); |
554 | + Objects.requireNonNull(sessionId); | |
491 | 555 | if (msg.getEvent() == SessionEvent.OPEN) { |
492 | 556 | if (sessions.containsKey(sessionId)) { |
493 | 557 | log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId); |
494 | 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 | 562 | sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId()))); |
504 | 563 | if (sessions.size() == 1) { |
505 | 564 | reportSessionOpen(); |
... | ... | @@ -520,8 +579,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
520 | 579 | |
521 | 580 | private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) { |
522 | 581 | UUID sessionId = getSessionId(sessionInfoProto); |
582 | + Objects.requireNonNull(sessionId); | |
583 | + | |
523 | 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 | 587 | sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime()); |
527 | 588 | sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription()); |
... | ... | @@ -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 | 620 | private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) { |
555 | 621 | SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto |
556 | 622 | .newBuilder() |
... | ... | @@ -565,7 +631,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
565 | 631 | |
566 | 632 | void notifyTransportAboutProfileUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) { |
567 | 633 | log.info("2) LwM2Mtype: "); |
568 | - TransportProtos.ToTransportUpdateCredentialsProto.Builder notification = TransportProtos.ToTransportUpdateCredentialsProto.newBuilder(); | |
634 | + ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder(); | |
569 | 635 | notification.addCredentialsId(deviceCredentials.getCredentialsId()); |
570 | 636 | notification.addCredentialsValue(deviceCredentials.getCredentialsValue()); |
571 | 637 | ToTransportMsg msg = ToTransportMsg.newBuilder() |
... | ... | @@ -640,7 +706,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
640 | 706 | ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent); |
641 | 707 | Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
642 | 708 | @Override |
643 | - public void onSuccess( EdgeEvent result) { | |
709 | + public void onSuccess(EdgeEvent result) { | |
644 | 710 | systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId); |
645 | 711 | } |
646 | 712 | |
... | ... | @@ -697,7 +763,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
697 | 763 | return builder.build(); |
698 | 764 | } |
699 | 765 | |
700 | - private void restoreSessions() { | |
766 | + void restoreSessions() { | |
701 | 767 | log.debug("[{}] Restoring sessions from cache", deviceId); |
702 | 768 | DeviceSessionsCacheEntry sessionsDump = null; |
703 | 769 | try { |
... | ... | @@ -710,6 +776,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
710 | 776 | log.debug("[{}] No session information found", deviceId); |
711 | 777 | return; |
712 | 778 | } |
779 | + // TODO: Take latest max allowed sessions size from cache | |
713 | 780 | for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) { |
714 | 781 | SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo(); |
715 | 782 | UUID sessionId = getSessionId(sessionInfoProto); |
... | ... | @@ -756,11 +823,30 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
756 | 823 | .addAllSessions(sessionsList).build().toByteArray()); |
757 | 824 | } |
758 | 825 | |
759 | - void initSessionTimeout(TbActorCtx ctx) { | |
826 | + void init(TbActorCtx ctx) { | |
760 | 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 | 848 | void checkSessionsTimeout() { |
849 | + log.debug("[{}] checkSessionsTimeout started. Size before check {}", deviceId, sessions.size()); | |
764 | 850 | long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout(); |
765 | 851 | Map<UUID, SessionInfoMetaData> sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
766 | 852 | sessionsToRemove.forEach((sessionId, sessionMD) -> { |
... | ... | @@ -772,6 +858,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { |
772 | 858 | if (!sessionsToRemove.isEmpty()) { |
773 | 859 | dumpSessions(); |
774 | 860 | } |
861 | + log.debug("[{}] checkSessionsTimeout finished. Size after check {}", deviceId, sessions.size()); | |
775 | 862 | } |
776 | 863 | |
777 | 864 | } | ... | ... |
... | ... | @@ -245,11 +245,11 @@ public class TenantActor extends RuleChainManagerActor { |
245 | 245 | EdgeId edgeId = new EdgeId(msg.getEntityId().getId()); |
246 | 246 | EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService(); |
247 | 247 | if (msg.getEvent() == ComponentLifecycleEvent.DELETED) { |
248 | - edgeRpcService.deleteEdge(edgeId); | |
248 | + edgeRpcService.deleteEdge(tenantId, edgeId); | |
249 | 249 | } else { |
250 | 250 | Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId); |
251 | 251 | if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) { |
252 | - edgeRpcService.updateEdge(edge); | |
252 | + edgeRpcService.updateEdge(tenantId, edge); | |
253 | 253 | } |
254 | 254 | } |
255 | 255 | } else if (isRuleEngineForCurrentTenant) { |
... | ... | @@ -277,7 +277,7 @@ public class TenantActor extends RuleChainManagerActor { |
277 | 277 | |
278 | 278 | private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) { |
279 | 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 | 283 | public static class ActorCreator extends ContextBasedCreator { | ... | ... |
... | ... | @@ -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 | 139 | public ResponseEntity<String> checkActivateToken( |
140 | 140 | @RequestParam(value = "activateToken") String activateToken) { |
141 | 141 | HttpHeaders headers = new HttpHeaders(); |
... | ... | @@ -159,7 +159,7 @@ public class AuthController extends BaseController { |
159 | 159 | |
160 | 160 | @RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST) |
161 | 161 | @ResponseStatus(value = HttpStatus.OK) |
162 | - public void requestResetPasswordByEmail ( | |
162 | + public void requestResetPasswordByEmail( | |
163 | 163 | @RequestBody JsonNode resetPasswordByEmailRequest, |
164 | 164 | HttpServletRequest request) throws ThingsboardException { |
165 | 165 | try { |
... | ... | @@ -170,13 +170,13 @@ public class AuthController extends BaseController { |
170 | 170 | String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl, |
171 | 171 | userCredentials.getResetToken()); |
172 | 172 | |
173 | - mailService.sendResetPasswordEmail(resetUrl, email); | |
173 | + mailService.sendResetPasswordEmailAsync(resetUrl, email); | |
174 | 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 | 180 | public ResponseEntity<String> checkResetToken( |
181 | 181 | @RequestParam(value = "resetToken") String resetToken) { |
182 | 182 | HttpHeaders headers = new HttpHeaders(); | ... | ... |
... | ... | @@ -37,12 +37,12 @@ import org.thingsboard.server.common.data.EdgeUtils; |
37 | 37 | import org.thingsboard.server.common.data.EntityType; |
38 | 38 | import org.thingsboard.server.common.data.EntityView; |
39 | 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 | 40 | import org.thingsboard.server.common.data.HasName; |
43 | 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 | 44 | import org.thingsboard.server.common.data.TbResource; |
45 | +import org.thingsboard.server.common.data.TbResourceInfo; | |
46 | 46 | import org.thingsboard.server.common.data.Tenant; |
47 | 47 | import org.thingsboard.server.common.data.TenantInfo; |
48 | 48 | import org.thingsboard.server.common.data.TenantProfile; |
... | ... | @@ -69,20 +69,23 @@ import org.thingsboard.server.common.data.id.EntityId; |
69 | 69 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
70 | 70 | import org.thingsboard.server.common.data.id.EntityViewId; |
71 | 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 | 73 | import org.thingsboard.server.common.data.id.RuleChainId; |
74 | 74 | import org.thingsboard.server.common.data.id.RuleNodeId; |
75 | +import org.thingsboard.server.common.data.id.TbResourceId; | |
75 | 76 | import org.thingsboard.server.common.data.id.TenantId; |
76 | 77 | import org.thingsboard.server.common.data.id.TenantProfileId; |
77 | 78 | import org.thingsboard.server.common.data.id.UserId; |
78 | 79 | import org.thingsboard.server.common.data.id.WidgetTypeId; |
79 | 80 | import org.thingsboard.server.common.data.id.WidgetsBundleId; |
81 | +import org.thingsboard.server.common.data.page.PageDataIterableByTenantIdEntityId; | |
80 | 82 | import org.thingsboard.server.common.data.page.PageLink; |
81 | 83 | import org.thingsboard.server.common.data.page.SortOrder; |
82 | 84 | import org.thingsboard.server.common.data.page.TimePageLink; |
83 | 85 | import org.thingsboard.server.common.data.plugin.ComponentDescriptor; |
84 | 86 | import org.thingsboard.server.common.data.plugin.ComponentType; |
85 | 87 | import org.thingsboard.server.common.data.relation.EntityRelation; |
88 | +import org.thingsboard.server.common.data.rpc.Rpc; | |
86 | 89 | import org.thingsboard.server.common.data.rule.RuleChain; |
87 | 90 | import org.thingsboard.server.common.data.rule.RuleChainType; |
88 | 91 | import org.thingsboard.server.common.data.rule.RuleNode; |
... | ... | @@ -101,11 +104,12 @@ import org.thingsboard.server.dao.edge.EdgeService; |
101 | 104 | import org.thingsboard.server.dao.entityview.EntityViewService; |
102 | 105 | import org.thingsboard.server.dao.exception.DataValidationException; |
103 | 106 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
104 | -import org.thingsboard.server.dao.ota.OtaPackageService; | |
105 | 107 | import org.thingsboard.server.dao.model.ModelConstants; |
106 | 108 | import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; |
107 | 109 | import org.thingsboard.server.dao.oauth2.OAuth2Service; |
110 | +import org.thingsboard.server.dao.ota.OtaPackageService; | |
108 | 111 | import org.thingsboard.server.dao.relation.RelationService; |
112 | +import org.thingsboard.server.dao.rpc.RpcService; | |
109 | 113 | import org.thingsboard.server.dao.rule.RuleChainService; |
110 | 114 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
111 | 115 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
... | ... | @@ -120,11 +124,10 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
120 | 124 | import org.thingsboard.server.queue.util.TbCoreComponent; |
121 | 125 | import org.thingsboard.server.service.action.RuleEngineEntityActionService; |
122 | 126 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
123 | -import org.thingsboard.server.service.ota.OtaPackageStateService; | |
124 | 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 | 129 | import org.thingsboard.server.service.lwm2m.LwM2MServerSecurityInfoRepository; |
130 | +import org.thingsboard.server.service.ota.OtaPackageStateService; | |
128 | 131 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
129 | 132 | import org.thingsboard.server.service.queue.TbClusterService; |
130 | 133 | import org.thingsboard.server.service.resource.TbResourceService; |
... | ... | @@ -138,6 +141,8 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
138 | 141 | |
139 | 142 | import javax.mail.MessagingException; |
140 | 143 | import javax.servlet.http.HttpServletResponse; |
144 | +import java.util.ArrayList; | |
145 | +import java.util.Collections; | |
141 | 146 | import java.util.List; |
142 | 147 | import java.util.Optional; |
143 | 148 | import java.util.Set; |
... | ... | @@ -153,6 +158,8 @@ public abstract class BaseController { |
153 | 158 | protected static final String DEFAULT_DASHBOARD = "defaultDashboardId"; |
154 | 159 | protected static final String HOME_DASHBOARD = "homeDashboardId"; |
155 | 160 | |
161 | + private static final int DEFAULT_PAGE_SIZE = 1000; | |
162 | + | |
156 | 163 | private static final ObjectMapper json = new ObjectMapper(); |
157 | 164 | |
158 | 165 | @Autowired |
... | ... | @@ -246,6 +253,9 @@ public abstract class BaseController { |
246 | 253 | protected OtaPackageStateService otaPackageStateService; |
247 | 254 | |
248 | 255 | @Autowired |
256 | + protected RpcService rpcService; | |
257 | + | |
258 | + @Autowired | |
249 | 259 | protected TbQueueProducerProvider producerProvider; |
250 | 260 | |
251 | 261 | @Autowired |
... | ... | @@ -264,10 +274,7 @@ public abstract class BaseController { |
264 | 274 | protected EdgeNotificationService edgeNotificationService; |
265 | 275 | |
266 | 276 | @Autowired(required = false) |
267 | - protected SyncEdgeService syncEdgeService; | |
268 | - | |
269 | - @Autowired(required = false) | |
270 | - protected EdgeGrpcService edgeGrpcService; | |
277 | + protected EdgeRpcService edgeGrpcService; | |
271 | 278 | |
272 | 279 | @Autowired |
273 | 280 | protected RuleEngineEntityActionService ruleEngineEntityActionService; |
... | ... | @@ -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 | 808 | @SuppressWarnings("unchecked") |
790 | 809 | protected <I extends EntityId> I emptyId(EntityType entityType) { |
791 | 810 | return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); |
... | ... | @@ -899,11 +918,14 @@ public abstract class BaseController { |
899 | 918 | if (!edgesEnabled) { |
900 | 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 | 930 | return result; |
909 | 931 | } | ... | ... |
... | ... | @@ -782,15 +782,17 @@ public class DeviceController extends BaseController { |
782 | 782 | } |
783 | 783 | |
784 | 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 | 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 | 789 | checkParameter("OtaPackageType", otaPackageType); |
790 | 790 | checkParameter("DeviceProfileId", deviceProfileId); |
791 | 791 | try { |
792 | 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 | 796 | } catch (Exception e) { |
795 | 797 | throw handleException(e); |
796 | 798 | } | ... | ... |
... | ... | @@ -49,7 +49,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; |
49 | 49 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
50 | 50 | import org.thingsboard.server.dao.model.ModelConstants; |
51 | 51 | import org.thingsboard.server.queue.util.TbCoreComponent; |
52 | -import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession; | |
53 | 52 | import org.thingsboard.server.service.security.model.SecurityUser; |
54 | 53 | import org.thingsboard.server.service.security.permission.Operation; |
55 | 54 | import org.thingsboard.server.service.security.permission.Resource; |
... | ... | @@ -128,7 +127,7 @@ public class EdgeController extends BaseController { |
128 | 127 | accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, |
129 | 128 | edge.getId(), edge); |
130 | 129 | |
131 | - Edge savedEdge = checkNotNull(edgeService.saveEdge(edge)); | |
130 | + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge, true)); | |
132 | 131 | |
133 | 132 | if (created) { |
134 | 133 | ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId()); |
... | ... | @@ -536,9 +535,7 @@ public class EdgeController extends BaseController { |
536 | 535 | edgeId = checkNotNull(edgeId); |
537 | 536 | SecurityUser user = getCurrentUser(); |
538 | 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 | 539 | } else { |
543 | 540 | throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); |
544 | 541 | } | ... | ... |
... | ... | @@ -32,12 +32,13 @@ import org.springframework.web.multipart.MultipartFile; |
32 | 32 | import org.thingsboard.server.common.data.EntityType; |
33 | 33 | import org.thingsboard.server.common.data.OtaPackage; |
34 | 34 | import org.thingsboard.server.common.data.OtaPackageInfo; |
35 | +import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest; | |
35 | 36 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | 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 | 38 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
40 | 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 | 42 | import org.thingsboard.server.common.data.page.PageData; |
42 | 43 | import org.thingsboard.server.common.data.page.PageLink; |
43 | 44 | import org.thingsboard.server.queue.util.TbCoreComponent; |
... | ... | @@ -109,12 +110,12 @@ public class OtaPackageController extends BaseController { |
109 | 110 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
110 | 111 | @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) |
111 | 112 | @ResponseBody |
112 | - public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException { | |
113 | + public OtaPackageInfo saveOtaPackageInfo(@RequestBody SaveOtaPackageInfoRequest otaPackageInfo) throws ThingsboardException { | |
113 | 114 | boolean created = otaPackageInfo.getId() == null; |
114 | 115 | try { |
115 | 116 | otaPackageInfo.setTenantId(getTenantId()); |
116 | 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 | 119 | logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo, |
119 | 120 | null, created ? ActionType.ADDED : ActionType.UPDATED, null); |
120 | 121 | return savedOtaPackageInfo; | ... | ... |
... | ... | @@ -29,17 +29,24 @@ import org.springframework.web.bind.annotation.PathVariable; |
29 | 29 | import org.springframework.web.bind.annotation.RequestBody; |
30 | 30 | import org.springframework.web.bind.annotation.RequestMapping; |
31 | 31 | import org.springframework.web.bind.annotation.RequestMethod; |
32 | +import org.springframework.web.bind.annotation.RequestParam; | |
32 | 33 | import org.springframework.web.bind.annotation.ResponseBody; |
33 | 34 | import org.springframework.web.bind.annotation.RestController; |
34 | 35 | import org.springframework.web.context.request.async.DeferredResult; |
35 | 36 | import org.thingsboard.rule.engine.api.RpcError; |
37 | +import org.thingsboard.server.common.data.DataConstants; | |
36 | 38 | import org.thingsboard.server.common.data.audit.ActionType; |
37 | 39 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
38 | 40 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
39 | 41 | import org.thingsboard.server.common.data.id.DeviceId; |
40 | 42 | import org.thingsboard.server.common.data.id.EntityId; |
43 | +import org.thingsboard.server.common.data.id.RpcId; | |
41 | 44 | import org.thingsboard.server.common.data.id.TenantId; |
42 | 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 | 50 | import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; |
44 | 51 | import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; |
45 | 52 | import org.thingsboard.server.queue.util.TbCoreComponent; |
... | ... | @@ -93,6 +100,52 @@ public class RpcController extends BaseController { |
93 | 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 | 149 | private DeferredResult<ResponseEntity> handleDeviceRPCRequest(boolean oneWay, DeviceId deviceId, String requestBody) throws ThingsboardException { |
97 | 150 | try { |
98 | 151 | JsonNode rpcRequestBody = jsonMapper.readTree(requestBody); |
... | ... | @@ -103,6 +156,7 @@ public class RpcController extends BaseController { |
103 | 156 | long timeout = rpcRequestBody.has("timeout") ? rpcRequestBody.get("timeout").asLong() : defaultTimeout; |
104 | 157 | long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); |
105 | 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 | 160 | accessValidator.validate(currentUser, Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<DeferredResult<ResponseEntity>>() { |
107 | 161 | @Override |
108 | 162 | public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) { |
... | ... | @@ -111,7 +165,8 @@ public class RpcController extends BaseController { |
111 | 165 | deviceId, |
112 | 166 | oneWay, |
113 | 167 | expTime, |
114 | - body | |
168 | + body, | |
169 | + persisted | |
115 | 170 | ); |
116 | 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 | 50 | import org.thingsboard.server.common.data.id.RuleNodeId; |
51 | 51 | import org.thingsboard.server.common.data.id.TenantId; |
52 | 52 | import org.thingsboard.server.common.data.page.PageData; |
53 | +import org.thingsboard.server.common.data.page.PageDataIterableByTenant; | |
53 | 54 | import org.thingsboard.server.common.data.page.PageLink; |
54 | 55 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
55 | 56 | import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; |
... | ... | @@ -70,10 +71,12 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
70 | 71 | import org.thingsboard.server.service.security.permission.Operation; |
71 | 72 | import org.thingsboard.server.service.security.permission.Resource; |
72 | 73 | |
74 | +import java.util.ArrayList; | |
73 | 75 | import java.util.List; |
74 | 76 | import java.util.Map; |
75 | 77 | import java.util.Set; |
76 | 78 | import java.util.concurrent.ConcurrentMap; |
79 | +import java.util.concurrent.TimeUnit; | |
77 | 80 | import java.util.stream.Collectors; |
78 | 81 | |
79 | 82 | @Slf4j |
... | ... | @@ -85,7 +88,10 @@ public class RuleChainController extends BaseController { |
85 | 88 | public static final String RULE_CHAIN_ID = "ruleChainId"; |
86 | 89 | public static final String RULE_NODE_ID = "ruleNodeId"; |
87 | 90 | |
91 | + private static final int DEFAULT_PAGE_SIZE = 1000; | |
92 | + | |
88 | 93 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
94 | + public static final int TIMEOUT = 20; | |
89 | 95 | |
90 | 96 | @Autowired |
91 | 97 | private InstallScripts installScripts; |
... | ... | @@ -388,25 +394,25 @@ public class RuleChainController extends BaseController { |
388 | 394 | TbMsg inMsg = TbMsg.newMsg(msgType, null, new TbMsgMetaData(metadata), TbMsgDataType.JSON, data); |
389 | 395 | switch (scriptType) { |
390 | 396 | case "update": |
391 | - output = msgToOutput(engine.executeUpdate(inMsg)); | |
397 | + output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); | |
392 | 398 | break; |
393 | 399 | case "generate": |
394 | - output = msgToOutput(engine.executeGenerate(inMsg)); | |
400 | + output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS)); | |
395 | 401 | break; |
396 | 402 | case "filter": |
397 | - boolean result = engine.executeFilter(inMsg); | |
403 | + boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); | |
398 | 404 | output = Boolean.toString(result); |
399 | 405 | break; |
400 | 406 | case "switch": |
401 | - Set<String> states = engine.executeSwitch(inMsg); | |
407 | + Set<String> states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); | |
402 | 408 | output = objectMapper.writeValueAsString(states); |
403 | 409 | break; |
404 | 410 | case "json": |
405 | - JsonNode json = engine.executeJson(inMsg); | |
411 | + JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); | |
406 | 412 | output = objectMapper.writeValueAsString(json); |
407 | 413 | break; |
408 | 414 | case "string": |
409 | - output = engine.executeToString(inMsg); | |
415 | + output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS); | |
410 | 416 | break; |
411 | 417 | default: |
412 | 418 | throw new IllegalArgumentException("Unsupported script type: " + scriptType); |
... | ... | @@ -477,7 +483,7 @@ public class RuleChainController extends BaseController { |
477 | 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 | 487 | ObjectNode msgData = objectMapper.createObjectNode(); |
482 | 488 | if (!StringUtils.isEmpty(msg.getData())) { |
483 | 489 | msgData.set("msg", objectMapper.readTree(msg.getData())); |
... | ... | @@ -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 | 642 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
636 | 643 | @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET) |
637 | 644 | @ResponseBody |
638 | 645 | public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException { |
639 | 646 | try { |
640 | 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 | 655 | } catch (Exception e) { |
643 | 656 | throw handleException(e); |
644 | 657 | } | ... | ... |
... | ... | @@ -95,9 +95,7 @@ public class TenantController extends BaseController { |
95 | 95 | tenant = checkNotNull(tenantService.saveTenant(tenant)); |
96 | 96 | if (newTenant) { |
97 | 97 | installScripts.createDefaultRuleChains(tenant.getId()); |
98 | - if (edgesEnabled) { | |
99 | - installScripts.createDefaultEdgeRuleChains(tenant.getId()); | |
100 | - } | |
98 | + installScripts.createDefaultEdgeRuleChains(tenant.getId()); | |
101 | 99 | } |
102 | 100 | tenantProfileCache.evict(tenant.getId()); |
103 | 101 | tbClusterService.onTenantChange(tenant, null); | ... | ... |
... | ... | @@ -25,9 +25,12 @@ import org.springframework.security.authentication.BadCredentialsException; |
25 | 25 | import org.springframework.security.authentication.DisabledException; |
26 | 26 | import org.springframework.security.authentication.LockedException; |
27 | 27 | import org.springframework.security.core.AuthenticationException; |
28 | +import org.springframework.security.core.userdetails.UsernameNotFoundException; | |
28 | 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 | 32 | import org.springframework.web.client.HttpClientErrorException; |
33 | +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | |
31 | 34 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
32 | 35 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
33 | 36 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
... | ... | @@ -40,14 +43,15 @@ import javax.servlet.http.HttpServletRequest; |
40 | 43 | import javax.servlet.http.HttpServletResponse; |
41 | 44 | import java.io.IOException; |
42 | 45 | |
43 | -@Component | |
44 | 46 | @Slf4j |
45 | -public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { | |
47 | +@RestControllerAdvice | |
48 | +public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler { | |
46 | 49 | |
47 | 50 | @Autowired |
48 | 51 | private ObjectMapper mapper; |
49 | 52 | |
50 | 53 | @Override |
54 | + @ExceptionHandler(AccessDeniedException.class) | |
51 | 55 | public void handle(HttpServletRequest request, HttpServletResponse response, |
52 | 56 | AccessDeniedException accessDeniedException) throws IOException, |
53 | 57 | ServletException { |
... | ... | @@ -60,6 +64,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { |
60 | 64 | } |
61 | 65 | } |
62 | 66 | |
67 | + @ExceptionHandler(Exception.class) | |
63 | 68 | public void handle(Exception exception, HttpServletResponse response) { |
64 | 69 | log.debug("Processing exception {}", exception.getMessage(), exception); |
65 | 70 | if (!response.isCommitted()) { |
... | ... | @@ -148,7 +153,7 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { |
148 | 153 | |
149 | 154 | private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException { |
150 | 155 | response.setStatus(HttpStatus.UNAUTHORIZED.value()); |
151 | - if (authenticationException instanceof BadCredentialsException) { | |
156 | + if (authenticationException instanceof BadCredentialsException || authenticationException instanceof UsernameNotFoundException) { | |
152 | 157 | mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); |
153 | 158 | } else if (authenticationException instanceof DisabledException) { |
154 | 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 | 164 | } else if (authenticationException instanceof AuthMethodNotSupportedException) { |
160 | 165 | mapper.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)); |
161 | 166 | } else if (authenticationException instanceof UserPasswordExpiredException) { |
162 | - UserPasswordExpiredException expiredException = (UserPasswordExpiredException)authenticationException; | |
167 | + UserPasswordExpiredException expiredException = (UserPasswordExpiredException) authenticationException; | |
163 | 168 | String resetToken = expiredException.getResetToken(); |
164 | 169 | mapper.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken)); |
165 | 170 | } else { | ... | ... |
... | ... | @@ -15,9 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.edge; |
17 | 17 | |
18 | -import com.fasterxml.jackson.core.JsonProcessingException; | |
19 | 18 | import com.fasterxml.jackson.databind.JsonNode; |
20 | -import com.fasterxml.jackson.databind.ObjectMapper; | |
21 | 19 | import com.google.common.util.concurrent.FutureCallback; |
22 | 20 | import com.google.common.util.concurrent.Futures; |
23 | 21 | import com.google.common.util.concurrent.ListenableFuture; |
... | ... | @@ -25,45 +23,30 @@ import lombok.extern.slf4j.Slf4j; |
25 | 23 | import org.checkerframework.checker.nullness.qual.Nullable; |
26 | 24 | import org.springframework.beans.factory.annotation.Autowired; |
27 | 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 | 26 | import org.thingsboard.server.common.data.edge.Edge; |
33 | 27 | import org.thingsboard.server.common.data.edge.EdgeEvent; |
34 | 28 | import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
35 | 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 | 30 | import org.thingsboard.server.common.data.id.EdgeId; |
39 | 31 | import org.thingsboard.server.common.data.id.EntityId; |
40 | -import org.thingsboard.server.common.data.id.EntityIdFactory; | |
41 | 32 | import org.thingsboard.server.common.data.id.RuleChainId; |
42 | 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 | 34 | import org.thingsboard.server.common.msg.queue.TbCallback; |
50 | -import org.thingsboard.server.dao.alarm.AlarmService; | |
51 | 35 | import org.thingsboard.server.dao.edge.EdgeEventService; |
52 | 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 | 37 | import org.thingsboard.server.gen.transport.TransportProtos; |
56 | 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 | 44 | import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
58 | 45 | import org.thingsboard.server.service.queue.TbClusterService; |
59 | 46 | |
60 | 47 | import javax.annotation.PostConstruct; |
61 | 48 | import javax.annotation.PreDestroy; |
62 | 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 | 50 | import java.util.UUID; |
68 | 51 | import java.util.concurrent.ExecutorService; |
69 | 52 | import java.util.concurrent.Executors; |
... | ... | @@ -73,30 +56,32 @@ import java.util.concurrent.Executors; |
73 | 56 | @Slf4j |
74 | 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 | 65 | @Autowired |
81 | - private EdgeService edgeService; | |
66 | + private TbClusterService clusterService; | |
82 | 67 | |
83 | 68 | @Autowired |
84 | - private AlarmService alarmService; | |
69 | + private DbCallbackExecutorService dbCallbackExecutorService; | |
85 | 70 | |
86 | 71 | @Autowired |
87 | - private UserService userService; | |
72 | + private EdgeProcessor edgeProcessor; | |
88 | 73 | |
89 | 74 | @Autowired |
90 | - private RuleChainService ruleChainService; | |
75 | + private EntityEdgeProcessor entityProcessor; | |
91 | 76 | |
92 | 77 | @Autowired |
93 | - private EdgeEventService edgeEventService; | |
78 | + private AlarmEdgeProcessor alarmProcessor; | |
94 | 79 | |
95 | 80 | @Autowired |
96 | - private TbClusterService clusterService; | |
81 | + private RelationEdgeProcessor relationProcessor; | |
97 | 82 | |
98 | 83 | @Autowired |
99 | - private DbCallbackExecutorService dbCallbackExecutorService; | |
84 | + private CustomerEdgeProcessor customerProcessor; | |
100 | 85 | |
101 | 86 | private ExecutorService tsCallBackExecutor; |
102 | 87 | |
... | ... | @@ -115,7 +100,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { |
115 | 100 | @Override |
116 | 101 | public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { |
117 | 102 | edge.setRootRuleChainId(ruleChainId); |
118 | - Edge savedEdge = edgeService.saveEdge(edge); | |
103 | + Edge savedEdge = edgeService.saveEdge(edge, true); | |
119 | 104 | saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null); |
120 | 105 | return savedEdge; |
121 | 106 | } |
... | ... | @@ -160,7 +145,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { |
160 | 145 | EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
161 | 146 | switch (type) { |
162 | 147 | case EDGE: |
163 | - processEdge(tenantId, edgeNotificationMsg); | |
148 | + edgeProcessor.processEdgeNotification(tenantId, edgeNotificationMsg); | |
164 | 149 | break; |
165 | 150 | case USER: |
166 | 151 | case ASSET: |
... | ... | @@ -169,20 +154,20 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { |
169 | 154 | case ENTITY_VIEW: |
170 | 155 | case DASHBOARD: |
171 | 156 | case RULE_CHAIN: |
172 | - processEntity(tenantId, edgeNotificationMsg); | |
157 | + entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg); | |
173 | 158 | break; |
174 | 159 | case CUSTOMER: |
175 | - processCustomer(tenantId, edgeNotificationMsg); | |
160 | + customerProcessor.processCustomerNotification(tenantId, edgeNotificationMsg); | |
176 | 161 | break; |
177 | 162 | case WIDGETS_BUNDLE: |
178 | 163 | case WIDGET_TYPE: |
179 | - processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg); | |
164 | + entityProcessor.processEntityNotificationForAllEdges(tenantId, edgeNotificationMsg); | |
180 | 165 | break; |
181 | 166 | case ALARM: |
182 | - processAlarm(tenantId, edgeNotificationMsg); | |
167 | + alarmProcessor.processAlarmNotification(tenantId, edgeNotificationMsg); | |
183 | 168 | break; |
184 | 169 | case RELATION: |
185 | - processRelation(tenantId, edgeNotificationMsg); | |
170 | + relationProcessor.processRelationNotification(tenantId, edgeNotificationMsg); | |
186 | 171 | break; |
187 | 172 | default: |
188 | 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 | 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 | 16 | package org.thingsboard.server.service.edge; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | -import lombok.Getter; | |
20 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
21 | 20 | import org.springframework.context.annotation.Lazy; |
22 | 21 | import org.springframework.stereotype.Component; |
23 | -import org.thingsboard.server.actors.service.ActorService; | |
24 | -import org.thingsboard.server.dao.alarm.AlarmService; | |
25 | 22 | import org.thingsboard.server.dao.asset.AssetService; |
26 | 23 | import org.thingsboard.server.dao.attributes.AttributesService; |
27 | -import org.thingsboard.server.dao.customer.CustomerService; | |
28 | 24 | import org.thingsboard.server.dao.dashboard.DashboardService; |
29 | -import org.thingsboard.server.dao.device.DeviceCredentialsService; | |
30 | 25 | import org.thingsboard.server.dao.device.DeviceProfileService; |
31 | -import org.thingsboard.server.dao.device.DeviceService; | |
32 | 26 | import org.thingsboard.server.dao.edge.EdgeEventService; |
33 | 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 | 28 | import org.thingsboard.server.dao.rule.RuleChainService; |
29 | +import org.thingsboard.server.dao.settings.AdminSettingsService; | |
37 | 30 | import org.thingsboard.server.dao.user.UserService; |
38 | -import org.thingsboard.server.dao.widget.WidgetTypeService; | |
39 | 31 | import org.thingsboard.server.dao.widget.WidgetsBundleService; |
40 | -import org.thingsboard.server.queue.discovery.PartitionService; | |
41 | 32 | import org.thingsboard.server.queue.util.TbCoreComponent; |
42 | 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 | 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 | 53 | @Component |
67 | 54 | @TbCoreComponent |
68 | 55 | @Data |
56 | +@Lazy | |
69 | 57 | public class EdgeContextComponent { |
70 | 58 | |
71 | - @Lazy | |
72 | 59 | @Autowired |
73 | 60 | private EdgeService edgeService; |
74 | 61 | |
75 | 62 | @Autowired |
76 | - private PartitionService partitionService; | |
77 | - | |
78 | - @Lazy | |
79 | - @Autowired | |
80 | 63 | private EdgeEventService edgeEventService; |
81 | 64 | |
82 | - @Lazy | |
83 | 65 | @Autowired |
84 | - private AssetService assetService; | |
66 | + private AdminSettingsService adminSettingsService; | |
85 | 67 | |
86 | - @Lazy | |
87 | 68 | @Autowired |
88 | - private DeviceService deviceService; | |
69 | + private AssetService assetService; | |
89 | 70 | |
90 | - @Lazy | |
91 | 71 | @Autowired |
92 | 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 | 74 | @Autowired |
104 | 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 | 77 | @Autowired |
120 | 78 | private DashboardService dashboardService; |
121 | 79 | |
122 | - @Lazy | |
123 | 80 | @Autowired |
124 | 81 | private RuleChainService ruleChainService; |
125 | 82 | |
126 | - @Lazy | |
127 | 83 | @Autowired |
128 | 84 | private UserService userService; |
129 | 85 | |
130 | - @Lazy | |
131 | - @Autowired | |
132 | - private ActorService actorService; | |
133 | - | |
134 | - @Lazy | |
135 | 86 | @Autowired |
136 | 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 | 89 | @Autowired |
152 | - private SyncEdgeService syncEdgeService; | |
90 | + private EdgeRequestsService edgeRequestsService; | |
153 | 91 | |
154 | - @Lazy | |
155 | 92 | @Autowired |
156 | - private RuleChainMsgConstructor ruleChainMsgConstructor; | |
93 | + private AlarmEdgeProcessor alarmProcessor; | |
157 | 94 | |
158 | - @Lazy | |
159 | 95 | @Autowired |
160 | - private AlarmMsgConstructor alarmMsgConstructor; | |
96 | + private DeviceProfileEdgeProcessor deviceProfileProcessor; | |
161 | 97 | |
162 | - @Lazy | |
163 | 98 | @Autowired |
164 | - private DeviceMsgConstructor deviceMsgConstructor; | |
99 | + private DeviceEdgeProcessor deviceProcessor; | |
165 | 100 | |
166 | - @Lazy | |
167 | 101 | @Autowired |
168 | - private DeviceProfileMsgConstructor deviceProfileMsgConstructor; | |
102 | + private EntityEdgeProcessor entityProcessor; | |
169 | 103 | |
170 | - @Lazy | |
171 | 104 | @Autowired |
172 | - private AssetMsgConstructor assetMsgConstructor; | |
105 | + private AssetEdgeProcessor assetProcessor; | |
173 | 106 | |
174 | - @Lazy | |
175 | 107 | @Autowired |
176 | - private EntityViewMsgConstructor entityViewMsgConstructor; | |
108 | + private EntityViewEdgeProcessor entityViewProcessor; | |
177 | 109 | |
178 | - @Lazy | |
179 | 110 | @Autowired |
180 | - private DashboardMsgConstructor dashboardMsgConstructor; | |
111 | + private UserEdgeProcessor userProcessor; | |
181 | 112 | |
182 | - @Lazy | |
183 | 113 | @Autowired |
184 | - private CustomerMsgConstructor customerMsgConstructor; | |
114 | + private RelationEdgeProcessor relationProcessor; | |
185 | 115 | |
186 | - @Lazy | |
187 | 116 | @Autowired |
188 | - private UserMsgConstructor userMsgConstructor; | |
117 | + private TelemetryEdgeProcessor telemetryProcessor; | |
189 | 118 | |
190 | - @Lazy | |
191 | 119 | @Autowired |
192 | - private RelationMsgConstructor relationMsgConstructor; | |
120 | + private DashboardEdgeProcessor dashboardProcessor; | |
193 | 121 | |
194 | - @Lazy | |
195 | 122 | @Autowired |
196 | - private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; | |
123 | + private RuleChainEdgeProcessor ruleChainProcessor; | |
197 | 124 | |
198 | - @Lazy | |
199 | 125 | @Autowired |
200 | - private WidgetTypeMsgConstructor widgetTypeMsgConstructor; | |
126 | + private CustomerEdgeProcessor customerProcessor; | |
201 | 127 | |
202 | - @Lazy | |
203 | 128 | @Autowired |
204 | - private AdminSettingsMsgConstructor adminSettingsMsgConstructor; | |
129 | + private WidgetBundleEdgeProcessor widgetBundleProcessor; | |
205 | 130 | |
206 | - @Lazy | |
207 | 131 | @Autowired |
208 | - private EntityDataMsgConstructor entityDataMsgConstructor; | |
132 | + private WidgetTypeEdgeProcessor widgetTypeProcessor; | |
209 | 133 | |
210 | - @Lazy | |
211 | 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 | 137 | @Autowired |
228 | 138 | private EdgeEventStorageSettings edgeEventStorageSettings; |
229 | 139 | |
230 | 140 | @Autowired |
231 | - @Getter | |
232 | 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 | 16 | package org.thingsboard.server.service.edge.rpc; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; |
19 | -import com.google.common.io.Resources; | |
20 | 19 | import com.google.common.util.concurrent.FutureCallback; |
20 | +import com.google.common.util.concurrent.Futures; | |
21 | 21 | import io.grpc.Server; |
22 | 22 | import io.grpc.netty.NettyServerBuilder; |
23 | 23 | import io.grpc.stub.StreamObserver; |
... | ... | @@ -28,16 +28,16 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
28 | 28 | import org.springframework.stereotype.Service; |
29 | 29 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
30 | 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 | 32 | import org.thingsboard.server.common.data.edge.Edge; |
33 | 33 | import org.thingsboard.server.common.data.id.EdgeId; |
34 | 34 | import org.thingsboard.server.common.data.id.TenantId; |
35 | 35 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
36 | 36 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
37 | 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 | 41 | import org.thingsboard.server.queue.util.TbCoreComponent; |
42 | 42 | import org.thingsboard.server.service.edge.EdgeContextComponent; |
43 | 43 | import org.thingsboard.server.service.state.DefaultDeviceStateService; |
... | ... | @@ -46,9 +46,10 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
46 | 46 | import javax.annotation.Nullable; |
47 | 47 | import javax.annotation.PostConstruct; |
48 | 48 | import javax.annotation.PreDestroy; |
49 | -import java.io.File; | |
50 | 49 | import java.io.IOException; |
50 | +import java.io.InputStream; | |
51 | 51 | import java.util.Collections; |
52 | +import java.util.HashMap; | |
52 | 53 | import java.util.Map; |
53 | 54 | import java.util.UUID; |
54 | 55 | import java.util.concurrent.ConcurrentHashMap; |
... | ... | @@ -57,6 +58,8 @@ import java.util.concurrent.Executors; |
57 | 58 | import java.util.concurrent.ScheduledExecutorService; |
58 | 59 | import java.util.concurrent.ScheduledFuture; |
59 | 60 | import java.util.concurrent.TimeUnit; |
61 | +import java.util.concurrent.locks.Lock; | |
62 | +import java.util.concurrent.locks.ReentrantLock; | |
60 | 63 | |
61 | 64 | @Service |
62 | 65 | @Slf4j |
... | ... | @@ -65,7 +68,8 @@ import java.util.concurrent.TimeUnit; |
65 | 68 | public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService { |
66 | 69 | |
67 | 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 | 73 | private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>(); |
70 | 74 | private static final ObjectMapper mapper = new ObjectMapper(); |
71 | 75 | |
... | ... | @@ -81,10 +85,15 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i |
81 | 85 | private boolean persistToTelemetry; |
82 | 86 | @Value("${edges.rpc.client_max_keep_alive_time_sec}") |
83 | 87 | private int clientMaxKeepAliveTimeSec; |
88 | + @Value("${edges.rpc.max_inbound_message_size:4194304}") | |
89 | + private int maxInboundMessageSize; | |
84 | 90 | |
85 | 91 | @Value("${edges.scheduler_pool_size}") |
86 | 92 | private int schedulerPoolSize; |
87 | 93 | |
94 | + @Value("${edges.send_scheduler_pool_size}") | |
95 | + private int sendSchedulerPoolSize; | |
96 | + | |
88 | 97 | @Autowired |
89 | 98 | private EdgeContextComponent ctx; |
90 | 99 | |
... | ... | @@ -93,19 +102,22 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i |
93 | 102 | |
94 | 103 | private Server server; |
95 | 104 | |
96 | - private ScheduledExecutorService scheduler; | |
105 | + private ScheduledExecutorService edgeEventProcessingExecutorService; | |
106 | + | |
107 | + private ScheduledExecutorService sendDownlinkExecutorService; | |
97 | 108 | |
98 | 109 | @PostConstruct |
99 | 110 | public void init() { |
100 | 111 | log.info("Initializing Edge RPC service!"); |
101 | 112 | NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort) |
102 | 113 | .permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS) |
114 | + .maxInboundMessageSize(maxInboundMessageSize) | |
103 | 115 | .addService(this); |
104 | 116 | if (sslEnabled) { |
105 | 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 | 121 | } catch (Exception e) { |
110 | 122 | log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e); |
111 | 123 | throw new RuntimeException("Unable to set up SSL context!", e); |
... | ... | @@ -119,7 +131,8 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i |
119 | 131 | log.error("Failed to start Edge RPC server!", e); |
120 | 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 | 136 | log.info("Edge RPC service initialized!"); |
124 | 137 | } |
125 | 138 | |
... | ... | @@ -136,62 +149,84 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i |
136 | 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 | 160 | @Override |
145 | 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 | 165 | @Override |
150 | - public void updateEdge(Edge edge) { | |
166 | + public void updateEdge(TenantId tenantId, Edge edge) { | |
151 | 167 | EdgeGrpcSession session = sessions.get(edge.getId()); |
152 | 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 | 170 | session.onConfigurationUpdate(edge); |
155 | 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 | 176 | @Override |
161 | - public void deleteEdge(EdgeId edgeId) { | |
177 | + public void deleteEdge(TenantId tenantId, EdgeId edgeId) { | |
162 | 178 | EdgeGrpcSession session = sessions.get(edgeId); |
163 | 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 | 181 | session.close(); |
166 | 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 | 190 | cancelScheduleEdgeEventsCheck(edgeId); |
169 | 191 | } |
170 | 192 | } |
171 | 193 | |
172 | 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 | 209 | private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { |
182 | 210 | log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId); |
183 | 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 | 219 | save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); |
186 | 220 | save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis()); |
187 | 221 | cancelScheduleEdgeEventsCheck(edgeId); |
188 | 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 | 227 | EdgeGrpcSession session = sessions.get(edgeId); |
193 | 228 | if (session != null && session.isConnected()) { |
194 | - return session; | |
229 | + session.startSyncProcess(tenantId, edgeId); | |
195 | 230 | } else { |
196 | 231 | log.error("[{}] Edge is not connected [{}]", tenantId, edgeId); |
197 | 232 | throw new RuntimeException("Edge is not connected"); |
... | ... | @@ -202,19 +237,37 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase i |
202 | 237 | EdgeId edgeId = session.getEdge().getId(); |
203 | 238 | UUID tenantId = session.getEdge().getTenantId().getId(); |
204 | 239 | if (sessions.containsKey(edgeId)) { |
205 | - ScheduledFuture<?> schedule = scheduler.schedule(() -> { | |
240 | + ScheduledFuture<?> edgeEventCheckTask = edgeEventProcessingExecutorService.schedule(() -> { | |
206 | 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 | 266 | } catch (Exception e) { |
213 | 267 | log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e); |
214 | 268 | } |
215 | - scheduleEdgeEventsCheck(session); | |
216 | 269 | }, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS); |
217 | - sessionEdgeEventChecks.put(edgeId, schedule); | |
270 | + sessionEdgeEventChecks.put(edgeId, edgeEventCheckTask); | |
218 | 271 | log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId()); |
219 | 272 | } else { |
220 | 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 | 289 | private void onEdgeDisconnect(EdgeId edgeId) { |
237 | 290 | log.info("[{}] edge disconnected!", edgeId); |
238 | 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 | 299 | save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); |
241 | 300 | save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis()); |
242 | 301 | cancelScheduleEdgeEventsCheck(edgeId); | ... | ... |
... | ... | @@ -16,111 +16,76 @@ |
16 | 16 | package org.thingsboard.server.service.edge.rpc; |
17 | 17 | |
18 | 18 | import com.datastax.oss.driver.api.core.uuid.Uuids; |
19 | -import com.fasterxml.jackson.core.JsonProcessingException; | |
20 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; |
21 | -import com.fasterxml.jackson.databind.node.ObjectNode; | |
22 | 20 | import com.google.common.util.concurrent.FutureCallback; |
23 | 21 | import com.google.common.util.concurrent.Futures; |
24 | 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 | 24 | import io.grpc.stub.StreamObserver; |
28 | 25 | import lombok.Data; |
29 | 26 | import lombok.extern.slf4j.Slf4j; |
30 | 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 | 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 | 31 | import org.thingsboard.server.common.data.edge.Edge; |
43 | 32 | import org.thingsboard.server.common.data.edge.EdgeEvent; |
44 | 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 | 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 | 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 | 36 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
61 | 37 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
62 | 38 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
63 | 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 | 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 | 73 | import java.io.Closeable; |
113 | 74 | import java.util.ArrayList; |
114 | 75 | import java.util.Collections; |
76 | +import java.util.LinkedHashMap; | |
115 | 77 | import java.util.List; |
78 | +import java.util.Map; | |
79 | +import java.util.Objects; | |
116 | 80 | import java.util.Optional; |
117 | 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 | 84 | import java.util.concurrent.TimeUnit; |
121 | 85 | import java.util.concurrent.locks.ReentrantLock; |
122 | 86 | import java.util.function.BiConsumer; |
123 | 87 | import java.util.function.Consumer; |
88 | +import java.util.stream.Collectors; | |
124 | 89 | |
125 | 90 | @Slf4j |
126 | 91 | @Data |
... | ... | @@ -135,27 +100,31 @@ public final class EdgeGrpcSession implements Closeable { |
135 | 100 | private final Consumer<EdgeId> sessionCloseListener; |
136 | 101 | private final ObjectMapper mapper; |
137 | 102 | |
103 | + private final EdgeSessionState sessionState = new EdgeSessionState(); | |
104 | + | |
138 | 105 | private EdgeContextComponent ctx; |
139 | 106 | private Edge edge; |
140 | 107 | private StreamObserver<RequestMsg> inputStream; |
141 | 108 | private StreamObserver<ResponseMsg> outputStream; |
142 | 109 | private boolean connected; |
110 | + private boolean syncCompleted; | |
143 | 111 | |
144 | - private CountDownLatch latch; | |
112 | + private ScheduledExecutorService sendDownlinkExecutorService; | |
145 | 113 | |
146 | 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 | 116 | this.sessionId = UUID.randomUUID(); |
149 | 117 | this.ctx = ctx; |
150 | 118 | this.outputStream = outputStream; |
151 | 119 | this.sessionOpenListener = sessionOpenListener; |
152 | 120 | this.sessionCloseListener = sessionCloseListener; |
153 | 121 | this.mapper = mapper; |
122 | + this.sendDownlinkExecutorService = sendDownlinkExecutorService; | |
154 | 123 | initInputStream(); |
155 | 124 | } |
156 | 125 | |
157 | 126 | private void initInputStream() { |
158 | - this.inputStream = new StreamObserver<RequestMsg>() { | |
127 | + this.inputStream = new StreamObserver<>() { | |
159 | 128 | @Override |
160 | 129 | public void onNext(RequestMsg requestMsg) { |
161 | 130 | if (!connected && requestMsg.getMsgType().equals(RequestMsgType.CONNECT_RPC_MESSAGE)) { |
... | ... | @@ -169,15 +138,21 @@ public final class EdgeGrpcSession implements Closeable { |
169 | 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 | 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 | 173 | if (edge != null) { |
199 | 174 | try { |
200 | 175 | sessionCloseListener.accept(edge.getId()); |
201 | - } catch (Exception ignored) {} | |
176 | + } catch (Exception ignored) { | |
177 | + } | |
202 | 178 | } |
203 | 179 | try { |
204 | 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 | 227 | private void onUplinkMsg(UplinkMsg uplinkMsg) { |
211 | 228 | ListenableFuture<List<Void>> future = processUplinkMsg(uplinkMsg); |
212 | - Futures.addCallback(future, new FutureCallback<List<Void>>() { | |
229 | + Futures.addCallback(future, new FutureCallback<>() { | |
213 | 230 | @Override |
214 | 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 | 235 | sendDownlinkMsg(ResponseMsg.newBuilder() |
217 | 236 | .setUplinkResponseMsg(uplinkResponseMsg) |
218 | 237 | .build()); |
... | ... | @@ -220,22 +239,32 @@ public final class EdgeGrpcSession implements Closeable { |
220 | 239 | |
221 | 240 | @Override |
222 | 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 | 246 | sendDownlinkMsg(ResponseMsg.newBuilder() |
225 | 247 | .setUplinkResponseMsg(uplinkResponseMsg) |
226 | 248 | .build()); |
227 | 249 | } |
228 | - }, MoreExecutors.directExecutor()); | |
250 | + }, ctx.getGrpcCallbackExecutorService()); | |
229 | 251 | } |
230 | 252 | |
231 | 253 | private void onDownlinkResponse(DownlinkResponseMsg msg) { |
232 | 254 | try { |
233 | 255 | if (msg.getSuccess()) { |
256 | + sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId()); | |
234 | 257 | log.debug("[{}] Msg has been processed successfully! {}", edge.getRoutingKey(), msg); |
235 | 258 | } else { |
236 | 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 | 268 | } catch (Exception e) { |
240 | 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 | 273 | private void sendDownlinkMsg(ResponseMsg downlinkMsg) { |
245 | 274 | log.trace("[{}] Sending downlink msg [{}]", this.sessionId, downlinkMsg); |
246 | 275 | if (isConnected()) { |
276 | + downlinkMsgLock.lock(); | |
247 | 277 | try { |
248 | - downlinkMsgLock.lock(); | |
249 | 278 | outputStream.onNext(downlinkMsg); |
250 | 279 | } catch (Exception e) { |
251 | 280 | log.error("[{}] Failed to send downlink message [{}]", this.sessionId, downlinkMsg, e); |
... | ... | @@ -269,149 +298,192 @@ public final class EdgeGrpcSession implements Closeable { |
269 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 489 | private ListenableFuture<Long> getQueueStartTs() { |
... | ... | @@ -424,52 +496,14 @@ public final class EdgeGrpcSession implements Closeable { |
424 | 496 | } else { |
425 | 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 | 503 | log.trace("[{}] updating QueueStartTs [{}][{}]", this.sessionId, edge.getId(), newStartTs); |
432 | 504 | newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1 |
433 | 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 | 509 | private DownlinkMsg processEntityMessage(EdgeEvent edgeEvent, EdgeEventActionType action) { |
... | ... | @@ -477,411 +511,39 @@ public final class EdgeGrpcSession implements Closeable { |
477 | 511 | log.trace("Executing processEntityMessage, edgeEvent [{}], action [{}], msgType [{}]", edgeEvent, action, msgType); |
478 | 512 | switch (edgeEvent.getType()) { |
479 | 513 | case DEVICE: |
480 | - return processDevice(edgeEvent, msgType, action); | |
514 | + return ctx.getDeviceProcessor().processDeviceToEdge(edge, edgeEvent, msgType, action); | |
481 | 515 | case DEVICE_PROFILE: |
482 | - return processDeviceProfile(edgeEvent, msgType, action); | |
516 | + return ctx.getDeviceProfileProcessor().processDeviceProfileToEdge(edgeEvent, msgType, action); | |
483 | 517 | case ASSET: |
484 | - return processAsset(edgeEvent, msgType, action); | |
518 | + return ctx.getAssetProcessor().processAssetToEdge(edge, edgeEvent, msgType, action); | |
485 | 519 | case ENTITY_VIEW: |
486 | - return processEntityView(edgeEvent, msgType, action); | |
520 | + return ctx.getEntityViewProcessor().processEntityViewToEdge(edge, edgeEvent, msgType, action); | |
487 | 521 | case DASHBOARD: |
488 | - return processDashboard(edgeEvent, msgType, action); | |
522 | + return ctx.getDashboardProcessor().processDashboardToEdge(edge, edgeEvent, msgType, action); | |
489 | 523 | case CUSTOMER: |
490 | - return processCustomer(edgeEvent, msgType, action); | |
524 | + return ctx.getCustomerProcessor().processCustomerToEdge(edgeEvent, msgType, action); | |
491 | 525 | case RULE_CHAIN: |
492 | - return processRuleChain(edgeEvent, msgType, action); | |
526 | + return ctx.getRuleChainProcessor().processRuleChainToEdge(edge, edgeEvent, msgType, action); | |
493 | 527 | case RULE_CHAIN_METADATA: |
494 | - return processRuleChainMetadata(edgeEvent, msgType); | |
528 | + return ctx.getRuleChainProcessor().processRuleChainMetadataToEdge(edgeEvent, msgType); | |
495 | 529 | case ALARM: |
496 | - return processAlarm(edgeEvent, msgType); | |
530 | + return ctx.getAlarmProcessor().processAlarmToEdge(edge, edgeEvent, msgType); | |
497 | 531 | case USER: |
498 | - return processUser(edgeEvent, msgType, action); | |
532 | + return ctx.getUserProcessor().processUserToEdge(edge, edgeEvent, msgType, action); | |
499 | 533 | case RELATION: |
500 | - return processRelation(edgeEvent, msgType); | |
534 | + return ctx.getRelationProcessor().processRelationToEdge(edgeEvent, msgType); | |
501 | 535 | case WIDGETS_BUNDLE: |
502 | - return processWidgetsBundle(edgeEvent, msgType, action); | |
536 | + return ctx.getWidgetBundleProcessor().processWidgetsBundleToEdge(edgeEvent, msgType, action); | |
503 | 537 | case WIDGET_TYPE: |
504 | - return processWidgetType(edgeEvent, msgType, action); | |
538 | + return ctx.getWidgetTypeProcessor().processWidgetTypeToEdge(edgeEvent, msgType, action); | |
505 | 539 | case ADMIN_SETTINGS: |
506 | - return processAdminSettings(edgeEvent); | |
540 | + return ctx.getAdminSettingsProcessor().processAdminSettingsToEdge(edgeEvent); | |
507 | 541 | default: |
508 | 542 | log.warn("Unsupported edge event type [{}]", edgeEvent); |
509 | 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 | 547 | private UpdateMsgType getResponseMsgType(EdgeEventActionType actionType) { |
886 | 548 | switch (actionType) { |
887 | 549 | case UPDATED: |
... | ... | @@ -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 | 571 | private ListenableFuture<List<Void>> processUplinkMsg(UplinkMsg uplinkMsg) { |
919 | 572 | List<ListenableFuture<Void>> result = new ArrayList<>(); |
920 | 573 | try { |
921 | 574 | if (uplinkMsg.getEntityDataCount() > 0) { |
922 | 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 | 579 | if (uplinkMsg.getDeviceUpdateMsgCount() > 0) { |
927 | 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 | 584 | if (uplinkMsg.getDeviceCredentialsUpdateMsgCount() > 0) { |
932 | 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 | 589 | if (uplinkMsg.getAlarmUpdateMsgCount() > 0) { |
937 | 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 | 594 | if (uplinkMsg.getRelationUpdateMsgCount() > 0) { |
942 | 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 | 599 | if (uplinkMsg.getRuleChainMetadataRequestMsgCount() > 0) { |
947 | 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 | 604 | if (uplinkMsg.getAttributesRequestMsgCount() > 0) { |
952 | 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 | 609 | if (uplinkMsg.getRelationRequestMsgCount() > 0) { |
957 | 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 | 614 | if (uplinkMsg.getUserCredentialsRequestMsgCount() > 0) { |
962 | 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 | 619 | if (uplinkMsg.getDeviceCredentialsRequestMsgCount() > 0) { |
967 | 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 | 624 | if (uplinkMsg.getDeviceRpcCallMsgCount() > 0) { |
972 | 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 | 644 | } catch (Exception e) { |
... | ... | @@ -1026,7 +694,7 @@ public final class EdgeGrpcSession implements Closeable { |
1026 | 694 | .setCloudType("CE"); |
1027 | 695 | if (edge.getCustomerId() != null) { |
1028 | 696 | builder.setCustomerIdMSB(edge.getCustomerId().getId().getMostSignificantBits()) |
1029 | - .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits()); | |
697 | + .setCustomerIdLSB(edge.getCustomerId().getId().getLeastSignificantBits()); | |
1030 | 698 | } |
1031 | 699 | return builder |
1032 | 700 | .build(); | ... | ... |
... | ... | @@ -17,12 +17,15 @@ package org.thingsboard.server.service.edge.rpc; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.edge.Edge; |
19 | 19 | import org.thingsboard.server.common.data.id.EdgeId; |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
20 | 21 | |
21 | 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 | 18 | import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.data.AdminSettings; |
20 | 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 | 22 | import org.thingsboard.server.queue.util.TbCoreComponent; |
23 | 23 | |
24 | 24 | @Component | ... | ... |
... | ... | @@ -26,8 +26,8 @@ import org.thingsboard.server.dao.asset.AssetService; |
26 | 26 | import org.thingsboard.server.dao.device.DeviceService; |
27 | 27 | import org.thingsboard.server.dao.entityview.EntityViewService; |
28 | 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 | 31 | import org.thingsboard.server.queue.util.TbCoreComponent; |
32 | 32 | |
33 | 33 | @Component | ... | ... |
... | ... | @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.asset.Asset; |
20 | 20 | import org.thingsboard.server.common.data.id.AssetId; |
21 | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | 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 | 25 | import org.thingsboard.server.queue.util.TbCoreComponent; |
26 | 26 | |
27 | 27 | @Component | ... | ... |
... | ... | @@ -19,8 +19,8 @@ import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.data.Customer; |
20 | 20 | import org.thingsboard.server.common.data.id.CustomerId; |
21 | 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 | 24 | import org.thingsboard.server.queue.util.TbCoreComponent; |
25 | 25 | |
26 | 26 | @Component | ... | ... |
... | ... | @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.Dashboard; |
20 | 20 | import org.thingsboard.server.common.data.id.CustomerId; |
21 | 21 | import org.thingsboard.server.common.data.id.DashboardId; |
22 | 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 | 25 | import org.thingsboard.server.queue.util.TbCoreComponent; |
26 | 26 | |
27 | 27 | @Component | ... | ... |
... | ... | @@ -23,11 +23,11 @@ import org.thingsboard.server.common.data.Device; |
23 | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | 24 | import org.thingsboard.server.common.data.id.DeviceId; |
25 | 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 | 31 | import org.thingsboard.server.queue.util.TbCoreComponent; |
32 | 32 | |
33 | 33 | import java.util.UUID; | ... | ... |
... | ... | @@ -21,8 +21,8 @@ import org.springframework.stereotype.Component; |
21 | 21 | import org.thingsboard.server.common.data.DeviceProfile; |
22 | 22 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
23 | 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 | 26 | import org.thingsboard.server.queue.util.TbCoreComponent; |
27 | 27 | |
28 | 28 | @Component |
... | ... | @@ -41,7 +41,7 @@ public class DeviceProfileMsgConstructor { |
41 | 41 | .setDefault(deviceProfile.isDefault()) |
42 | 42 | .setType(deviceProfile.getType().name()) |
43 | 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 | 45 | // if (deviceProfile.getDefaultRuleChainId() != null) { |
46 | 46 | // builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits()) |
47 | 47 | // .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits()); | ... | ... |
... | ... | @@ -25,8 +25,8 @@ import org.springframework.stereotype.Component; |
25 | 25 | import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
26 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
27 | 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 | 30 | import org.thingsboard.server.gen.transport.TransportProtos; |
31 | 31 | import org.thingsboard.server.queue.util.TbCoreComponent; |
32 | 32 | ... | ... |
... | ... | @@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.EntityView; |
20 | 20 | import org.thingsboard.server.common.data.id.CustomerId; |
21 | 21 | import org.thingsboard.server.common.data.id.EntityViewId; |
22 | 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 | 26 | import org.thingsboard.server.queue.util.TbCoreComponent; |
27 | 27 | |
28 | 28 | @Component | ... | ... |
... | ... | @@ -18,8 +18,8 @@ package org.thingsboard.server.service.edge.rpc.constructor; |
18 | 18 | import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.data.relation.EntityRelation; |
20 | 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 | 23 | import org.thingsboard.server.queue.util.TbCoreComponent; |
24 | 24 | |
25 | 25 | @Component | ... | ... |
... | ... | @@ -26,12 +26,12 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; |
26 | 26 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
27 | 27 | import org.thingsboard.server.common.data.rule.RuleNode; |
28 | 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 | 35 | import org.thingsboard.server.queue.util.TbCoreComponent; |
36 | 36 | |
37 | 37 | import java.util.ArrayList; | ... | ... |
... | ... | @@ -21,9 +21,9 @@ import org.thingsboard.server.common.data.id.CustomerId; |
21 | 21 | import org.thingsboard.server.common.data.id.UserId; |
22 | 22 | import org.thingsboard.server.common.data.security.UserCredentials; |
23 | 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 | 27 | import org.thingsboard.server.queue.util.TbCoreComponent; |
28 | 28 | |
29 | 29 | @Component | ... | ... |
... | ... | @@ -20,8 +20,8 @@ import org.thingsboard.server.common.data.id.TenantId; |
20 | 20 | import org.thingsboard.server.common.data.id.WidgetTypeId; |
21 | 21 | import org.thingsboard.server.common.data.widget.WidgetType; |
22 | 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 | 25 | import org.thingsboard.server.queue.util.TbCoreComponent; |
26 | 26 | |
27 | 27 | @Component | ... | ... |
... | ... | @@ -20,8 +20,8 @@ import org.springframework.stereotype.Component; |
20 | 20 | import org.thingsboard.server.common.data.id.TenantId; |
21 | 21 | import org.thingsboard.server.common.data.id.WidgetsBundleId; |
22 | 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 | 25 | import org.thingsboard.server.queue.util.TbCoreComponent; |
26 | 26 | |
27 | 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 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/AssetsEdgeEventFetcher.java
0 → 100644
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 | +} | ... | ... |