Merge pull request #6535 from ViacheslavKlimov/fix/alarms-issues
[3.4] Fixes for handling alarm deletion and attributes deletion in DeviceState
This commit is contained in:
commit
d13575a9e9
@ -197,7 +197,8 @@ class DeviceState {
|
||||
|
||||
private void processAlarmDeleteNotification(TbContext ctx, TbMsg msg) {
|
||||
Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class);
|
||||
alarmStates.values().removeIf(alarmState -> alarmState.getCurrentAlarm().getId().equals(alarm.getId()));
|
||||
alarmStates.values().removeIf(alarmState -> alarmState.getCurrentAlarm() != null
|
||||
&& alarmState.getCurrentAlarm().getId().equals(alarm.getId()));
|
||||
ctx.tellSuccess(msg);
|
||||
}
|
||||
|
||||
@ -219,11 +220,15 @@ class DeviceState {
|
||||
}
|
||||
if (!keys.isEmpty()) {
|
||||
EntityKeyType keyType = getKeyTypeFromScope(scope);
|
||||
keys.forEach(key -> latestValues.removeValue(new EntityKey(keyType, key)));
|
||||
Set<AlarmConditionFilterKey> removedKeys = keys.stream().map(key -> new EntityKey(keyType, key))
|
||||
.peek(latestValues::removeValue)
|
||||
.map(DataSnapshot::toConditionKey).collect(Collectors.toSet());
|
||||
SnapshotUpdate update = new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, removedKeys);
|
||||
|
||||
for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) {
|
||||
AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(),
|
||||
a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx));
|
||||
stateChanged |= alarmState.process(ctx, msg, latestValues, null);
|
||||
stateChanged |= alarmState.process(ctx, msg, latestValues, update);
|
||||
}
|
||||
}
|
||||
ctx.tellSuccess(msg);
|
||||
|
||||
@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Copyright © 2016-2022 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.rule.engine.profile;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||
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.AlarmConditionFilter;
|
||||
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
|
||||
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.SimpleAlarmConditionSpec;
|
||||
import org.thingsboard.server.common.data.id.AlarmId;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
|
||||
import org.thingsboard.server.common.data.query.EntityKeyValueType;
|
||||
import org.thingsboard.server.common.data.query.FilterPredicateValue;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||
import org.thingsboard.server.common.msg.session.SessionMsgType;
|
||||
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||
import org.thingsboard.server.dao.device.DeviceService;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyCollection;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class DeviceStateTest {
|
||||
|
||||
private TbContext ctx;
|
||||
|
||||
@Before
|
||||
public void beforeEach() {
|
||||
ctx = mock(TbContext.class);
|
||||
|
||||
when(ctx.getDeviceService()).thenReturn(mock(DeviceService.class));
|
||||
|
||||
AttributesService attributesService = mock(AttributesService.class);
|
||||
when(attributesService.find(any(), any(), any(), anyCollection())).thenReturn(Futures.immediateFuture(Collections.emptyList()));
|
||||
when(ctx.getAttributesService()).thenReturn(attributesService);
|
||||
|
||||
RuleEngineAlarmService alarmService = mock(RuleEngineAlarmService.class);
|
||||
when(alarmService.findLatestByOriginatorAndType(any(), any(), any())).thenReturn(Futures.immediateFuture(null));
|
||||
when(alarmService.createOrUpdateAlarm(any())).thenAnswer(invocationOnMock -> {
|
||||
Alarm alarm = invocationOnMock.getArgument(0);
|
||||
alarm.setId(new AlarmId(UUID.randomUUID()));
|
||||
return alarm;
|
||||
});
|
||||
when(ctx.getAlarmService()).thenReturn(alarmService);
|
||||
|
||||
when(ctx.newMsg(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> {
|
||||
String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1);
|
||||
return TbMsg.newMsg(null, null, new TbMsgMetaData(), data);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() throws Exception {
|
||||
|
||||
DeviceProfileAlarm alarmConfig = createAlarmConfigWithBoolAttrCondition("enabled", false);
|
||||
DeviceId deviceId = new DeviceId(UUID.randomUUID());
|
||||
DeviceState deviceState = createDeviceState(deviceId, alarmConfig);
|
||||
|
||||
TbMsg attributeUpdateMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(),
|
||||
deviceId, new TbMsgMetaData(), "{ \"enabled\": false }");
|
||||
|
||||
deviceState.process(ctx, attributeUpdateMsg);
|
||||
|
||||
ArgumentCaptor<TbMsg> resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
||||
verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created"));
|
||||
Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class);
|
||||
|
||||
deviceState.process(ctx, TbMsg.newMsg(DataConstants.ALARM_CLEAR, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm)));
|
||||
reset(ctx);
|
||||
|
||||
String deletedAttributes = "{ \"attributes\": [ \"other\" ] }";
|
||||
deviceState.process(ctx, TbMsg.newMsg(DataConstants.ATTRIBUTES_DELETED, deviceId, new TbMsgMetaData(), deletedAttributes));
|
||||
verify(ctx, never()).enqueueForTellNext(any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDeletingClearedAlarm_thenNoError() throws Exception {
|
||||
DeviceProfileAlarm alarmConfig = createAlarmConfigWithBoolAttrCondition("enabled", false);
|
||||
DeviceId deviceId = new DeviceId(UUID.randomUUID());
|
||||
DeviceState deviceState = createDeviceState(deviceId, alarmConfig);
|
||||
|
||||
TbMsg attributeUpdateMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(),
|
||||
deviceId, new TbMsgMetaData(), "{ \"enabled\": false }");
|
||||
|
||||
deviceState.process(ctx, attributeUpdateMsg);
|
||||
ArgumentCaptor<TbMsg> resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
||||
verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created"));
|
||||
Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class);
|
||||
|
||||
deviceState.process(ctx, TbMsg.newMsg(DataConstants.ALARM_CLEAR, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm)));
|
||||
|
||||
TbMsg alarmDeleteNotification = TbMsg.newMsg(DataConstants.ALARM_DELETE, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm));
|
||||
assertDoesNotThrow(() -> {
|
||||
deviceState.process(ctx, alarmDeleteNotification);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private DeviceState createDeviceState(DeviceId deviceId, DeviceProfileAlarm... alarmConfigs) {
|
||||
DeviceProfile deviceProfile = new DeviceProfile();
|
||||
DeviceProfileData profileData = new DeviceProfileData();
|
||||
profileData.setAlarms(List.of(alarmConfigs));
|
||||
deviceProfile.setProfileData(profileData);
|
||||
|
||||
ProfileState profileState = new ProfileState(deviceProfile);
|
||||
return new DeviceState(ctx, new TbDeviceProfileNodeConfiguration(),
|
||||
deviceId, profileState, null);
|
||||
}
|
||||
|
||||
private DeviceProfileAlarm createAlarmConfigWithBoolAttrCondition(String key, boolean value) {
|
||||
|
||||
AlarmConditionFilter condition = new AlarmConditionFilter();
|
||||
condition.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, key));
|
||||
condition.setValueType(EntityKeyValueType.BOOLEAN);
|
||||
BooleanFilterPredicate predicate = new BooleanFilterPredicate();
|
||||
predicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
|
||||
predicate.setValue(new FilterPredicateValue<>(value));
|
||||
condition.setPredicate(predicate);
|
||||
|
||||
DeviceProfileAlarm alarmConfig = new DeviceProfileAlarm();
|
||||
alarmConfig.setId("MyAlarmID");
|
||||
alarmConfig.setAlarmType("MyAlarm");
|
||||
AlarmRule alarmRule = new AlarmRule();
|
||||
AlarmCondition alarmCondition = new AlarmCondition();
|
||||
alarmCondition.setSpec(new SimpleAlarmConditionSpec());
|
||||
alarmCondition.setCondition(List.of(condition));
|
||||
alarmRule.setCondition(alarmCondition);
|
||||
alarmConfig.setCreateRules(new TreeMap<>(Map.of(AlarmSeverity.CRITICAL, alarmRule)));
|
||||
|
||||
return alarmConfig;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user