|
|
|
@ -42,6 +42,8 @@ import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; |
|
|
|
import org.thingsboard.server.common.data.device.profile.AlarmRule; |
|
|
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; |
|
|
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
|
|
|
import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; |
|
|
|
import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; |
|
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceId; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|
|
|
@ -64,12 +66,15 @@ import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; |
|
|
|
import org.thingsboard.server.dao.model.sql.AttributeKvEntity; |
|
|
|
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
|
|
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.math.RoundingMode; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.TreeMap; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
import static org.mockito.ArgumentMatchers.eq; |
|
|
|
import static org.mockito.Mockito.verify; |
|
|
|
@ -94,10 +99,10 @@ public class TbDeviceProfileNodeTest { |
|
|
|
@Mock |
|
|
|
private AttributesService attributesService; |
|
|
|
|
|
|
|
private TenantId tenantId = new TenantId(UUID.randomUUID()); |
|
|
|
private DeviceId deviceId = new DeviceId(UUID.randomUUID()); |
|
|
|
private CustomerId customerId = new CustomerId(UUID.randomUUID()); |
|
|
|
private DeviceProfileId deviceProfileId = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
private final TenantId tenantId = new TenantId(UUID.randomUUID()); |
|
|
|
private final DeviceId deviceId = new DeviceId(UUID.randomUUID()); |
|
|
|
private final CustomerId customerId = new CustomerId(UUID.randomUUID()); |
|
|
|
private final DeviceProfileId deviceProfileId = new DeviceProfileId(UUID.randomUUID()); |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testRandomMessageType() throws Exception { |
|
|
|
@ -444,6 +449,642 @@ public class TbDeviceProfileNodeTest { |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testCurrentDeviceAttributeForDynamicDurationValue() throws Exception { |
|
|
|
init(); |
|
|
|
|
|
|
|
DeviceProfile deviceProfile = new DeviceProfile(); |
|
|
|
deviceProfile.setId(deviceProfileId); |
|
|
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|
|
|
|
|
|
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "greaterAttribute" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); |
|
|
|
attributeKvEntity.setId(compositeKey); |
|
|
|
attributeKvEntity.setLongValue(30L); |
|
|
|
attributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvCompositeKey alarmDelayCompositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "alarm_delay" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity alarmDelayAttributeKvEntity = new AttributeKvEntity(); |
|
|
|
alarmDelayAttributeKvEntity.setId(alarmDelayCompositeKey); |
|
|
|
long alarmDelayInSeconds = 5L; |
|
|
|
alarmDelayAttributeKvEntity.setLongValue(alarmDelayInSeconds); |
|
|
|
alarmDelayAttributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvEntry entry = attributeKvEntity.toData(); |
|
|
|
|
|
|
|
AttributeKvEntry alarmDelayAttributeKvEntry = alarmDelayAttributeKvEntity.toData(); |
|
|
|
|
|
|
|
ListenableFuture<List<AttributeKvEntry>> listListenableFuture = |
|
|
|
Futures.immediateFuture(Arrays.asList(entry, alarmDelayAttributeKvEntry)); |
|
|
|
|
|
|
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); |
|
|
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); |
|
|
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC); |
|
|
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); |
|
|
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
|
|
|
highTemperaturePredicate.setValue(new FilterPredicateValue<>( |
|
|
|
0.0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "greaterAttribute", false) |
|
|
|
)); |
|
|
|
highTempFilter.setPredicate(highTemperaturePredicate); |
|
|
|
AlarmCondition alarmCondition = new AlarmCondition(); |
|
|
|
alarmCondition.setCondition(Collections.singletonList(highTempFilter)); |
|
|
|
|
|
|
|
FilterPredicateValue<Long> filterPredicateValue = new FilterPredicateValue<>( |
|
|
|
10L, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarm_delay", false) |
|
|
|
); |
|
|
|
|
|
|
|
DurationAlarmConditionSpec durationSpec = new DurationAlarmConditionSpec(); |
|
|
|
durationSpec.setUnit(TimeUnit.SECONDS); |
|
|
|
durationSpec.setPredicate(filterPredicateValue); |
|
|
|
alarmCondition.setSpec(durationSpec); |
|
|
|
|
|
|
|
AlarmRule alarmRule = new AlarmRule(); |
|
|
|
alarmRule.setCondition(alarmCondition); |
|
|
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
|
|
|
dpa.setId("highTemperatureAlarmID"); |
|
|
|
dpa.setAlarmType("highTemperatureAlarm"); |
|
|
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
|
|
|
|
|
|
|
deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
|
|
|
deviceProfile.setProfileData(deviceProfileData); |
|
|
|
|
|
|
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
|
|
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
|
|
|
.thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) |
|
|
|
.thenReturn(Futures.immediateFuture(null)); |
|
|
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
|
|
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) |
|
|
|
.thenReturn(listListenableFuture); |
|
|
|
|
|
|
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |
|
|
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) |
|
|
|
.thenReturn(theMsg); |
|
|
|
|
|
|
|
ObjectNode data = mapper.createObjectNode(); |
|
|
|
data.put("temperature", 35); |
|
|
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
|
verify(ctx).tellSuccess(msg); |
|
|
|
int halfOfAlarmDelay = new BigDecimal(alarmDelayInSeconds) |
|
|
|
.multiply(BigDecimal.valueOf(1000)) |
|
|
|
.divide(BigDecimal.valueOf(2), 3, RoundingMode.HALF_EVEN) |
|
|
|
.intValueExact(); |
|
|
|
Thread.sleep(halfOfAlarmDelay); |
|
|
|
|
|
|
|
verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); |
|
|
|
|
|
|
|
Thread.sleep(halfOfAlarmDelay); |
|
|
|
|
|
|
|
TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg2); |
|
|
|
verify(ctx).tellSuccess(msg2); |
|
|
|
verify(ctx).tellNext(theMsg, "Alarm Created"); |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testInheritTenantAttributeForDuration() throws Exception { |
|
|
|
init(); |
|
|
|
|
|
|
|
DeviceProfile deviceProfile = new DeviceProfile(); |
|
|
|
deviceProfile.setId(deviceProfileId); |
|
|
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|
|
|
|
|
|
|
Device device = new Device(); |
|
|
|
device.setId(deviceId); |
|
|
|
device.setCustomerId(customerId); |
|
|
|
|
|
|
|
|
|
|
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "greaterAttribute" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); |
|
|
|
attributeKvEntity.setId(compositeKey); |
|
|
|
attributeKvEntity.setLongValue(30L); |
|
|
|
attributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvCompositeKey alarmDelayCompositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "alarm_delay" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity alarmDelayAttributeKvEntity = new AttributeKvEntity(); |
|
|
|
alarmDelayAttributeKvEntity.setId(alarmDelayCompositeKey); |
|
|
|
long alarmDelayInSeconds = 5L; |
|
|
|
alarmDelayAttributeKvEntity.setLongValue(alarmDelayInSeconds); |
|
|
|
alarmDelayAttributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvEntry entry = attributeKvEntity.toData(); |
|
|
|
|
|
|
|
AttributeKvEntry alarmDelayAttributeKvEntry = alarmDelayAttributeKvEntity.toData(); |
|
|
|
|
|
|
|
ListenableFuture<Optional<AttributeKvEntry>> optionalDurationAttribute = |
|
|
|
Futures.immediateFuture(Optional.of(alarmDelayAttributeKvEntry)); |
|
|
|
ListenableFuture<List<AttributeKvEntry>> listNoDurationAttribute = |
|
|
|
Futures.immediateFuture(Collections.singletonList(entry)); |
|
|
|
ListenableFuture<Optional<AttributeKvEntry>> emptyOptional = |
|
|
|
Futures.immediateFuture(Optional.empty()); |
|
|
|
|
|
|
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); |
|
|
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); |
|
|
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC); |
|
|
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); |
|
|
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
|
|
|
highTemperaturePredicate.setValue(new FilterPredicateValue<>( |
|
|
|
0.0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "greaterAttribute", false) |
|
|
|
)); |
|
|
|
highTempFilter.setPredicate(highTemperaturePredicate); |
|
|
|
AlarmCondition alarmCondition = new AlarmCondition(); |
|
|
|
alarmCondition.setCondition(Collections.singletonList(highTempFilter)); |
|
|
|
|
|
|
|
FilterPredicateValue<Long> filterPredicateValue = new FilterPredicateValue<>( |
|
|
|
10L, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarm_delay", true) |
|
|
|
); |
|
|
|
|
|
|
|
DurationAlarmConditionSpec durationSpec = new DurationAlarmConditionSpec(); |
|
|
|
durationSpec.setUnit(TimeUnit.SECONDS); |
|
|
|
durationSpec.setPredicate(filterPredicateValue); |
|
|
|
alarmCondition.setSpec(durationSpec); |
|
|
|
|
|
|
|
AlarmRule alarmRule = new AlarmRule(); |
|
|
|
alarmRule.setCondition(alarmCondition); |
|
|
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
|
|
|
dpa.setId("highTemperatureAlarmID"); |
|
|
|
dpa.setAlarmType("highTemperatureAlarm"); |
|
|
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
|
|
|
|
|
|
|
deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
|
|
|
deviceProfile.setProfileData(deviceProfileData); |
|
|
|
|
|
|
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
|
|
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
|
|
|
.thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) |
|
|
|
.thenReturn(Futures.immediateFuture(null)); |
|
|
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
|
|
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) |
|
|
|
.thenReturn(optionalDurationAttribute); |
|
|
|
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) |
|
|
|
.thenReturn(device); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) |
|
|
|
.thenReturn(emptyOptional); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) |
|
|
|
.thenReturn(listNoDurationAttribute); |
|
|
|
|
|
|
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |
|
|
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) |
|
|
|
.thenReturn(theMsg); |
|
|
|
|
|
|
|
ObjectNode data = mapper.createObjectNode(); |
|
|
|
data.put("temperature", 150); |
|
|
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
|
verify(ctx).tellSuccess(msg); |
|
|
|
int halfOfAlarmDelay = new BigDecimal(alarmDelayInSeconds) |
|
|
|
.multiply(BigDecimal.valueOf(1000)) |
|
|
|
.divide(BigDecimal.valueOf(2), 3, RoundingMode.HALF_EVEN) |
|
|
|
.intValueExact(); |
|
|
|
Thread.sleep(halfOfAlarmDelay); |
|
|
|
|
|
|
|
verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); |
|
|
|
|
|
|
|
Thread.sleep(halfOfAlarmDelay); |
|
|
|
|
|
|
|
TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg2); |
|
|
|
verify(ctx).tellSuccess(msg2); |
|
|
|
verify(ctx).tellNext(theMsg, "Alarm Created"); |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testCurrentDeviceAttributeForDynamicRepeatingValue() throws Exception { |
|
|
|
init(); |
|
|
|
|
|
|
|
DeviceProfile deviceProfile = new DeviceProfile(); |
|
|
|
deviceProfile.setId(deviceProfileId); |
|
|
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|
|
|
|
|
|
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "greaterAttribute" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); |
|
|
|
attributeKvEntity.setId(compositeKey); |
|
|
|
attributeKvEntity.setLongValue(30L); |
|
|
|
attributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvCompositeKey alarmDelayCompositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "alarm_delay" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity alarmDelayAttributeKvEntity = new AttributeKvEntity(); |
|
|
|
alarmDelayAttributeKvEntity.setId(alarmDelayCompositeKey); |
|
|
|
long alarmRepeating = 2; |
|
|
|
alarmDelayAttributeKvEntity.setLongValue(alarmRepeating); |
|
|
|
alarmDelayAttributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvEntry entry = attributeKvEntity.toData(); |
|
|
|
|
|
|
|
AttributeKvEntry alarmDelayAttributeKvEntry = alarmDelayAttributeKvEntity.toData(); |
|
|
|
|
|
|
|
ListenableFuture<List<AttributeKvEntry>> listListenableFuture = |
|
|
|
Futures.immediateFuture(Arrays.asList(entry, alarmDelayAttributeKvEntry)); |
|
|
|
|
|
|
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); |
|
|
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); |
|
|
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC); |
|
|
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); |
|
|
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
|
|
|
highTemperaturePredicate.setValue(new FilterPredicateValue<>( |
|
|
|
0.0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "greaterAttribute", false) |
|
|
|
)); |
|
|
|
highTempFilter.setPredicate(highTemperaturePredicate); |
|
|
|
AlarmCondition alarmCondition = new AlarmCondition(); |
|
|
|
alarmCondition.setCondition(Collections.singletonList(highTempFilter)); |
|
|
|
|
|
|
|
FilterPredicateValue<Integer> filterPredicateValue = new FilterPredicateValue<>( |
|
|
|
10, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarm_delay", false) |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
RepeatingAlarmConditionSpec repeatingSpec = new RepeatingAlarmConditionSpec(); |
|
|
|
repeatingSpec.setPredicate(filterPredicateValue); |
|
|
|
alarmCondition.setSpec(repeatingSpec); |
|
|
|
|
|
|
|
AlarmRule alarmRule = new AlarmRule(); |
|
|
|
alarmRule.setCondition(alarmCondition); |
|
|
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
|
|
|
dpa.setId("highTemperatureAlarmID"); |
|
|
|
dpa.setAlarmType("highTemperatureAlarm"); |
|
|
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
|
|
|
|
|
|
|
deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
|
|
|
deviceProfile.setProfileData(deviceProfileData); |
|
|
|
|
|
|
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
|
|
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
|
|
|
.thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) |
|
|
|
.thenReturn(Futures.immediateFuture(null)); |
|
|
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
|
|
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) |
|
|
|
.thenReturn(listListenableFuture); |
|
|
|
|
|
|
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |
|
|
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) |
|
|
|
.thenReturn(theMsg); |
|
|
|
|
|
|
|
ObjectNode data = mapper.createObjectNode(); |
|
|
|
data.put("temperature", 150); |
|
|
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
|
verify(ctx).tellSuccess(msg); |
|
|
|
|
|
|
|
verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); |
|
|
|
|
|
|
|
TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg2); |
|
|
|
verify(ctx).tellSuccess(msg2); |
|
|
|
verify(ctx).tellNext(theMsg, "Alarm Created"); |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testInheritTenantAttributeForRepeating() throws Exception { |
|
|
|
init(); |
|
|
|
|
|
|
|
DeviceProfile deviceProfile = new DeviceProfile(); |
|
|
|
deviceProfile.setId(deviceProfileId); |
|
|
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|
|
|
|
|
|
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "greaterAttribute" |
|
|
|
); |
|
|
|
|
|
|
|
Device device = new Device(); |
|
|
|
device.setId(deviceId); |
|
|
|
device.setCustomerId(customerId); |
|
|
|
|
|
|
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); |
|
|
|
attributeKvEntity.setId(compositeKey); |
|
|
|
attributeKvEntity.setLongValue(30L); |
|
|
|
attributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvCompositeKey alarmDelayCompositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "alarm_delay" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity alarmDelayAttributeKvEntity = new AttributeKvEntity(); |
|
|
|
alarmDelayAttributeKvEntity.setId(alarmDelayCompositeKey); |
|
|
|
long repeatingCondition = 2; |
|
|
|
alarmDelayAttributeKvEntity.setLongValue(repeatingCondition); |
|
|
|
alarmDelayAttributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvEntry entry = attributeKvEntity.toData(); |
|
|
|
|
|
|
|
AttributeKvEntry alarmDelayAttributeKvEntry = alarmDelayAttributeKvEntity.toData(); |
|
|
|
|
|
|
|
ListenableFuture<Optional<AttributeKvEntry>> optionalDurationAttribute = |
|
|
|
Futures.immediateFuture(Optional.of(alarmDelayAttributeKvEntry)); |
|
|
|
ListenableFuture<List<AttributeKvEntry>> listNoDurationAttribute = |
|
|
|
Futures.immediateFuture(Collections.singletonList(entry)); |
|
|
|
ListenableFuture<Optional<AttributeKvEntry>> emptyOptional = |
|
|
|
Futures.immediateFuture(Optional.empty()); |
|
|
|
|
|
|
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); |
|
|
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); |
|
|
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC); |
|
|
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); |
|
|
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
|
|
|
highTemperaturePredicate.setValue(new FilterPredicateValue<>( |
|
|
|
0.0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "greaterAttribute", false) |
|
|
|
)); |
|
|
|
highTempFilter.setPredicate(highTemperaturePredicate); |
|
|
|
AlarmCondition alarmCondition = new AlarmCondition(); |
|
|
|
alarmCondition.setCondition(Collections.singletonList(highTempFilter)); |
|
|
|
|
|
|
|
FilterPredicateValue<Integer> filterPredicateValue = new FilterPredicateValue<>( |
|
|
|
10, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarm_delay", true) |
|
|
|
); |
|
|
|
|
|
|
|
RepeatingAlarmConditionSpec repeatingSpec = new RepeatingAlarmConditionSpec(); |
|
|
|
repeatingSpec.setPredicate(filterPredicateValue); |
|
|
|
alarmCondition.setSpec(repeatingSpec); |
|
|
|
|
|
|
|
AlarmRule alarmRule = new AlarmRule(); |
|
|
|
alarmRule.setCondition(alarmCondition); |
|
|
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
|
|
|
dpa.setId("highTemperatureAlarmID"); |
|
|
|
dpa.setAlarmType("highTemperatureAlarm"); |
|
|
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
|
|
|
|
|
|
|
deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
|
|
|
deviceProfile.setProfileData(deviceProfileData); |
|
|
|
|
|
|
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
|
|
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
|
|
|
.thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) |
|
|
|
.thenReturn(Futures.immediateFuture(null)); |
|
|
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
|
|
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) |
|
|
|
.thenReturn(optionalDurationAttribute); |
|
|
|
Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) |
|
|
|
.thenReturn(device); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(customerId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) |
|
|
|
.thenReturn(emptyOptional); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) |
|
|
|
.thenReturn(listNoDurationAttribute); |
|
|
|
|
|
|
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |
|
|
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) |
|
|
|
.thenReturn(theMsg); |
|
|
|
|
|
|
|
ObjectNode data = mapper.createObjectNode(); |
|
|
|
data.put("temperature", 150); |
|
|
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
|
verify(ctx).tellSuccess(msg); |
|
|
|
|
|
|
|
verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); |
|
|
|
|
|
|
|
TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg2); |
|
|
|
verify(ctx).tellSuccess(msg2); |
|
|
|
verify(ctx).tellNext(theMsg, "Alarm Created"); |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testCurrentDeviceAttributeForUseDefaultDurationWhenDynamicDurationValueIsNull() throws Exception { |
|
|
|
init(); |
|
|
|
|
|
|
|
long alarmDelayInSeconds = 5; |
|
|
|
DeviceProfile deviceProfile = new DeviceProfile(); |
|
|
|
deviceProfile.setId(deviceProfileId); |
|
|
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|
|
|
|
|
|
|
Device device = new Device(); |
|
|
|
device.setId(deviceId); |
|
|
|
device.setCustomerId(customerId); |
|
|
|
|
|
|
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "greaterAttribute" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); |
|
|
|
attributeKvEntity.setId(compositeKey); |
|
|
|
attributeKvEntity.setLongValue(30L); |
|
|
|
attributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvEntry entry = attributeKvEntity.toData(); |
|
|
|
|
|
|
|
ListenableFuture<List<AttributeKvEntry>> listListenableFuture = |
|
|
|
Futures.immediateFuture(Collections.singletonList(entry)); |
|
|
|
|
|
|
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); |
|
|
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); |
|
|
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC); |
|
|
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); |
|
|
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
|
|
|
highTemperaturePredicate.setValue(new FilterPredicateValue<>( |
|
|
|
0.0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "greaterAttribute") |
|
|
|
)); |
|
|
|
highTempFilter.setPredicate(highTemperaturePredicate); |
|
|
|
AlarmCondition alarmCondition = new AlarmCondition(); |
|
|
|
alarmCondition.setCondition(Collections.singletonList(highTempFilter)); |
|
|
|
|
|
|
|
FilterPredicateValue<Long> filterPredicateValue = new FilterPredicateValue<>( |
|
|
|
alarmDelayInSeconds, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, null, false) |
|
|
|
); |
|
|
|
|
|
|
|
DurationAlarmConditionSpec durationSpec = new DurationAlarmConditionSpec(); |
|
|
|
durationSpec.setUnit(TimeUnit.SECONDS); |
|
|
|
durationSpec.setPredicate(filterPredicateValue); |
|
|
|
alarmCondition.setSpec(durationSpec); |
|
|
|
|
|
|
|
AlarmRule alarmRule = new AlarmRule(); |
|
|
|
alarmRule.setCondition(alarmCondition); |
|
|
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
|
|
|
dpa.setId("highTemperatureAlarmID"); |
|
|
|
dpa.setAlarmType("highTemperatureAlarm"); |
|
|
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
|
|
|
|
|
|
|
deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
|
|
|
deviceProfile.setProfileData(deviceProfileData); |
|
|
|
|
|
|
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
|
|
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
|
|
|
.thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) |
|
|
|
.thenReturn(Futures.immediateFuture(null)); |
|
|
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
|
|
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) |
|
|
|
.thenReturn(listListenableFuture); |
|
|
|
|
|
|
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |
|
|
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) |
|
|
|
.thenReturn(theMsg); |
|
|
|
|
|
|
|
ObjectNode data = mapper.createObjectNode(); |
|
|
|
data.put("temperature", 35); |
|
|
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
|
verify(ctx).tellSuccess(msg); |
|
|
|
int halfOfAlarmDelay = new BigDecimal(alarmDelayInSeconds) |
|
|
|
.multiply(BigDecimal.valueOf(1000)) |
|
|
|
.divide(BigDecimal.valueOf(2), 3, RoundingMode.HALF_EVEN) |
|
|
|
.intValueExact(); |
|
|
|
Thread.sleep(halfOfAlarmDelay); |
|
|
|
|
|
|
|
verify(ctx, Mockito.never()).tellNext(theMsg, "Alarm Created"); |
|
|
|
|
|
|
|
Thread.sleep(halfOfAlarmDelay); |
|
|
|
|
|
|
|
TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg2); |
|
|
|
verify(ctx).tellSuccess(msg2); |
|
|
|
verify(ctx).tellNext(theMsg, "Alarm Created"); |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testCurrentDeviceAttributeForUseDefaultRepeatingWhenDynamicDurationValueIsNull() throws Exception { |
|
|
|
init(); |
|
|
|
|
|
|
|
DeviceProfile deviceProfile = new DeviceProfile(); |
|
|
|
deviceProfile.setId(deviceProfileId); |
|
|
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
|
|
|
|
|
|
|
Device device = new Device(); |
|
|
|
device.setId(deviceId); |
|
|
|
device.setCustomerId(customerId); |
|
|
|
|
|
|
|
AttributeKvCompositeKey compositeKey = new AttributeKvCompositeKey( |
|
|
|
EntityType.TENANT, deviceId.getId(), "SERVER_SCOPE", "greaterAttribute" |
|
|
|
); |
|
|
|
|
|
|
|
AttributeKvEntity attributeKvEntity = new AttributeKvEntity(); |
|
|
|
attributeKvEntity.setId(compositeKey); |
|
|
|
attributeKvEntity.setLongValue(30L); |
|
|
|
attributeKvEntity.setLastUpdateTs(0L); |
|
|
|
|
|
|
|
AttributeKvEntry entry = attributeKvEntity.toData(); |
|
|
|
|
|
|
|
ListenableFuture<List<AttributeKvEntry>> listListenableFuture = |
|
|
|
Futures.immediateFuture(Collections.singletonList(entry)); |
|
|
|
|
|
|
|
AlarmConditionFilter highTempFilter = new AlarmConditionFilter(); |
|
|
|
highTempFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); |
|
|
|
highTempFilter.setValueType(EntityKeyValueType.NUMERIC); |
|
|
|
NumericFilterPredicate highTemperaturePredicate = new NumericFilterPredicate(); |
|
|
|
highTemperaturePredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
|
|
|
highTemperaturePredicate.setValue(new FilterPredicateValue<>( |
|
|
|
0.0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "greaterAttribute") |
|
|
|
)); |
|
|
|
highTempFilter.setPredicate(highTemperaturePredicate); |
|
|
|
AlarmCondition alarmCondition = new AlarmCondition(); |
|
|
|
alarmCondition.setCondition(Collections.singletonList(highTempFilter)); |
|
|
|
|
|
|
|
RepeatingAlarmConditionSpec repeating = new RepeatingAlarmConditionSpec(); |
|
|
|
repeating.setPredicate(new FilterPredicateValue<>( |
|
|
|
0, |
|
|
|
null, |
|
|
|
new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "alarm_rule", false) |
|
|
|
)); |
|
|
|
alarmCondition.setSpec(repeating); |
|
|
|
|
|
|
|
AlarmRule alarmRule = new AlarmRule(); |
|
|
|
alarmRule.setCondition(alarmCondition); |
|
|
|
DeviceProfileAlarm dpa = new DeviceProfileAlarm(); |
|
|
|
dpa.setId("highTemperatureAlarmID"); |
|
|
|
dpa.setAlarmType("highTemperatureAlarm"); |
|
|
|
dpa.setCreateRules(new TreeMap<>(Collections.singletonMap(AlarmSeverity.CRITICAL, alarmRule))); |
|
|
|
|
|
|
|
deviceProfileData.setAlarms(Collections.singletonList(dpa)); |
|
|
|
deviceProfile.setProfileData(deviceProfileData); |
|
|
|
|
|
|
|
Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); |
|
|
|
Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) |
|
|
|
.thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) |
|
|
|
.thenReturn(Futures.immediateFuture(null)); |
|
|
|
Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); |
|
|
|
Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); |
|
|
|
Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) |
|
|
|
.thenReturn(listListenableFuture); |
|
|
|
|
|
|
|
TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); |
|
|
|
Mockito.when(ctx.newMsg(Mockito.anyString(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())) |
|
|
|
.thenReturn(theMsg); |
|
|
|
|
|
|
|
ObjectNode data = mapper.createObjectNode(); |
|
|
|
data.put("temperature", 35); |
|
|
|
TbMsg msg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), |
|
|
|
TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); |
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
|
verify(ctx).tellSuccess(msg); |
|
|
|
verify(ctx).tellNext(theMsg, "Alarm Created"); |
|
|
|
verify(ctx, Mockito.never()).tellFailure(Mockito.any(), Mockito.any()); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Test |
|
|
|
public void testCurrentCustomersAttributeForDynamicValue() throws Exception { |
|
|
|
init(); |
|
|
|
|