Commit 1278339e611537be6b4a929fa4d00e69b74f12c2

Authored by Andrii Shvaika
1 parent b5bfb082

DeviceProfileRuleNode

Showing 17 changed files with 450 additions and 39 deletions
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.common.data.device.profile; 16 package org.thingsboard.server.common.data.device.profile;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
18 import lombok.Data; 19 import lombok.Data;
19 import org.thingsboard.server.common.data.query.KeyFilter; 20 import org.thingsboard.server.common.data.query.KeyFilter;
20 21
@@ -22,10 +23,10 @@ import java.util.List; @@ -22,10 +23,10 @@ import java.util.List;
22 import java.util.concurrent.TimeUnit; 23 import java.util.concurrent.TimeUnit;
23 24
24 @Data 25 @Data
  26 +@JsonIgnoreProperties(ignoreUnknown = true)
25 public class AlarmCondition { 27 public class AlarmCondition {
26 28
27 private List<KeyFilter> condition; 29 private List<KeyFilter> condition;
28 - private TimeUnit durationUnit;  
29 - private long durationValue; 30 + private AlarmConditionSpec spec;
30 31
31 } 32 }
  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.device.profile;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  19 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  20 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  21 +
  22 +@JsonIgnoreProperties(ignoreUnknown = true)
  23 +@JsonTypeInfo(
  24 + use = JsonTypeInfo.Id.NAME,
  25 + include = JsonTypeInfo.As.PROPERTY,
  26 + property = "type")
  27 +@JsonSubTypes({
  28 + @JsonSubTypes.Type(value = SimpleAlarmConditionSpec.class, name = "SIMPLE"),
  29 + @JsonSubTypes.Type(value = DurationAlarmConditionSpec.class, name = "DURATION"),
  30 + @JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "REPEATING")})
  31 +public interface AlarmConditionSpec {
  32 +
  33 + AlarmConditionSpecType getType();
  34 +
  35 +}
  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.device.profile;
  17 +
  18 +public enum AlarmConditionSpecType {
  19 +
  20 + SIMPLE,
  21 + DURATION,
  22 + REPEATING
  23 +
  24 +}
@@ -21,6 +21,7 @@ import lombok.Data; @@ -21,6 +21,7 @@ import lombok.Data;
21 public class AlarmRule { 21 public class AlarmRule {
22 22
23 private AlarmCondition condition; 23 private AlarmCondition condition;
  24 + private AlarmSchedule schedule;
24 // Advanced 25 // Advanced
25 private String alarmDetails; 26 private String alarmDetails;
26 27
  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.device.profile;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  19 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  20 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  21 +
  22 +@JsonIgnoreProperties(ignoreUnknown = true)
  23 +@JsonTypeInfo(
  24 + use = JsonTypeInfo.Id.NAME,
  25 + include = JsonTypeInfo.As.PROPERTY,
  26 + property = "type")
  27 +@JsonSubTypes({
  28 + @JsonSubTypes.Type(value = SimpleAlarmConditionSpec.class, name = "ANY_TIME"),
  29 + @JsonSubTypes.Type(value = DurationAlarmConditionSpec.class, name = "SPECIFIC_TIME"),
  30 + @JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "CUSTOM")})
  31 +public interface AlarmSchedule {
  32 +
  33 + AlarmScheduleType getType();
  34 +
  35 +}
  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.device.profile;
  17 +
  18 +public enum AlarmScheduleType {
  19 +
  20 + ANY_TIME,
  21 + SPECIFIC_TIME,
  22 + CUSTOM
  23 +
  24 +}
  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.device.profile;
  17 +
  18 +public class AnyTimeSchedule implements AlarmSchedule {
  19 +
  20 + @Override
  21 + public AlarmScheduleType getType() {
  22 + return AlarmScheduleType.ANY_TIME;
  23 + }
  24 +
  25 +}
  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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.List;
  21 +
  22 +@Data
  23 +public class CustomTimeSchedule implements AlarmSchedule {
  24 +
  25 + private String timezone;
  26 + private List<CustomTimeScheduleItem> items;
  27 +
  28 + @Override
  29 + public AlarmScheduleType getType() {
  30 + return AlarmScheduleType.CUSTOM;
  31 + }
  32 +
  33 +}
  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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.List;
  21 +
  22 +@Data
  23 +public class CustomTimeScheduleItem {
  24 +
  25 + private boolean enabled;
  26 + private Integer dayOfWeek;
  27 + private long startsOn;
  28 + private long endsOn;
  29 +
  30 +}
  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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.concurrent.TimeUnit;
  21 +
  22 +@Data
  23 +public class DurationAlarmConditionSpec implements AlarmConditionSpec {
  24 +
  25 + private TimeUnit unit;
  26 + private long value;
  27 +
  28 + @Override
  29 + public AlarmConditionSpecType getType() {
  30 + return AlarmConditionSpecType.SIMPLE;
  31 + }
  32 +}
  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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.concurrent.TimeUnit;
  21 +
  22 +@Data
  23 +public class RepeatingAlarmConditionSpec implements AlarmConditionSpec {
  24 +
  25 + private int count;
  26 +
  27 + @Override
  28 + public AlarmConditionSpecType getType() {
  29 + return AlarmConditionSpecType.SIMPLE;
  30 + }
  31 +}
  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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class SimpleAlarmConditionSpec implements AlarmConditionSpec {
  22 + @Override
  23 + public AlarmConditionSpecType getType() {
  24 + return AlarmConditionSpecType.SIMPLE;
  25 + }
  26 +}
  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.device.profile;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.List;
  21 +
  22 +@Data
  23 +public class SpecificTimeSchedule implements AlarmSchedule {
  24 +
  25 + private String timezone;
  26 + private List<Integer> daysOfWeek;
  27 + private long startsOn;
  28 + private long endsOn;
  29 +
  30 + @Override
  31 + public AlarmScheduleType getType() {
  32 + return AlarmScheduleType.SPECIFIC_TIME;
  33 + }
  34 +
  35 +}
@@ -19,7 +19,11 @@ import lombok.Data; @@ -19,7 +19,11 @@ import lombok.Data;
19 import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState; 19 import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
20 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 20 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
21 import org.thingsboard.server.common.data.device.profile.AlarmCondition; 21 import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  22 +import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
22 import org.thingsboard.server.common.data.device.profile.AlarmRule; 23 import org.thingsboard.server.common.data.device.profile.AlarmRule;
  24 +import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec;
  25 +import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec;
  26 +import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec;
23 import org.thingsboard.server.common.data.query.BooleanFilterPredicate; 27 import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
24 import org.thingsboard.server.common.data.query.ComplexFilterPredicate; 28 import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
25 import org.thingsboard.server.common.data.query.KeyFilter; 29 import org.thingsboard.server.common.data.query.KeyFilter;
@@ -32,7 +36,9 @@ public class AlarmRuleState { @@ -32,7 +36,9 @@ public class AlarmRuleState {
32 36
33 private final AlarmSeverity severity; 37 private final AlarmSeverity severity;
34 private final AlarmRule alarmRule; 38 private final AlarmRule alarmRule;
  39 + private final AlarmConditionSpec spec;
35 private final long requiredDurationInMs; 40 private final long requiredDurationInMs;
  41 + private final long requiredRepeats;
36 private PersistedAlarmRuleState state; 42 private PersistedAlarmRuleState state;
37 private boolean updateFlag; 43 private boolean updateFlag;
38 44
@@ -42,13 +48,31 @@ public class AlarmRuleState { @@ -42,13 +48,31 @@ public class AlarmRuleState {
42 if (state != null) { 48 if (state != null) {
43 this.state = state; 49 this.state = state;
44 } else { 50 } else {
45 - this.state = new PersistedAlarmRuleState(0L, 0L); 51 + this.state = new PersistedAlarmRuleState(0L, 0L, 0L);
46 } 52 }
47 - if (alarmRule.getCondition().getDurationValue() > 0) {  
48 - requiredDurationInMs = alarmRule.getCondition().getDurationUnit().toMillis(alarmRule.getCondition().getDurationValue());  
49 - } else {  
50 - requiredDurationInMs = 0; 53 + this.spec = getSpec(alarmRule);
  54 + long requiredDurationInMs = 0;
  55 + long requiredRepeats = 0;
  56 + switch (spec.getType()) {
  57 + case DURATION:
  58 + DurationAlarmConditionSpec duration = (DurationAlarmConditionSpec) spec;
  59 + requiredDurationInMs = duration.getUnit().toMillis(duration.getValue());
  60 + break;
  61 + case REPEATING:
  62 + RepeatingAlarmConditionSpec repeating = (RepeatingAlarmConditionSpec) spec;
  63 + requiredRepeats = repeating.getCount();
  64 + break;
51 } 65 }
  66 + this.requiredDurationInMs = requiredDurationInMs;
  67 + this.requiredRepeats = requiredRepeats;
  68 + }
  69 +
  70 + public AlarmConditionSpec getSpec(AlarmRule alarmRule) {
  71 + AlarmConditionSpec spec = alarmRule.getCondition().getSpec();
  72 + if (spec == null) {
  73 + spec = new SimpleAlarmConditionSpec();
  74 + }
  75 + return spec;
52 } 76 }
53 77
54 public boolean checkUpdate() { 78 public boolean checkUpdate() {
@@ -61,38 +85,70 @@ public class AlarmRuleState { @@ -61,38 +85,70 @@ public class AlarmRuleState {
61 } 85 }
62 86
63 public boolean eval(DeviceDataSnapshot data) { 87 public boolean eval(DeviceDataSnapshot data) {
64 - if (requiredDurationInMs > 0) {  
65 - boolean eval = eval(alarmRule.getCondition(), data);  
66 - if (eval) {  
67 - if (state.getLastEventTs() > 0) {  
68 - if (data.getTs() > state.getLastEventTs()) {  
69 - state.setDuration(state.getDuration() + (data.getTs() - state.getLastEventTs()));  
70 - state.setLastEventTs(data.getTs());  
71 - updateFlag = true;  
72 - }  
73 - } else { 88 + switch (spec.getType()) {
  89 + case SIMPLE:
  90 + return eval(alarmRule.getCondition(), data);
  91 + case DURATION:
  92 + return evalDuration(data);
  93 + case REPEATING:
  94 + return evalRepeating(data);
  95 + default:
  96 + return false;
  97 + }
  98 + }
  99 +
  100 + private boolean evalRepeating(DeviceDataSnapshot data) {
  101 + boolean eval = eval(alarmRule.getCondition(), data);
  102 + if (eval) {
  103 + state.setEventCount(state.getEventCount() + 1);
  104 + updateFlag = true;
  105 + return state.getEventCount() > requiredRepeats;
  106 + } else {
  107 + if (state.getEventCount() > 0) {
  108 + state.setEventCount(0L);
  109 + updateFlag = true;
  110 + }
  111 + return false;
  112 + }
  113 + }
  114 +
  115 + private boolean evalDuration(DeviceDataSnapshot data) {
  116 + boolean eval = eval(alarmRule.getCondition(), data);
  117 + if (eval) {
  118 + if (state.getLastEventTs() > 0) {
  119 + if (data.getTs() > state.getLastEventTs()) {
  120 + state.setDuration(state.getDuration() + (data.getTs() - state.getLastEventTs()));
74 state.setLastEventTs(data.getTs()); 121 state.setLastEventTs(data.getTs());
75 - state.setDuration(0L);  
76 updateFlag = true; 122 updateFlag = true;
77 } 123 }
78 - return state.getDuration() > requiredDurationInMs;  
79 } else { 124 } else {
80 - state.setLastEventTs(0L); 125 + state.setLastEventTs(data.getTs());
81 state.setDuration(0L); 126 state.setDuration(0L);
82 updateFlag = true; 127 updateFlag = true;
83 - return false;  
84 } 128 }
  129 + return state.getDuration() > requiredDurationInMs;
85 } else { 130 } else {
86 - return eval(alarmRule.getCondition(), data); 131 + if (state.getLastEventTs() > 0 || state.getDuration() > 0) {
  132 + state.setLastEventTs(0L);
  133 + state.setDuration(0L);
  134 + updateFlag = true;
  135 + }
  136 + return false;
87 } 137 }
88 } 138 }
89 139
90 public boolean eval(long ts) { 140 public boolean eval(long ts) {
91 - if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) {  
92 - long duration = state.getDuration() + (ts - state.getLastEventTs());  
93 - return duration > requiredDurationInMs;  
94 - } else {  
95 - return false; 141 + switch (spec.getType()) {
  142 + case SIMPLE:
  143 + case REPEATING:
  144 + return false;
  145 + case DURATION:
  146 + if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) {
  147 + long duration = state.getDuration() + (ts - state.getLastEventTs());
  148 + return duration > requiredDurationInMs;
  149 + }
  150 + default:
  151 + return false;
96 } 152 }
97 } 153 }
98 154
@@ -144,7 +200,6 @@ public class AlarmRuleState { @@ -144,7 +200,6 @@ public class AlarmRuleState {
144 } 200 }
145 } 201 }
146 202
147 -  
148 private boolean evalBoolPredicate(EntityKeyValue ekv, BooleanFilterPredicate predicate) { 203 private boolean evalBoolPredicate(EntityKeyValue ekv, BooleanFilterPredicate predicate) {
149 Boolean value; 204 Boolean value;
150 switch (ekv.getDataType()) { 205 switch (ekv.getDataType()) {
@@ -62,18 +62,22 @@ class DeviceState { @@ -62,18 +62,22 @@ class DeviceState {
62 private DeviceDataSnapshot latestValues; 62 private DeviceDataSnapshot latestValues;
63 private final ConcurrentMap<String, DeviceProfileAlarmState> alarmStates = new ConcurrentHashMap<>(); 63 private final ConcurrentMap<String, DeviceProfileAlarmState> alarmStates = new ConcurrentHashMap<>();
64 64
65 - public DeviceState(TbContext ctx, TbDeviceProfileNodeConfiguration config, DeviceId deviceId, DeviceProfileState deviceProfile) { 65 + public DeviceState(TbContext ctx, TbDeviceProfileNodeConfiguration config, DeviceId deviceId, DeviceProfileState deviceProfile, RuleNodeState state) {
66 this.persistState = config.isPersistAlarmRulesState(); 66 this.persistState = config.isPersistAlarmRulesState();
67 this.deviceId = deviceId; 67 this.deviceId = deviceId;
68 this.deviceProfile = deviceProfile; 68 this.deviceProfile = deviceProfile;
69 if (config.isPersistAlarmRulesState()) { 69 if (config.isPersistAlarmRulesState()) {
70 - state = ctx.findRuleNodeStateForEntity(deviceId);  
71 if (state != null) { 70 if (state != null) {
72 - pds = JacksonUtil.fromString(state.getStateData(), PersistedDeviceState.class); 71 + this.state = state;
73 } else { 72 } else {
74 - state = new RuleNodeState();  
75 - state.setRuleNodeId(ctx.getSelfId());  
76 - state.setEntityId(deviceId); 73 + this.state = ctx.findRuleNodeStateForEntity(deviceId);
  74 + }
  75 + if (this.state != null) {
  76 + pds = JacksonUtil.fromString(this.state.getStateData(), PersistedDeviceState.class);
  77 + } else {
  78 + this.state = new RuleNodeState();
  79 + this.state.setRuleNodeId(ctx.getSelfId());
  80 + this.state.setEntityId(deviceId);
77 pds = new PersistedDeviceState(); 81 pds = new PersistedDeviceState();
78 pds.setAlarmStates(new HashMap<>()); 82 pds.setAlarmStates(new HashMap<>());
79 } 83 }
@@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.DeviceProfile;
30 import org.thingsboard.server.common.data.EntityType; 30 import org.thingsboard.server.common.data.EntityType;
31 import org.thingsboard.server.common.data.id.DeviceId; 31 import org.thingsboard.server.common.data.id.DeviceId;
32 import org.thingsboard.server.common.data.id.DeviceProfileId; 32 import org.thingsboard.server.common.data.id.DeviceProfileId;
  33 +import org.thingsboard.server.common.data.page.PageData;
  34 +import org.thingsboard.server.common.data.page.PageLink;
33 import org.thingsboard.server.common.data.plugin.ComponentType; 35 import org.thingsboard.server.common.data.plugin.ComponentType;
34 import org.thingsboard.server.common.data.rule.RuleNodeState; 36 import org.thingsboard.server.common.data.rule.RuleNodeState;
35 import org.thingsboard.server.common.msg.TbMsg; 37 import org.thingsboard.server.common.msg.TbMsg;
@@ -65,11 +67,28 @@ public class TbDeviceProfileNode implements TbNode { @@ -65,11 +67,28 @@ public class TbDeviceProfileNode implements TbNode {
65 this.config = TbNodeUtils.convert(configuration, TbDeviceProfileNodeConfiguration.class); 67 this.config = TbNodeUtils.convert(configuration, TbDeviceProfileNodeConfiguration.class);
66 this.cache = ctx.getDeviceProfileCache(); 68 this.cache = ctx.getDeviceProfileCache();
67 scheduleAlarmHarvesting(ctx); 69 scheduleAlarmHarvesting(ctx);
68 - //TODO: launch a process of fetching the alarm rule states from the database; 70 + if (config.isFetchAlarmRulesStateOnStart()) {
  71 + PageLink pageLink = new PageLink(1024);
  72 + while (true) {
  73 + PageData<RuleNodeState> states = ctx.findRuleNodeStates(pageLink);
  74 + if (!states.getData().isEmpty()) {
  75 + for (RuleNodeState rns : states.getData()) {
  76 + if (rns.getEntityId().getEntityType().equals(EntityType.DEVICE) && ctx.isLocalEntity(rns.getEntityId())) {
  77 + getOrCreateDeviceState(ctx, new DeviceId(rns.getEntityId().getId()), rns);
  78 + }
  79 + }
  80 + }
  81 + if (!states.hasNext()) {
  82 + break;
  83 + } else {
  84 + pageLink = pageLink.nextPageLink();
  85 + }
  86 + }
  87 + }
69 } 88 }
70 89
71 /** 90 /**
72 - * 2. Dynamic values evaluation; 91 + * TODO: Dynamic values evaluation;
73 */ 92 */
74 @Override 93 @Override
75 public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { 94 public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {
@@ -85,7 +104,7 @@ public class TbDeviceProfileNode implements TbNode { @@ -85,7 +104,7 @@ public class TbDeviceProfileNode implements TbNode {
85 } else if (msg.getType().equals(DataConstants.ENTITY_DELETED)) { 104 } else if (msg.getType().equals(DataConstants.ENTITY_DELETED)) {
86 deviceStates.remove(deviceId); 105 deviceStates.remove(deviceId);
87 } else { 106 } else {
88 - DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId); 107 + DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId, null);
89 if (deviceState != null) { 108 if (deviceState != null) {
90 deviceState.process(ctx, msg); 109 deviceState.process(ctx, msg);
91 } else { 110 } else {
@@ -124,12 +143,12 @@ public class TbDeviceProfileNode implements TbNode { @@ -124,12 +143,12 @@ public class TbDeviceProfileNode implements TbNode {
124 deviceStates.clear(); 143 deviceStates.clear();
125 } 144 }
126 145
127 - protected DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId) { 146 + protected DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId, RuleNodeState rns) {
128 DeviceState deviceState = deviceStates.get(deviceId); 147 DeviceState deviceState = deviceStates.get(deviceId);
129 if (deviceState == null) { 148 if (deviceState == null) {
130 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceId); 149 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceId);
131 if (deviceProfile != null) { 150 if (deviceProfile != null) {
132 - deviceState = new DeviceState(ctx, config, deviceId, new DeviceProfileState(deviceProfile)); 151 + deviceState = new DeviceState(ctx, config, deviceId, new DeviceProfileState(deviceProfile), rns);
133 deviceStates.put(deviceId, deviceState); 152 deviceStates.put(deviceId, deviceState);
134 } 153 }
135 } 154 }
@@ -26,5 +26,6 @@ public class PersistedAlarmRuleState { @@ -26,5 +26,6 @@ public class PersistedAlarmRuleState {
26 26
27 private long lastEventTs; 27 private long lastEventTs;
28 private long duration; 28 private long duration;
  29 + private long eventCount;
29 30
30 } 31 }