Commit 62caeefbf1452e323bd40f791c52ba7e55b7d139

Authored by Volodymyr Babak
2 parents 90e0c44e 5f9b9cb8

Merge remote-tracking branch 'upstream/master' into sql-dao-improvements

Showing 18 changed files with 507 additions and 91 deletions
@@ -17,16 +17,14 @@ @@ -17,16 +17,14 @@
17 package org.thingsboard.server; 17 package org.thingsboard.server;
18 18
19 import org.springframework.boot.SpringApplication; 19 import org.springframework.boot.SpringApplication;
20 -import org.springframework.boot.autoconfigure.EnableAutoConfiguration;  
21 -import org.springframework.boot.autoconfigure.SpringBootApplication; 20 +import org.springframework.boot.SpringBootConfiguration;
22 import org.springframework.context.ConfigurableApplicationContext; 21 import org.springframework.context.ConfigurableApplicationContext;
23 import org.springframework.context.annotation.ComponentScan; 22 import org.springframework.context.annotation.ComponentScan;
24 import org.thingsboard.server.install.ThingsboardInstallService; 23 import org.thingsboard.server.install.ThingsboardInstallService;
25 24
26 import java.util.Arrays; 25 import java.util.Arrays;
27 26
28 -@EnableAutoConfiguration  
29 -@SpringBootApplication 27 +@SpringBootConfiguration
30 @ComponentScan({"org.thingsboard.server.install", 28 @ComponentScan({"org.thingsboard.server.install",
31 "org.thingsboard.server.service.component", 29 "org.thingsboard.server.service.component",
32 "org.thingsboard.server.service.install", 30 "org.thingsboard.server.service.install",
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
37 import org.thingsboard.server.common.msg.cluster.ServerAddress; 37 import org.thingsboard.server.common.msg.cluster.ServerAddress;
38 import org.thingsboard.server.common.transport.auth.DeviceAuthService; 38 import org.thingsboard.server.common.transport.auth.DeviceAuthService;
39 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint; 39 import org.thingsboard.server.controller.plugin.PluginWebSocketMsgEndpoint;
  40 +import org.thingsboard.server.dao.alarm.AlarmService;
40 import org.thingsboard.server.dao.asset.AssetService; 41 import org.thingsboard.server.dao.asset.AssetService;
41 import org.thingsboard.server.dao.attributes.AttributesService; 42 import org.thingsboard.server.dao.attributes.AttributesService;
42 import org.thingsboard.server.dao.customer.CustomerService; 43 import org.thingsboard.server.dao.customer.CustomerService;
@@ -106,6 +107,9 @@ public class ActorSystemContext { @@ -106,6 +107,9 @@ public class ActorSystemContext {
106 @Getter private EventService eventService; 107 @Getter private EventService eventService;
107 108
108 @Autowired 109 @Autowired
  110 + @Getter private AlarmService alarmService;
  111 +
  112 + @Autowired
109 @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint; 113 @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint;
110 114
111 @Value("${actors.session.sync.timeout}") 115 @Value("${actors.session.sync.timeout}")
@@ -15,22 +15,27 @@ @@ -15,22 +15,27 @@
15 */ 15 */
16 package org.thingsboard.server.actors.rule; 16 package org.thingsboard.server.actors.rule;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 import org.thingsboard.server.actors.ActorSystemContext; 19 import org.thingsboard.server.actors.ActorSystemContext;
19 import org.thingsboard.server.common.data.Event; 20 import org.thingsboard.server.common.data.Event;
  21 +import org.thingsboard.server.common.data.alarm.Alarm;
  22 +import org.thingsboard.server.common.data.alarm.AlarmId;
20 import org.thingsboard.server.common.data.id.*; 23 import org.thingsboard.server.common.data.id.*;
  24 +import org.thingsboard.server.dao.alarm.AlarmService;
21 import org.thingsboard.server.dao.event.EventService; 25 import org.thingsboard.server.dao.event.EventService;
22 import org.thingsboard.server.dao.timeseries.TimeseriesService; 26 import org.thingsboard.server.dao.timeseries.TimeseriesService;
23 -import org.thingsboard.server.extensions.api.device.DeviceAttributes;  
24 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 27 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
25 import org.thingsboard.server.extensions.api.device.DeviceMetaData; 28 import org.thingsboard.server.extensions.api.device.DeviceMetaData;
26 import org.thingsboard.server.extensions.api.rules.RuleContext; 29 import org.thingsboard.server.extensions.api.rules.RuleContext;
27 30
28 import java.util.Optional; 31 import java.util.Optional;
  32 +import java.util.concurrent.ExecutionException;
29 33
30 public class RuleProcessingContext implements RuleContext { 34 public class RuleProcessingContext implements RuleContext {
31 35
32 private final TimeseriesService tsService; 36 private final TimeseriesService tsService;
33 private final EventService eventService; 37 private final EventService eventService;
  38 + private final AlarmService alarmService;
34 private final RuleId ruleId; 39 private final RuleId ruleId;
35 private TenantId tenantId; 40 private TenantId tenantId;
36 private CustomerId customerId; 41 private CustomerId customerId;
@@ -40,6 +45,7 @@ public class RuleProcessingContext implements RuleContext { @@ -40,6 +45,7 @@ public class RuleProcessingContext implements RuleContext {
40 RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) { 45 RuleProcessingContext(ActorSystemContext systemContext, RuleId ruleId) {
41 this.tsService = systemContext.getTsService(); 46 this.tsService = systemContext.getTsService();
42 this.eventService = systemContext.getEventService(); 47 this.eventService = systemContext.getEventService();
  48 + this.alarmService = systemContext.getAlarmService();
43 this.ruleId = ruleId; 49 this.ruleId = ruleId;
44 } 50 }
45 51
@@ -77,6 +83,25 @@ public class RuleProcessingContext implements RuleContext { @@ -77,6 +83,25 @@ public class RuleProcessingContext implements RuleContext {
77 return eventService.findEvent(tenantId, deviceId, eventType, eventUid); 83 return eventService.findEvent(tenantId, deviceId, eventType, eventUid);
78 } 84 }
79 85
  86 + @Override
  87 + public Alarm createOrUpdateAlarm(Alarm alarm) {
  88 + alarm.setTenantId(tenantId);
  89 + return alarmService.createOrUpdateAlarm(alarm);
  90 + }
  91 +
  92 + public Optional<Alarm> findLatestAlarm(EntityId originator, String alarmType) {
  93 + try {
  94 + return Optional.ofNullable(alarmService.findLatestByOriginatorAndType(tenantId, originator, alarmType).get());
  95 + } catch (InterruptedException | ExecutionException e) {
  96 + throw new RuntimeException("Failed to lookup alarm!", e);
  97 + }
  98 + }
  99 +
  100 + @Override
  101 + public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long clearTs) {
  102 + return alarmService.clearAlarm(alarmId, clearTs);
  103 + }
  104 +
80 private void checkEvent(Event event) { 105 private void checkEvent(Event event) {
81 if (event.getTenantId() == null) { 106 if (event.getTenantId() == null) {
82 event.setTenantId(tenantId); 107 event.setTenantId(tenantId);
@@ -20,10 +20,12 @@ import lombok.extern.slf4j.Slf4j; @@ -20,10 +20,12 @@ import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.context.annotation.Profile; 21 import org.springframework.context.annotation.Profile;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.server.dao.util.SqlDao;
23 24
24 @Service 25 @Service
25 @Profile("install") 26 @Profile("install")
26 @Slf4j 27 @Slf4j
  28 +@SqlDao
27 public class SqlDatabaseSchemaService implements DatabaseSchemaService { 29 public class SqlDatabaseSchemaService implements DatabaseSchemaService {
28 30
29 @Value("${install.data_dir}") 31 @Value("${install.data_dir}")
@@ -110,7 +110,6 @@ public class EntityRelation { @@ -110,7 +110,6 @@ public class EntityRelation {
110 if (to != null ? !to.equals(that.to) : that.to != null) return false; 110 if (to != null ? !to.equals(that.to) : that.to != null) return false;
111 if (type != null ? !type.equals(that.type) : that.type != null) return false; 111 if (type != null ? !type.equals(that.type) : that.type != null) return false;
112 return typeGroup == that.typeGroup; 112 return typeGroup == that.typeGroup;
113 -  
114 } 113 }
115 114
116 @Override 115 @Override
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm; @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm;
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.alarm.*; 19 import org.thingsboard.server.common.data.alarm.*;
20 import org.thingsboard.server.common.data.id.EntityId; 20 import org.thingsboard.server.common.data.id.EntityId;
  21 +import org.thingsboard.server.common.data.id.TenantId;
21 import org.thingsboard.server.common.data.page.TimePageData; 22 import org.thingsboard.server.common.data.page.TimePageData;
22 23
23 /** 24 /**
@@ -40,4 +41,6 @@ public interface AlarmService { @@ -40,4 +41,6 @@ public interface AlarmService {
40 AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus, 41 AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus,
41 AlarmStatus alarmStatus); 42 AlarmStatus alarmStatus);
42 43
  44 + ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
  45 +
43 } 46 }
@@ -27,6 +27,7 @@ import org.springframework.util.StringUtils; @@ -27,6 +27,7 @@ import org.springframework.util.StringUtils;
27 import org.thingsboard.server.common.data.Tenant; 27 import org.thingsboard.server.common.data.Tenant;
28 import org.thingsboard.server.common.data.alarm.*; 28 import org.thingsboard.server.common.data.alarm.*;
29 import org.thingsboard.server.common.data.id.EntityId; 29 import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.page.TimePageData; 31 import org.thingsboard.server.common.data.page.TimePageData;
31 import org.thingsboard.server.common.data.page.TimePageLink; 32 import org.thingsboard.server.common.data.page.TimePageLink;
32 import org.thingsboard.server.common.data.relation.EntityRelation; 33 import org.thingsboard.server.common.data.relation.EntityRelation;
@@ -111,6 +112,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -111,6 +112,10 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
111 } 112 }
112 } 113 }
113 114
  115 + public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
  116 + return alarmDao.findLatestByOriginatorAndType(tenantId, originator, type);
  117 + }
  118 +
114 private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { 119 private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
115 log.debug("New Alarm : {}", alarm); 120 log.debug("New Alarm : {}", alarm);
116 Alarm saved = alarmDao.save(alarm); 121 Alarm saved = alarmDao.save(alarm);
@@ -204,15 +209,15 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -204,15 +209,15 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
204 validateId(alarmId, "Incorrect alarmId " + alarmId); 209 validateId(alarmId, "Incorrect alarmId " + alarmId);
205 return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()), 210 return Futures.transform(alarmDao.findAlarmByIdAsync(alarmId.getId()),
206 (AsyncFunction<Alarm, AlarmInfo>) alarm1 -> { 211 (AsyncFunction<Alarm, AlarmInfo>) alarm1 -> {
207 - AlarmInfo alarmInfo = new AlarmInfo(alarm1);  
208 - return Futures.transform(  
209 - entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)  
210 - originatorName -> {  
211 - alarmInfo.setOriginatorName(originatorName);  
212 - return alarmInfo;  
213 - }  
214 - );  
215 - }); 212 + AlarmInfo alarmInfo = new AlarmInfo(alarm1);
  213 + return Futures.transform(
  214 + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
  215 + originatorName -> {
  216 + alarmInfo.setOriginatorName(originatorName);
  217 + return alarmInfo;
  218 + }
  219 + );
  220 + });
216 } 221 }
217 222
218 @Override 223 @Override
@@ -234,7 +239,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -234,7 +239,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
234 )); 239 ));
235 } 240 }
236 return Futures.successfulAsList(alarmFutures); 241 return Futures.successfulAsList(alarmFutures);
237 - }); 242 + });
238 } 243 }
239 return Futures.transform(alarms, new Function<List<AlarmInfo>, TimePageData<AlarmInfo>>() { 244 return Futures.transform(alarms, new Function<List<AlarmInfo>, TimePageData<AlarmInfo>>() {
240 @Nullable 245 @Nullable
@@ -247,7 +252,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -247,7 +252,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
247 252
248 @Override 253 @Override
249 public AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus, 254 public AlarmSeverity findHighestAlarmSeverity(EntityId entityId, AlarmSearchStatus alarmSearchStatus,
250 - AlarmStatus alarmStatus) { 255 + AlarmStatus alarmStatus) {
251 TimePageLink nextPageLink = new TimePageLink(100); 256 TimePageLink nextPageLink = new TimePageLink(100);
252 boolean hasNext = true; 257 boolean hasNext = true;
253 AlarmSeverity highestSeverity = null; 258 AlarmSeverity highestSeverity = null;
@@ -321,7 +326,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -321,7 +326,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
321 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); 326 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
322 for (EntityId parentId : parentEntities) { 327 for (EntityId parentId : parentEntities) {
323 updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus); 328 updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus);
324 - } 329 + }
325 updateAlarmRelation(alarm.getOriginator(), alarm.getId(), oldStatus, newStatus); 330 updateAlarmRelation(alarm.getOriginator(), alarm.getId(), oldStatus, newStatus);
326 } catch (ExecutionException | InterruptedException e) { 331 } catch (ExecutionException | InterruptedException e) {
327 log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus); 332 log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
@@ -15,9 +15,12 @@ @@ -15,9 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.extensions.api.rules; 16 package org.thingsboard.server.extensions.api.rules;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 import org.thingsboard.server.common.data.Event; 19 import org.thingsboard.server.common.data.Event;
  20 +import org.thingsboard.server.common.data.alarm.Alarm;
  21 +import org.thingsboard.server.common.data.alarm.AlarmId;
  22 +import org.thingsboard.server.common.data.id.EntityId;
19 import org.thingsboard.server.common.data.id.RuleId; 23 import org.thingsboard.server.common.data.id.RuleId;
20 -import org.thingsboard.server.extensions.api.device.DeviceAttributes;  
21 import org.thingsboard.server.extensions.api.device.DeviceMetaData; 24 import org.thingsboard.server.extensions.api.device.DeviceMetaData;
22 25
23 import java.util.Optional; 26 import java.util.Optional;
@@ -34,4 +37,9 @@ public interface RuleContext { @@ -34,4 +37,9 @@ public interface RuleContext {
34 37
35 Optional<Event> findEvent(String eventType, String eventUid); 38 Optional<Event> findEvent(String eventType, String eventUid);
36 39
  40 + Optional<Alarm> findLatestAlarm(EntityId originator, String alarmType);
  41 +
  42 + Alarm createOrUpdateAlarm(Alarm alarm);
  43 +
  44 + ListenableFuture<Boolean> clearAlarm(AlarmId id, long clearTs);
37 } 45 }
@@ -18,6 +18,8 @@ package org.thingsboard.server.extensions.api.rules; @@ -18,6 +18,8 @@ package org.thingsboard.server.extensions.api.rules;
18 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 18 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
19 import org.thingsboard.server.extensions.api.component.ConfigurableComponent; 19 import org.thingsboard.server.extensions.api.component.ConfigurableComponent;
20 20
  21 +import javax.script.ScriptException;
  22 +
21 /** 23 /**
22 * @author Andrew Shvayka 24 * @author Andrew Shvayka
23 */ 25 */
@@ -75,18 +75,4 @@ public abstract class BasicJsFilter implements RuleFilter<JsFilterConfiguration> @@ -75,18 +75,4 @@ public abstract class BasicJsFilter implements RuleFilter<JsFilterConfiguration>
75 } 75 }
76 } 76 }
77 77
78 - protected static Object getValue(KvEntry attr) {  
79 - switch (attr.getDataType()) {  
80 - case STRING:  
81 - return attr.getStrValue().get();  
82 - case LONG:  
83 - return attr.getLongValue().get();  
84 - case DOUBLE:  
85 - return attr.getDoubleValue().get();  
86 - case BOOLEAN:  
87 - return attr.getBooleanValue().get();  
88 - }  
89 - return null;  
90 - }  
91 -  
92 } 78 }
@@ -16,9 +16,6 @@ @@ -16,9 +16,6 @@
16 package org.thingsboard.server.extensions.core.filter; 16 package org.thingsboard.server.extensions.core.filter;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 -import org.slf4j.Logger;  
20 -import org.slf4j.LoggerFactory;  
21 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
22 import org.thingsboard.server.common.msg.core.UpdateAttributesRequest; 19 import org.thingsboard.server.common.msg.core.UpdateAttributesRequest;
23 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; 20 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
24 import org.thingsboard.server.common.msg.session.FromDeviceMsg; 21 import org.thingsboard.server.common.msg.session.FromDeviceMsg;
@@ -28,10 +25,6 @@ import org.thingsboard.server.extensions.api.rules.RuleContext; @@ -28,10 +25,6 @@ import org.thingsboard.server.extensions.api.rules.RuleContext;
28 25
29 import javax.script.Bindings; 26 import javax.script.Bindings;
30 import javax.script.ScriptException; 27 import javax.script.ScriptException;
31 -import javax.script.SimpleBindings;  
32 -import java.util.Collection;  
33 -import java.util.HashMap;  
34 -import java.util.Map;  
35 28
36 /** 29 /**
37 * @author Andrew Shvayka 30 * @author Andrew Shvayka
@@ -40,25 +33,18 @@ import java.util.Map; @@ -40,25 +33,18 @@ import java.util.Map;
40 @Slf4j 33 @Slf4j
41 public class DeviceAttributesFilter extends BasicJsFilter { 34 public class DeviceAttributesFilter extends BasicJsFilter {
42 35
43 - public static final String CLIENT_SIDE = "cs";  
44 - public static final String SERVER_SIDE = "ss";  
45 - public static final String SHARED = "shared";  
46 -  
47 @Override 36 @Override
48 protected boolean doFilter(RuleContext ctx, ToDeviceActorMsg msg) throws ScriptException { 37 protected boolean doFilter(RuleContext ctx, ToDeviceActorMsg msg) throws ScriptException {
49 return evaluator.execute(toBindings(ctx.getDeviceMetaData().getDeviceAttributes(), msg != null ? msg.getPayload() : null)); 38 return evaluator.execute(toBindings(ctx.getDeviceMetaData().getDeviceAttributes(), msg != null ? msg.getPayload() : null));
50 } 39 }
51 40
52 private Bindings toBindings(DeviceAttributes attributes, FromDeviceMsg msg) { 41 private Bindings toBindings(DeviceAttributes attributes, FromDeviceMsg msg) {
53 - Bindings bindings = new SimpleBindings();  
54 - convertListEntries(bindings, CLIENT_SIDE, attributes.getClientSideAttributes());  
55 - convertListEntries(bindings, SERVER_SIDE, attributes.getServerSideAttributes());  
56 - convertListEntries(bindings, SHARED, attributes.getServerSidePublicAttributes()); 42 + Bindings bindings = NashornJsEvaluator.getAttributeBindings(attributes);
57 43
58 if (msg != null) { 44 if (msg != null) {
59 switch (msg.getMsgType()) { 45 switch (msg.getMsgType()) {
60 case POST_ATTRIBUTES_REQUEST: 46 case POST_ATTRIBUTES_REQUEST:
61 - updateBindings(bindings, (UpdateAttributesRequest) msg); 47 + bindings = NashornJsEvaluator.updateBindings(bindings, (UpdateAttributesRequest) msg);
62 break; 48 break;
63 } 49 }
64 } 50 }
@@ -66,28 +52,4 @@ public class DeviceAttributesFilter extends BasicJsFilter { @@ -66,28 +52,4 @@ public class DeviceAttributesFilter extends BasicJsFilter {
66 return bindings; 52 return bindings;
67 } 53 }
68 54
69 - private void updateBindings(Bindings bindings, UpdateAttributesRequest msg) {  
70 - Map<String, Object> attrMap = (Map<String, Object>) bindings.get(CLIENT_SIDE);  
71 - for (AttributeKvEntry attr : msg.getAttributes()) {  
72 - if (!CLIENT_SIDE.equalsIgnoreCase(attr.getKey()) && !SERVER_SIDE.equalsIgnoreCase(attr.getKey())  
73 - && !SHARED.equalsIgnoreCase(attr.getKey())) {  
74 - bindings.put(attr.getKey(), getValue(attr));  
75 - }  
76 - attrMap.put(attr.getKey(), getValue(attr));  
77 - }  
78 - bindings.put(CLIENT_SIDE, attrMap);  
79 - }  
80 -  
81 - public static Bindings convertListEntries(Bindings bindings, String attributesVarName, Collection<AttributeKvEntry> attributes) {  
82 - Map<String, Object> attrMap = new HashMap<>();  
83 - for (AttributeKvEntry attr : attributes) {  
84 - if (!CLIENT_SIDE.equalsIgnoreCase(attr.getKey()) && !SERVER_SIDE.equalsIgnoreCase(attr.getKey())  
85 - && !SHARED.equalsIgnoreCase(attr.getKey())) {  
86 - bindings.put(attr.getKey(), getValue(attr));  
87 - }  
88 - attrMap.put(attr.getKey(), getValue(attr));  
89 - }  
90 - bindings.put(attributesVarName, attrMap);  
91 - return bindings;  
92 - }  
93 } 55 }
@@ -23,9 +23,7 @@ import org.thingsboard.server.common.msg.session.FromDeviceMsg; @@ -23,9 +23,7 @@ import org.thingsboard.server.common.msg.session.FromDeviceMsg;
23 import org.thingsboard.server.extensions.api.component.Filter; 23 import org.thingsboard.server.extensions.api.component.Filter;
24 import org.thingsboard.server.extensions.api.rules.RuleContext; 24 import org.thingsboard.server.extensions.api.rules.RuleContext;
25 25
26 -import javax.script.Bindings;  
27 import javax.script.ScriptException; 26 import javax.script.ScriptException;
28 -import javax.script.SimpleBindings;  
29 import java.util.List; 27 import java.util.List;
30 28
31 /** 29 /**
@@ -41,7 +39,7 @@ public class DeviceTelemetryFilter extends BasicJsFilter { @@ -41,7 +39,7 @@ public class DeviceTelemetryFilter extends BasicJsFilter {
41 if (deviceMsg instanceof TelemetryUploadRequest) { 39 if (deviceMsg instanceof TelemetryUploadRequest) {
42 TelemetryUploadRequest telemetryMsg = (TelemetryUploadRequest) deviceMsg; 40 TelemetryUploadRequest telemetryMsg = (TelemetryUploadRequest) deviceMsg;
43 for (List<KvEntry> entries : telemetryMsg.getData().values()) { 41 for (List<KvEntry> entries : telemetryMsg.getData().values()) {
44 - if (evaluator.execute(toBindings(entries))) { 42 + if (evaluator.execute(NashornJsEvaluator.toBindings(entries))) {
45 return true; 43 return true;
46 } 44 }
47 } 45 }
@@ -49,12 +47,4 @@ public class DeviceTelemetryFilter extends BasicJsFilter { @@ -49,12 +47,4 @@ public class DeviceTelemetryFilter extends BasicJsFilter {
49 return false; 47 return false;
50 } 48 }
51 49
52 - private Bindings toBindings(List<KvEntry> entries) {  
53 - Bindings bindings = new SimpleBindings();  
54 - for (KvEntry entry : entries) {  
55 - bindings.put(entry.getKey(), getValue(entry));  
56 - }  
57 - return bindings;  
58 - }  
59 -  
60 } 50 }
@@ -17,10 +17,16 @@ package org.thingsboard.server.extensions.core.filter; @@ -17,10 +17,16 @@ package org.thingsboard.server.extensions.core.filter;
17 17
18 import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 18 import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 -import org.slf4j.Logger;  
21 -import org.slf4j.LoggerFactory; 20 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  21 +import org.thingsboard.server.common.data.kv.KvEntry;
  22 +import org.thingsboard.server.common.msg.core.UpdateAttributesRequest;
  23 +import org.thingsboard.server.extensions.api.device.DeviceAttributes;
22 24
23 import javax.script.*; 25 import javax.script.*;
  26 +import java.util.Collection;
  27 +import java.util.HashMap;
  28 +import java.util.List;
  29 +import java.util.Map;
24 30
25 /** 31 /**
26 * @author Andrew Shvayka 32 * @author Andrew Shvayka
@@ -28,6 +34,9 @@ import javax.script.*; @@ -28,6 +34,9 @@ import javax.script.*;
28 @Slf4j 34 @Slf4j
29 public class NashornJsEvaluator { 35 public class NashornJsEvaluator {
30 36
  37 + public static final String CLIENT_SIDE = "cs";
  38 + public static final String SERVER_SIDE = "ss";
  39 + public static final String SHARED = "shared";
31 private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 40 private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
32 41
33 private CompiledScript engine; 42 private CompiledScript engine;
@@ -47,6 +56,65 @@ public class NashornJsEvaluator { @@ -47,6 +56,65 @@ public class NashornJsEvaluator {
47 } 56 }
48 } 57 }
49 58
  59 + public static Bindings convertListEntries(Bindings bindings, String attributesVarName, Collection<AttributeKvEntry> attributes) {
  60 + Map<String, Object> attrMap = new HashMap<>();
  61 + for (AttributeKvEntry attr : attributes) {
  62 + if (!CLIENT_SIDE.equalsIgnoreCase(attr.getKey()) && !SERVER_SIDE.equalsIgnoreCase(attr.getKey())
  63 + && !SHARED.equalsIgnoreCase(attr.getKey())) {
  64 + bindings.put(attr.getKey(), getValue(attr));
  65 + }
  66 + attrMap.put(attr.getKey(), getValue(attr));
  67 + }
  68 + bindings.put(attributesVarName, attrMap);
  69 + return bindings;
  70 + }
  71 +
  72 + public static Bindings updateBindings(Bindings bindings, UpdateAttributesRequest msg) {
  73 + Map<String, Object> attrMap = (Map<String, Object>) bindings.get(CLIENT_SIDE);
  74 + for (AttributeKvEntry attr : msg.getAttributes()) {
  75 + if (!CLIENT_SIDE.equalsIgnoreCase(attr.getKey()) && !SERVER_SIDE.equalsIgnoreCase(attr.getKey())
  76 + && !SHARED.equalsIgnoreCase(attr.getKey())) {
  77 + bindings.put(attr.getKey(), getValue(attr));
  78 + }
  79 + attrMap.put(attr.getKey(), getValue(attr));
  80 + }
  81 + bindings.put(CLIENT_SIDE, attrMap);
  82 + return bindings;
  83 + }
  84 +
  85 + protected static Object getValue(KvEntry attr) {
  86 + switch (attr.getDataType()) {
  87 + case STRING:
  88 + return attr.getStrValue().get();
  89 + case LONG:
  90 + return attr.getLongValue().get();
  91 + case DOUBLE:
  92 + return attr.getDoubleValue().get();
  93 + case BOOLEAN:
  94 + return attr.getBooleanValue().get();
  95 + }
  96 + return null;
  97 + }
  98 +
  99 + public static Bindings toBindings(List<KvEntry> entries) {
  100 + return toBindings(new SimpleBindings(), entries);
  101 + }
  102 +
  103 + public static Bindings toBindings(Bindings bindings, List<KvEntry> entries) {
  104 + for (KvEntry entry : entries) {
  105 + bindings.put(entry.getKey(), getValue(entry));
  106 + }
  107 + return bindings;
  108 + }
  109 +
  110 + public static Bindings getAttributeBindings(DeviceAttributes attributes) {
  111 + Bindings bindings = new SimpleBindings();
  112 + convertListEntries(bindings, CLIENT_SIDE, attributes.getClientSideAttributes());
  113 + convertListEntries(bindings, SERVER_SIDE, attributes.getServerSideAttributes());
  114 + convertListEntries(bindings, SHARED, attributes.getServerSidePublicAttributes());
  115 + return bindings;
  116 + }
  117 +
50 public Boolean execute(Bindings bindings) throws ScriptException { 118 public Boolean execute(Bindings bindings) throws ScriptException {
51 Object eval = engine.eval(bindings); 119 Object eval = engine.eval(bindings);
52 if (eval instanceof Boolean) { 120 if (eval instanceof Boolean) {
@@ -32,7 +32,7 @@ import java.util.Optional; @@ -32,7 +32,7 @@ import java.util.Optional;
32 /** 32 /**
33 * @author Andrew Shvayka 33 * @author Andrew Shvayka
34 */ 34 */
35 -@Processor(name = "Alarm Deduplication Processor", descriptor = "AlarmDeduplicationProcessorDescriptor.json", 35 +@Processor(name = "(Deprecated) Alarm Deduplication Processor", descriptor = "AlarmDeduplicationProcessorDescriptor.json",
36 configuration = AlarmDeduplicationProcessorConfiguration.class) 36 configuration = AlarmDeduplicationProcessorConfiguration.class)
37 @Slf4j 37 @Slf4j
38 public class AlarmDeduplicationProcessor extends SimpleRuleLifecycleComponent 38 public class AlarmDeduplicationProcessor extends SimpleRuleLifecycleComponent
  1 +/**
  2 + * Copyright © 2016-2017 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.extensions.core.processor;
  17 +
  18 +import java.util.Optional;
  19 +
  20 +import com.fasterxml.jackson.core.JsonProcessingException;
  21 +import com.fasterxml.jackson.databind.ObjectMapper;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.apache.velocity.Template;
  24 +import org.apache.velocity.VelocityContext;
  25 +import org.apache.velocity.runtime.parser.ParseException;
  26 +import org.thingsboard.server.common.data.alarm.Alarm;
  27 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  28 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  29 +import org.thingsboard.server.common.data.kv.KvEntry;
  30 +import org.thingsboard.server.common.msg.core.TelemetryUploadRequest;
  31 +import org.thingsboard.server.common.msg.core.UpdateAttributesRequest;
  32 +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
  33 +import org.thingsboard.server.common.msg.session.FromDeviceMsg;
  34 +import org.thingsboard.server.extensions.api.component.Processor;
  35 +import org.thingsboard.server.extensions.api.rules.*;
  36 +import org.thingsboard.server.extensions.core.filter.NashornJsEvaluator;
  37 +import org.thingsboard.server.extensions.core.utils.VelocityUtils;
  38 +
  39 +import javax.script.Bindings;
  40 +import javax.script.ScriptException;
  41 +import java.io.IOException;
  42 +import java.util.List;
  43 +
  44 +/**
  45 + * @author Andrew Shvayka
  46 + */
  47 +@Processor(name = "Alarm Processor", descriptor = "AlarmProcessorDescriptor.json",
  48 + configuration = AlarmProcessorConfiguration.class)
  49 +@Slf4j
  50 +public class AlarmProcessor implements RuleProcessor<AlarmProcessorConfiguration> {
  51 +
  52 + static final String IS_NEW_ALARM = "isNewAlarm";
  53 + static final String IS_EXISTING_ALARM = "isExistingAlarm";
  54 + static final String IS_CLEARED_ALARM = "isClearedAlarm";
  55 +
  56 + protected NashornJsEvaluator newAlarmEvaluator;
  57 + protected NashornJsEvaluator clearAlarmEvaluator;
  58 +
  59 + private ObjectMapper mapper = new ObjectMapper();
  60 + private AlarmProcessorConfiguration configuration;
  61 + private AlarmStatus status;
  62 + private AlarmSeverity severity;
  63 + private Template alarmTypeTemplate;
  64 + private Template alarmDetailsTemplate;
  65 +
  66 +
  67 + @Override
  68 + public void init(AlarmProcessorConfiguration configuration) {
  69 + this.configuration = configuration;
  70 + try {
  71 + this.alarmTypeTemplate = VelocityUtils.create(configuration.getAlarmTypeTemplate(), "Alarm Type Template");
  72 + this.alarmDetailsTemplate = VelocityUtils.create(configuration.getAlarmDetailsTemplate(), "Alarm Details Template");
  73 + this.status = AlarmStatus.valueOf(configuration.getAlarmStatus());
  74 + this.severity = AlarmSeverity.valueOf(configuration.getAlarmSeverity());
  75 + initEvaluators();
  76 + } catch (Exception e) {
  77 + log.error("Failed to create templates based on provided configuration!", e);
  78 + throw new RuntimeException("Failed to create templates based on provided configuration!", e);
  79 + }
  80 + }
  81 +
  82 + @Override
  83 + public void resume() {
  84 + initEvaluators();
  85 + log.debug("Resume method was called, but no impl provided!");
  86 + }
  87 +
  88 + @Override
  89 + public void suspend() {
  90 + destroyEvaluators();
  91 + log.debug("Suspend method was called, but no impl provided!");
  92 + }
  93 +
  94 + @Override
  95 + public void stop() {
  96 + destroyEvaluators();
  97 + log.debug("Stop method was called, but no impl provided!");
  98 + }
  99 +
  100 + @Override
  101 + public RuleProcessingMetaData process(RuleContext ctx, ToDeviceActorMsg wrapper) throws RuleException {
  102 + RuleProcessingMetaData md = new RuleProcessingMetaData();
  103 +
  104 + FromDeviceMsg msg = wrapper.getPayload();
  105 + Bindings bindings = buildBindings(ctx, msg);
  106 +
  107 + boolean isActiveAlarm;
  108 + boolean isClearedAlarm;
  109 +
  110 + try {
  111 + isActiveAlarm = newAlarmEvaluator.execute(bindings);
  112 + isClearedAlarm = clearAlarmEvaluator.execute(bindings);
  113 + } catch (ScriptException e) {
  114 + log.debug("[{}] Failed to evaluate alarm expressions!", ctx.getRuleId(), e);
  115 + throw new RuleException("Failed to evaluate alarm expressions!", e);
  116 + }
  117 +
  118 + if (!isActiveAlarm && !isClearedAlarm) {
  119 + log.debug("[{}] Incoming message do not trigger alarm", ctx.getRuleId());
  120 + return md;
  121 + }
  122 +
  123 + Alarm existing = null;
  124 + if (isActiveAlarm) {
  125 + Alarm alarm = buildAlarm(ctx, msg);
  126 + existing = ctx.createOrUpdateAlarm(alarm);
  127 + if (existing.getStartTs() == alarm.getStartTs()) {
  128 + log.debug("[{}][{}] New Active Alarm detected");
  129 + md.put(IS_NEW_ALARM, Boolean.TRUE);
  130 + } else {
  131 + log.debug("[{}][{}] Existing Active Alarm detected");
  132 + md.put(IS_EXISTING_ALARM, Boolean.TRUE);
  133 + }
  134 + } else if (isClearedAlarm) {
  135 + VelocityContext context = VelocityUtils.createContext(ctx.getDeviceMetaData(), msg);
  136 + String alarmType = VelocityUtils.merge(alarmTypeTemplate, context);
  137 + Optional<Alarm> alarm = ctx.findLatestAlarm(ctx.getDeviceMetaData().getDeviceId(), alarmType);
  138 + if (alarm.isPresent()) {
  139 + ctx.clearAlarm(alarm.get().getId(), System.currentTimeMillis());
  140 + log.debug("[{}][{}] Existing Active Alarm cleared");
  141 + md.put(IS_CLEARED_ALARM, Boolean.TRUE);
  142 + existing = alarm.get();
  143 + }
  144 + }
  145 + //TODO: handle cleared alarms
  146 +
  147 + if (existing != null) {
  148 + md.put("alarmId", existing.getId().getId());
  149 + md.put("alarmType", existing.getType());
  150 + md.put("alarmSeverity", existing.getSeverity());
  151 + try {
  152 + md.put("alarmDetails", mapper.writeValueAsString(existing.getDetails()));
  153 + } catch (JsonProcessingException e) {
  154 + throw new RuleException("Failed to serialize alarm details", e);
  155 + }
  156 + }
  157 +
  158 + return md;
  159 + }
  160 +
  161 + private Alarm buildAlarm(RuleContext ctx, FromDeviceMsg msg) throws RuleException {
  162 + VelocityContext context = VelocityUtils.createContext(ctx.getDeviceMetaData(), msg);
  163 + String alarmType = VelocityUtils.merge(alarmTypeTemplate, context);
  164 + String alarmDetails = VelocityUtils.merge(alarmDetailsTemplate, context);
  165 +
  166 + Alarm alarm = new Alarm();
  167 + alarm.setOriginator(ctx.getDeviceMetaData().getDeviceId());
  168 + alarm.setType(alarmType);
  169 +
  170 + alarm.setStatus(status);
  171 + alarm.setSeverity(severity);
  172 + alarm.setPropagate(configuration.isAlarmPropagateFlag());
  173 +
  174 + try {
  175 + alarm.setDetails(mapper.readTree(alarmDetails));
  176 + } catch (IOException e) {
  177 + log.debug("[{}] Failed to parse alarm details {} as json string after evaluation.", ctx.getRuleId(), e);
  178 + throw new RuleException("Failed to parse alarm details as json string after evaluation!", e);
  179 + }
  180 + return alarm;
  181 + }
  182 +
  183 + private Bindings buildBindings(RuleContext ctx, FromDeviceMsg msg) {
  184 + Bindings bindings = NashornJsEvaluator.getAttributeBindings(ctx.getDeviceMetaData().getDeviceAttributes());
  185 + if (msg != null) {
  186 + switch (msg.getMsgType()) {
  187 + case POST_ATTRIBUTES_REQUEST:
  188 + bindings = NashornJsEvaluator.updateBindings(bindings, (UpdateAttributesRequest) msg);
  189 + break;
  190 + case POST_TELEMETRY_REQUEST:
  191 + TelemetryUploadRequest telemetryMsg = (TelemetryUploadRequest) msg;
  192 + for (List<KvEntry> entries : telemetryMsg.getData().values()) {
  193 + bindings = NashornJsEvaluator.toBindings(bindings, entries);
  194 + }
  195 + }
  196 + }
  197 + return bindings;
  198 + }
  199 +
  200 + private void initEvaluators() {
  201 + newAlarmEvaluator = new NashornJsEvaluator(configuration.getNewAlarmExpression());
  202 + clearAlarmEvaluator = new NashornJsEvaluator(configuration.getClearAlarmExpression());
  203 + }
  204 +
  205 + private void destroyEvaluators() {
  206 + if (newAlarmEvaluator != null) {
  207 + newAlarmEvaluator.destroy();
  208 + }
  209 + if (clearAlarmEvaluator != null) {
  210 + clearAlarmEvaluator.destroy();
  211 + }
  212 + }
  213 +}
  1 +/**
  2 + * Copyright © 2016-2017 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.extensions.core.processor;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.util.List;
  21 +
  22 +/**
  23 + * @author Andrew Shvayka
  24 + */
  25 +@Data
  26 +public class AlarmProcessorConfiguration {
  27 +
  28 + private String newAlarmExpression;
  29 + private String clearAlarmExpression;
  30 +
  31 + private String alarmTypeTemplate;
  32 + private String alarmSeverity;
  33 + private String alarmStatus;
  34 + private boolean alarmPropagateFlag;
  35 +
  36 + private String alarmDetailsTemplate;
  37 +
  38 +}
@@ -29,7 +29,7 @@ import org.thingsboard.server.common.msg.session.FromDeviceMsg; @@ -29,7 +29,7 @@ import org.thingsboard.server.common.msg.session.FromDeviceMsg;
29 import org.thingsboard.server.extensions.api.device.DeviceAttributes; 29 import org.thingsboard.server.extensions.api.device.DeviceAttributes;
30 import org.thingsboard.server.extensions.api.device.DeviceMetaData; 30 import org.thingsboard.server.extensions.api.device.DeviceMetaData;
31 import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData; 31 import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData;
32 -import org.thingsboard.server.extensions.core.filter.DeviceAttributesFilter; 32 +import org.thingsboard.server.extensions.core.filter.NashornJsEvaluator;
33 33
34 import java.io.StringReader; 34 import java.io.StringReader;
35 import java.io.StringWriter; 35 import java.io.StringWriter;
@@ -70,9 +70,9 @@ public class VelocityUtils { @@ -70,9 +70,9 @@ public class VelocityUtils {
70 context.put("date", new DateTool()); 70 context.put("date", new DateTool());
71 DeviceAttributes deviceAttributes = deviceMetaData.getDeviceAttributes(); 71 DeviceAttributes deviceAttributes = deviceMetaData.getDeviceAttributes();
72 72
73 - pushAttributes(context, deviceAttributes.getClientSideAttributes(), DeviceAttributesFilter.CLIENT_SIDE);  
74 - pushAttributes(context, deviceAttributes.getServerSideAttributes(), DeviceAttributesFilter.SERVER_SIDE);  
75 - pushAttributes(context, deviceAttributes.getServerSidePublicAttributes(), DeviceAttributesFilter.SHARED); 73 + pushAttributes(context, deviceAttributes.getClientSideAttributes(), NashornJsEvaluator.CLIENT_SIDE);
  74 + pushAttributes(context, deviceAttributes.getServerSideAttributes(), NashornJsEvaluator.SERVER_SIDE);
  75 + pushAttributes(context, deviceAttributes.getServerSidePublicAttributes(), NashornJsEvaluator.SHARED);
76 76
77 switch (payload.getMsgType()) { 77 switch (payload.getMsgType()) {
78 case POST_TELEMETRY_REQUEST: 78 case POST_TELEMETRY_REQUEST:
  1 +{
  2 + "schema": {
  3 + "title": "Alarm Configuration",
  4 + "type": "object",
  5 + "properties": {
  6 + "newAlarmExpression": {
  7 + "title": "Alarm trigger expression",
  8 + "type": "string",
  9 + "default": ""
  10 + },
  11 + "clearAlarmExpression": {
  12 + "title": "Alarm clear expression",
  13 + "type": "string",
  14 + "default": ""
  15 + },
  16 + "alarmTypeTemplate": {
  17 + "title": "Alarm type",
  18 + "type": "string"
  19 + },
  20 + "alarmSeverity": {
  21 + "title": "Severity",
  22 + "type": "string"
  23 + },
  24 + "alarmStatus": {
  25 + "title": "Status",
  26 + "type": "string"
  27 + },
  28 + "alarmPropagateFlag": {
  29 + "title": "Propagate Alarm",
  30 + "type": "boolean"
  31 + },
  32 + "alarmDetailsTemplate": {
  33 + "title": "Alarm details",
  34 + "type": "string"
  35 + }
  36 + },
  37 + "required": [
  38 + "newAlarmExpression",
  39 + "clearAlarmExpression",
  40 + "alarmSeverity",
  41 + "alarmStatus",
  42 + "alarmTypeTemplate",
  43 + "alarmDetailsTemplate"
  44 + ]
  45 + },
  46 + "form": [
  47 + {
  48 + "key": "newAlarmExpression",
  49 + "type": "javascript"
  50 + },
  51 + {
  52 + "key": "clearAlarmExpression",
  53 + "type": "javascript"
  54 + },
  55 + {
  56 + "key": "alarmSeverity",
  57 + "type": "rc-select",
  58 + "multiple": false,
  59 + "items": [
  60 + {
  61 + "value": "CRITICAL",
  62 + "label": "Critical"
  63 + },
  64 + {
  65 + "value": "MAJOR",
  66 + "label": "Major"
  67 + },
  68 + {
  69 + "value": "MINOR",
  70 + "label": "Minor"
  71 + },
  72 + {
  73 + "value": "WARNING",
  74 + "label": "Warning"
  75 + },
  76 + {
  77 + "value": "INDETERMINATE",
  78 + "label": "Indeterminate"
  79 + }
  80 + ]
  81 + },
  82 + {
  83 + "key": "alarmStatus",
  84 + "type": "rc-select",
  85 + "multiple": false,
  86 + "items": [
  87 + {
  88 + "value": "ACTIVE_UNACK",
  89 + "label": "Active Unacknowledged"
  90 + },
  91 + {
  92 + "value": "ACTIVE_ACK",
  93 + "label": "Active Acknowledged"
  94 + },
  95 + {
  96 + "value": "CLEARED_UNACK",
  97 + "label": "Cleared Unacknowledged"
  98 + },
  99 + {
  100 + "value": "CLEARED_ACK",
  101 + "label": "Cleared Acknowledged"
  102 + }
  103 + ]
  104 + },
  105 + "alarmTypeTemplate",
  106 + "alarmPropagateFlag",
  107 + {
  108 + "key": "alarmDetailsTemplate",
  109 + "type": "textarea",
  110 + "rows": 5
  111 + }
  112 + ]
  113 +}