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 15 */
16 16 package org.thingsboard.server.common.data.device.profile;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
18 19 import lombok.Data;
19 20 import org.thingsboard.server.common.data.query.KeyFilter;
20 21
... ... @@ -22,10 +23,10 @@ import java.util.List;
22 23 import java.util.concurrent.TimeUnit;
23 24
24 25 @Data
  26 +@JsonIgnoreProperties(ignoreUnknown = true)
25 27 public class AlarmCondition {
26 28
27 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 21 public class AlarmRule {
22 22
23 23 private AlarmCondition condition;
  24 + private AlarmSchedule schedule;
24 25 // Advanced
25 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 19 import org.thingsboard.rule.engine.profile.state.PersistedAlarmRuleState;
20 20 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
21 21 import org.thingsboard.server.common.data.device.profile.AlarmCondition;
  22 +import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
22 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 27 import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
24 28 import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
25 29 import org.thingsboard.server.common.data.query.KeyFilter;
... ... @@ -32,7 +36,9 @@ public class AlarmRuleState {
32 36
33 37 private final AlarmSeverity severity;
34 38 private final AlarmRule alarmRule;
  39 + private final AlarmConditionSpec spec;
35 40 private final long requiredDurationInMs;
  41 + private final long requiredRepeats;
36 42 private PersistedAlarmRuleState state;
37 43 private boolean updateFlag;
38 44
... ... @@ -42,13 +48,31 @@ public class AlarmRuleState {
42 48 if (state != null) {
43 49 this.state = state;
44 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 78 public boolean checkUpdate() {
... ... @@ -61,38 +85,70 @@ public class AlarmRuleState {
61 85 }
62 86
63 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 121 state.setLastEventTs(data.getTs());
75   - state.setDuration(0L);
76 122 updateFlag = true;
77 123 }
78   - return state.getDuration() > requiredDurationInMs;
79 124 } else {
80   - state.setLastEventTs(0L);
  125 + state.setLastEventTs(data.getTs());
81 126 state.setDuration(0L);
82 127 updateFlag = true;
83   - return false;
84 128 }
  129 + return state.getDuration() > requiredDurationInMs;
85 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 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 200 }
145 201 }
146 202
147   -
148 203 private boolean evalBoolPredicate(EntityKeyValue ekv, BooleanFilterPredicate predicate) {
149 204 Boolean value;
150 205 switch (ekv.getDataType()) {
... ...
... ... @@ -62,18 +62,22 @@ class DeviceState {
62 62 private DeviceDataSnapshot latestValues;
63 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 66 this.persistState = config.isPersistAlarmRulesState();
67 67 this.deviceId = deviceId;
68 68 this.deviceProfile = deviceProfile;
69 69 if (config.isPersistAlarmRulesState()) {
70   - state = ctx.findRuleNodeStateForEntity(deviceId);
71 70 if (state != null) {
72   - pds = JacksonUtil.fromString(state.getStateData(), PersistedDeviceState.class);
  71 + this.state = state;
73 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 81 pds = new PersistedDeviceState();
78 82 pds.setAlarmStates(new HashMap<>());
79 83 }
... ...
... ... @@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.DeviceProfile;
30 30 import org.thingsboard.server.common.data.EntityType;
31 31 import org.thingsboard.server.common.data.id.DeviceId;
32 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 35 import org.thingsboard.server.common.data.plugin.ComponentType;
34 36 import org.thingsboard.server.common.data.rule.RuleNodeState;
35 37 import org.thingsboard.server.common.msg.TbMsg;
... ... @@ -65,11 +67,28 @@ public class TbDeviceProfileNode implements TbNode {
65 67 this.config = TbNodeUtils.convert(configuration, TbDeviceProfileNodeConfiguration.class);
66 68 this.cache = ctx.getDeviceProfileCache();
67 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 93 @Override
75 94 public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {
... ... @@ -85,7 +104,7 @@ public class TbDeviceProfileNode implements TbNode {
85 104 } else if (msg.getType().equals(DataConstants.ENTITY_DELETED)) {
86 105 deviceStates.remove(deviceId);
87 106 } else {
88   - DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId);
  107 + DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId, null);
89 108 if (deviceState != null) {
90 109 deviceState.process(ctx, msg);
91 110 } else {
... ... @@ -124,12 +143,12 @@ public class TbDeviceProfileNode implements TbNode {
124 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 147 DeviceState deviceState = deviceStates.get(deviceId);
129 148 if (deviceState == null) {
130 149 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceId);
131 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 152 deviceStates.put(deviceId, deviceState);
134 153 }
135 154 }
... ...
... ... @@ -26,5 +26,6 @@ public class PersistedAlarmRuleState {
26 26
27 27 private long lastEventTs;
28 28 private long duration;
  29 + private long eventCount;
29 30
30 31 }
... ...