Commit 4a3b28d331afef9a74f070e9dd6b1d9f343f3ec7
1 parent
e353ab3c
Method to create default rule chain for device profile
Showing
7 changed files
with
252 additions
and
18 deletions
1 | +{ | ||
2 | + "ruleChain": { | ||
3 | + "additionalInfo": { | ||
4 | + "description": "" | ||
5 | + }, | ||
6 | + "name": "Device Profile Rule Chain Template", | ||
7 | + "firstRuleNodeId": null, | ||
8 | + "root": false, | ||
9 | + "debugMode": false, | ||
10 | + "configuration": null | ||
11 | + }, | ||
12 | + "metadata": { | ||
13 | + "firstNodeIndex": 6, | ||
14 | + "nodes": [ | ||
15 | + { | ||
16 | + "additionalInfo": { | ||
17 | + "layoutX": 822, | ||
18 | + "layoutY": 294 | ||
19 | + }, | ||
20 | + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", | ||
21 | + "name": "Save Timeseries", | ||
22 | + "debugMode": false, | ||
23 | + "configuration": { | ||
24 | + "defaultTTL": 0 | ||
25 | + } | ||
26 | + }, | ||
27 | + { | ||
28 | + "additionalInfo": { | ||
29 | + "layoutX": 824, | ||
30 | + "layoutY": 221 | ||
31 | + }, | ||
32 | + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", | ||
33 | + "name": "Save Client Attributes", | ||
34 | + "debugMode": false, | ||
35 | + "configuration": { | ||
36 | + "scope": "CLIENT_SCOPE" | ||
37 | + } | ||
38 | + }, | ||
39 | + { | ||
40 | + "additionalInfo": { | ||
41 | + "layoutX": 494, | ||
42 | + "layoutY": 309 | ||
43 | + }, | ||
44 | + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", | ||
45 | + "name": "Message Type Switch", | ||
46 | + "debugMode": false, | ||
47 | + "configuration": { | ||
48 | + "version": 0 | ||
49 | + } | ||
50 | + }, | ||
51 | + { | ||
52 | + "additionalInfo": { | ||
53 | + "layoutX": 824, | ||
54 | + "layoutY": 383 | ||
55 | + }, | ||
56 | + "type": "org.thingsboard.rule.engine.action.TbLogNode", | ||
57 | + "name": "Log RPC from Device", | ||
58 | + "debugMode": false, | ||
59 | + "configuration": { | ||
60 | + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | ||
61 | + } | ||
62 | + }, | ||
63 | + { | ||
64 | + "additionalInfo": { | ||
65 | + "layoutX": 823, | ||
66 | + "layoutY": 444 | ||
67 | + }, | ||
68 | + "type": "org.thingsboard.rule.engine.action.TbLogNode", | ||
69 | + "name": "Log Other", | ||
70 | + "debugMode": false, | ||
71 | + "configuration": { | ||
72 | + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | ||
73 | + } | ||
74 | + }, | ||
75 | + { | ||
76 | + "additionalInfo": { | ||
77 | + "layoutX": 822, | ||
78 | + "layoutY": 507 | ||
79 | + }, | ||
80 | + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", | ||
81 | + "name": "RPC Call Request", | ||
82 | + "debugMode": false, | ||
83 | + "configuration": { | ||
84 | + "timeoutInSeconds": 60 | ||
85 | + } | ||
86 | + }, | ||
87 | + { | ||
88 | + "additionalInfo": { | ||
89 | + "description": "", | ||
90 | + "layoutX": 209, | ||
91 | + "layoutY": 307 | ||
92 | + }, | ||
93 | + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", | ||
94 | + "name": "Device Profile Node", | ||
95 | + "debugMode": false, | ||
96 | + "configuration": { | ||
97 | + "version": 0 | ||
98 | + } | ||
99 | + } | ||
100 | + ], | ||
101 | + "connections": [ | ||
102 | + { | ||
103 | + "fromIndex": 2, | ||
104 | + "toIndex": 4, | ||
105 | + "type": "Other" | ||
106 | + }, | ||
107 | + { | ||
108 | + "fromIndex": 2, | ||
109 | + "toIndex": 1, | ||
110 | + "type": "Post attributes" | ||
111 | + }, | ||
112 | + { | ||
113 | + "fromIndex": 2, | ||
114 | + "toIndex": 0, | ||
115 | + "type": "Post telemetry" | ||
116 | + }, | ||
117 | + { | ||
118 | + "fromIndex": 2, | ||
119 | + "toIndex": 3, | ||
120 | + "type": "RPC Request from Device" | ||
121 | + }, | ||
122 | + { | ||
123 | + "fromIndex": 2, | ||
124 | + "toIndex": 5, | ||
125 | + "type": "RPC Request to Device" | ||
126 | + }, | ||
127 | + { | ||
128 | + "fromIndex": 6, | ||
129 | + "toIndex": 2, | ||
130 | + "type": "Success" | ||
131 | + } | ||
132 | + ], | ||
133 | + "ruleChainConnections": null | ||
134 | + } | ||
135 | +} |
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.id.TenantId; | @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.id.TenantId; | ||
47 | import org.thingsboard.server.common.data.page.PageData; | 47 | import org.thingsboard.server.common.data.page.PageData; |
48 | import org.thingsboard.server.common.data.page.PageLink; | 48 | import org.thingsboard.server.common.data.page.PageLink; |
49 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | 49 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
50 | +import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest; | ||
50 | import org.thingsboard.server.common.data.rule.RuleChain; | 51 | import org.thingsboard.server.common.data.rule.RuleChain; |
51 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; | 52 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
52 | import org.thingsboard.server.common.data.rule.RuleNode; | 53 | import org.thingsboard.server.common.data.rule.RuleNode; |
@@ -55,6 +56,7 @@ import org.thingsboard.server.common.msg.TbMsgDataType; | @@ -55,6 +56,7 @@ import org.thingsboard.server.common.msg.TbMsgDataType; | ||
55 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 56 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
56 | import org.thingsboard.server.dao.event.EventService; | 57 | import org.thingsboard.server.dao.event.EventService; |
57 | import org.thingsboard.server.queue.util.TbCoreComponent; | 58 | import org.thingsboard.server.queue.util.TbCoreComponent; |
59 | +import org.thingsboard.server.service.install.InstallScripts; | ||
58 | import org.thingsboard.server.service.script.JsInvokeService; | 60 | import org.thingsboard.server.service.script.JsInvokeService; |
59 | import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; | 61 | import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; |
60 | import org.thingsboard.server.service.security.permission.Operation; | 62 | import org.thingsboard.server.service.security.permission.Operation; |
@@ -78,6 +80,9 @@ public class RuleChainController extends BaseController { | @@ -78,6 +80,9 @@ public class RuleChainController extends BaseController { | ||
78 | private static final ObjectMapper objectMapper = new ObjectMapper(); | 80 | private static final ObjectMapper objectMapper = new ObjectMapper(); |
79 | 81 | ||
80 | @Autowired | 82 | @Autowired |
83 | + private InstallScripts installScripts; | ||
84 | + | ||
85 | + @Autowired | ||
81 | private EventService eventService; | 86 | private EventService eventService; |
82 | 87 | ||
83 | @Autowired | 88 | @Autowired |
@@ -147,6 +152,27 @@ public class RuleChainController extends BaseController { | @@ -147,6 +152,27 @@ public class RuleChainController extends BaseController { | ||
147 | } | 152 | } |
148 | 153 | ||
149 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | 154 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
155 | + @RequestMapping(value = "/ruleChain/device/default", method = RequestMethod.POST) | ||
156 | + @ResponseBody | ||
157 | + public RuleChain saveRuleChain(@RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException { | ||
158 | + try { | ||
159 | + checkNotNull(request); | ||
160 | + checkNotNull(request.getName()); | ||
161 | + | ||
162 | + RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); | ||
163 | + | ||
164 | + logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); | ||
165 | + | ||
166 | + return savedRuleChain; | ||
167 | + } catch (Exception e) { | ||
168 | + RuleChain ruleChain = new RuleChain(); | ||
169 | + ruleChain.setName(request.getName()); | ||
170 | + logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain, null, ActionType.ADDED, e); | ||
171 | + throw handleException(e); | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | ||
150 | @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST) | 176 | @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST) |
151 | @ResponseBody | 177 | @ResponseBody |
152 | public RuleChain setRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { | 178 | public RuleChain setRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { |
@@ -57,6 +57,7 @@ public class InstallScripts { | @@ -57,6 +57,7 @@ public class InstallScripts { | ||
57 | public static final String JSON_DIR = "json"; | 57 | public static final String JSON_DIR = "json"; |
58 | public static final String SYSTEM_DIR = "system"; | 58 | public static final String SYSTEM_DIR = "system"; |
59 | public static final String TENANT_DIR = "tenant"; | 59 | public static final String TENANT_DIR = "tenant"; |
60 | + public static final String DEVICE_PROFILE_DIR = "device_profile"; | ||
60 | public static final String DEMO_DIR = "demo"; | 61 | public static final String DEMO_DIR = "demo"; |
61 | public static final String RULE_CHAINS_DIR = "rule_chains"; | 62 | public static final String RULE_CHAINS_DIR = "rule_chains"; |
62 | public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; | 63 | public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; |
@@ -83,6 +84,10 @@ public class InstallScripts { | @@ -83,6 +84,10 @@ public class InstallScripts { | ||
83 | return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); | 84 | return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); |
84 | } | 85 | } |
85 | 86 | ||
87 | + public Path getDeviceProfileDefaultRuleChainTemplateFilePath() { | ||
88 | + return Paths.get(getDataDir(), JSON_DIR, DEVICE_PROFILE_DIR, "rule_chain_template.json"); | ||
89 | + } | ||
90 | + | ||
86 | public String getDataDir() { | 91 | public String getDataDir() { |
87 | if (!StringUtils.isEmpty(dataDir)) { | 92 | if (!StringUtils.isEmpty(dataDir)) { |
88 | if (!Paths.get(this.dataDir).toFile().isDirectory()) { | 93 | if (!Paths.get(this.dataDir).toFile().isDirectory()) { |
@@ -110,15 +115,7 @@ public class InstallScripts { | @@ -110,15 +115,7 @@ public class InstallScripts { | ||
110 | dirStream.forEach( | 115 | dirStream.forEach( |
111 | path -> { | 116 | path -> { |
112 | try { | 117 | try { |
113 | - JsonNode ruleChainJson = objectMapper.readTree(path.toFile()); | ||
114 | - RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); | ||
115 | - RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); | ||
116 | - | ||
117 | - ruleChain.setTenantId(tenantId); | ||
118 | - ruleChain = ruleChainService.saveRuleChain(ruleChain); | ||
119 | - | ||
120 | - ruleChainMetaData.setRuleChainId(ruleChain.getId()); | ||
121 | - ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData); | 118 | + createRuleChainFromFile(tenantId, path, null); |
122 | } catch (Exception e) { | 119 | } catch (Exception e) { |
123 | log.error("Unable to load rule chain from json: [{}]", path.toString()); | 120 | log.error("Unable to load rule chain from json: [{}]", path.toString()); |
124 | throw new RuntimeException("Unable to load rule chain from json", e); | 121 | throw new RuntimeException("Unable to load rule chain from json", e); |
@@ -128,6 +125,28 @@ public class InstallScripts { | @@ -128,6 +125,28 @@ public class InstallScripts { | ||
128 | } | 125 | } |
129 | } | 126 | } |
130 | 127 | ||
128 | + public RuleChain createDefaultRuleChain(TenantId tenantId, String ruleChainName) throws IOException { | ||
129 | + return createRuleChainFromFile(tenantId, getDeviceProfileDefaultRuleChainTemplateFilePath(), ruleChainName); | ||
130 | + } | ||
131 | + | ||
132 | + public RuleChain createRuleChainFromFile(TenantId tenantId, Path templateFilePath, String newRuleChainName) throws IOException { | ||
133 | + JsonNode ruleChainJson = objectMapper.readTree(templateFilePath.toFile()); | ||
134 | + RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); | ||
135 | + RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); | ||
136 | + | ||
137 | + ruleChain.setTenantId(tenantId); | ||
138 | + if (!StringUtils.isEmpty(newRuleChainName)) { | ||
139 | + ruleChain.setName(newRuleChainName); | ||
140 | + } | ||
141 | + ruleChain = ruleChainService.saveRuleChain(ruleChain); | ||
142 | + | ||
143 | + ruleChainMetaData.setRuleChainId(ruleChain.getId()); | ||
144 | + ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData); | ||
145 | + | ||
146 | + return ruleChain; | ||
147 | + } | ||
148 | + | ||
149 | + | ||
131 | public void loadSystemWidgets() throws Exception { | 150 | public void loadSystemWidgets() throws Exception { |
132 | Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); | 151 | Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); |
133 | try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { | 152 | try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { |
common/data/src/main/java/org/thingsboard/server/common/data/rule/DefaultRuleChainCreateRequest.java
0 → 100644
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.common.data.rule; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import lombok.extern.slf4j.Slf4j; | ||
20 | + | ||
21 | +import java.io.Serializable; | ||
22 | + | ||
23 | +@Data | ||
24 | +@Slf4j | ||
25 | +public class DefaultRuleChainCreateRequest implements Serializable { | ||
26 | + | ||
27 | + private static final long serialVersionUID = 5600333716030561537L; | ||
28 | + | ||
29 | + private String name; | ||
30 | + | ||
31 | +} |
@@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; | @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; | ||
22 | import org.thingsboard.server.common.data.DataConstants; | 22 | import org.thingsboard.server.common.data.DataConstants; |
23 | import org.thingsboard.server.common.data.alarm.Alarm; | 23 | import org.thingsboard.server.common.data.alarm.Alarm; |
24 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 24 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
25 | +import org.thingsboard.server.common.data.alarm.AlarmStatus; | ||
25 | import org.thingsboard.server.common.data.device.profile.AlarmCondition; | 26 | import org.thingsboard.server.common.data.device.profile.AlarmCondition; |
26 | import org.thingsboard.server.common.data.device.profile.AlarmRule; | 27 | import org.thingsboard.server.common.data.device.profile.AlarmRule; |
27 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | 28 | import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
@@ -34,11 +35,13 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate; | @@ -34,11 +35,13 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate; | ||
34 | import org.thingsboard.server.common.data.query.StringFilterPredicate; | 35 | import org.thingsboard.server.common.data.query.StringFilterPredicate; |
35 | import org.thingsboard.server.common.msg.TbMsg; | 36 | import org.thingsboard.server.common.msg.TbMsg; |
36 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 37 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
38 | +import org.thingsboard.server.dao.alarm.AlarmService; | ||
37 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; | 39 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
38 | 40 | ||
39 | import java.util.Comparator; | 41 | import java.util.Comparator; |
40 | import java.util.Map; | 42 | import java.util.Map; |
41 | import java.util.TreeMap; | 43 | import java.util.TreeMap; |
44 | +import java.util.concurrent.ExecutionException; | ||
42 | 45 | ||
43 | @Data | 46 | @Data |
44 | class DeviceProfileAlarmState { | 47 | class DeviceProfileAlarmState { |
@@ -47,6 +50,7 @@ class DeviceProfileAlarmState { | @@ -47,6 +50,7 @@ class DeviceProfileAlarmState { | ||
47 | private final DeviceProfileAlarm alarmDefinition; | 50 | private final DeviceProfileAlarm alarmDefinition; |
48 | private volatile Map<AlarmSeverity, AlarmRule> createRulesSortedBySeverityDesc; | 51 | private volatile Map<AlarmSeverity, AlarmRule> createRulesSortedBySeverityDesc; |
49 | private volatile Alarm currentAlarm; | 52 | private volatile Alarm currentAlarm; |
53 | + private volatile boolean initialFetchDone; | ||
50 | 54 | ||
51 | public DeviceProfileAlarmState(EntityId originator, DeviceProfileAlarm alarmDefinition) { | 55 | public DeviceProfileAlarmState(EntityId originator, DeviceProfileAlarm alarmDefinition) { |
52 | this.originator = originator; | 56 | this.originator = originator; |
@@ -55,7 +59,15 @@ class DeviceProfileAlarmState { | @@ -55,7 +59,15 @@ class DeviceProfileAlarmState { | ||
55 | this.createRulesSortedBySeverityDesc.putAll(alarmDefinition.getCreateRules()); | 59 | this.createRulesSortedBySeverityDesc.putAll(alarmDefinition.getCreateRules()); |
56 | } | 60 | } |
57 | 61 | ||
58 | - public void process(TbContext ctx, TbMsg msg, DeviceDataSnapshot data) { | 62 | + public void process(TbContext ctx, TbMsg msg, DeviceDataSnapshot data) throws ExecutionException, InterruptedException { |
63 | + if (!initialFetchDone) { | ||
64 | + Alarm alarm = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), originator, alarmDefinition.getAlarmType()).get(); | ||
65 | + if (alarm != null && !alarm.getStatus().isCleared()) { | ||
66 | + currentAlarm = alarm; | ||
67 | + } | ||
68 | + initialFetchDone = true; | ||
69 | + } | ||
70 | + | ||
59 | AlarmSeverity resultSeverity = null; | 71 | AlarmSeverity resultSeverity = null; |
60 | for (Map.Entry<AlarmSeverity, AlarmRule> kv : createRulesSortedBySeverityDesc.entrySet()) { | 72 | for (Map.Entry<AlarmSeverity, AlarmRule> kv : createRulesSortedBySeverityDesc.entrySet()) { |
61 | AlarmRule alarmRule = kv.getValue(); | 73 | AlarmRule alarmRule = kv.getValue(); |
@@ -69,6 +81,7 @@ class DeviceProfileAlarmState { | @@ -69,6 +81,7 @@ class DeviceProfileAlarmState { | ||
69 | } else if (currentAlarm != null) { | 81 | } else if (currentAlarm != null) { |
70 | AlarmRule clearRule = alarmDefinition.getClearRule(); | 82 | AlarmRule clearRule = alarmDefinition.getClearRule(); |
71 | if (eval(clearRule.getCondition(), data)) { | 83 | if (eval(clearRule.getCondition(), data)) { |
84 | + ctx.getAlarmService().clearAlarm(ctx.getTenantId(), currentAlarm.getId(), JacksonUtil.OBJECT_MAPPER.createObjectNode(), System.currentTimeMillis()); | ||
72 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm), msg); | 85 | pushMsg(ctx, new TbAlarmResult(false, false, true, currentAlarm), msg); |
73 | currentAlarm = null; | 86 | currentAlarm = null; |
74 | } | 87 | } |
@@ -112,6 +125,8 @@ class DeviceProfileAlarmState { | @@ -112,6 +125,8 @@ class DeviceProfileAlarmState { | ||
112 | } | 125 | } |
113 | } else { | 126 | } else { |
114 | currentAlarm = new Alarm(); | 127 | currentAlarm = new Alarm(); |
128 | + currentAlarm.setType(alarmDefinition.getAlarmType()); | ||
129 | + currentAlarm.setStatus(AlarmStatus.ACTIVE_UNACK); | ||
115 | currentAlarm.setSeverity(severity); | 130 | currentAlarm.setSeverity(severity); |
116 | currentAlarm.setStartTs(System.currentTimeMillis()); | 131 | currentAlarm.setStartTs(System.currentTimeMillis()); |
117 | currentAlarm.setEndTs(currentAlarm.getStartTs()); | 132 | currentAlarm.setEndTs(currentAlarm.getStartTs()); |
@@ -62,15 +62,17 @@ class DeviceState { | @@ -62,15 +62,17 @@ class DeviceState { | ||
62 | } | 62 | } |
63 | } | 63 | } |
64 | 64 | ||
65 | - private void processTelemetry(TbContext ctx, TbMsg msg) { | 65 | + private void processTelemetry(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { |
66 | Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToSortedTelemetry(new JsonParser().parse(msg.getData()), TbMsgTimeseriesNode.getTs(msg)); | 66 | Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToSortedTelemetry(new JsonParser().parse(msg.getData()), TbMsgTimeseriesNode.getTs(msg)); |
67 | - tsKvMap.forEach((ts, data) -> { | 67 | + for (Map.Entry<Long, List<KvEntry>> entry : tsKvMap.entrySet()) { |
68 | + Long ts = entry.getKey(); | ||
69 | + List<KvEntry> data = entry.getValue(); | ||
68 | latestValues = merge(latestValues, ts, data); | 70 | latestValues = merge(latestValues, ts, data); |
69 | for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { | 71 | for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { |
70 | DeviceProfileAlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new DeviceProfileAlarmState(msg.getOriginator(), alarm)); | 72 | DeviceProfileAlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new DeviceProfileAlarmState(msg.getOriginator(), alarm)); |
71 | alarmState.process(ctx, msg, latestValues); | 73 | alarmState.process(ctx, msg, latestValues); |
72 | } | 74 | } |
73 | - }); | 75 | + } |
74 | ctx.tellSuccess(msg); | 76 | ctx.tellSuccess(msg); |
75 | } | 77 | } |
76 | 78 | ||
@@ -140,7 +142,9 @@ class DeviceState { | @@ -140,7 +142,9 @@ class DeviceState { | ||
140 | if (!latestTsKeys.isEmpty()) { | 142 | if (!latestTsKeys.isEmpty()) { |
141 | List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get(); | 143 | List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), originator, latestTsKeys).get(); |
142 | for (TsKvEntry entry : data) { | 144 | for (TsKvEntry entry : data) { |
143 | - result.putValue(new EntityKey(EntityKeyType.TIME_SERIES, entry.getKey()), toEntityValue(entry)); | 145 | + if (entry.getValue() != null) { |
146 | + result.putValue(new EntityKey(EntityKeyType.TIME_SERIES, entry.getKey()), toEntityValue(entry)); | ||
147 | + } | ||
144 | } | 148 | } |
145 | } | 149 | } |
146 | if (!clientAttributeKeys.isEmpty()) { | 150 | if (!clientAttributeKeys.isEmpty()) { |
@@ -161,10 +165,12 @@ class DeviceState { | @@ -161,10 +165,12 @@ class DeviceState { | ||
161 | 165 | ||
162 | private void addToSnapshot(DeviceDataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) { | 166 | private void addToSnapshot(DeviceDataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) { |
163 | for (AttributeKvEntry entry : data) { | 167 | for (AttributeKvEntry entry : data) { |
164 | - EntityKeyValue value = toEntityValue(entry); | ||
165 | - snapshot.putValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, entry.getKey()), value); | ||
166 | - if (commonAttributeKeys.contains(entry.getKey())) { | ||
167 | - snapshot.putValue(new EntityKey(EntityKeyType.ATTRIBUTE, entry.getKey()), value); | 168 | + if (entry.getValue() != null) { |
169 | + EntityKeyValue value = toEntityValue(entry); | ||
170 | + snapshot.putValue(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, entry.getKey()), value); | ||
171 | + if (commonAttributeKeys.contains(entry.getKey())) { | ||
172 | + snapshot.putValue(new EntityKey(EntityKeyType.ATTRIBUTE, entry.getKey()), value); | ||
173 | + } | ||
168 | } | 174 | } |
169 | } | 175 | } |
170 | } | 176 | } |
@@ -137,6 +137,7 @@ public class TbDeviceProfileNodeTest { | @@ -137,6 +137,7 @@ public class TbDeviceProfileNodeTest { | ||
137 | alarmRule.setCondition(alarmCondition); | 137 | alarmRule.setCondition(alarmCondition); |
138 | DeviceProfileAlarm dpa = new DeviceProfileAlarm(); | 138 | DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
139 | dpa.setId("highTemperatureAlarmID"); | 139 | dpa.setId("highTemperatureAlarmID"); |
140 | + dpa.setAlarmType("highTemperatureAlarm"); | ||
140 | dpa.setCreateRules(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)); | 141 | dpa.setCreateRules(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule)); |
141 | deviceProfileData.setAlarms(Collections.singletonList(dpa)); | 142 | deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
142 | deviceProfile.setProfileData(deviceProfileData); | 143 | deviceProfile.setProfileData(deviceProfileData); |
@@ -144,6 +145,7 @@ public class TbDeviceProfileNodeTest { | @@ -144,6 +145,7 @@ public class TbDeviceProfileNodeTest { | ||
144 | Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); | 145 | Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
145 | Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) | 146 | Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
146 | .thenReturn(Futures.immediateFuture(Collections.emptyList())); | 147 | .thenReturn(Futures.immediateFuture(Collections.emptyList())); |
148 | + Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(Futures.immediateFuture(null)); | ||
147 | Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); | 149 | Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
148 | 150 | ||
149 | TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); | 151 | TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |