Commit e86c07599c54d434d44196133f2e2322e0008752

Authored by zbeacon
2 parents b80fc659 259af14b

Merge branch 'develop/3.2' of https://github.com/thingsboard/thingsboard into fe…

…ature/device-provision-3.2-onlyProfileVersion
@@ -282,6 +282,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement @@ -282,6 +282,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
282 } 282 }
283 } 283 }
284 284
  285 +
  286 +
285 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { 287 private <T> TransportServiceCallback<Void> getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) {
286 return new TransportServiceCallback<Void>() { 288 return new TransportServiceCallback<Void>() {
287 @Override 289 @Override
@@ -34,7 +34,7 @@ public class JacksonUtil { @@ -34,7 +34,7 @@ public class JacksonUtil {
34 return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null; 34 return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null;
35 } catch (IllegalArgumentException e) { 35 } catch (IllegalArgumentException e) {
36 throw new IllegalArgumentException("The given object value: " 36 throw new IllegalArgumentException("The given object value: "
37 - + fromValue + " cannot be converted to " + toValueType); 37 + + fromValue + " cannot be converted to " + toValueType, e);
38 } 38 }
39 } 39 }
40 40
@@ -43,7 +43,7 @@ public class JacksonUtil { @@ -43,7 +43,7 @@ public class JacksonUtil {
43 return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; 43 return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null;
44 } catch (IOException e) { 44 } catch (IOException e) {
45 throw new IllegalArgumentException("The given string value: " 45 throw new IllegalArgumentException("The given string value: "
46 - + string + " cannot be transformed to Json object"); 46 + + string + " cannot be transformed to Json object", e);
47 } 47 }
48 } 48 }
49 49
@@ -52,7 +52,7 @@ public class JacksonUtil { @@ -52,7 +52,7 @@ public class JacksonUtil {
52 return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null; 52 return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null;
53 } catch (JsonProcessingException e) { 53 } catch (JsonProcessingException e) {
54 throw new IllegalArgumentException("The given Json object value: " 54 throw new IllegalArgumentException("The given Json object value: "
55 - + value + " cannot be transformed to a String"); 55 + + value + " cannot be transformed to a String", e);
56 } 56 }
57 } 57 }
58 58
@@ -71,7 +71,7 @@ public class JacksonUtil { @@ -71,7 +71,7 @@ public class JacksonUtil {
71 return fromString(toString(value), (Class<T>) value.getClass()); 71 return fromString(toString(value), (Class<T>) value.getClass());
72 } 72 }
73 73
74 - public static JsonNode valueToTree(Alarm alarm) { 74 + public static <T> JsonNode valueToTree(T alarm) {
75 return OBJECT_MAPPER.valueToTree(alarm); 75 return OBJECT_MAPPER.valueToTree(alarm);
76 } 76 }
77 } 77 }
@@ -15,6 +15,6 @@ @@ -15,6 +15,6 @@
15 -- 15 --
16 16
17 DROP TYPE json IF EXISTS; 17 DROP TYPE json IF EXISTS;
18 -CREATE TYPE json AS text; 18 +CREATE TYPE json AS varchar;
19 DROP TYPE jsonb IF EXISTS; 19 DROP TYPE jsonb IF EXISTS;
20 CREATE TYPE jsonb AS other; 20 CREATE TYPE jsonb AS other;
@@ -47,16 +47,14 @@ import java.util.concurrent.ExecutionException; @@ -47,16 +47,14 @@ import java.util.concurrent.ExecutionException;
47 class DeviceProfileAlarmState { 47 class DeviceProfileAlarmState {
48 48
49 private final EntityId originator; 49 private final EntityId originator;
50 - private final DeviceProfileAlarm alarmDefinition; 50 + private DeviceProfileAlarm alarmDefinition;
51 private volatile Map<AlarmSeverity, AlarmRule> createRulesSortedBySeverityDesc; 51 private volatile Map<AlarmSeverity, AlarmRule> createRulesSortedBySeverityDesc;
52 private volatile Alarm currentAlarm; 52 private volatile Alarm currentAlarm;
53 private volatile boolean initialFetchDone; 53 private volatile boolean initialFetchDone;
54 54
55 public DeviceProfileAlarmState(EntityId originator, DeviceProfileAlarm alarmDefinition) { 55 public DeviceProfileAlarmState(EntityId originator, DeviceProfileAlarm alarmDefinition) {
56 this.originator = originator; 56 this.originator = originator;
57 - this.alarmDefinition = alarmDefinition;  
58 - this.createRulesSortedBySeverityDesc = new TreeMap<>(Comparator.comparingInt(AlarmSeverity::ordinal));  
59 - this.createRulesSortedBySeverityDesc.putAll(alarmDefinition.getCreateRules()); 57 + this.updateState(alarmDefinition);
60 } 58 }
61 59
62 public void process(TbContext ctx, TbMsg msg, DeviceDataSnapshot data) throws ExecutionException, InterruptedException { 60 public void process(TbContext ctx, TbMsg msg, DeviceDataSnapshot data) throws ExecutionException, InterruptedException {
@@ -111,6 +109,12 @@ class DeviceProfileAlarmState { @@ -111,6 +109,12 @@ class DeviceProfileAlarmState {
111 ctx.tellNext(newMsg, relationType); 109 ctx.tellNext(newMsg, relationType);
112 } 110 }
113 111
  112 + public void updateState(DeviceProfileAlarm alarm) {
  113 + this.alarmDefinition = alarm;
  114 + this.createRulesSortedBySeverityDesc = new TreeMap<>(Comparator.comparingInt(AlarmSeverity::ordinal));
  115 + this.createRulesSortedBySeverityDesc.putAll(alarmDefinition.getCreateRules());
  116 + }
  117 +
114 private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmSeverity severity) { 118 private TbAlarmResult calculateAlarmResult(TbContext ctx, AlarmSeverity severity) {
115 if (currentAlarm != null) { 119 if (currentAlarm != null) {
116 currentAlarm.setEndTs(System.currentTimeMillis()); 120 currentAlarm.setEndTs(System.currentTimeMillis());
@@ -20,6 +20,7 @@ import lombok.Getter; @@ -20,6 +20,7 @@ import lombok.Getter;
20 import org.thingsboard.server.common.data.DeviceProfile; 20 import org.thingsboard.server.common.data.DeviceProfile;
21 import org.thingsboard.server.common.data.device.profile.AlarmRule; 21 import org.thingsboard.server.common.data.device.profile.AlarmRule;
22 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 22 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
  23 +import org.thingsboard.server.common.data.id.DeviceProfileId;
23 import org.thingsboard.server.common.data.query.EntityKey; 24 import org.thingsboard.server.common.data.query.EntityKey;
24 import org.thingsboard.server.common.data.query.KeyFilter; 25 import org.thingsboard.server.common.data.query.KeyFilter;
25 26
@@ -55,4 +56,8 @@ class DeviceProfileState { @@ -55,4 +56,8 @@ class DeviceProfileState {
55 } 56 }
56 } 57 }
57 } 58 }
  59 +
  60 + public DeviceProfileId getProfileId() {
  61 + return deviceProfile.getId();
  62 + }
58 } 63 }
@@ -20,8 +20,10 @@ import org.thingsboard.rule.engine.api.TbContext; @@ -20,8 +20,10 @@ import org.thingsboard.rule.engine.api.TbContext;
20 import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode; 20 import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode;
21 import org.thingsboard.server.common.data.DataConstants; 21 import org.thingsboard.server.common.data.DataConstants;
22 import org.thingsboard.server.common.data.Device; 22 import org.thingsboard.server.common.data.Device;
  23 +import org.thingsboard.server.common.data.DeviceProfile;
23 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; 24 import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
24 import org.thingsboard.server.common.data.id.DeviceId; 25 import org.thingsboard.server.common.data.id.DeviceId;
  26 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
26 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 28 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
27 import org.thingsboard.server.common.data.kv.KvEntry; 29 import org.thingsboard.server.common.data.kv.KvEntry;
@@ -40,20 +42,44 @@ import java.util.Set; @@ -40,20 +42,44 @@ import java.util.Set;
40 import java.util.concurrent.ConcurrentHashMap; 42 import java.util.concurrent.ConcurrentHashMap;
41 import java.util.concurrent.ConcurrentMap; 43 import java.util.concurrent.ConcurrentMap;
42 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.ExecutionException;
  45 +import java.util.stream.Collectors;
43 46
44 class DeviceState { 47 class DeviceState {
45 48
  49 + private final DeviceId deviceId;
46 private DeviceProfileState deviceProfile; 50 private DeviceProfileState deviceProfile;
47 private DeviceDataSnapshot latestValues; 51 private DeviceDataSnapshot latestValues;
48 private final ConcurrentMap<String, DeviceProfileAlarmState> alarmStates = new ConcurrentHashMap<>(); 52 private final ConcurrentMap<String, DeviceProfileAlarmState> alarmStates = new ConcurrentHashMap<>();
49 53
50 - public DeviceState(DeviceProfileState deviceProfile) { 54 + public DeviceState(DeviceId deviceId, DeviceProfileState deviceProfile) {
  55 + this.deviceId = deviceId;
51 this.deviceProfile = deviceProfile; 56 this.deviceProfile = deviceProfile;
52 } 57 }
53 58
  59 + public void updateProfile(TbContext ctx, DeviceProfile deviceProfile) throws ExecutionException, InterruptedException {
  60 + Set<EntityKey> oldKeys = this.deviceProfile.getEntityKeys();
  61 + this.deviceProfile.updateDeviceProfile(deviceProfile);
  62 + if (latestValues != null) {
  63 + Set<EntityKey> keysToFetch = new HashSet<>(this.deviceProfile.getEntityKeys());
  64 + keysToFetch.removeAll(oldKeys);
  65 + if (!keysToFetch.isEmpty()) {
  66 + addEntityKeysToSnapshot(ctx, deviceId, keysToFetch, latestValues);
  67 + }
  68 + }
  69 + Set<String> newAlarmStateIds = this.deviceProfile.getAlarmSettings().stream().map(DeviceProfileAlarm::getId).collect(Collectors.toSet());
  70 + alarmStates.keySet().removeIf(id -> !newAlarmStateIds.contains(id));
  71 + for (DeviceProfileAlarm alarm : this.deviceProfile.getAlarmSettings()) {
  72 + if (alarmStates.containsKey(alarm.getId())) {
  73 + alarmStates.get(alarm.getId()).updateState(alarm);
  74 + } else {
  75 + alarmStates.putIfAbsent(alarm.getId(), new DeviceProfileAlarmState(deviceId, alarm));
  76 + }
  77 + }
  78 + }
  79 +
54 public void process(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { 80 public void process(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {
55 if (latestValues == null) { 81 if (latestValues == null) {
56 - latestValues = fetchLatestValues(ctx, msg.getOriginator()); 82 + latestValues = fetchLatestValues(ctx, deviceId);
57 } 83 }
58 if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { 84 if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) {
59 processTelemetry(ctx, msg); 85 processTelemetry(ctx, msg);
@@ -69,7 +95,7 @@ class DeviceState { @@ -69,7 +95,7 @@ class DeviceState {
69 List<KvEntry> data = entry.getValue(); 95 List<KvEntry> data = entry.getValue();
70 latestValues = merge(latestValues, ts, data); 96 latestValues = merge(latestValues, ts, data);
71 for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { 97 for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) {
72 - DeviceProfileAlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new DeviceProfileAlarmState(msg.getOriginator(), alarm)); 98 + DeviceProfileAlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new DeviceProfileAlarmState(deviceId, alarm));
73 alarmState.process(ctx, msg, latestValues); 99 alarmState.process(ctx, msg, latestValues);
74 } 100 }
75 } 101 }
@@ -85,8 +111,13 @@ class DeviceState { @@ -85,8 +111,13 @@ class DeviceState {
85 } 111 }
86 112
87 private DeviceDataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException { 113 private DeviceDataSnapshot fetchLatestValues(TbContext ctx, EntityId originator) throws ExecutionException, InterruptedException {
88 - DeviceDataSnapshot result = new DeviceDataSnapshot(deviceProfile.getEntityKeys()); 114 + Set<EntityKey> entityKeysToFetch = deviceProfile.getEntityKeys();
  115 + DeviceDataSnapshot result = new DeviceDataSnapshot(entityKeysToFetch);
  116 + addEntityKeysToSnapshot(ctx, originator, entityKeysToFetch, result);
  117 + return result;
  118 + }
89 119
  120 + private void addEntityKeysToSnapshot(TbContext ctx, EntityId originator, Set<EntityKey> entityKeysToFetch, DeviceDataSnapshot result) throws InterruptedException, ExecutionException {
90 Set<String> serverAttributeKeys = new HashSet<>(); 121 Set<String> serverAttributeKeys = new HashSet<>();
91 Set<String> clientAttributeKeys = new HashSet<>(); 122 Set<String> clientAttributeKeys = new HashSet<>();
92 Set<String> sharedAttributeKeys = new HashSet<>(); 123 Set<String> sharedAttributeKeys = new HashSet<>();
@@ -94,7 +125,7 @@ class DeviceState { @@ -94,7 +125,7 @@ class DeviceState {
94 Set<String> latestTsKeys = new HashSet<>(); 125 Set<String> latestTsKeys = new HashSet<>();
95 126
96 Device device = null; 127 Device device = null;
97 - for (EntityKey entityKey : deviceProfile.getEntityKeys()) { 128 + for (EntityKey entityKey : entityKeysToFetch) {
98 String key = entityKey.getKey(); 129 String key = entityKey.getKey();
99 switch (entityKey.getType()) { 130 switch (entityKey.getType()) {
100 case SERVER_ATTRIBUTE: 131 case SERVER_ATTRIBUTE:
@@ -159,8 +190,6 @@ class DeviceState { @@ -159,8 +190,6 @@ class DeviceState {
159 addToSnapshot(result, commonAttributeKeys, 190 addToSnapshot(result, commonAttributeKeys,
160 ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, serverAttributeKeys).get()); 191 ctx.getAttributesService().find(ctx.getTenantId(), originator, DataConstants.SERVER_SCOPE, serverAttributeKeys).get());
161 } 192 }
162 -  
163 - return result;  
164 } 193 }
165 194
166 private void addToSnapshot(DeviceDataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) { 195 private void addToSnapshot(DeviceDataSnapshot snapshot, Set<String> commonAttributeKeys, List<AttributeKvEntry> data) {
@@ -192,4 +221,8 @@ class DeviceState { @@ -192,4 +221,8 @@ class DeviceState {
192 } 221 }
193 } 222 }
194 223
  224 + public DeviceProfileId getProfileId() {
  225 + return deviceProfile.getProfileId();
  226 + }
  227 +
195 } 228 }
@@ -16,15 +16,6 @@ @@ -16,15 +16,6 @@
16 package org.thingsboard.rule.engine.profile; 16 package org.thingsboard.rule.engine.profile;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 -import org.apache.commons.lang3.BooleanUtils;  
20 -import org.apache.kafka.clients.producer.KafkaProducer;  
21 -import org.apache.kafka.clients.producer.Producer;  
22 -import org.apache.kafka.clients.producer.ProducerConfig;  
23 -import org.apache.kafka.clients.producer.ProducerRecord;  
24 -import org.apache.kafka.clients.producer.RecordMetadata;  
25 -import org.apache.kafka.common.header.Headers;  
26 -import org.apache.kafka.common.header.internals.RecordHeader;  
27 -import org.apache.kafka.common.header.internals.RecordHeaders;  
28 import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; 19 import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
29 import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; 20 import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
30 import org.thingsboard.rule.engine.api.RuleNode; 21 import org.thingsboard.rule.engine.api.RuleNode;
@@ -32,22 +23,14 @@ import org.thingsboard.rule.engine.api.TbContext; @@ -32,22 +23,14 @@ import org.thingsboard.rule.engine.api.TbContext;
32 import org.thingsboard.rule.engine.api.TbNode; 23 import org.thingsboard.rule.engine.api.TbNode;
33 import org.thingsboard.rule.engine.api.TbNodeConfiguration; 24 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
34 import org.thingsboard.rule.engine.api.TbNodeException; 25 import org.thingsboard.rule.engine.api.TbNodeException;
35 -import org.thingsboard.rule.engine.api.TbRelationTypes;  
36 -import org.thingsboard.rule.engine.api.util.TbNodeUtils;  
37 -import org.thingsboard.rule.engine.kafka.TbKafkaNodeConfiguration;  
38 import org.thingsboard.server.common.data.DeviceProfile; 26 import org.thingsboard.server.common.data.DeviceProfile;
39 import org.thingsboard.server.common.data.EntityType; 27 import org.thingsboard.server.common.data.EntityType;
40 import org.thingsboard.server.common.data.id.DeviceId; 28 import org.thingsboard.server.common.data.id.DeviceId;
41 -import org.thingsboard.server.common.data.id.EntityId;  
42 import org.thingsboard.server.common.data.plugin.ComponentType; 29 import org.thingsboard.server.common.data.plugin.ComponentType;
43 import org.thingsboard.server.common.msg.TbMsg; 30 import org.thingsboard.server.common.msg.TbMsg;
44 -import org.thingsboard.server.common.msg.TbMsgMetaData; 31 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
45 32
46 -import java.nio.charset.Charset;  
47 -import java.nio.charset.StandardCharsets;  
48 -import java.util.HashMap;  
49 import java.util.Map; 33 import java.util.Map;
50 -import java.util.Properties;  
51 import java.util.concurrent.ConcurrentHashMap; 34 import java.util.concurrent.ConcurrentHashMap;
52 import java.util.concurrent.ExecutionException; 35 import java.util.concurrent.ExecutionException;
53 36
@@ -76,7 +59,6 @@ public class TbDeviceProfileNode implements TbNode { @@ -76,7 +59,6 @@ public class TbDeviceProfileNode implements TbNode {
76 /** 59 /**
77 * TODO: 60 * TODO:
78 * 1. Duration in the alarm conditions; 61 * 1. Duration in the alarm conditions;
79 - * 2. Update of the Profile (rules);  
80 * 3. Update of the Device attributes (client, server and shared); 62 * 3. Update of the Device attributes (client, server and shared);
81 * 4. Dynamic values evaluation; 63 * 4. Dynamic values evaluation;
82 */ 64 */
@@ -86,26 +68,37 @@ public class TbDeviceProfileNode implements TbNode { @@ -86,26 +68,37 @@ public class TbDeviceProfileNode implements TbNode {
86 EntityType originatorType = msg.getOriginator().getEntityType(); 68 EntityType originatorType = msg.getOriginator().getEntityType();
87 if (EntityType.DEVICE.equals(originatorType)) { 69 if (EntityType.DEVICE.equals(originatorType)) {
88 DeviceId deviceId = new DeviceId(msg.getOriginator().getId()); 70 DeviceId deviceId = new DeviceId(msg.getOriginator().getId());
89 - DeviceState deviceState = getOrCreateDeviceState(ctx, msg, deviceId);  
90 - if (deviceState != null) {  
91 - deviceState.process(ctx, msg); 71 + if (msg.getType().equals("ENTITY_UPDATED")) {
  72 + //TODO: handle if device profile id has changed.
92 } else { 73 } else {
93 - ctx.tellFailure(msg, new IllegalStateException("Device profile for device [" + deviceId + "] not found!")); 74 + DeviceState deviceState = getOrCreateDeviceState(ctx, deviceId);
  75 + if (deviceState != null) {
  76 + deviceState.process(ctx, msg);
  77 + } else {
  78 + ctx.tellFailure(msg, new IllegalStateException("Device profile for device [" + deviceId + "] not found!"));
  79 + }
94 } 80 }
95 } else if (EntityType.DEVICE_PROFILE.equals(originatorType)) { 81 } else if (EntityType.DEVICE_PROFILE.equals(originatorType)) {
96 - //TODO: check that the profile rule set was changed. If yes - invalidate the rules. 82 + if (msg.getType().equals("ENTITY_UPDATED")) {
  83 + DeviceProfile deviceProfile = JacksonUtil.fromString(msg.getData(), DeviceProfile.class);
  84 + for (DeviceState state : deviceStates.values()) {
  85 + if (deviceProfile.getId().equals(state.getProfileId())) {
  86 + state.updateProfile(ctx, deviceProfile);
  87 + }
  88 + }
  89 + }
97 ctx.tellSuccess(msg); 90 ctx.tellSuccess(msg);
98 } else { 91 } else {
99 ctx.tellSuccess(msg); 92 ctx.tellSuccess(msg);
100 } 93 }
101 } 94 }
102 95
103 - private DeviceState getOrCreateDeviceState(TbContext ctx, TbMsg msg, DeviceId deviceId) { 96 + private DeviceState getOrCreateDeviceState(TbContext ctx, DeviceId deviceId) {
104 DeviceState deviceState = deviceStates.get(deviceId); 97 DeviceState deviceState = deviceStates.get(deviceId);
105 if (deviceState == null) { 98 if (deviceState == null) {
106 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceId); 99 DeviceProfile deviceProfile = cache.get(ctx.getTenantId(), deviceId);
107 if (deviceProfile != null) { 100 if (deviceProfile != null) {
108 - deviceState = new DeviceState(new DeviceProfileState(deviceProfile)); 101 + deviceState = new DeviceState(deviceId, new DeviceProfileState(deviceProfile));
109 deviceStates.put(deviceId, deviceState); 102 deviceStates.put(deviceId, deviceState);
110 } 103 }
111 } 104 }
@@ -127,8 +127,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { @@ -127,8 +127,8 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
127 127
128 updateForm(entity: DeviceProfile) { 128 updateForm(entity: DeviceProfile) {
129 this.entityForm.patchValue({name: entity.name}); 129 this.entityForm.patchValue({name: entity.name});
130 - this.entityForm.patchValue({type: entity.type});  
131 - this.entityForm.patchValue({transportType: entity.transportType}); 130 + this.entityForm.patchValue({type: entity.type}, {emitEvent: false});
  131 + this.entityForm.patchValue({transportType: entity.transportType}, {emitEvent: false});
132 this.entityForm.patchValue({profileData: entity.profileData}); 132 this.entityForm.patchValue({profileData: entity.profileData});
133 this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}); 133 this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null});
134 this.entityForm.patchValue({description: entity.description}); 134 this.entityForm.patchValue({description: entity.description});
@@ -17,11 +17,11 @@ @@ -17,11 +17,11 @@
17 --> 17 -->
18 <mat-form-field [formGroup]="selectRuleChainFormGroup" class="mat-block"> 18 <mat-form-field [formGroup]="selectRuleChainFormGroup" class="mat-block">
19 <input matInput type="text" placeholder="{{ ruleChainLabel | translate }}" 19 <input matInput type="text" placeholder="{{ ruleChainLabel | translate }}"
20 - #entityInput 20 + #ruleChainInput
21 formControlName="ruleChainId" 21 formControlName="ruleChainId"
22 (focusin)="onFocus()" 22 (focusin)="onFocus()"
23 [required]="required" 23 [required]="required"
24 - [matAutocomplete]="entityAutocomplete"> 24 + [matAutocomplete]="ruleChainAutocomplete">
25 <button *ngIf="selectRuleChainFormGroup.get('ruleChainId').value && !disabled" 25 <button *ngIf="selectRuleChainFormGroup.get('ruleChainId').value && !disabled"
26 type="button" 26 type="button"
27 matSuffix mat-button mat-icon-button aria-label="Clear" 27 matSuffix mat-button mat-icon-button aria-label="Clear"
@@ -29,10 +29,10 @@ @@ -29,10 +29,10 @@
29 <mat-icon class="material-icons">close</mat-icon> 29 <mat-icon class="material-icons">close</mat-icon>
30 </button> 30 </button>
31 <mat-autocomplete class="tb-autocomplete" 31 <mat-autocomplete class="tb-autocomplete"
32 - #entityAutocomplete="matAutocomplete"  
33 - [displayWith]="displayEntityFn">  
34 - <mat-option *ngFor="let entity of filteredRuleChains | async" [value]="entity">  
35 - <span [innerHTML]="entity.name | highlight:searchText"></span> 32 + #ruleChainAutocomplete="matAutocomplete"
  33 + [displayWith]="displayRuleChainFn">
  34 + <mat-option *ngFor="let ruleChain of filteredRuleChains | async" [value]="ruleChain">
  35 + <span [innerHTML]="ruleChain.name | highlight:searchText"></span>
36 </mat-option> 36 </mat-option>
37 <mat-option *ngIf="!(filteredRuleChains | async)?.length" [value]="null" class="tb-not-found"> 37 <mat-option *ngIf="!(filteredRuleChains | async)?.length" [value]="null" class="tb-not-found">
38 <div class="tb-not-found-content" (click)="$event.stopPropagation()"> 38 <div class="tb-not-found-content" (click)="$event.stopPropagation()">
@@ -66,7 +66,8 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI @@ -66,7 +66,8 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
66 @Input() 66 @Input()
67 disabled: boolean; 67 disabled: boolean;
68 68
69 - @ViewChild('entityInput', {static: true}) entityInput: ElementRef; 69 + @ViewChild('ruleChainInput', {static: true}) ruleChainInput: ElementRef;
  70 + @ViewChild('ruleChainInput', {read: MatAutocompleteTrigger}) ruleChainAutocomplete: MatAutocompleteTrigger;
70 71
71 filteredRuleChains: Observable<Array<BaseData<EntityId>>>; 72 filteredRuleChains: Observable<Array<BaseData<EntityId>>>;
72 73
@@ -117,9 +118,9 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI @@ -117,9 +118,9 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
117 ngAfterViewInit(): void {} 118 ngAfterViewInit(): void {}
118 119
119 getCurrentEntity(): BaseData<EntityId> | null { 120 getCurrentEntity(): BaseData<EntityId> | null {
120 - const currentEntity = this.selectRuleChainFormGroup.get('ruleChainId').value;  
121 - if (currentEntity && typeof currentEntity !== 'string') {  
122 - return currentEntity as BaseData<EntityId>; 121 + const currentRuleChain = this.selectRuleChainFormGroup.get('ruleChainId').value;
  122 + if (currentRuleChain && typeof currentRuleChain !== 'string') {
  123 + return currentRuleChain as BaseData<EntityId>;
123 } else { 124 } else {
124 return null; 125 return null;
125 } 126 }
@@ -180,8 +181,8 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI @@ -180,8 +181,8 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
180 } 181 }
181 } 182 }
182 183
183 - displayEntityFn(entity?: BaseData<EntityId>): string | undefined {  
184 - return entity ? entity.name : undefined; 184 + displayRuleChainFn(ruleChain?: BaseData<EntityId>): string | undefined {
  185 + return ruleChain ? ruleChain.name : undefined;
185 } 186 }
186 187
187 fetchRuleChain(searchText?: string): Observable<Array<BaseData<EntityId>>> { 188 fetchRuleChain(searchText?: string): Observable<Array<BaseData<EntityId>>> {
@@ -193,12 +194,14 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI @@ -193,12 +194,14 @@ export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnI
193 clear() { 194 clear() {
194 this.selectRuleChainFormGroup.get('ruleChainId').patchValue('', {emitEvent: true}); 195 this.selectRuleChainFormGroup.get('ruleChainId').patchValue('', {emitEvent: true});
195 setTimeout(() => { 196 setTimeout(() => {
196 - this.entityInput.nativeElement.blur();  
197 - this.entityInput.nativeElement.focus(); 197 + this.ruleChainInput.nativeElement.blur();
  198 + this.ruleChainInput.nativeElement.focus();
198 }, 0); 199 }, 0);
199 } 200 }
200 201
201 createDefaultRuleChain($event: Event, ruleChainName: string) { 202 createDefaultRuleChain($event: Event, ruleChainName: string) {
  203 + $event.preventDefault();
  204 + this.ruleChainAutocomplete.closePanel();
202 this.ruleChainService.createDefaultRuleChain(ruleChainName).subscribe((ruleChain) => { 205 this.ruleChainService.createDefaultRuleChain(ruleChainName).subscribe((ruleChain) => {
203 this.updateView(ruleChain.id.id); 206 this.updateView(ruleChain.id.id);
204 }); 207 });