Scheduler for Device Profile Alarms
This commit is contained in:
parent
dafc0bebc1
commit
c9f3af73fd
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.server.common.data.device.profile;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
@ -30,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
@JsonSubTypes.Type(value = RepeatingAlarmConditionSpec.class, name = "REPEATING")})
|
||||
public interface AlarmConditionSpec {
|
||||
|
||||
@JsonIgnore
|
||||
AlarmConditionSpecType getType();
|
||||
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import java.util.List;
|
||||
public class CustomTimeScheduleItem {
|
||||
|
||||
private boolean enabled;
|
||||
private Integer dayOfWeek;
|
||||
private int dayOfWeek;
|
||||
private long startsOn;
|
||||
private long endsOn;
|
||||
|
||||
|
||||
@ -29,6 +29,6 @@ public class DurationAlarmConditionSpec implements AlarmConditionSpec {
|
||||
|
||||
@Override
|
||||
public AlarmConditionSpecType getType() {
|
||||
return AlarmConditionSpecType.SIMPLE;
|
||||
return AlarmConditionSpecType.DURATION;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,6 @@ public class RepeatingAlarmConditionSpec implements AlarmConditionSpec {
|
||||
|
||||
@Override
|
||||
public AlarmConditionSpecType getType() {
|
||||
return AlarmConditionSpecType.SIMPLE;
|
||||
return AlarmConditionSpecType.REPEATING;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,12 +18,13 @@ package org.thingsboard.server.common.data.device.profile;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Data
|
||||
public class SpecificTimeSchedule implements AlarmSchedule {
|
||||
|
||||
private String timezone;
|
||||
private List<Integer> daysOfWeek;
|
||||
private Set<Integer> daysOfWeek;
|
||||
private long startsOn;
|
||||
private long endsOn;
|
||||
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright © 2016-2020 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.common.msg.tools;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class SchedulerUtils {
|
||||
|
||||
private static final ConcurrentMap<String, ZoneId> tzMap = new ConcurrentHashMap<>();
|
||||
|
||||
public static ZoneId getZoneId(String tz) {
|
||||
return tzMap.computeIfAbsent(tz == null || tz.isEmpty() ? "UTC" : tz, ZoneId::of);
|
||||
}
|
||||
|
||||
}
|
||||
@ -21,15 +21,24 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
|
||||
import org.thingsboard.server.common.data.device.profile.AlarmConditionSpec;
|
||||
import org.thingsboard.server.common.data.device.profile.AlarmRule;
|
||||
import org.thingsboard.server.common.data.device.profile.CustomTimeSchedule;
|
||||
import org.thingsboard.server.common.data.device.profile.CustomTimeScheduleItem;
|
||||
import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec;
|
||||
import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec;
|
||||
import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec;
|
||||
import org.thingsboard.server.common.data.device.profile.SpecificTimeSchedule;
|
||||
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
|
||||
import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
|
||||
import org.thingsboard.server.common.data.query.KeyFilter;
|
||||
import org.thingsboard.server.common.data.query.KeyFilterPredicate;
|
||||
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
||||
import org.thingsboard.server.common.data.query.StringFilterPredicate;
|
||||
import org.thingsboard.server.common.msg.tools.SchedulerUtils;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Calendar;
|
||||
|
||||
@Data
|
||||
public class AlarmRuleState {
|
||||
@ -85,21 +94,72 @@ public class AlarmRuleState {
|
||||
}
|
||||
|
||||
public boolean eval(DeviceDataSnapshot data) {
|
||||
boolean active = isActive(data.getTs());
|
||||
switch (spec.getType()) {
|
||||
case SIMPLE:
|
||||
return eval(alarmRule.getCondition(), data);
|
||||
return active && eval(alarmRule.getCondition(), data);
|
||||
case DURATION:
|
||||
return evalDuration(data);
|
||||
return evalDuration(data, active);
|
||||
case REPEATING:
|
||||
return evalRepeating(data);
|
||||
return evalRepeating(data, active);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean evalRepeating(DeviceDataSnapshot data) {
|
||||
boolean eval = eval(alarmRule.getCondition(), data);
|
||||
if (eval) {
|
||||
private boolean isActive(long eventTs) {
|
||||
if (eventTs == 0L) {
|
||||
eventTs = System.currentTimeMillis();
|
||||
}
|
||||
if (alarmRule.getSchedule() == null) {
|
||||
return true;
|
||||
}
|
||||
switch (alarmRule.getSchedule().getType()) {
|
||||
case ANY_TIME:
|
||||
return true;
|
||||
case SPECIFIC_TIME:
|
||||
return isActiveSpecific((SpecificTimeSchedule) alarmRule.getSchedule(), eventTs);
|
||||
case CUSTOM:
|
||||
return isActiveCustom((CustomTimeSchedule) alarmRule.getSchedule(), eventTs);
|
||||
default:
|
||||
throw new RuntimeException("Unsupported schedule type: " + alarmRule.getSchedule().getType());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isActiveSpecific(SpecificTimeSchedule schedule, long eventTs) {
|
||||
ZoneId zoneId = SchedulerUtils.getZoneId(schedule.getTimezone());
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(eventTs), zoneId);
|
||||
if (schedule.getDaysOfWeek().size() != 7) {
|
||||
int dayOfWeek = zdt.getDayOfWeek().getValue();
|
||||
if (!schedule.getDaysOfWeek().contains(dayOfWeek)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
long startOfDay = zdt.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli();
|
||||
long msFromStartOfDay = eventTs - startOfDay;
|
||||
return schedule.getStartsOn() <= msFromStartOfDay && schedule.getEndsOn() > msFromStartOfDay;
|
||||
}
|
||||
|
||||
private boolean isActiveCustom(CustomTimeSchedule schedule, long eventTs) {
|
||||
ZoneId zoneId = SchedulerUtils.getZoneId(schedule.getTimezone());
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(eventTs), zoneId);
|
||||
int dayOfWeek = zdt.toLocalDate().getDayOfWeek().getValue();
|
||||
for (CustomTimeScheduleItem item : schedule.getItems()) {
|
||||
if (item.getDayOfWeek() == dayOfWeek) {
|
||||
if (item.isEnabled()) {
|
||||
long startOfDay = zdt.toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli();
|
||||
long msFromStartOfDay = eventTs - startOfDay;
|
||||
return item.getStartsOn() <= msFromStartOfDay && item.getEndsOn() > msFromStartOfDay;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean evalRepeating(DeviceDataSnapshot data, boolean active) {
|
||||
if (active && eval(alarmRule.getCondition(), data)) {
|
||||
state.setEventCount(state.getEventCount() + 1);
|
||||
updateFlag = true;
|
||||
return state.getEventCount() > requiredRepeats;
|
||||
@ -112,9 +172,8 @@ public class AlarmRuleState {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean evalDuration(DeviceDataSnapshot data) {
|
||||
boolean eval = eval(alarmRule.getCondition(), data);
|
||||
if (eval) {
|
||||
private boolean evalDuration(DeviceDataSnapshot data, boolean active) {
|
||||
if (active && eval(alarmRule.getCondition(), data)) {
|
||||
if (state.getLastEventTs() > 0) {
|
||||
if (data.getTs() > state.getLastEventTs()) {
|
||||
state.setDuration(state.getDuration() + (data.getTs() - state.getLastEventTs()));
|
||||
@ -145,7 +204,13 @@ public class AlarmRuleState {
|
||||
case DURATION:
|
||||
if (requiredDurationInMs > 0 && state.getLastEventTs() > 0 && ts > state.getLastEventTs()) {
|
||||
long duration = state.getDuration() + (ts - state.getLastEventTs());
|
||||
return duration > requiredDurationInMs;
|
||||
boolean result = duration > requiredDurationInMs && isActive(ts);
|
||||
if (result) {
|
||||
state.setLastEventTs(0L);
|
||||
state.setDuration(0L);
|
||||
updateFlag = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user