Commit 0ea4723e2e3f97481d7c8df540ebafe72c22c62b
Committed by
GitHub
Merge pull request #3572 from ShvaykaD/feature/device-profile-telemetry-proto-schema
[3.2] Custom proto schemas for MQTT telemetry & attributes upload.
Showing
19 changed files
with
467 additions
and
229 deletions
@@ -219,8 +219,8 @@ | @@ -219,8 +219,8 @@ | ||
219 | "defaultPageSize": 10, | 219 | "defaultPageSize": 10, |
220 | "defaultSortOrder": "-createdTime", | 220 | "defaultSortOrder": "-createdTime", |
221 | "enableSelectColumnDisplay": false, | 221 | "enableSelectColumnDisplay": false, |
222 | - "enableStatusFilter": true, | ||
223 | - "alarmsTitle": "Alarms" | 222 | + "alarmsTitle": "Alarms", |
223 | + "enableFilter": true | ||
224 | }, | 224 | }, |
225 | "title": "New Alarms table", | 225 | "title": "New Alarms table", |
226 | "dropShadow": true, | 226 | "dropShadow": true, |
@@ -234,6 +234,9 @@ | @@ -234,6 +234,9 @@ | ||
234 | "showLegend": false, | 234 | "showLegend": false, |
235 | "alarmSource": { | 235 | "alarmSource": { |
236 | "type": "entity", | 236 | "type": "entity", |
237 | + "name": "alarms", | ||
238 | + "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e", | ||
239 | + "filterId": null, | ||
237 | "dataKeys": [ | 240 | "dataKeys": [ |
238 | { | 241 | { |
239 | "name": "createdTime", | 242 | "name": "createdTime", |
@@ -275,9 +278,7 @@ | @@ -275,9 +278,7 @@ | ||
275 | "settings": {}, | 278 | "settings": {}, |
276 | "_hash": 0.7977920750136249 | 279 | "_hash": 0.7977920750136249 |
277 | } | 280 | } |
278 | - ], | ||
279 | - "entityAliasId": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813", | ||
280 | - "name": "alarms" | 281 | + ] |
281 | }, | 282 | }, |
282 | "alarmSearchStatus": "ANY", | 283 | "alarmSearchStatus": "ANY", |
283 | "alarmsPollingInterval": 5, | 284 | "alarmsPollingInterval": 5, |
@@ -1031,7 +1032,8 @@ | @@ -1031,7 +1032,8 @@ | ||
1031 | "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;", | 1032 | "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;", |
1032 | "useLabelFunction": true, | 1033 | "useLabelFunction": true, |
1033 | "provider": "openstreet-map", | 1034 | "provider": "openstreet-map", |
1034 | - "draggableMarker": true | 1035 | + "draggableMarker": true, |
1036 | + "editablePolygon": true | ||
1035 | }, | 1037 | }, |
1036 | "title": "New Markers Placement - OpenStreetMap", | 1038 | "title": "New Markers Placement - OpenStreetMap", |
1037 | "dropShadow": true, | 1039 | "dropShadow": true, |
@@ -1062,61 +1064,6 @@ | @@ -1062,61 +1064,6 @@ | ||
1062 | "displayTimewindow": true | 1064 | "displayTimewindow": true |
1063 | }, | 1065 | }, |
1064 | "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf" | 1066 | "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf" |
1065 | - }, | ||
1066 | - "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": { | ||
1067 | - "isSystemType": true, | ||
1068 | - "bundleAlias": "input_widgets", | ||
1069 | - "typeAlias": "update_double_timeseries", | ||
1070 | - "type": "latest", | ||
1071 | - "title": "New widget", | ||
1072 | - "sizeX": 7.5, | ||
1073 | - "sizeY": 3, | ||
1074 | - "config": { | ||
1075 | - "datasources": [ | ||
1076 | - { | ||
1077 | - "type": "entity", | ||
1078 | - "name": null, | ||
1079 | - "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547", | ||
1080 | - "dataKeys": [ | ||
1081 | - { | ||
1082 | - "name": "temperature", | ||
1083 | - "type": "timeseries", | ||
1084 | - "label": "temperature", | ||
1085 | - "color": "#2196f3", | ||
1086 | - "settings": {}, | ||
1087 | - "_hash": 0.4164505192982848 | ||
1088 | - } | ||
1089 | - ] | ||
1090 | - } | ||
1091 | - ], | ||
1092 | - "timewindow": { | ||
1093 | - "realtime": { | ||
1094 | - "timewindowMs": 60000 | ||
1095 | - } | ||
1096 | - }, | ||
1097 | - "showTitle": true, | ||
1098 | - "backgroundColor": "#fff", | ||
1099 | - "color": "rgba(0, 0, 0, 0.87)", | ||
1100 | - "padding": "8px", | ||
1101 | - "settings": { | ||
1102 | - "showResultMessage": true, | ||
1103 | - "showLabel": true | ||
1104 | - }, | ||
1105 | - "title": "New Update double timeseries", | ||
1106 | - "dropShadow": true, | ||
1107 | - "enableFullscreen": false, | ||
1108 | - "widgetStyle": {}, | ||
1109 | - "titleStyle": { | ||
1110 | - "fontSize": "16px", | ||
1111 | - "fontWeight": 400 | ||
1112 | - }, | ||
1113 | - "useDashboardTimewindow": true, | ||
1114 | - "showLegend": false, | ||
1115 | - "actions": {} | ||
1116 | - }, | ||
1117 | - "row": 0, | ||
1118 | - "col": 0, | ||
1119 | - "id": "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3" | ||
1120 | } | 1067 | } |
1121 | }, | 1068 | }, |
1122 | "states": { | 1069 | "states": { |
@@ -1215,12 +1162,6 @@ | @@ -1215,12 +1162,6 @@ | ||
1215 | "sizeY": 6, | 1162 | "sizeY": 6, |
1216 | "row": 6, | 1163 | "row": 6, |
1217 | "col": 0 | 1164 | "col": 0 |
1218 | - }, | ||
1219 | - "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": { | ||
1220 | - "sizeX": 7.5, | ||
1221 | - "sizeY": 3, | ||
1222 | - "row": 12, | ||
1223 | - "col": 0 | ||
1224 | } | 1165 | } |
1225 | }, | 1166 | }, |
1226 | "gridSettings": { | 1167 | "gridSettings": { |
@@ -1257,16 +1198,6 @@ | @@ -1257,16 +1198,6 @@ | ||
1257 | "stateEntityParamName": null, | 1198 | "stateEntityParamName": null, |
1258 | "defaultStateEntity": null | 1199 | "defaultStateEntity": null |
1259 | } | 1200 | } |
1260 | - }, | ||
1261 | - "ce27a9d0-93bf-b7a4-054d-d0369a8cf813": { | ||
1262 | - "id": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813", | ||
1263 | - "alias": "Thermostat-alarm", | ||
1264 | - "filter": { | ||
1265 | - "type": "entityName", | ||
1266 | - "resolveMultiple": false, | ||
1267 | - "entityType": "ASSET", | ||
1268 | - "entityNameFilter": "Thermostat Alarms" | ||
1269 | - } | ||
1270 | } | 1201 | } |
1271 | }, | 1202 | }, |
1272 | "timewindow": { | 1203 | "timewindow": { |
@@ -1301,7 +1232,8 @@ | @@ -1301,7 +1232,8 @@ | ||
1301 | "showDashboardTimewindow": true, | 1232 | "showDashboardTimewindow": true, |
1302 | "showDashboardExport": true, | 1233 | "showDashboardExport": true, |
1303 | "toolbarAlwaysOpen": true | 1234 | "toolbarAlwaysOpen": true |
1304 | - } | 1235 | + }, |
1236 | + "filters": {} | ||
1305 | }, | 1237 | }, |
1306 | "name": "Thermostats" | 1238 | "name": "Thermostats" |
1307 | } | 1239 | } |
1 | { | 1 | { |
2 | "ruleChain": { | 2 | "ruleChain": { |
3 | - "additionalInfo": null, | 3 | + "additionalInfo": { |
4 | + "description": "" | ||
5 | + }, | ||
4 | "name": "Thermostat Alarms", | 6 | "name": "Thermostat Alarms", |
5 | "firstRuleNodeId": null, | 7 | "firstRuleNodeId": null, |
6 | "root": false, | 8 | "root": false, |
@@ -8,131 +10,126 @@ | @@ -8,131 +10,126 @@ | ||
8 | "configuration": null | 10 | "configuration": null |
9 | }, | 11 | }, |
10 | "metadata": { | 12 | "metadata": { |
11 | - "firstNodeIndex": 5, | 13 | + "firstNodeIndex": 6, |
12 | "nodes": [ | 14 | "nodes": [ |
13 | { | 15 | { |
14 | "additionalInfo": { | 16 | "additionalInfo": { |
15 | - "layoutX": 929, | ||
16 | - "layoutY": 67 | 17 | + "layoutX": 822, |
18 | + "layoutY": 294 | ||
17 | }, | 19 | }, |
18 | - "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode", | ||
19 | - "name": "Create Temp Alarm", | 20 | + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
21 | + "name": "Save Timeseries", | ||
20 | "debugMode": false, | 22 | "debugMode": false, |
21 | "configuration": { | 23 | "configuration": { |
22 | - "alarmType": "High Temperature", | ||
23 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.temperature;\nreturn details;", | ||
24 | - "severity": "MAJOR", | ||
25 | - "propagate": true, | ||
26 | - "useMessageAlarmData": false, | ||
27 | - "relationTypes": [ | ||
28 | - "ToAlarmPropagationAsset" | ||
29 | - ] | 24 | + "defaultTTL": 0 |
30 | } | 25 | } |
31 | }, | 26 | }, |
32 | { | 27 | { |
33 | "additionalInfo": { | 28 | "additionalInfo": { |
34 | - "layoutX": 930, | ||
35 | - "layoutY": 201 | 29 | + "description": null, |
30 | + "layoutX": 824, | ||
31 | + "layoutY": 221 | ||
36 | }, | 32 | }, |
37 | - "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode", | ||
38 | - "name": "Clear Temp Alarm", | 33 | + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
34 | + "name": "Save Client Attributes", | ||
39 | "debugMode": false, | 35 | "debugMode": false, |
40 | "configuration": { | 36 | "configuration": { |
41 | - "alarmType": "High Temperature", | ||
42 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;" | 37 | + "scope": "SERVER_SCOPE", |
38 | + "notifyDevice": null | ||
43 | } | 39 | } |
44 | }, | 40 | }, |
45 | { | 41 | { |
46 | "additionalInfo": { | 42 | "additionalInfo": { |
47 | - "layoutX": 930, | ||
48 | - "layoutY": 131 | 43 | + "layoutX": 494, |
44 | + "layoutY": 309 | ||
49 | }, | 45 | }, |
50 | - "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode", | ||
51 | - "name": "Create Humidity Alarm", | 46 | + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", |
47 | + "name": "Message Type Switch", | ||
52 | "debugMode": false, | 48 | "debugMode": false, |
53 | "configuration": { | 49 | "configuration": { |
54 | - "alarmType": "Low Humidity", | ||
55 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.humidity;\nreturn details;", | ||
56 | - "severity": "MINOR", | ||
57 | - "propagate": true, | ||
58 | - "useMessageAlarmData": false, | ||
59 | - "relationTypes": [ | ||
60 | - "ToAlarmPropagationAsset" | ||
61 | - ] | 50 | + "version": 0 |
62 | } | 51 | } |
63 | }, | 52 | }, |
64 | { | 53 | { |
65 | "additionalInfo": { | 54 | "additionalInfo": { |
66 | - "layoutX": 929, | ||
67 | - "layoutY": 275 | 55 | + "layoutX": 824, |
56 | + "layoutY": 383 | ||
68 | }, | 57 | }, |
69 | - "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode", | ||
70 | - "name": "Clear Humidity Alarm", | 58 | + "type": "org.thingsboard.rule.engine.action.TbLogNode", |
59 | + "name": "Log RPC from Device", | ||
71 | "debugMode": false, | 60 | "debugMode": false, |
72 | "configuration": { | 61 | "configuration": { |
73 | - "alarmType": "Low Humidity", | ||
74 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;" | 62 | + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
75 | } | 63 | } |
76 | }, | 64 | }, |
77 | { | 65 | { |
78 | "additionalInfo": { | 66 | "additionalInfo": { |
79 | - "layoutX": 586, | ||
80 | - "layoutY": 148 | 67 | + "layoutX": 823, |
68 | + "layoutY": 444 | ||
81 | }, | 69 | }, |
82 | - "type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode", | ||
83 | - "name": "Check Alarms", | 70 | + "type": "org.thingsboard.rule.engine.action.TbLogNode", |
71 | + "name": "Log Other", | ||
84 | "debugMode": false, | 72 | "debugMode": false, |
85 | "configuration": { | 73 | "configuration": { |
86 | - "jsScript": "var relations = [];\nif(metadata[\"ss_alarmTemperature\"] === \"true\"){\n if(msg.temperature > metadata[\"ss_thresholdTemperature\"]){\n relations.push(\"NewTempAlarm\");\n } else {\n relations.push(\"ClearTempAlarm\");\n }\n}\nif(metadata[\"ss_alarmHumidity\"] === \"true\"){\n if(msg.humidity < metadata[\"ss_thresholdHumidity\"]){\n relations.push(\"NewHumidityAlarm\");\n } else {\n relations.push(\"ClearHumidityAlarm\");\n }\n}\n\nreturn relations;" | 74 | + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
87 | } | 75 | } |
88 | }, | 76 | }, |
89 | { | 77 | { |
90 | "additionalInfo": { | 78 | "additionalInfo": { |
91 | - "layoutX": 321, | ||
92 | - "layoutY": 149 | 79 | + "layoutX": 822, |
80 | + "layoutY": 507 | ||
93 | }, | 81 | }, |
94 | - "type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode", | ||
95 | - "name": "Fetch Configuration", | 82 | + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", |
83 | + "name": "RPC Call Request", | ||
96 | "debugMode": false, | 84 | "debugMode": false, |
97 | "configuration": { | 85 | "configuration": { |
98 | - "clientAttributeNames": [], | ||
99 | - "sharedAttributeNames": [], | ||
100 | - "serverAttributeNames": [ | ||
101 | - "alarmTemperature", | ||
102 | - "thresholdTemperature", | ||
103 | - "alarmHumidity", | ||
104 | - "thresholdHumidity" | ||
105 | - ], | ||
106 | - "latestTsKeyNames": [], | ||
107 | - "tellFailureIfAbsent": false, | ||
108 | - "getLatestValueWithTs": false | 86 | + "timeoutInSeconds": 60 |
87 | + } | ||
88 | + }, | ||
89 | + { | ||
90 | + "additionalInfo": { | ||
91 | + "description": "", | ||
92 | + "layoutX": 209, | ||
93 | + "layoutY": 307 | ||
94 | + }, | ||
95 | + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", | ||
96 | + "name": "Device Profile Node", | ||
97 | + "debugMode": false, | ||
98 | + "configuration": { | ||
99 | + "persistAlarmRulesState": false, | ||
100 | + "fetchAlarmRulesStateOnStart": false | ||
109 | } | 101 | } |
110 | } | 102 | } |
111 | ], | 103 | ], |
112 | "connections": [ | 104 | "connections": [ |
113 | { | 105 | { |
114 | - "fromIndex": 4, | ||
115 | - "toIndex": 0, | ||
116 | - "type": "NewTempAlarm" | 106 | + "fromIndex": 2, |
107 | + "toIndex": 4, | ||
108 | + "type": "Other" | ||
117 | }, | 109 | }, |
118 | { | 110 | { |
119 | - "fromIndex": 4, | 111 | + "fromIndex": 2, |
120 | "toIndex": 1, | 112 | "toIndex": 1, |
121 | - "type": "ClearTempAlarm" | 113 | + "type": "Post attributes" |
122 | }, | 114 | }, |
123 | { | 115 | { |
124 | - "fromIndex": 4, | ||
125 | - "toIndex": 2, | ||
126 | - "type": "NewHumidityAlarm" | 116 | + "fromIndex": 2, |
117 | + "toIndex": 0, | ||
118 | + "type": "Post telemetry" | ||
127 | }, | 119 | }, |
128 | { | 120 | { |
129 | - "fromIndex": 4, | 121 | + "fromIndex": 2, |
130 | "toIndex": 3, | 122 | "toIndex": 3, |
131 | - "type": "ClearHumidityAlarm" | 123 | + "type": "RPC Request from Device" |
132 | }, | 124 | }, |
133 | { | 125 | { |
134 | - "fromIndex": 5, | ||
135 | - "toIndex": 4, | 126 | + "fromIndex": 2, |
127 | + "toIndex": 5, | ||
128 | + "type": "RPC Request to Device" | ||
129 | + }, | ||
130 | + { | ||
131 | + "fromIndex": 6, | ||
132 | + "toIndex": 2, | ||
136 | "type": "Success" | 133 | "type": "Success" |
137 | } | 134 | } |
138 | ], | 135 | ], |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -28,12 +28,21 @@ import org.thingsboard.server.common.data.Customer; | @@ -28,12 +28,21 @@ import org.thingsboard.server.common.data.Customer; | ||
28 | import org.thingsboard.server.common.data.DataConstants; | 28 | import org.thingsboard.server.common.data.DataConstants; |
29 | import org.thingsboard.server.common.data.Device; | 29 | import org.thingsboard.server.common.data.Device; |
30 | import org.thingsboard.server.common.data.DeviceProfile; | 30 | import org.thingsboard.server.common.data.DeviceProfile; |
31 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | ||
32 | +import org.thingsboard.server.common.data.DeviceProfileType; | ||
33 | +import org.thingsboard.server.common.data.DeviceTransportType; | ||
31 | import org.thingsboard.server.common.data.Tenant; | 34 | import org.thingsboard.server.common.data.Tenant; |
32 | import org.thingsboard.server.common.data.TenantProfile; | 35 | import org.thingsboard.server.common.data.TenantProfile; |
33 | -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | ||
34 | -import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | ||
35 | import org.thingsboard.server.common.data.User; | 36 | import org.thingsboard.server.common.data.User; |
36 | -import org.thingsboard.server.common.data.asset.Asset; | 37 | +import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
38 | +import org.thingsboard.server.common.data.device.profile.AlarmCondition; | ||
39 | +import org.thingsboard.server.common.data.device.profile.AlarmRule; | ||
40 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | ||
41 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; | ||
42 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | ||
43 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | ||
44 | +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; | ||
45 | +import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; | ||
37 | import org.thingsboard.server.common.data.id.CustomerId; | 46 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | import org.thingsboard.server.common.data.id.DeviceId; | 47 | import org.thingsboard.server.common.data.id.DeviceId; |
39 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 48 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
@@ -42,19 +51,29 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | @@ -42,19 +51,29 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | ||
42 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 51 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
43 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 52 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
44 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 53 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
45 | -import org.thingsboard.server.common.data.relation.EntityRelation; | 54 | +import org.thingsboard.server.common.data.page.PageLink; |
55 | +import org.thingsboard.server.common.data.query.BooleanFilterPredicate; | ||
56 | +import org.thingsboard.server.common.data.query.DynamicValue; | ||
57 | +import org.thingsboard.server.common.data.query.DynamicValueSourceType; | ||
58 | +import org.thingsboard.server.common.data.query.EntityKey; | ||
59 | +import org.thingsboard.server.common.data.query.EntityKeyType; | ||
60 | +import org.thingsboard.server.common.data.query.EntityKeyValueType; | ||
61 | +import org.thingsboard.server.common.data.query.FilterPredicateValue; | ||
62 | +import org.thingsboard.server.common.data.query.KeyFilter; | ||
63 | +import org.thingsboard.server.common.data.query.NumericFilterPredicate; | ||
46 | import org.thingsboard.server.common.data.security.Authority; | 64 | import org.thingsboard.server.common.data.security.Authority; |
47 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 65 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
48 | import org.thingsboard.server.common.data.security.UserCredentials; | 66 | import org.thingsboard.server.common.data.security.UserCredentials; |
67 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | ||
68 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | ||
49 | import org.thingsboard.server.common.data.widget.WidgetsBundle; | 69 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
50 | -import org.thingsboard.server.dao.asset.AssetService; | ||
51 | import org.thingsboard.server.dao.attributes.AttributesService; | 70 | import org.thingsboard.server.dao.attributes.AttributesService; |
52 | import org.thingsboard.server.dao.customer.CustomerService; | 71 | import org.thingsboard.server.dao.customer.CustomerService; |
53 | import org.thingsboard.server.dao.device.DeviceCredentialsService; | 72 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
54 | import org.thingsboard.server.dao.device.DeviceProfileService; | 73 | import org.thingsboard.server.dao.device.DeviceProfileService; |
55 | import org.thingsboard.server.dao.device.DeviceService; | 74 | import org.thingsboard.server.dao.device.DeviceService; |
56 | import org.thingsboard.server.dao.exception.DataValidationException; | 75 | import org.thingsboard.server.dao.exception.DataValidationException; |
57 | -import org.thingsboard.server.dao.relation.RelationService; | 76 | +import org.thingsboard.server.dao.rule.RuleChainService; |
58 | import org.thingsboard.server.dao.settings.AdminSettingsService; | 77 | import org.thingsboard.server.dao.settings.AdminSettingsService; |
59 | import org.thingsboard.server.dao.tenant.TenantProfileService; | 78 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
60 | import org.thingsboard.server.dao.tenant.TenantService; | 79 | import org.thingsboard.server.dao.tenant.TenantService; |
@@ -62,6 +81,8 @@ import org.thingsboard.server.dao.user.UserService; | @@ -62,6 +81,8 @@ import org.thingsboard.server.dao.user.UserService; | ||
62 | import org.thingsboard.server.dao.widget.WidgetsBundleService; | 81 | import org.thingsboard.server.dao.widget.WidgetsBundleService; |
63 | 82 | ||
64 | import java.util.Arrays; | 83 | import java.util.Arrays; |
84 | +import java.util.Collections; | ||
85 | +import java.util.LinkedHashMap; | ||
65 | 86 | ||
66 | @Service | 87 | @Service |
67 | @Profile("install") | 88 | @Profile("install") |
@@ -97,12 +118,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -97,12 +118,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
97 | private CustomerService customerService; | 118 | private CustomerService customerService; |
98 | 119 | ||
99 | @Autowired | 120 | @Autowired |
100 | - private RelationService relationService; | ||
101 | - | ||
102 | - @Autowired | ||
103 | - private AssetService assetService; | ||
104 | - | ||
105 | - @Autowired | ||
106 | private DeviceService deviceService; | 121 | private DeviceService deviceService; |
107 | 122 | ||
108 | @Autowired | 123 | @Autowired |
@@ -114,6 +129,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -114,6 +129,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
114 | @Autowired | 129 | @Autowired |
115 | private DeviceCredentialsService deviceCredentialsService; | 130 | private DeviceCredentialsService deviceCredentialsService; |
116 | 131 | ||
132 | + @Autowired | ||
133 | + private RuleChainService ruleChainService; | ||
134 | + | ||
117 | @Bean | 135 | @Bean |
118 | protected BCryptPasswordEncoder passwordEncoder() { | 136 | protected BCryptPasswordEncoder passwordEncoder() { |
119 | return new BCryptPasswordEncoder(); | 137 | return new BCryptPasswordEncoder(); |
@@ -245,19 +263,133 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -245,19 +263,133 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
245 | createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + | 263 | createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + |
246 | "Raspberry Pi GPIO control sample application"); | 264 | "Raspberry Pi GPIO control sample application"); |
247 | 265 | ||
248 | - Asset thermostatAlarms = new Asset(); | ||
249 | - thermostatAlarms.setTenantId(demoTenant.getId()); | ||
250 | - thermostatAlarms.setName("Thermostat Alarms"); | ||
251 | - thermostatAlarms.setType("AlarmPropagationAsset"); | ||
252 | - thermostatAlarms = assetService.saveAsset(thermostatAlarms); | ||
253 | - | ||
254 | - DeviceProfile thermostatDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), "thermostat"); | ||
255 | - | ||
256 | - DeviceId t1Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | ||
257 | - DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | ||
258 | - | ||
259 | - relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset")); | ||
260 | - relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset")); | 266 | + DeviceProfile thermostatDeviceProfile = new DeviceProfile(); |
267 | + thermostatDeviceProfile.setTenantId(demoTenant.getId()); | ||
268 | + thermostatDeviceProfile.setDefault(false); | ||
269 | + thermostatDeviceProfile.setName("thermostat"); | ||
270 | + thermostatDeviceProfile.setType(DeviceProfileType.DEFAULT); | ||
271 | + thermostatDeviceProfile.setTransportType(DeviceTransportType.DEFAULT); | ||
272 | + thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | ||
273 | + thermostatDeviceProfile.setDescription("Thermostat device profile"); | ||
274 | + thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains( | ||
275 | + demoTenant.getId(), new PageLink(1, 0, "Thermostat Alarms")).getData().get(0).getId()); | ||
276 | + | ||
277 | + DeviceProfileData deviceProfileData = new DeviceProfileData(); | ||
278 | + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | ||
279 | + DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration(); | ||
280 | + DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); | ||
281 | + deviceProfileData.setConfiguration(configuration); | ||
282 | + deviceProfileData.setTransportConfiguration(transportConfiguration); | ||
283 | + deviceProfileData.setProvisionConfiguration(provisionConfiguration); | ||
284 | + thermostatDeviceProfile.setProfileData(deviceProfileData); | ||
285 | + | ||
286 | + DeviceProfileAlarm highTemperature = new DeviceProfileAlarm(); | ||
287 | + highTemperature.setId("highTemperatureAlarmID"); | ||
288 | + highTemperature.setAlarmType("High Temperature"); | ||
289 | + AlarmRule temperatureRule = new AlarmRule(); | ||
290 | + AlarmCondition temperatureCondition = new AlarmCondition(); | ||
291 | + temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | ||
292 | + | ||
293 | + KeyFilter alarmTemperatureAttributeFilter = new KeyFilter(); | ||
294 | + alarmTemperatureAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmTemperature")); | ||
295 | + alarmTemperatureAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | ||
296 | + BooleanFilterPredicate alarmTemperatureAttributePredicate = new BooleanFilterPredicate(); | ||
297 | + alarmTemperatureAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | ||
298 | + alarmTemperatureAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | ||
299 | + alarmTemperatureAttributeFilter.setPredicate(alarmTemperatureAttributePredicate); | ||
300 | + | ||
301 | + KeyFilter temperatureTimeseriesFilter = new KeyFilter(); | ||
302 | + temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | ||
303 | + temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | ||
304 | + NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); | ||
305 | + temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); | ||
306 | + FilterPredicateValue<Double> temperatureTimeseriesPredicateValue = | ||
307 | + new FilterPredicateValue<>(25.0, null, | ||
308 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature")); | ||
309 | + temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue); | ||
310 | + temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate); | ||
311 | + temperatureCondition.setCondition(Arrays.asList(alarmTemperatureAttributeFilter, temperatureTimeseriesFilter)); | ||
312 | + temperatureRule.setAlarmDetails("Current temperature = ${temperature}"); | ||
313 | + temperatureRule.setCondition(temperatureCondition); | ||
314 | + highTemperature.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule))); | ||
315 | + | ||
316 | + AlarmRule clearTemperatureRule = new AlarmRule(); | ||
317 | + AlarmCondition clearTemperatureCondition = new AlarmCondition(); | ||
318 | + clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | ||
319 | + | ||
320 | + KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter(); | ||
321 | + clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | ||
322 | + clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | ||
323 | + NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); | ||
324 | + clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); | ||
325 | + FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue = | ||
326 | + new FilterPredicateValue<>(25.0, null, | ||
327 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature")); | ||
328 | + | ||
329 | + clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue); | ||
330 | + clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate); | ||
331 | + clearTemperatureCondition.setCondition(Collections.singletonList(clearTemperatureTimeseriesFilter)); | ||
332 | + clearTemperatureRule.setCondition(clearTemperatureCondition); | ||
333 | + clearTemperatureRule.setAlarmDetails("Current temperature = ${temperature}"); | ||
334 | + highTemperature.setClearRule(clearTemperatureRule); | ||
335 | + | ||
336 | + DeviceProfileAlarm lowHumidity = new DeviceProfileAlarm(); | ||
337 | + lowHumidity.setId("lowHumidityAlarmID"); | ||
338 | + lowHumidity.setAlarmType("Low Humidity"); | ||
339 | + AlarmRule humidityRule = new AlarmRule(); | ||
340 | + AlarmCondition humidityCondition = new AlarmCondition(); | ||
341 | + humidityCondition.setSpec(new SimpleAlarmConditionSpec()); | ||
342 | + | ||
343 | + KeyFilter alarmHumidityAttributeFilter = new KeyFilter(); | ||
344 | + alarmHumidityAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmHumidity")); | ||
345 | + alarmHumidityAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | ||
346 | + BooleanFilterPredicate alarmHumidityAttributePredicate = new BooleanFilterPredicate(); | ||
347 | + alarmHumidityAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | ||
348 | + alarmHumidityAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | ||
349 | + alarmHumidityAttributeFilter.setPredicate(alarmHumidityAttributePredicate); | ||
350 | + | ||
351 | + KeyFilter humidityTimeseriesFilter = new KeyFilter(); | ||
352 | + humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | ||
353 | + humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | ||
354 | + NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); | ||
355 | + humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); | ||
356 | + FilterPredicateValue<Double> humidityTimeseriesPredicateValue = | ||
357 | + new FilterPredicateValue<>(60.0, null, | ||
358 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity")); | ||
359 | + humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue); | ||
360 | + humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate); | ||
361 | + humidityCondition.setCondition(Arrays.asList(alarmHumidityAttributeFilter, humidityTimeseriesFilter)); | ||
362 | + | ||
363 | + humidityRule.setCondition(humidityCondition); | ||
364 | + humidityRule.setAlarmDetails("Current humidity = ${humidity}"); | ||
365 | + lowHumidity.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule))); | ||
366 | + | ||
367 | + AlarmRule clearHumidityRule = new AlarmRule(); | ||
368 | + AlarmCondition clearHumidityCondition = new AlarmCondition(); | ||
369 | + clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); | ||
370 | + | ||
371 | + KeyFilter clearHumidityTimeseriesFilter = new KeyFilter(); | ||
372 | + clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | ||
373 | + clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | ||
374 | + NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); | ||
375 | + clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); | ||
376 | + FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue = | ||
377 | + new FilterPredicateValue<>(60.0, null, | ||
378 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity")); | ||
379 | + | ||
380 | + clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue); | ||
381 | + clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate); | ||
382 | + clearHumidityCondition.setCondition(Collections.singletonList(clearHumidityTimeseriesFilter)); | ||
383 | + clearHumidityRule.setCondition(clearHumidityCondition); | ||
384 | + clearHumidityRule.setAlarmDetails("Current humidity = ${humidity}"); | ||
385 | + lowHumidity.setClearRule(clearHumidityRule); | ||
386 | + | ||
387 | + deviceProfileData.setAlarms(Arrays.asList(highTemperature, lowHumidity)); | ||
388 | + | ||
389 | + DeviceProfile savedThermostatDeviceProfile = deviceProfileService.saveDeviceProfile(thermostatDeviceProfile); | ||
390 | + | ||
391 | + DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | ||
392 | + DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | ||
261 | 393 | ||
262 | attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE, | 394 | attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE, |
263 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)), | 395 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)), |
@@ -62,19 +62,28 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -62,19 +62,28 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
62 | 62 | ||
63 | private static final AtomicInteger atomicInteger = new AtomicInteger(2); | 63 | private static final AtomicInteger atomicInteger = new AtomicInteger(2); |
64 | 64 | ||
65 | - protected static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + | 65 | + public static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + |
66 | "\n" + | 66 | "\n" + |
67 | "package test;\n" + | 67 | "package test;\n" + |
68 | - " \n" + | 68 | + "\n" + |
69 | "message PostTelemetry {\n" + | 69 | "message PostTelemetry {\n" + |
70 | " string key1 = 1;\n" + | 70 | " string key1 = 1;\n" + |
71 | " bool key2 = 2;\n" + | 71 | " bool key2 = 2;\n" + |
72 | " double key3 = 3;\n" + | 72 | " double key3 = 3;\n" + |
73 | " int32 key4 = 4;\n" + | 73 | " int32 key4 = 4;\n" + |
74 | - " string key5 = 5;\n" + | 74 | + " JsonObject key5 = 5;\n" + |
75 | + "\n" + | ||
76 | + " message JsonObject {\n" + | ||
77 | + " int32 someNumber = 6;\n" + | ||
78 | + " repeated int32 someArray = 7;\n" + | ||
79 | + " NestedJsonObject someNestedObject = 8;\n" + | ||
80 | + " message NestedJsonObject {\n" + | ||
81 | + " string key = 9;\n" + | ||
82 | + " }\n" + | ||
83 | + " }\n" + | ||
75 | "}"; | 84 | "}"; |
76 | 85 | ||
77 | - protected static final String DEVICE_ATTRIBUTES_PROTO_SCHEMA = "syntax =\"proto3\";\n" + | 86 | + public static final String DEVICE_ATTRIBUTES_PROTO_SCHEMA = "syntax =\"proto3\";\n" + |
78 | "\n" + | 87 | "\n" + |
79 | "package test;\n" + | 88 | "package test;\n" + |
80 | "\n" + | 89 | "\n" + |
@@ -83,7 +92,16 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | @@ -83,7 +92,16 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest | ||
83 | " bool key2 = 2;\n" + | 92 | " bool key2 = 2;\n" + |
84 | " double key3 = 3;\n" + | 93 | " double key3 = 3;\n" + |
85 | " int32 key4 = 4;\n" + | 94 | " int32 key4 = 4;\n" + |
86 | - " string key5 = 5;\n" + | 95 | + " JsonObject key5 = 5;\n" + |
96 | + "\n" + | ||
97 | + " message JsonObject {\n" + | ||
98 | + " int32 someNumber = 6;\n" + | ||
99 | + " repeated int32 someArray = 7;\n" + | ||
100 | + " NestedJsonObject someNestedObject = 8;\n" + | ||
101 | + " message NestedJsonObject {\n" + | ||
102 | + " string key = 9;\n" + | ||
103 | + " }\n" + | ||
104 | + " }\n" + | ||
87 | "}"; | 105 | "}"; |
88 | 106 | ||
89 | protected Tenant savedTenant; | 107 | protected Tenant savedTenant; |
@@ -26,13 +26,13 @@ import java.util.Arrays; | @@ -26,13 +26,13 @@ import java.util.Arrays; | ||
26 | 26 | ||
27 | @RunWith(ClasspathSuite.class) | 27 | @RunWith(ClasspathSuite.class) |
28 | @ClasspathSuite.ClassnameFilters({ | 28 | @ClasspathSuite.ClassnameFilters({ |
29 | -// "org.thingsboard.server.mqtt.rpc.sql.*Test", | ||
30 | -// "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test", | ||
31 | -// "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", | ||
32 | -// "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", | 29 | + "org.thingsboard.server.mqtt.rpc.sql.*Test", |
30 | + "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test", | ||
31 | + "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test", | ||
32 | + "org.thingsboard.server.mqtt.attributes.updates.sql.*Test", | ||
33 | "org.thingsboard.server.mqtt.attributes.request.sql.*Test", | 33 | "org.thingsboard.server.mqtt.attributes.request.sql.*Test", |
34 | -// "org.thingsboard.server.mqtt.claim.sql.*Test", | ||
35 | -// "org.thingsboard.server.mqtt.provision.sql.*Test" | 34 | + "org.thingsboard.server.mqtt.claim.sql.*Test", |
35 | + "org.thingsboard.server.mqtt.provision.sql.*Test" | ||
36 | }) | 36 | }) |
37 | public class MqttSqlTestSuite { | 37 | public class MqttSqlTestSuite { |
38 | 38 |
@@ -122,7 +122,7 @@ public abstract class AbstractMqttAttributesRequestIntegrationTest extends Abstr | @@ -122,7 +122,7 @@ public abstract class AbstractMqttAttributesRequestIntegrationTest extends Abstr | ||
122 | client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); | 122 | client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); |
123 | latch.await(3, TimeUnit.SECONDS); | 123 | latch.await(3, TimeUnit.SECONDS); |
124 | assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); | 124 | assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
125 | - String expectedRequestPayload = "{\"client\":{\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}},\"attribute4\":73,\"attribute1\":\"value1\",\"attribute3\":42.0,\"attribute2\":true},\"shared\":{\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}},\"attribute4\":73,\"attribute1\":\"value1\",\"attribute3\":42.0,\"attribute2\":true}}"; | 125 | + String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
126 | assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); | 126 | assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
127 | } | 127 | } |
128 | 128 |
@@ -62,7 +62,16 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends | @@ -62,7 +62,16 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends | ||
62 | " bool attribute2 = 2;\n" + | 62 | " bool attribute2 = 2;\n" + |
63 | " double attribute3 = 3;\n" + | 63 | " double attribute3 = 3;\n" + |
64 | " int32 attribute4 = 4;\n" + | 64 | " int32 attribute4 = 4;\n" + |
65 | - " string attribute5 = 5;\n" + | 65 | + " JsonObject attribute5 = 5;\n" + |
66 | + "\n" + | ||
67 | + " message JsonObject {\n" + | ||
68 | + " int32 someNumber = 6;\n" + | ||
69 | + " repeated int32 someArray = 7;\n" + | ||
70 | + " NestedJsonObject someNestedObject = 8;\n" + | ||
71 | + " message NestedJsonObject {\n" + | ||
72 | + " string key = 9;\n" + | ||
73 | + " }\n" + | ||
74 | + " }\n" + | ||
66 | "}"; | 75 | "}"; |
67 | 76 | ||
68 | @After | 77 | @After |
@@ -93,6 +102,23 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends | @@ -93,6 +102,23 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends | ||
93 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | 102 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; |
94 | ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(ATTRIBUTES_SCHEMA_STR); | 103 | ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(ATTRIBUTES_SCHEMA_STR); |
95 | DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | 104 | DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); |
105 | + | ||
106 | + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); | ||
107 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | ||
108 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | ||
109 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | ||
110 | + | ||
111 | + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); | ||
112 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | ||
113 | + assertNotNull(jsonObjectBuilderDescriptor); | ||
114 | + DynamicMessage jsonObject = jsonObjectBuilder | ||
115 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | ||
116 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | ||
117 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | ||
118 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | ||
119 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | ||
120 | + .build(); | ||
121 | + | ||
96 | DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); | 122 | DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); |
97 | Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); | 123 | Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); |
98 | assertNotNull(postAttributesMsgDescriptor); | 124 | assertNotNull(postAttributesMsgDescriptor); |
@@ -101,7 +127,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends | @@ -101,7 +127,7 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends | ||
101 | .setField(postAttributesMsgDescriptor.findFieldByName("attribute2"), true) | 127 | .setField(postAttributesMsgDescriptor.findFieldByName("attribute2"), true) |
102 | .setField(postAttributesMsgDescriptor.findFieldByName("attribute3"), 42.0) | 128 | .setField(postAttributesMsgDescriptor.findFieldByName("attribute3"), 42.0) |
103 | .setField(postAttributesMsgDescriptor.findFieldByName("attribute4"), 73) | 129 | .setField(postAttributesMsgDescriptor.findFieldByName("attribute4"), 73) |
104 | - .setField(postAttributesMsgDescriptor.findFieldByName("attribute5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}") | 130 | + .setField(postAttributesMsgDescriptor.findFieldByName("attribute5"), jsonObject) |
105 | .build(); | 131 | .build(); |
106 | byte[] payload = postAttributesMsg.toByteArray(); | 132 | byte[] payload = postAttributesMsg.toByteArray(); |
107 | client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); | 133 | client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); |
@@ -163,16 +163,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt | @@ -163,16 +163,10 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt | ||
163 | break; | 163 | break; |
164 | case "key5": | 164 | case "key5": |
165 | assertNotNull(value); | 165 | assertNotNull(value); |
166 | - LinkedHashMap valueMap; | ||
167 | - if (value instanceof String) { | ||
168 | - valueMap = mapper.readValue((String) value, LinkedHashMap.class); | ||
169 | - } else { | ||
170 | - valueMap = (LinkedHashMap) value; | ||
171 | - } | ||
172 | - assertEquals(3, valueMap.size()); | ||
173 | - assertEquals(42, valueMap.get("someNumber")); | ||
174 | - assertEquals(Arrays.asList(1, 2, 3), valueMap.get("someArray")); | ||
175 | - LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) valueMap.get("someNestedObject"); | 166 | + assertEquals(3, ((LinkedHashMap) value).size()); |
167 | + assertEquals(42, ((LinkedHashMap) value).get("someNumber")); | ||
168 | + assertEquals(Arrays.asList(1, 2, 3), ((LinkedHashMap) value).get("someArray")); | ||
169 | + LinkedHashMap<String, String> someNestedObject = (LinkedHashMap) ((LinkedHashMap) value).get("someNestedObject"); | ||
176 | assertEquals("value", someNestedObject.get("key")); | 170 | assertEquals("value", someNestedObject.get("key")); |
177 | break; | 171 | break; |
178 | } | 172 | } |
@@ -58,6 +58,23 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac | @@ -58,6 +58,23 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac | ||
58 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | 58 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; |
59 | ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA); | 59 | ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA); |
60 | DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | 60 | DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); |
61 | + | ||
62 | + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); | ||
63 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | ||
64 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | ||
65 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | ||
66 | + | ||
67 | + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); | ||
68 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | ||
69 | + assertNotNull(jsonObjectBuilderDescriptor); | ||
70 | + DynamicMessage jsonObject = jsonObjectBuilder | ||
71 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | ||
72 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | ||
73 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | ||
74 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | ||
75 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | ||
76 | + .build(); | ||
77 | + | ||
61 | DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); | 78 | DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); |
62 | Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); | 79 | Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); |
63 | assertNotNull(postAttributesMsgDescriptor); | 80 | assertNotNull(postAttributesMsgDescriptor); |
@@ -66,7 +83,7 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac | @@ -66,7 +83,7 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac | ||
66 | .setField(postAttributesMsgDescriptor.findFieldByName("key2"), true) | 83 | .setField(postAttributesMsgDescriptor.findFieldByName("key2"), true) |
67 | .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 3.0) | 84 | .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 3.0) |
68 | .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) | 85 | .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) |
69 | - .setField(postAttributesMsgDescriptor.findFieldByName("key5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}") | 86 | + .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) |
70 | .build(); | 87 | .build(); |
71 | processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, postAttributesMsg.toByteArray()); | 88 | processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, postAttributesMsg.toByteArray()); |
72 | } | 89 | } |
@@ -62,6 +62,23 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | @@ -62,6 +62,23 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | ||
62 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | 62 | ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; |
63 | ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA); | 63 | ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA); |
64 | DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | 64 | DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); |
65 | + | ||
66 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | ||
67 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | ||
68 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | ||
69 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | ||
70 | + | ||
71 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | ||
72 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | ||
73 | + assertNotNull(jsonObjectBuilderDescriptor); | ||
74 | + DynamicMessage jsonObject = jsonObjectBuilder | ||
75 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | ||
76 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | ||
77 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | ||
78 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | ||
79 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | ||
80 | + .build(); | ||
81 | + | ||
65 | DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | 82 | DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); |
66 | Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | 83 | Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); |
67 | assertNotNull(postTelemetryMsgDescriptor); | 84 | assertNotNull(postTelemetryMsgDescriptor); |
@@ -70,7 +87,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | @@ -70,7 +87,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | ||
70 | .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), true) | 87 | .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), true) |
71 | .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 3.0) | 88 | .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 3.0) |
72 | .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) | 89 | .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) |
73 | - .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}") | 90 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) |
74 | .build(); | 91 | .build(); |
75 | processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), false); | 92 | processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), false); |
76 | } | 93 | } |
@@ -80,19 +97,27 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | @@ -80,19 +97,27 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | ||
80 | String schemaStr = "syntax =\"proto3\";\n" + | 97 | String schemaStr = "syntax =\"proto3\";\n" + |
81 | "\n" + | 98 | "\n" + |
82 | "package test;\n" + | 99 | "package test;\n" + |
83 | - " \n" + | ||
84 | - "message PostTelemetry {\n" + | ||
85 | - "\n" + | ||
86 | - " message Values {\n" + | ||
87 | - " string key1 = 1;\n" + | ||
88 | - " bool key2 = 2;\n" + | ||
89 | - " double key3 = 3;\n" + | ||
90 | - " int32 key4 = 4;\n" + | ||
91 | - " string key5 = 5;\n" + | ||
92 | - " }\n" + | ||
93 | "\n" + | 100 | "\n" + |
101 | + "message PostTelemetry {\n" + | ||
94 | " int64 ts = 1;\n" + | 102 | " int64 ts = 1;\n" + |
95 | " Values values = 2;\n" + | 103 | " Values values = 2;\n" + |
104 | + " \n" + | ||
105 | + " message Values {\n" + | ||
106 | + " string key1 = 3;\n" + | ||
107 | + " bool key2 = 4;\n" + | ||
108 | + " double key3 = 5;\n" + | ||
109 | + " int32 key4 = 6;\n" + | ||
110 | + " JsonObject key5 = 7;\n" + | ||
111 | + " }\n" + | ||
112 | + " \n" + | ||
113 | + " message JsonObject {\n" + | ||
114 | + " int32 someNumber = 8;\n" + | ||
115 | + " repeated int32 someArray = 9;\n" + | ||
116 | + " NestedJsonObject someNestedObject = 10;\n" + | ||
117 | + " message NestedJsonObject {\n" + | ||
118 | + " string key = 11;\n" + | ||
119 | + " }\n" + | ||
120 | + " }\n" + | ||
96 | "}"; | 121 | "}"; |
97 | super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, DeviceProfileProvisionType.DISABLED, null, null); | 122 | super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, DeviceProfileProvisionType.DISABLED, null, null); |
98 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); | 123 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
@@ -105,6 +130,23 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | @@ -105,6 +130,23 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | ||
105 | ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr); | 130 | ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr); |
106 | DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | 131 | DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); |
107 | 132 | ||
133 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | ||
134 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | ||
135 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | ||
136 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | ||
137 | + | ||
138 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | ||
139 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | ||
140 | + assertNotNull(jsonObjectBuilderDescriptor); | ||
141 | + DynamicMessage jsonObject = jsonObjectBuilder | ||
142 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | ||
143 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | ||
144 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | ||
145 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | ||
146 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | ||
147 | + .build(); | ||
148 | + | ||
149 | + | ||
108 | DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values"); | 150 | DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values"); |
109 | Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType(); | 151 | Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType(); |
110 | assertNotNull(valuesDescriptor); | 152 | assertNotNull(valuesDescriptor); |
@@ -114,7 +156,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | @@ -114,7 +156,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac | ||
114 | .setField(valuesDescriptor.findFieldByName("key2"), true) | 156 | .setField(valuesDescriptor.findFieldByName("key2"), true) |
115 | .setField(valuesDescriptor.findFieldByName("key3"), 3.0) | 157 | .setField(valuesDescriptor.findFieldByName("key3"), 3.0) |
116 | .setField(valuesDescriptor.findFieldByName("key4"), 4) | 158 | .setField(valuesDescriptor.findFieldByName("key4"), 4) |
117 | - .setField(valuesDescriptor.findFieldByName("key5"), "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}") | 159 | + .setField(valuesDescriptor.findFieldByName("key5"), jsonObject) |
118 | .build(); | 160 | .build(); |
119 | 161 | ||
120 | DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | 162 | DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); |
@@ -128,19 +128,8 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC | @@ -128,19 +128,8 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC | ||
128 | List<MessageDefinition> messageDefinitions = new ArrayList<>(); | 128 | List<MessageDefinition> messageDefinitions = new ArrayList<>(); |
129 | messageElementsList.forEach(messageElement -> { | 129 | messageElementsList.forEach(messageElement -> { |
130 | MessageDefinition.Builder messageDefinitionBuilder = MessageDefinition.newBuilder(messageElement.getName()); | 130 | MessageDefinition.Builder messageDefinitionBuilder = MessageDefinition.newBuilder(messageElement.getName()); |
131 | - List<FieldElement> messageElementFields = messageElement.getFields(); | ||
132 | - List<OneOfElement> oneOfs = messageElement.getOneOfs(); | ||
133 | 131 | ||
134 | List<TypeElement> nestedTypes = messageElement.getNestedTypes(); | 132 | List<TypeElement> nestedTypes = messageElement.getNestedTypes(); |
135 | - if (!messageElementFields.isEmpty()) { | ||
136 | - addMessageFieldsToTheMessageDefinition(messageElementFields, messageDefinitionBuilder); | ||
137 | - } | ||
138 | - if (!oneOfs.isEmpty()) { | ||
139 | - for (OneOfElement oneOfelement : oneOfs) { | ||
140 | - MessageDefinition.OneofBuilder oneofBuilder = messageDefinitionBuilder.addOneof(oneOfelement.getName()); | ||
141 | - addMessageFieldsToTheOneOfDefinition(oneOfelement.getFields(), oneofBuilder); | ||
142 | - } | ||
143 | - } | ||
144 | if (!nestedTypes.isEmpty()) { | 133 | if (!nestedTypes.isEmpty()) { |
145 | List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes); | 134 | List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes); |
146 | if (!nestedEnumTypes.isEmpty()) { | 135 | if (!nestedEnumTypes.isEmpty()) { |
@@ -153,6 +142,17 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC | @@ -153,6 +142,17 @@ public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeC | ||
153 | List<MessageDefinition> nestedMessageDefinitions = getMessageDefinitions(nestedMessageTypes); | 142 | List<MessageDefinition> nestedMessageDefinitions = getMessageDefinitions(nestedMessageTypes); |
154 | nestedMessageDefinitions.forEach(messageDefinitionBuilder::addMessageDefinition); | 143 | nestedMessageDefinitions.forEach(messageDefinitionBuilder::addMessageDefinition); |
155 | } | 144 | } |
145 | + List<FieldElement> messageElementFields = messageElement.getFields(); | ||
146 | + List<OneOfElement> oneOfs = messageElement.getOneOfs(); | ||
147 | + if (!oneOfs.isEmpty()) { | ||
148 | + for (OneOfElement oneOfelement : oneOfs) { | ||
149 | + MessageDefinition.OneofBuilder oneofBuilder = messageDefinitionBuilder.addOneof(oneOfelement.getName()); | ||
150 | + addMessageFieldsToTheOneOfDefinition(oneOfelement.getFields(), oneofBuilder); | ||
151 | + } | ||
152 | + } | ||
153 | + if (!messageElementFields.isEmpty()) { | ||
154 | + addMessageFieldsToTheMessageDefinition(messageElementFields, messageDefinitionBuilder); | ||
155 | + } | ||
156 | messageDefinitions.add(messageDefinitionBuilder.build()); | 156 | messageDefinitions.add(messageDefinitionBuilder.build()); |
157 | }); | 157 | }); |
158 | return messageDefinitions; | 158 | return messageDefinitions; |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.transport.mqtt.util; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class AlwaysTrueTopicFilter implements MqttTopicFilter { | ||
22 | + | ||
23 | + @Override | ||
24 | + public boolean filter(String topic) { | ||
25 | + return true; | ||
26 | + } | ||
27 | +} |
@@ -33,7 +33,9 @@ public class MqttTopicFilterFactory { | @@ -33,7 +33,9 @@ public class MqttTopicFilterFactory { | ||
33 | throw new IllegalArgumentException("Topic filter can't be empty!"); | 33 | throw new IllegalArgumentException("Topic filter can't be empty!"); |
34 | } | 34 | } |
35 | return filters.computeIfAbsent(topicFilter, filter -> { | 35 | return filters.computeIfAbsent(topicFilter, filter -> { |
36 | - if (filter.contains("+") || filter.contains("#")) { | 36 | + if (filter.equals("#")) { |
37 | + return new AlwaysTrueTopicFilter(); | ||
38 | + } else if (filter.contains("+") || filter.contains("#")) { | ||
37 | String regex = filter | 39 | String regex = filter |
38 | .replace("\\", "\\\\") | 40 | .replace("\\", "\\\\") |
39 | .replace("+", "[^/]+") | 41 | .replace("+", "[^/]+") |
@@ -20,7 +20,6 @@ import org.junit.runner.RunWith; | @@ -20,7 +20,6 @@ import org.junit.runner.RunWith; | ||
20 | import org.mockito.runners.MockitoJUnitRunner; | 20 | import org.mockito.runners.MockitoJUnitRunner; |
21 | 21 | ||
22 | import javax.script.ScriptException; | 22 | import javax.script.ScriptException; |
23 | -import java.util.regex.Pattern; | ||
24 | 23 | ||
25 | import static org.junit.Assert.assertFalse; | 24 | import static org.junit.Assert.assertFalse; |
26 | import static org.junit.Assert.assertTrue; | 25 | import static org.junit.Assert.assertTrue; |
@@ -31,6 +30,9 @@ public class MqttTopicFilterFactoryTest { | @@ -31,6 +30,9 @@ public class MqttTopicFilterFactoryTest { | ||
31 | private static String TEST_STR_1 = "Sensor/Temperature/House/48"; | 30 | private static String TEST_STR_1 = "Sensor/Temperature/House/48"; |
32 | private static String TEST_STR_2 = "Sensor/Temperature"; | 31 | private static String TEST_STR_2 = "Sensor/Temperature"; |
33 | private static String TEST_STR_3 = "Sensor/Temperature2/House/48"; | 32 | private static String TEST_STR_3 = "Sensor/Temperature2/House/48"; |
33 | + private static String TEST_STR_4 = "/Sensor/Temperature2/House/48"; | ||
34 | + private static String TEST_STR_5 = "Sensor/ Temperature"; | ||
35 | + private static String TEST_STR_6 = "/"; | ||
34 | 36 | ||
35 | @Test | 37 | @Test |
36 | public void metadataCanBeUpdated() throws ScriptException { | 38 | public void metadataCanBeUpdated() throws ScriptException { |
@@ -51,6 +53,17 @@ public class MqttTopicFilterFactoryTest { | @@ -51,6 +53,17 @@ public class MqttTopicFilterFactoryTest { | ||
51 | assertTrue(filter.filter(TEST_STR_1)); | 53 | assertTrue(filter.filter(TEST_STR_1)); |
52 | assertTrue(filter.filter(TEST_STR_2)); | 54 | assertTrue(filter.filter(TEST_STR_2)); |
53 | assertFalse(filter.filter(TEST_STR_3)); | 55 | assertFalse(filter.filter(TEST_STR_3)); |
56 | + | ||
57 | + filter = MqttTopicFilterFactory.toFilter("#"); | ||
58 | + assertTrue(filter.filter(TEST_STR_1)); | ||
59 | + assertTrue(filter.filter(TEST_STR_2)); | ||
60 | + assertTrue(filter.filter(TEST_STR_3)); | ||
61 | + assertTrue(filter.filter(TEST_STR_4)); | ||
62 | + assertTrue(filter.filter(TEST_STR_5)); | ||
63 | + assertTrue(filter.filter(TEST_STR_6)); | ||
64 | + | ||
65 | + filter = MqttTopicFilterFactory.toFilter("Sensor/Temperature#"); | ||
66 | + assertFalse(filter.filter(TEST_STR_2)); | ||
54 | } | 67 | } |
55 | 68 | ||
56 | } | 69 | } |
@@ -33,6 +33,7 @@ import org.springframework.cache.Cache; | @@ -33,6 +33,7 @@ import org.springframework.cache.Cache; | ||
33 | import org.springframework.cache.CacheManager; | 33 | import org.springframework.cache.CacheManager; |
34 | import org.springframework.cache.annotation.Cacheable; | 34 | import org.springframework.cache.annotation.Cacheable; |
35 | import org.springframework.stereotype.Service; | 35 | import org.springframework.stereotype.Service; |
36 | +import org.springframework.util.CollectionUtils; | ||
36 | import org.thingsboard.server.common.data.Device; | 37 | import org.thingsboard.server.common.data.Device; |
37 | import org.thingsboard.server.common.data.DeviceProfile; | 38 | import org.thingsboard.server.common.data.DeviceProfile; |
38 | import org.thingsboard.server.common.data.DeviceProfileInfo; | 39 | import org.thingsboard.server.common.data.DeviceProfileInfo; |
@@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; | @@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; | ||
42 | import org.thingsboard.server.common.data.Tenant; | 43 | import org.thingsboard.server.common.data.Tenant; |
43 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | 44 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
44 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; | 45 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; |
46 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | ||
45 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | 47 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
46 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | 48 | import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; |
47 | import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; | 49 | import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; |
@@ -60,8 +62,10 @@ import org.thingsboard.server.dao.tenant.TenantDao; | @@ -60,8 +62,10 @@ import org.thingsboard.server.dao.tenant.TenantDao; | ||
60 | 62 | ||
61 | import java.util.Arrays; | 63 | import java.util.Arrays; |
62 | import java.util.Collections; | 64 | import java.util.Collections; |
65 | +import java.util.HashSet; | ||
63 | import java.util.List; | 66 | import java.util.List; |
64 | import java.util.stream.Collectors; | 67 | import java.util.stream.Collectors; |
68 | +import java.util.Set; | ||
65 | 69 | ||
66 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; | 70 | import static org.thingsboard.server.common.data.CacheConstants.DEVICE_PROFILE_CACHE; |
67 | import static org.thingsboard.server.dao.service.Validator.validateId; | 71 | import static org.thingsboard.server.dao.service.Validator.validateId; |
@@ -136,7 +140,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -136,7 +140,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
136 | if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) { | 140 | if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_profile_name_unq_key")) { |
137 | throw new DataValidationException("Device profile with such name already exists!"); | 141 | throw new DataValidationException("Device profile with such name already exists!"); |
138 | } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) { | 142 | } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_provision_key_unq_key")) { |
139 | - throw new DataValidationException("Device profile with such provision device key already exists!"); | 143 | + throw new DataValidationException("Device profile with such provision device key already exists!"); |
140 | } else { | 144 | } else { |
141 | throw t; | 145 | throw t; |
142 | } | 146 | } |
@@ -347,6 +351,22 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -347,6 +351,22 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
347 | } | 351 | } |
348 | } | 352 | } |
349 | } | 353 | } |
354 | + | ||
355 | + List<DeviceProfileAlarm> profileAlarms = deviceProfile.getProfileData().getAlarms(); | ||
356 | + | ||
357 | + if (!CollectionUtils.isEmpty(profileAlarms)) { | ||
358 | + Set<String> alarmTypes = new HashSet<>(); | ||
359 | + for (DeviceProfileAlarm alarm : profileAlarms) { | ||
360 | + String alarmType = alarm.getAlarmType(); | ||
361 | + if (StringUtils.isEmpty(alarmType)) { | ||
362 | + throw new DataValidationException("Alarm rule type should be specified!"); | ||
363 | + } | ||
364 | + if (!alarmTypes.add(alarmType)) { | ||
365 | + throw new DataValidationException(String.format("Can't create device profile with the same alarm rule types: \"%s\"!", alarmType)); | ||
366 | + } | ||
367 | + } | ||
368 | + } | ||
369 | + | ||
350 | } | 370 | } |
351 | 371 | ||
352 | @Override | 372 | @Override |
@@ -393,6 +413,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | @@ -393,6 +413,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D | ||
393 | " for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!"); | 413 | " for " + schemaName + " provided! Only " + Syntax.PROTO_3 + " allowed!"); |
394 | } | 414 | } |
395 | } | 415 | } |
416 | + | ||
396 | private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) { | 417 | private void checkProtoFileCommonSettings(String schemaName, boolean isEmptySettings, String invalidSettingsMessage) { |
397 | if (!isEmptySettings) { | 418 | if (!isEmptySettings) { |
398 | throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage); | 419 | throw new IllegalArgumentException(invalidSchemaProvidedMessage(schemaName) + invalidSettingsMessage); |
@@ -41,6 +41,9 @@ | @@ -41,6 +41,9 @@ | ||
41 | <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')"> | 41 | <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('required')"> |
42 | {{ 'device-profile.alarm-type-required' | translate }} | 42 | {{ 'device-profile.alarm-type-required' | translate }} |
43 | </mat-error> | 43 | </mat-error> |
44 | + <mat-error *ngIf="alarmFormGroup.get('alarmType').hasError('unique')"> | ||
45 | + {{ 'device-profile.alarm-type-unique' | translate }} | ||
46 | + </mat-error> | ||
44 | <mat-hint *ngIf="!disabled" | 47 | <mat-hint *ngIf="!disabled" |
45 | innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint> | 48 | innerHTML="{{ 'device-profile.alarm-type-pattern-hint' | translate }}"></mat-hint> |
46 | </mat-form-field> | 49 | </mat-form-field> |
@@ -133,6 +133,19 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit | @@ -133,6 +133,19 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit | ||
133 | } | 133 | } |
134 | 134 | ||
135 | public validate(c: FormControl) { | 135 | public validate(c: FormControl) { |
136 | + if (c.parent) { | ||
137 | + const alarmType = c.value.alarmType; | ||
138 | + const profileAlarmsType = []; | ||
139 | + c.parent.getRawValue().forEach((alarm: DeviceProfileAlarm) => { | ||
140 | + profileAlarmsType.push(alarm.alarmType); | ||
141 | + } | ||
142 | + ); | ||
143 | + if (profileAlarmsType.filter(profileAlarmType => profileAlarmType === alarmType).length > 1) { | ||
144 | + this.alarmFormGroup.get('alarmType').setErrors({ | ||
145 | + unique: true | ||
146 | + }); | ||
147 | + } | ||
148 | + } | ||
136 | return (this.alarmFormGroup.valid) ? null : { | 149 | return (this.alarmFormGroup.valid) ? null : { |
137 | alarm: { | 150 | alarm: { |
138 | valid: false, | 151 | valid: false, |
@@ -38,7 +38,7 @@ import { | @@ -38,7 +38,7 @@ import { | ||
38 | } from '@shared/models/device.models'; | 38 | } from '@shared/models/device.models'; |
39 | import { EntityType } from '@shared/models/entity-type.models'; | 39 | import { EntityType } from '@shared/models/entity-type.models'; |
40 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; | 40 | import { RuleChainId } from '@shared/models/id/rule-chain-id'; |
41 | -import {ServiceType} from "@shared/models/queue.models"; | 41 | +import { ServiceType } from '@shared/models/queue.models'; |
42 | 42 | ||
43 | @Component({ | 43 | @Component({ |
44 | selector: 'tb-device-profile', | 44 | selector: 'tb-device-profile', |
@@ -176,10 +176,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | @@ -176,10 +176,10 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { | ||
176 | transportConfiguration: entity.profileData?.transportConfiguration, | 176 | transportConfiguration: entity.profileData?.transportConfiguration, |
177 | alarms: entity.profileData?.alarms, | 177 | alarms: entity.profileData?.alarms, |
178 | provisionConfiguration: deviceProvisionConfiguration | 178 | provisionConfiguration: deviceProvisionConfiguration |
179 | - }}); | ||
180 | - this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}); | ||
181 | - this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}); | ||
182 | - this.entityForm.patchValue({description: entity.description}); | 179 | + }}, {emitEvent: false}); |
180 | + this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false}); | ||
181 | + this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false}); | ||
182 | + this.entityForm.patchValue({description: entity.description}, {emitEvent: false}); | ||
183 | } | 183 | } |
184 | 184 | ||
185 | prepareFormValue(formValue: any): any { | 185 | prepareFormValue(formValue: any): any { |
@@ -913,6 +913,7 @@ | @@ -913,6 +913,7 @@ | ||
913 | "edit-alarm-rule": "Edit alarm rule", | 913 | "edit-alarm-rule": "Edit alarm rule", |
914 | "alarm-type": "Alarm type", | 914 | "alarm-type": "Alarm type", |
915 | "alarm-type-required": "Alarm type is required.", | 915 | "alarm-type-required": "Alarm type is required.", |
916 | + "alarm-type-unique": "Alarm type must be unique within the device profile alarm rules.", | ||
916 | "alarm-type-pattern-hint": "Alarm type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata", | 917 | "alarm-type-pattern-hint": "Alarm type pattern, use <code>${metaKeyName}</code> to substitute variables from metadata", |
917 | "create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm", | 918 | "create-alarm-pattern": "Create <b>{{alarmType}}</b> alarm", |
918 | "create-alarm-rules": "Create alarm rules", | 919 | "create-alarm-rules": "Create alarm rules", |