...
|
...
|
@@ -15,6 +15,8 @@ |
15
|
15
|
*/
|
16
|
16
|
package org.thingsboard.server.service.install.update;
|
17
|
17
|
|
|
18
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
19
|
+import com.fasterxml.jackson.databind.node.ObjectNode;
|
18
|
20
|
import com.google.common.util.concurrent.FutureCallback;
|
19
|
21
|
import com.google.common.util.concurrent.Futures;
|
20
|
22
|
import com.google.common.util.concurrent.ListenableFuture;
|
...
|
...
|
@@ -23,9 +25,13 @@ import lombok.extern.slf4j.Slf4j; |
23
|
25
|
import org.springframework.beans.factory.annotation.Autowired;
|
24
|
26
|
import org.springframework.context.annotation.Profile;
|
25
|
27
|
import org.springframework.stereotype.Service;
|
|
28
|
+import org.springframework.util.StringUtils;
|
|
29
|
+import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
|
|
30
|
+import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
|
26
|
31
|
import org.thingsboard.server.common.data.EntityView;
|
27
|
32
|
import org.thingsboard.server.common.data.SearchTextBased;
|
28
|
33
|
import org.thingsboard.server.common.data.Tenant;
|
|
34
|
+import org.thingsboard.server.common.data.id.EntityId;
|
29
|
35
|
import org.thingsboard.server.common.data.id.EntityViewId;
|
30
|
36
|
import org.thingsboard.server.common.data.id.TenantId;
|
31
|
37
|
import org.thingsboard.server.common.data.id.UUIDBased;
|
...
|
...
|
@@ -35,10 +41,13 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; |
35
|
41
|
import org.thingsboard.server.common.data.page.PageData;
|
36
|
42
|
import org.thingsboard.server.common.data.page.PageLink;
|
37
|
43
|
import org.thingsboard.server.common.data.rule.RuleChain;
|
|
44
|
+import org.thingsboard.server.common.data.rule.RuleChainMetaData;
|
|
45
|
+import org.thingsboard.server.common.data.rule.RuleNode;
|
38
|
46
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
39
|
47
|
import org.thingsboard.server.dao.rule.RuleChainService;
|
40
|
48
|
import org.thingsboard.server.dao.tenant.TenantService;
|
41
|
49
|
import org.thingsboard.server.dao.timeseries.TimeseriesService;
|
|
50
|
+import org.thingsboard.server.dao.util.mapping.JacksonUtil;
|
42
|
51
|
import org.thingsboard.server.service.install.InstallScripts;
|
43
|
52
|
|
44
|
53
|
import javax.annotation.Nullable;
|
...
|
...
|
@@ -49,6 +58,7 @@ import java.util.concurrent.ExecutionException; |
49
|
58
|
import java.util.stream.Collectors;
|
50
|
59
|
|
51
|
60
|
import static org.apache.commons.lang.StringUtils.isBlank;
|
|
61
|
+import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
|
52
|
62
|
|
53
|
63
|
@Service
|
54
|
64
|
@Profile("install")
|
...
|
...
|
@@ -81,6 +91,10 @@ public class DefaultDataUpdateService implements DataUpdateService { |
81
|
91
|
log.info("Updating data from version 3.0.1 to 3.1.0 ...");
|
82
|
92
|
tenantsEntityViewsUpdater.updateEntities(null);
|
83
|
93
|
break;
|
|
94
|
+ case "3.1.1":
|
|
95
|
+ log.info("Updating data from version 3.1.1 to 3.2.0 ...");
|
|
96
|
+ tenantsRootRuleChainUpdater.updateEntities(null);
|
|
97
|
+ break;
|
84
|
98
|
default:
|
85
|
99
|
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
|
86
|
100
|
}
|
...
|
...
|
@@ -107,6 +121,60 @@ public class DefaultDataUpdateService implements DataUpdateService { |
107
|
121
|
}
|
108
|
122
|
};
|
109
|
123
|
|
|
124
|
+ private PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater =
|
|
125
|
+ new PaginatedUpdater<String, Tenant>() {
|
|
126
|
+
|
|
127
|
+ @Override
|
|
128
|
+ protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
|
|
129
|
+ return tenantService.findTenants(pageLink);
|
|
130
|
+ }
|
|
131
|
+
|
|
132
|
+ @Override
|
|
133
|
+ protected void updateEntity(Tenant tenant) {
|
|
134
|
+ try {
|
|
135
|
+ RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId());
|
|
136
|
+ if (ruleChain == null) {
|
|
137
|
+ installScripts.createDefaultRuleChains(tenant.getId());
|
|
138
|
+ } else {
|
|
139
|
+ RuleChainMetaData md = ruleChainService.loadRuleChainMetaData(tenant.getId(), ruleChain.getId());
|
|
140
|
+ int oldIdx = md.getFirstNodeIndex();
|
|
141
|
+ int newIdx = md.getNodes().size();
|
|
142
|
+
|
|
143
|
+ if (md.getNodes().size() < oldIdx) {
|
|
144
|
+ // Skip invalid rule chains
|
|
145
|
+ return;
|
|
146
|
+ }
|
|
147
|
+
|
|
148
|
+ RuleNode oldFirstNode = md.getNodes().get(oldIdx);
|
|
149
|
+ if (oldFirstNode.getType().equals(TbDeviceProfileNode.class.getName())) {
|
|
150
|
+ // No need to update the rule node twice.
|
|
151
|
+ return;
|
|
152
|
+ }
|
|
153
|
+
|
|
154
|
+ RuleNode ruleNode = new RuleNode();
|
|
155
|
+ ruleNode.setRuleChainId(ruleChain.getId());
|
|
156
|
+ ruleNode.setName("Device Profile Node");
|
|
157
|
+ ruleNode.setType(TbDeviceProfileNode.class.getName());
|
|
158
|
+ ruleNode.setDebugMode(false);
|
|
159
|
+ TbDeviceProfileNodeConfiguration ruleNodeConfiguration = new TbDeviceProfileNodeConfiguration().defaultConfiguration();
|
|
160
|
+ ruleNode.setConfiguration(JacksonUtil.valueToTree(ruleNodeConfiguration));
|
|
161
|
+ ObjectNode additionalInfo = JacksonUtil.newObjectNode();
|
|
162
|
+ additionalInfo.put("description", "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.");
|
|
163
|
+ additionalInfo.put("layoutX", 204);
|
|
164
|
+ additionalInfo.put("layoutY", 240);
|
|
165
|
+ ruleNode.setAdditionalInfo(additionalInfo);
|
|
166
|
+
|
|
167
|
+ md.getNodes().add(ruleNode);
|
|
168
|
+ md.setFirstNodeIndex(newIdx);
|
|
169
|
+ md.addConnectionInfo(newIdx, oldIdx, "Success");
|
|
170
|
+ ruleChainService.saveRuleChainMetaData(tenant.getId(), md);
|
|
171
|
+ }
|
|
172
|
+ } catch (Exception e) {
|
|
173
|
+ log.error("Unable to update Tenant", e);
|
|
174
|
+ }
|
|
175
|
+ }
|
|
176
|
+ };
|
|
177
|
+
|
110
|
178
|
private PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater =
|
111
|
179
|
new PaginatedUpdater<String, Tenant>() {
|
112
|
180
|
|
...
|
...
|
@@ -121,30 +189,30 @@ public class DefaultDataUpdateService implements DataUpdateService { |
121
|
189
|
}
|
122
|
190
|
};
|
123
|
191
|
|
124
|
|
- private void updateTenantEntityViews(TenantId tenantId) {
|
125
|
|
- PageLink pageLink = new PageLink(100);
|
126
|
|
- PageData<EntityView> pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
|
127
|
|
- boolean hasNext = true;
|
128
|
|
- while (hasNext) {
|
129
|
|
- List<ListenableFuture<List<Void>>> updateFutures = new ArrayList<>();
|
130
|
|
- for (EntityView entityView : pageData.getData()) {
|
131
|
|
- updateFutures.add(updateEntityViewLatestTelemetry(entityView));
|
132
|
|
- }
|
133
|
|
-
|
134
|
|
- try {
|
135
|
|
- Futures.allAsList(updateFutures).get();
|
136
|
|
- } catch (InterruptedException | ExecutionException e) {
|
137
|
|
- log.error("Failed to copy latest telemetry to entity view", e);
|
138
|
|
- }
|
139
|
|
-
|
140
|
|
- if (pageData.hasNext()) {
|
141
|
|
- pageLink = pageLink.nextPageLink();
|
142
|
|
- pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
|
143
|
|
- } else {
|
144
|
|
- hasNext = false;
|
145
|
|
- }
|
146
|
|
- }
|
147
|
|
- }
|
|
192
|
+ private void updateTenantEntityViews(TenantId tenantId) {
|
|
193
|
+ PageLink pageLink = new PageLink(100);
|
|
194
|
+ PageData<EntityView> pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
|
|
195
|
+ boolean hasNext = true;
|
|
196
|
+ while (hasNext) {
|
|
197
|
+ List<ListenableFuture<List<Void>>> updateFutures = new ArrayList<>();
|
|
198
|
+ for (EntityView entityView : pageData.getData()) {
|
|
199
|
+ updateFutures.add(updateEntityViewLatestTelemetry(entityView));
|
|
200
|
+ }
|
|
201
|
+
|
|
202
|
+ try {
|
|
203
|
+ Futures.allAsList(updateFutures).get();
|
|
204
|
+ } catch (InterruptedException | ExecutionException e) {
|
|
205
|
+ log.error("Failed to copy latest telemetry to entity view", e);
|
|
206
|
+ }
|
|
207
|
+
|
|
208
|
+ if (pageData.hasNext()) {
|
|
209
|
+ pageLink = pageLink.nextPageLink();
|
|
210
|
+ pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
|
|
211
|
+ } else {
|
|
212
|
+ hasNext = false;
|
|
213
|
+ }
|
|
214
|
+ }
|
|
215
|
+ }
|
148
|
216
|
|
149
|
217
|
private ListenableFuture<List<Void>> updateEntityViewLatestTelemetry(EntityView entityView) {
|
150
|
218
|
EntityViewId entityId = entityView.getId();
|
...
|
...
|
@@ -160,13 +228,13 @@ public class DefaultDataUpdateService implements DataUpdateService { |
160
|
228
|
keysFuture = Futures.immediateFuture(keys);
|
161
|
229
|
}
|
162
|
230
|
ListenableFuture<List<TsKvEntry>> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> {
|
163
|
|
- List<ReadTsKvQuery> queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList());
|
164
|
|
- if (!queries.isEmpty()) {
|
165
|
|
- return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries);
|
166
|
|
- } else {
|
167
|
|
- return Futures.immediateFuture(null);
|
168
|
|
- }
|
169
|
|
- }, MoreExecutors.directExecutor());
|
|
231
|
+ List<ReadTsKvQuery> queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList());
|
|
232
|
+ if (!queries.isEmpty()) {
|
|
233
|
+ return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries);
|
|
234
|
+ } else {
|
|
235
|
+ return Futures.immediateFuture(null);
|
|
236
|
+ }
|
|
237
|
+ }, MoreExecutors.directExecutor());
|
170
|
238
|
return Futures.transformAsync(latestFuture, latestValues -> {
|
171
|
239
|
if (latestValues != null && !latestValues.isEmpty()) {
|
172
|
240
|
ListenableFuture<List<Void>> saveFuture = tsService.saveLatest(TenantId.SYS_TENANT_ID, entityId, latestValues);
|
...
|
...
|
|