Commit 11cfdd6840badf4c0a82062ed1e0b05f02e69b46
Merge branch 'master' of github.com:thingsboard/thingsboard
Showing
7 changed files
with
505 additions
and
29 deletions
@@ -63,6 +63,7 @@ import java.util.ArrayList; | @@ -63,6 +63,7 @@ import java.util.ArrayList; | ||
63 | import java.util.Collections; | 63 | import java.util.Collections; |
64 | import java.util.List; | 64 | import java.util.List; |
65 | import java.util.concurrent.ExecutionException; | 65 | import java.util.concurrent.ExecutionException; |
66 | +import java.util.concurrent.atomic.AtomicLong; | ||
66 | import java.util.stream.Collectors; | 67 | import java.util.stream.Collectors; |
67 | 68 | ||
68 | import static org.apache.commons.lang3.StringUtils.isBlank; | 69 | import static org.apache.commons.lang3.StringUtils.isBlank; |
@@ -144,35 +145,38 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -144,35 +145,38 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
144 | 145 | ||
145 | @Override | 146 | @Override |
146 | protected void updateEntity(DeviceProfileEntity deviceProfile) { | 147 | protected void updateEntity(DeviceProfileEntity deviceProfile) { |
147 | - if (deviceProfile.getProfileData().has("alarms") && | ||
148 | - !deviceProfile.getProfileData().get("alarms").isNull()) { | ||
149 | - boolean isUpdated = false; | ||
150 | - JsonNode alarms = deviceProfile.getProfileData().get("alarms"); | ||
151 | - for (JsonNode alarm : alarms) { | ||
152 | - if (alarm.has("createRules")) { | ||
153 | - JsonNode createRules = alarm.get("createRules"); | ||
154 | - for (AlarmSeverity severity : AlarmSeverity.values()) { | ||
155 | - if (createRules.has(severity.name())) { | ||
156 | - JsonNode spec = createRules.get(severity.name()).get("condition").get("spec"); | ||
157 | - if (convertDeviceProfileAlarmRulesForVersion330(spec)) { | ||
158 | - isUpdated = true; | ||
159 | - } | ||
160 | - } | ||
161 | - } | ||
162 | - } | ||
163 | - if (alarm.has("clearRule") && !alarm.get("clearRule").isNull()) { | ||
164 | - JsonNode spec = alarm.get("clearRule").get("condition").get("spec"); | ||
165 | - if (convertDeviceProfileAlarmRulesForVersion330(spec)) { | ||
166 | - isUpdated = true; | ||
167 | - } | 148 | + if (convertDeviceProfileForVersion330(deviceProfile.getProfileData())) { |
149 | + deviceProfileRepository.save(deviceProfile); | ||
150 | + } | ||
151 | + } | ||
152 | + }; | ||
153 | + | ||
154 | + boolean convertDeviceProfileForVersion330(JsonNode profileData) { | ||
155 | + boolean isUpdated = false; | ||
156 | + if (profileData.has("alarms") && !profileData.get("alarms").isNull()) { | ||
157 | + JsonNode alarms = profileData.get("alarms"); | ||
158 | + for (JsonNode alarm : alarms) { | ||
159 | + if (alarm.has("createRules")) { | ||
160 | + JsonNode createRules = alarm.get("createRules"); | ||
161 | + for (AlarmSeverity severity : AlarmSeverity.values()) { | ||
162 | + if (createRules.has(severity.name())) { | ||
163 | + JsonNode spec = createRules.get(severity.name()).get("condition").get("spec"); | ||
164 | + if (convertDeviceProfileAlarmRulesForVersion330(spec)) { | ||
165 | + isUpdated = true; | ||
168 | } | 166 | } |
169 | } | 167 | } |
170 | - if (isUpdated) { | ||
171 | - deviceProfileRepository.save(deviceProfile); | ||
172 | - } | ||
173 | } | 168 | } |
174 | } | 169 | } |
175 | - }; | 170 | + if (alarm.has("clearRule") && !alarm.get("clearRule").isNull()) { |
171 | + JsonNode spec = alarm.get("clearRule").get("condition").get("spec"); | ||
172 | + if (convertDeviceProfileAlarmRulesForVersion330(spec)) { | ||
173 | + isUpdated = true; | ||
174 | + } | ||
175 | + } | ||
176 | + } | ||
177 | + } | ||
178 | + return isUpdated; | ||
179 | + } | ||
176 | 180 | ||
177 | private final PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater = | 181 | private final PaginatedUpdater<String, Tenant> tenantsDefaultRuleChainUpdater = |
178 | new PaginatedUpdater<>() { | 182 | new PaginatedUpdater<>() { |
@@ -382,6 +386,8 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -382,6 +386,8 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
382 | private final PaginatedUpdater<String, Tenant> tenantsAlarmsCustomerUpdater = | 386 | private final PaginatedUpdater<String, Tenant> tenantsAlarmsCustomerUpdater = |
383 | new PaginatedUpdater<>() { | 387 | new PaginatedUpdater<>() { |
384 | 388 | ||
389 | + final AtomicLong processed = new AtomicLong(); | ||
390 | + | ||
385 | @Override | 391 | @Override |
386 | protected String getName() { | 392 | protected String getName() { |
387 | return "Tenants alarms customer updater"; | 393 | return "Tenants alarms customer updater"; |
@@ -399,12 +405,12 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -399,12 +405,12 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
399 | 405 | ||
400 | @Override | 406 | @Override |
401 | protected void updateEntity(Tenant tenant) { | 407 | protected void updateEntity(Tenant tenant) { |
402 | - updateTenantAlarmsCustomer(tenant.getId()); | 408 | + updateTenantAlarmsCustomer(tenant.getId(), getName(), processed); |
403 | } | 409 | } |
404 | }; | 410 | }; |
405 | 411 | ||
406 | - private void updateTenantAlarmsCustomer(TenantId tenantId) { | ||
407 | - AlarmQuery alarmQuery = new AlarmQuery(null, new TimePageLink(100), null, null, false); | 412 | + private void updateTenantAlarmsCustomer(TenantId tenantId, String name, AtomicLong processed) { |
413 | + AlarmQuery alarmQuery = new AlarmQuery(null, new TimePageLink(1000), null, null, false); | ||
408 | PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, alarmQuery); | 414 | PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, alarmQuery); |
409 | boolean hasNext = true; | 415 | boolean hasNext = true; |
410 | while (hasNext) { | 416 | while (hasNext) { |
@@ -413,6 +419,9 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -413,6 +419,9 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
413 | alarm.setCustomerId(entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator())); | 419 | alarm.setCustomerId(entityService.fetchEntityCustomerId(tenantId, alarm.getOriginator())); |
414 | alarmDao.save(tenantId, alarm); | 420 | alarmDao.save(tenantId, alarm); |
415 | } | 421 | } |
422 | + if (processed.incrementAndGet() % 1000 == 0) { | ||
423 | + log.info("{}: {} alarms processed so far...", name, processed); | ||
424 | + } | ||
416 | } | 425 | } |
417 | if (alarms.hasNext()) { | 426 | if (alarms.hasNext()) { |
418 | alarmQuery.setPageLink(alarmQuery.getPageLink().nextPageLink()); | 427 | alarmQuery.setPageLink(alarmQuery.getPageLink().nextPageLink()); |
@@ -423,7 +432,7 @@ public class DefaultDataUpdateService implements DataUpdateService { | @@ -423,7 +432,7 @@ public class DefaultDataUpdateService implements DataUpdateService { | ||
423 | } | 432 | } |
424 | } | 433 | } |
425 | 434 | ||
426 | - private boolean convertDeviceProfileAlarmRulesForVersion330(JsonNode spec) { | 435 | + boolean convertDeviceProfileAlarmRulesForVersion330(JsonNode spec) { |
427 | if (spec != null) { | 436 | if (spec != null) { |
428 | if (spec.has("type") && spec.get("type").asText().equals("DURATION")) { | 437 | if (spec.has("type") && spec.get("type").asText().equals("DURATION")) { |
429 | if (spec.has("value")) { | 438 | if (spec.has("value")) { |
@@ -28,6 +28,7 @@ public abstract class PaginatedUpdater<I, D> { | @@ -28,6 +28,7 @@ public abstract class PaginatedUpdater<I, D> { | ||
28 | private int updated = 0; | 28 | private int updated = 0; |
29 | 29 | ||
30 | public void updateEntities(I id) { | 30 | public void updateEntities(I id) { |
31 | + log.info("{}: started...", getName()); | ||
31 | updated = 0; | 32 | updated = 0; |
32 | PageLink pageLink = new PageLink(DEFAULT_LIMIT); | 33 | PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
33 | boolean hasNext = true; | 34 | boolean hasNext = true; |
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.install.update; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
20 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | +import org.junit.jupiter.api.BeforeEach; | ||
22 | +import org.junit.jupiter.api.Test; | ||
23 | +import org.springframework.boot.test.context.SpringBootTest; | ||
24 | +import org.springframework.boot.test.mock.mockito.MockBean; | ||
25 | +import org.springframework.test.context.ActiveProfiles; | ||
26 | + | ||
27 | +import java.io.IOException; | ||
28 | + | ||
29 | +import static org.assertj.core.api.Assertions.assertThat; | ||
30 | +import static org.mockito.ArgumentMatchers.any; | ||
31 | +import static org.mockito.BDDMockito.willCallRealMethod; | ||
32 | + | ||
33 | +@ActiveProfiles("install") | ||
34 | +@SpringBootTest(classes = DefaultDataUpdateService.class) | ||
35 | +class DefaultDataUpdateServiceTest { | ||
36 | + | ||
37 | + ObjectMapper mapper = new ObjectMapper(); | ||
38 | + | ||
39 | + @MockBean | ||
40 | + DefaultDataUpdateService service; | ||
41 | + | ||
42 | + @BeforeEach | ||
43 | + void setUp() { | ||
44 | + willCallRealMethod().given(service).convertDeviceProfileAlarmRulesForVersion330(any()); | ||
45 | + willCallRealMethod().given(service).convertDeviceProfileForVersion330(any()); | ||
46 | + } | ||
47 | + | ||
48 | + JsonNode readFromResource(String resourceName) throws IOException { | ||
49 | + return mapper.readTree(this.getClass().getClassLoader().getResourceAsStream(resourceName)); | ||
50 | + } | ||
51 | + | ||
52 | + @Test | ||
53 | + void convertDeviceProfileAlarmRulesForVersion330FirstRun() throws IOException { | ||
54 | + JsonNode spec = readFromResource("update/330/device_profile_001_in.json"); | ||
55 | + JsonNode expected = readFromResource("update/330/device_profile_001_out.json"); | ||
56 | + | ||
57 | + assertThat(service.convertDeviceProfileForVersion330(spec.get("profileData"))).isTrue(); | ||
58 | + assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); // use IDE feature <Click to see difference> | ||
59 | + } | ||
60 | + | ||
61 | + @Test | ||
62 | + void convertDeviceProfileAlarmRulesForVersion330SecondRun() throws IOException { | ||
63 | + JsonNode spec = readFromResource("update/330/device_profile_001_out.json"); | ||
64 | + JsonNode expected = readFromResource("update/330/device_profile_001_out.json"); | ||
65 | + | ||
66 | + assertThat(service.convertDeviceProfileForVersion330(spec.get("profileData"))).isFalse(); | ||
67 | + assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); // use IDE feature <Click to see difference> | ||
68 | + } | ||
69 | + | ||
70 | + @Test | ||
71 | + void convertDeviceProfileAlarmRulesForVersion330EmptyJson() throws JsonProcessingException { | ||
72 | + JsonNode spec = mapper.readTree("{ }"); | ||
73 | + JsonNode expected = mapper.readTree("{ }"); | ||
74 | + | ||
75 | + assertThat(service.convertDeviceProfileForVersion330(spec)).isFalse(); | ||
76 | + assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); | ||
77 | + } | ||
78 | + | ||
79 | + @Test | ||
80 | + void convertDeviceProfileAlarmRulesForVersion330AlarmNodeNull() throws JsonProcessingException { | ||
81 | + JsonNode spec = mapper.readTree("{ \"alarms\" : null }"); | ||
82 | + JsonNode expected = mapper.readTree("{ \"alarms\" : null }"); | ||
83 | + | ||
84 | + assertThat(service.convertDeviceProfileForVersion330(spec)).isFalse(); | ||
85 | + assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); | ||
86 | + } | ||
87 | + | ||
88 | + @Test | ||
89 | + void convertDeviceProfileAlarmRulesForVersion330NoAlarmNode() throws JsonProcessingException { | ||
90 | + JsonNode spec = mapper.readTree("{ \"configuration\": { \"type\": \"DEFAULT\" } }"); | ||
91 | + JsonNode expected = mapper.readTree("{ \"configuration\": { \"type\": \"DEFAULT\" } }"); | ||
92 | + | ||
93 | + assertThat(service.convertDeviceProfileForVersion330(spec)).isFalse(); | ||
94 | + assertThat(spec.toPrettyString()).isEqualTo(expected.toPrettyString()); | ||
95 | + } | ||
96 | + | ||
97 | +} |
1 | +{ | ||
2 | + "id": { | ||
3 | + "entityType": "DEVICE_PROFILE", | ||
4 | + "id": "b99fde7a-33dd-4d5d-a325-d0637f6acbe5" | ||
5 | + }, | ||
6 | + "createdTime": 1627268171906, | ||
7 | + "tenantId": { | ||
8 | + "entityType": "TENANT", | ||
9 | + "id": "3db30ac6-db03-4788-98fe-6e024b422a15" | ||
10 | + }, | ||
11 | + "name": "LORAWAN 001", | ||
12 | + "description": "Tektelic - 001", | ||
13 | + "type": "DEFAULT", | ||
14 | + "transportType": "DEFAULT", | ||
15 | + "provisionType": "DISABLED", | ||
16 | + "defaultRuleChainId": { | ||
17 | + "entityType": "RULE_CHAIN", | ||
18 | + "id": "9c50f4df-f41e-443f-bb7d-37b5ac97f3c3" | ||
19 | + }, | ||
20 | + "defaultQueueName": "LORAWAN", | ||
21 | + "profileData": { | ||
22 | + "configuration": { | ||
23 | + "type": "DEFAULT" | ||
24 | + }, | ||
25 | + "transportConfiguration": { | ||
26 | + "type": "DEFAULT" | ||
27 | + }, | ||
28 | + "provisionConfiguration": { | ||
29 | + "type": "DISABLED", | ||
30 | + "provisionDeviceSecret": null | ||
31 | + }, | ||
32 | + "alarms": [ | ||
33 | + { | ||
34 | + "id": "b86271fd-5fee-4bd5-975c-d9c18f610cd5", | ||
35 | + "alarmType": "LORAWAN - Battery Alarm", | ||
36 | + "createRules": { | ||
37 | + "CRITICAL": { | ||
38 | + "condition": { | ||
39 | + "condition": [ | ||
40 | + { | ||
41 | + "key": { | ||
42 | + "type": "TIME_SERIES", | ||
43 | + "key": "batteryLevel" | ||
44 | + }, | ||
45 | + "valueType": "NUMERIC", | ||
46 | + "value": null, | ||
47 | + "predicate": { | ||
48 | + "type": "NUMERIC", | ||
49 | + "operation": "LESS", | ||
50 | + "value": { | ||
51 | + "defaultValue": 25.0, | ||
52 | + "userValue": null, | ||
53 | + "dynamicValue": null | ||
54 | + } | ||
55 | + } | ||
56 | + } | ||
57 | + ], | ||
58 | + "spec": { | ||
59 | + "type": "DURATION", | ||
60 | + "unit": "DAYS", | ||
61 | + "value": 1 | ||
62 | + } | ||
63 | + }, | ||
64 | + "schedule": null, | ||
65 | + "alarmDetails": null | ||
66 | + } | ||
67 | + }, | ||
68 | + "clearRule": { | ||
69 | + "condition": { | ||
70 | + "condition": [ | ||
71 | + { | ||
72 | + "key": { | ||
73 | + "type": "TIME_SERIES", | ||
74 | + "key": "batteryLevel" | ||
75 | + }, | ||
76 | + "valueType": "NUMERIC", | ||
77 | + "value": null, | ||
78 | + "predicate": { | ||
79 | + "type": "NUMERIC", | ||
80 | + "operation": "GREATER_OR_EQUAL", | ||
81 | + "value": { | ||
82 | + "defaultValue": 25.0, | ||
83 | + "userValue": null, | ||
84 | + "dynamicValue": null | ||
85 | + } | ||
86 | + } | ||
87 | + } | ||
88 | + ], | ||
89 | + "spec": { | ||
90 | + "type": "DURATION", | ||
91 | + "unit": "DAYS", | ||
92 | + "value": 1 | ||
93 | + } | ||
94 | + }, | ||
95 | + "schedule": null, | ||
96 | + "alarmDetails": null | ||
97 | + }, | ||
98 | + "propagate": true, | ||
99 | + "propagateRelationTypes": [ | ||
100 | + "UC-0007 LORAWAN" | ||
101 | + ] | ||
102 | + }, | ||
103 | + { | ||
104 | + "id": "c70aef4e-65cf-4578-acd9-e1927c08b469", | ||
105 | + "alarmType": "LORAWAN - No Data", | ||
106 | + "createRules": { | ||
107 | + "CRITICAL": { | ||
108 | + "condition": { | ||
109 | + "condition": [ | ||
110 | + { | ||
111 | + "key": { | ||
112 | + "type": "TIME_SERIES", | ||
113 | + "key": "active" | ||
114 | + }, | ||
115 | + "valueType": "BOOLEAN", | ||
116 | + "value": null, | ||
117 | + "predicate": { | ||
118 | + "type": "BOOLEAN", | ||
119 | + "operation": "EQUAL", | ||
120 | + "value": { | ||
121 | + "defaultValue": false, | ||
122 | + "userValue": null, | ||
123 | + "dynamicValue": null | ||
124 | + } | ||
125 | + } | ||
126 | + } | ||
127 | + ], | ||
128 | + "spec": { | ||
129 | + "type": "SIMPLE" | ||
130 | + } | ||
131 | + }, | ||
132 | + "schedule": null, | ||
133 | + "alarmDetails": null | ||
134 | + } | ||
135 | + }, | ||
136 | + "clearRule": { | ||
137 | + "condition": { | ||
138 | + "condition": [ | ||
139 | + { | ||
140 | + "key": { | ||
141 | + "type": "TIME_SERIES", | ||
142 | + "key": "active" | ||
143 | + }, | ||
144 | + "valueType": "BOOLEAN", | ||
145 | + "value": null, | ||
146 | + "predicate": { | ||
147 | + "type": "BOOLEAN", | ||
148 | + "operation": "EQUAL", | ||
149 | + "value": { | ||
150 | + "defaultValue": true, | ||
151 | + "userValue": null, | ||
152 | + "dynamicValue": null | ||
153 | + } | ||
154 | + } | ||
155 | + } | ||
156 | + ], | ||
157 | + "spec": { | ||
158 | + "type": "SIMPLE" | ||
159 | + } | ||
160 | + }, | ||
161 | + "schedule": null, | ||
162 | + "alarmDetails": null | ||
163 | + }, | ||
164 | + "propagate": true, | ||
165 | + "propagateRelationTypes": [ | ||
166 | + "LORAWAN 001 related" | ||
167 | + ] | ||
168 | + } | ||
169 | + ] | ||
170 | + }, | ||
171 | + "provisionDeviceKey": null, | ||
172 | + "default": false | ||
173 | +} |
1 | +{ | ||
2 | + "id": { | ||
3 | + "entityType": "DEVICE_PROFILE", | ||
4 | + "id": "b99fde7a-33dd-4d5d-a325-d0637f6acbe5" | ||
5 | + }, | ||
6 | + "createdTime": 1627268171906, | ||
7 | + "tenantId": { | ||
8 | + "entityType": "TENANT", | ||
9 | + "id": "3db30ac6-db03-4788-98fe-6e024b422a15" | ||
10 | + }, | ||
11 | + "name": "LORAWAN 001", | ||
12 | + "description": "Tektelic - 001", | ||
13 | + "type": "DEFAULT", | ||
14 | + "transportType": "DEFAULT", | ||
15 | + "provisionType": "DISABLED", | ||
16 | + "defaultRuleChainId": { | ||
17 | + "entityType": "RULE_CHAIN", | ||
18 | + "id": "9c50f4df-f41e-443f-bb7d-37b5ac97f3c3" | ||
19 | + }, | ||
20 | + "defaultQueueName": "LORAWAN", | ||
21 | + "profileData": { | ||
22 | + "configuration": { | ||
23 | + "type": "DEFAULT" | ||
24 | + }, | ||
25 | + "transportConfiguration": { | ||
26 | + "type": "DEFAULT" | ||
27 | + }, | ||
28 | + "provisionConfiguration": { | ||
29 | + "type": "DISABLED", | ||
30 | + "provisionDeviceSecret": null | ||
31 | + }, | ||
32 | + "alarms": [ | ||
33 | + { | ||
34 | + "id": "b86271fd-5fee-4bd5-975c-d9c18f610cd5", | ||
35 | + "alarmType": "LORAWAN - Battery Alarm", | ||
36 | + "createRules": { | ||
37 | + "CRITICAL": { | ||
38 | + "condition": { | ||
39 | + "condition": [ | ||
40 | + { | ||
41 | + "key": { | ||
42 | + "type": "TIME_SERIES", | ||
43 | + "key": "batteryLevel" | ||
44 | + }, | ||
45 | + "valueType": "NUMERIC", | ||
46 | + "value": null, | ||
47 | + "predicate": { | ||
48 | + "type": "NUMERIC", | ||
49 | + "operation": "LESS", | ||
50 | + "value": { | ||
51 | + "defaultValue": 25.0, | ||
52 | + "userValue": null, | ||
53 | + "dynamicValue": null | ||
54 | + } | ||
55 | + } | ||
56 | + } | ||
57 | + ], | ||
58 | + "spec": { | ||
59 | + "type": "DURATION", | ||
60 | + "unit": "DAYS", | ||
61 | + "predicate": { | ||
62 | + "defaultValue": 1, | ||
63 | + "userValue": null, | ||
64 | + "dynamicValue": { | ||
65 | + "sourceType": null, | ||
66 | + "sourceAttribute": null, | ||
67 | + "inherit": false | ||
68 | + } | ||
69 | + } | ||
70 | + } | ||
71 | + }, | ||
72 | + "schedule": null, | ||
73 | + "alarmDetails": null | ||
74 | + } | ||
75 | + }, | ||
76 | + "clearRule": { | ||
77 | + "condition": { | ||
78 | + "condition": [ | ||
79 | + { | ||
80 | + "key": { | ||
81 | + "type": "TIME_SERIES", | ||
82 | + "key": "batteryLevel" | ||
83 | + }, | ||
84 | + "valueType": "NUMERIC", | ||
85 | + "value": null, | ||
86 | + "predicate": { | ||
87 | + "type": "NUMERIC", | ||
88 | + "operation": "GREATER_OR_EQUAL", | ||
89 | + "value": { | ||
90 | + "defaultValue": 25.0, | ||
91 | + "userValue": null, | ||
92 | + "dynamicValue": null | ||
93 | + } | ||
94 | + } | ||
95 | + } | ||
96 | + ], | ||
97 | + "spec": { | ||
98 | + "type": "DURATION", | ||
99 | + "unit": "DAYS", | ||
100 | + "predicate": { | ||
101 | + "defaultValue": 1, | ||
102 | + "userValue": null, | ||
103 | + "dynamicValue": { | ||
104 | + "sourceType": null, | ||
105 | + "sourceAttribute": null, | ||
106 | + "inherit": false | ||
107 | + } | ||
108 | + } | ||
109 | + } | ||
110 | + }, | ||
111 | + "schedule": null, | ||
112 | + "alarmDetails": null | ||
113 | + }, | ||
114 | + "propagate": true, | ||
115 | + "propagateRelationTypes": [ | ||
116 | + "UC-0007 LORAWAN" | ||
117 | + ] | ||
118 | + }, | ||
119 | + { | ||
120 | + "id": "c70aef4e-65cf-4578-acd9-e1927c08b469", | ||
121 | + "alarmType": "LORAWAN - No Data", | ||
122 | + "createRules": { | ||
123 | + "CRITICAL": { | ||
124 | + "condition": { | ||
125 | + "condition": [ | ||
126 | + { | ||
127 | + "key": { | ||
128 | + "type": "TIME_SERIES", | ||
129 | + "key": "active" | ||
130 | + }, | ||
131 | + "valueType": "BOOLEAN", | ||
132 | + "value": null, | ||
133 | + "predicate": { | ||
134 | + "type": "BOOLEAN", | ||
135 | + "operation": "EQUAL", | ||
136 | + "value": { | ||
137 | + "defaultValue": false, | ||
138 | + "userValue": null, | ||
139 | + "dynamicValue": null | ||
140 | + } | ||
141 | + } | ||
142 | + } | ||
143 | + ], | ||
144 | + "spec": { | ||
145 | + "type": "SIMPLE" | ||
146 | + } | ||
147 | + }, | ||
148 | + "schedule": null, | ||
149 | + "alarmDetails": null | ||
150 | + } | ||
151 | + }, | ||
152 | + "clearRule": { | ||
153 | + "condition": { | ||
154 | + "condition": [ | ||
155 | + { | ||
156 | + "key": { | ||
157 | + "type": "TIME_SERIES", | ||
158 | + "key": "active" | ||
159 | + }, | ||
160 | + "valueType": "BOOLEAN", | ||
161 | + "value": null, | ||
162 | + "predicate": { | ||
163 | + "type": "BOOLEAN", | ||
164 | + "operation": "EQUAL", | ||
165 | + "value": { | ||
166 | + "defaultValue": true, | ||
167 | + "userValue": null, | ||
168 | + "dynamicValue": null | ||
169 | + } | ||
170 | + } | ||
171 | + } | ||
172 | + ], | ||
173 | + "spec": { | ||
174 | + "type": "SIMPLE" | ||
175 | + } | ||
176 | + }, | ||
177 | + "schedule": null, | ||
178 | + "alarmDetails": null | ||
179 | + }, | ||
180 | + "propagate": true, | ||
181 | + "propagateRelationTypes": [ | ||
182 | + "LORAWAN 001 related" | ||
183 | + ] | ||
184 | + } | ||
185 | + ] | ||
186 | + }, | ||
187 | + "provisionDeviceKey": null, | ||
188 | + "default": false | ||
189 | +} |
@@ -17,8 +17,12 @@ package org.thingsboard.server.common.data.page; | @@ -17,8 +17,12 @@ package org.thingsboard.server.common.data.page; | ||
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonIgnore; | 18 | import com.fasterxml.jackson.annotation.JsonIgnore; |
19 | import lombok.Data; | 19 | import lombok.Data; |
20 | +import lombok.EqualsAndHashCode; | ||
21 | +import lombok.ToString; | ||
20 | 22 | ||
21 | @Data | 23 | @Data |
24 | +@ToString(callSuper = true) | ||
25 | +@EqualsAndHashCode(callSuper = true) | ||
22 | public class TimePageLink extends PageLink { | 26 | public class TimePageLink extends PageLink { |
23 | 27 | ||
24 | private final Long startTime; | 28 | private final Long startTime; |