Commit e7cf3be6619619083c74dfe2ba5c8bf1410db8a0
Merge https://github.com/thingsboard/thingsboard into map/3.0
Showing
13 changed files
with
464 additions
and
55 deletions
@@ -92,19 +92,7 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest { | @@ -92,19 +92,7 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest { | ||
92 | .andExpect(status().isBadRequest()) | 92 | .andExpect(status().isBadRequest()) |
93 | .andExpect(statusReason(containsString("is prohibited"))); | 93 | .andExpect(statusReason(containsString("is prohibited"))); |
94 | } | 94 | } |
95 | - | ||
96 | - @Test | ||
97 | - public void testSaveAdminSettingsWithNewJsonStructure() throws Exception { | ||
98 | - loginSysAdmin(); | ||
99 | - AdminSettings adminSettings = doGet("/api/admin/settings/mail", AdminSettings.class); | ||
100 | - JsonNode json = adminSettings.getJsonValue(); | ||
101 | - ((ObjectNode) json).put("newKey", "my new value"); | ||
102 | - adminSettings.setJsonValue(json); | ||
103 | - doPost("/api/admin/settings", adminSettings) | ||
104 | - .andExpect(status().isBadRequest()) | ||
105 | - .andExpect(statusReason(containsString("Provided json structure is different"))); | ||
106 | - } | ||
107 | - | 95 | + |
108 | @Test | 96 | @Test |
109 | public void testSendTestMail() throws Exception { | 97 | public void testSendTestMail() throws Exception { |
110 | loginSysAdmin(); | 98 | loginSysAdmin(); |
@@ -73,9 +73,6 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { | @@ -73,9 +73,6 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { | ||
73 | if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) { | 73 | if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) { |
74 | throw new DataValidationException("Changing key of admin settings entry is prohibited!"); | 74 | throw new DataValidationException("Changing key of admin settings entry is prohibited!"); |
75 | } | 75 | } |
76 | - if (adminSettings.getKey().equals("mail")) { | ||
77 | - validateJsonStructure(existentAdminSettings.getJsonValue(), adminSettings.getJsonValue()); | ||
78 | - } | ||
79 | } | 76 | } |
80 | } | 77 | } |
81 | 78 |
@@ -15,9 +15,11 @@ | @@ -15,9 +15,11 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.sql.attributes; | 16 | package org.thingsboard.server.dao.sql.attributes; |
17 | 17 | ||
18 | +import org.springframework.data.jpa.repository.Modifying; | ||
18 | import org.springframework.data.jpa.repository.Query; | 19 | import org.springframework.data.jpa.repository.Query; |
19 | import org.springframework.data.repository.CrudRepository; | 20 | import org.springframework.data.repository.CrudRepository; |
20 | import org.springframework.data.repository.query.Param; | 21 | import org.springframework.data.repository.query.Param; |
22 | +import org.springframework.transaction.annotation.Transactional; | ||
21 | import org.thingsboard.server.common.data.EntityType; | 23 | import org.thingsboard.server.common.data.EntityType; |
22 | import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; | 24 | import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; |
23 | import org.thingsboard.server.dao.model.sql.AttributeKvEntity; | 25 | import org.thingsboard.server.dao.model.sql.AttributeKvEntity; |
@@ -34,5 +36,16 @@ public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, | @@ -34,5 +36,16 @@ public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, | ||
34 | List<AttributeKvEntity> findAllByEntityTypeAndEntityIdAndAttributeType(@Param("entityType") EntityType entityType, | 36 | List<AttributeKvEntity> findAllByEntityTypeAndEntityIdAndAttributeType(@Param("entityType") EntityType entityType, |
35 | @Param("entityId") String entityId, | 37 | @Param("entityId") String entityId, |
36 | @Param("attributeType") String attributeType); | 38 | @Param("attributeType") String attributeType); |
39 | + | ||
40 | + @Transactional | ||
41 | + @Modifying | ||
42 | + @Query("DELETE FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " + | ||
43 | + "AND a.id.entityId = :entityId " + | ||
44 | + "AND a.id.attributeType = :attributeType " + | ||
45 | + "AND a.id.attributeKey = :attributeKey") | ||
46 | + void delete(@Param("entityType") EntityType entityType, | ||
47 | + @Param("entityId") String entityId, | ||
48 | + @Param("attributeType") String attributeType, | ||
49 | + @Param("attributeKey") String attributeKey); | ||
37 | } | 50 | } |
38 | 51 |
@@ -138,16 +138,10 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl | @@ -138,16 +138,10 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl | ||
138 | 138 | ||
139 | @Override | 139 | @Override |
140 | public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String attributeType, List<String> keys) { | 140 | public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String attributeType, List<String> keys) { |
141 | - List<AttributeKvEntity> entitiesToDelete = keys | ||
142 | - .stream() | ||
143 | - .map(key -> { | ||
144 | - AttributeKvEntity entityToDelete = new AttributeKvEntity(); | ||
145 | - entityToDelete.setId(new AttributeKvCompositeKey(entityId.getEntityType(), fromTimeUUID(entityId.getId()), attributeType, key)); | ||
146 | - return entityToDelete; | ||
147 | - }).collect(Collectors.toList()); | ||
148 | - | ||
149 | return service.submit(() -> { | 141 | return service.submit(() -> { |
150 | - attributeKvRepository.deleteAll(entitiesToDelete); | 142 | + keys.forEach(key -> |
143 | + attributeKvRepository.delete(entityId.getEntityType(), UUIDConverter.fromTimeUUID(entityId.getId()), attributeType, key) | ||
144 | + ); | ||
151 | return null; | 145 | return null; |
152 | }); | 146 | }); |
153 | } | 147 | } |
dao/src/main/java/org/thingsboard/server/dao/sql/relation/AbstractRelationInsertRepository.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 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.dao.sql.relation; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.springframework.data.jpa.repository.Modifying; | ||
20 | +import org.thingsboard.server.dao.model.sql.RelationEntity; | ||
21 | + | ||
22 | +import javax.persistence.EntityManager; | ||
23 | +import javax.persistence.PersistenceContext; | ||
24 | +import javax.persistence.Query; | ||
25 | + | ||
26 | +@Slf4j | ||
27 | +public abstract class AbstractRelationInsertRepository implements RelationInsertRepository { | ||
28 | + | ||
29 | + @PersistenceContext | ||
30 | + protected EntityManager entityManager; | ||
31 | + | ||
32 | + protected Query getQuery(RelationEntity entity, String query) { | ||
33 | + Query nativeQuery = entityManager.createNativeQuery(query, RelationEntity.class); | ||
34 | + if (entity.getAdditionalInfo() == null) { | ||
35 | + nativeQuery.setParameter("additionalInfo", null); | ||
36 | + } else { | ||
37 | + nativeQuery.setParameter("additionalInfo", entity.getAdditionalInfo().toString()); | ||
38 | + } | ||
39 | + return nativeQuery | ||
40 | + .setParameter("fromId", entity.getFromId()) | ||
41 | + .setParameter("fromType", entity.getFromType()) | ||
42 | + .setParameter("toId", entity.getToId()) | ||
43 | + .setParameter("toType", entity.getToType()) | ||
44 | + .setParameter("relationTypeGroup", entity.getRelationTypeGroup()) | ||
45 | + .setParameter("relationType", entity.getRelationType()); | ||
46 | + } | ||
47 | + | ||
48 | + @Modifying | ||
49 | + protected abstract RelationEntity processSaveOrUpdate(RelationEntity entity); | ||
50 | + | ||
51 | +} |
dao/src/main/java/org/thingsboard/server/dao/sql/relation/HsqlRelationInsertRepository.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 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.dao.sql.relation; | ||
17 | + | ||
18 | +import org.springframework.stereotype.Repository; | ||
19 | +import org.springframework.transaction.annotation.Transactional; | ||
20 | +import org.thingsboard.server.dao.model.sql.RelationCompositeKey; | ||
21 | +import org.thingsboard.server.dao.model.sql.RelationEntity; | ||
22 | +import org.thingsboard.server.dao.util.HsqlDao; | ||
23 | +import org.thingsboard.server.dao.util.SqlDao; | ||
24 | + | ||
25 | +@HsqlDao | ||
26 | +@SqlDao | ||
27 | +@Repository | ||
28 | +@Transactional | ||
29 | +public class HsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { | ||
30 | + | ||
31 | + private static final String INSERT_ON_CONFLICT_DO_UPDATE = "MERGE INTO relation USING (VALUES :fromId, :fromType, :toId, :toType, :relationTypeGroup, :relationType, :additionalInfo) R " + | ||
32 | + "(from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info) " + | ||
33 | + "ON (relation.from_id = R.from_id AND relation.from_type = R.from_type AND relation.relation_type_group = R.relation_type_group AND relation.relation_type = R.relation_type AND relation.to_id = R.to_id AND relation.to_type = R.to_type) " + | ||
34 | + "WHEN MATCHED THEN UPDATE SET relation.additional_info = R.additional_info " + | ||
35 | + "WHEN NOT MATCHED THEN INSERT (from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info) VALUES (R.from_id, R.from_type, R.to_id, R.to_type, R.relation_type_group, R.relation_type, R.additional_info)"; | ||
36 | + | ||
37 | + @Override | ||
38 | + public RelationEntity saveOrUpdate(RelationEntity entity) { | ||
39 | + return processSaveOrUpdate(entity); | ||
40 | + } | ||
41 | + | ||
42 | + @Override | ||
43 | + protected RelationEntity processSaveOrUpdate(RelationEntity entity) { | ||
44 | + getQuery(entity, INSERT_ON_CONFLICT_DO_UPDATE).executeUpdate(); | ||
45 | + return entityManager.find(RelationEntity.class, new RelationCompositeKey(entity.toData())); | ||
46 | + } | ||
47 | +} |
@@ -58,6 +58,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple | @@ -58,6 +58,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple | ||
58 | @Autowired | 58 | @Autowired |
59 | private RelationRepository relationRepository; | 59 | private RelationRepository relationRepository; |
60 | 60 | ||
61 | + @Autowired | ||
62 | + private RelationInsertRepository relationInsertRepository; | ||
63 | + | ||
61 | @Override | 64 | @Override |
62 | public ListenableFuture<List<EntityRelation>> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) { | 65 | public ListenableFuture<List<EntityRelation>> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) { |
63 | return service.submit(() -> DaoUtil.convertDataList( | 66 | return service.submit(() -> DaoUtil.convertDataList( |
@@ -119,12 +122,12 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple | @@ -119,12 +122,12 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple | ||
119 | 122 | ||
120 | @Override | 123 | @Override |
121 | public boolean saveRelation(TenantId tenantId, EntityRelation relation) { | 124 | public boolean saveRelation(TenantId tenantId, EntityRelation relation) { |
122 | - return relationRepository.save(new RelationEntity(relation)) != null; | 125 | + return relationInsertRepository.saveOrUpdate(new RelationEntity(relation)) != null; |
123 | } | 126 | } |
124 | 127 | ||
125 | @Override | 128 | @Override |
126 | public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) { | 129 | public ListenableFuture<Boolean> saveRelationAsync(TenantId tenantId, EntityRelation relation) { |
127 | - return service.submit(() -> relationRepository.save(new RelationEntity(relation)) != null); | 130 | + return service.submit(() -> relationInsertRepository.saveOrUpdate(new RelationEntity(relation)) != null); |
128 | } | 131 | } |
129 | 132 | ||
130 | @Override | 133 | @Override |
dao/src/main/java/org/thingsboard/server/dao/sql/relation/PsqlRelationInsertRepository.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 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.dao.sql.relation; | ||
17 | + | ||
18 | +import org.springframework.stereotype.Repository; | ||
19 | +import org.springframework.transaction.annotation.Transactional; | ||
20 | +import org.thingsboard.server.dao.model.sql.RelationEntity; | ||
21 | +import org.thingsboard.server.dao.util.PsqlDao; | ||
22 | +import org.thingsboard.server.dao.util.SqlDao; | ||
23 | + | ||
24 | +@PsqlDao | ||
25 | +@SqlDao | ||
26 | +@Repository | ||
27 | +@Transactional | ||
28 | +public class PsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { | ||
29 | + | ||
30 | + private static final String INSERT_ON_CONFLICT_DO_UPDATE = "INSERT INTO relation (from_id, from_type, to_id, to_type, relation_type_group, relation_type, additional_info)" + | ||
31 | + " VALUES (:fromId, :fromType, :toId, :toType, :relationTypeGroup, :relationType, :additionalInfo) " + | ||
32 | + "ON CONFLICT (from_id, from_type, relation_type_group, relation_type, to_id, to_type) DO UPDATE SET additional_info = :additionalInfo returning *"; | ||
33 | + | ||
34 | + @Override | ||
35 | + public RelationEntity saveOrUpdate(RelationEntity entity) { | ||
36 | + return processSaveOrUpdate(entity); | ||
37 | + } | ||
38 | + | ||
39 | + @Override | ||
40 | + protected RelationEntity processSaveOrUpdate(RelationEntity entity) { | ||
41 | + return (RelationEntity) getQuery(entity, INSERT_ON_CONFLICT_DO_UPDATE).getSingleResult(); | ||
42 | + } | ||
43 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2020 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.dao.sql.relation; | ||
17 | + | ||
18 | +import org.thingsboard.server.dao.model.sql.RelationEntity; | ||
19 | + | ||
20 | +public interface RelationInsertRepository { | ||
21 | + | ||
22 | + RelationEntity saveOrUpdate(RelationEntity entity); | ||
23 | + | ||
24 | +} |
@@ -67,13 +67,4 @@ public abstract class BaseAdminSettingsServiceTest extends AbstractServiceTest { | @@ -67,13 +67,4 @@ public abstract class BaseAdminSettingsServiceTest extends AbstractServiceTest { | ||
67 | adminSettings.setKey("newKey"); | 67 | adminSettings.setKey("newKey"); |
68 | adminSettingsService.saveAdminSettings(SYSTEM_TENANT_ID, adminSettings); | 68 | adminSettingsService.saveAdminSettings(SYSTEM_TENANT_ID, adminSettings); |
69 | } | 69 | } |
70 | - | ||
71 | - @Test(expected = DataValidationException.class) | ||
72 | - public void testSaveAdminSettingsWithNewJsonStructure() { | ||
73 | - AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(SYSTEM_TENANT_ID, "mail"); | ||
74 | - JsonNode json = adminSettings.getJsonValue(); | ||
75 | - ((ObjectNode) json).put("newKey", "my new value"); | ||
76 | - adminSettings.setJsonValue(json); | ||
77 | - adminSettingsService.saveAdminSettings(SYSTEM_TENANT_ID, adminSettings); | ||
78 | - } | ||
79 | } | 70 | } |
@@ -25,7 +25,7 @@ export default function AdminController(adminService, toast, $scope, $rootScope, | @@ -25,7 +25,7 @@ export default function AdminController(adminService, toast, $scope, $rootScope, | ||
25 | return protocol; | 25 | return protocol; |
26 | }); | 26 | }); |
27 | 27 | ||
28 | - vm.tlsVersions = ['TLSv1.0', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']; | 28 | + vm.tlsVersions = ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']; |
29 | 29 | ||
30 | $translate('admin.test-mail-sent').then(function (translation) { | 30 | $translate('admin.test-mail-sent').then(function (translation) { |
31 | vm.testMailSent = translation; | 31 | vm.testMailSent = translation; |
@@ -104,26 +104,32 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { | @@ -104,26 +104,32 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { | ||
104 | } | 104 | } |
105 | 105 | ||
106 | var colorsCount = options.levelColors.length; | 106 | var colorsCount = options.levelColors.length; |
107 | - var inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; | 107 | + const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; |
108 | + var isColorProperty = angular.isString(options.levelColors[0]); | ||
109 | + | ||
108 | options.colorsRange = []; | 110 | options.colorsRange = []; |
109 | if (options.neonGlowBrightness) { | 111 | if (options.neonGlowBrightness) { |
110 | options.neonColorsRange = []; | 112 | options.neonColorsRange = []; |
111 | } | 113 | } |
112 | - for (var i = 0; i < options.levelColors.length; i++) { | ||
113 | - var percentage = inc * i; | ||
114 | - var tColor = tinycolor(options.levelColors[i]); | ||
115 | - options.colorsRange[i] = { | ||
116 | - pct: percentage, | ||
117 | - color: tColor.toRgb(), | ||
118 | - rgbString: tColor.toRgbString() | ||
119 | - }; | ||
120 | - if (options.neonGlowBrightness) { | ||
121 | - tColor = tinycolor(options.levelColors[i]).brighten(options.neonGlowBrightness); | ||
122 | - options.neonColorsRange[i] = { | 114 | + |
115 | + for (let i = 0; i < options.levelColors.length; i++) { | ||
116 | + const levelColor = options.levelColors[i]; | ||
117 | + if (levelColor !== null) { | ||
118 | + let percentage = isColorProperty ? inc * i : CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue); | ||
119 | + let tColor = tinycolor(isColorProperty ? levelColor : levelColor.color); | ||
120 | + options.colorsRange.push({ | ||
123 | pct: percentage, | 121 | pct: percentage, |
124 | color: tColor.toRgb(), | 122 | color: tColor.toRgb(), |
125 | rgbString: tColor.toRgbString() | 123 | rgbString: tColor.toRgbString() |
126 | - }; | 124 | + }); |
125 | + if (options.neonGlowBrightness) { | ||
126 | + tColor = tinycolor(isColorProperty ? levelColor : levelColor.color).brighten(options.neonGlowBrightness); | ||
127 | + options.neonColorsRange.push({ | ||
128 | + pct: percentage, | ||
129 | + color: tColor.toRgb(), | ||
130 | + rgbString: tColor.toRgbString() | ||
131 | + }); | ||
132 | + } | ||
127 | } | 133 | } |
128 | } | 134 | } |
129 | 135 | ||
@@ -137,6 +143,17 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { | @@ -137,6 +143,17 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { | ||
137 | return canvasGauges.BaseGauge.configure(options); | 143 | return canvasGauges.BaseGauge.configure(options); |
138 | } | 144 | } |
139 | 145 | ||
146 | + static normalizeValue (value, min, max) { | ||
147 | + let normalValue = (value - min) / (max - min); | ||
148 | + if (normalValue <= 0) { | ||
149 | + return 0; | ||
150 | + } | ||
151 | + if (normalValue >= 1) { | ||
152 | + return 1; | ||
153 | + } | ||
154 | + return normalValue; | ||
155 | + } | ||
156 | + | ||
140 | destroy() { | 157 | destroy() { |
141 | this.contextValueClone = null; | 158 | this.contextValueClone = null; |
142 | this.elementValueClone = null; | 159 | this.elementValueClone = null; |
@@ -50,10 +50,16 @@ export default class TbCanvasDigitalGauge { | @@ -50,10 +50,16 @@ export default class TbCanvasDigitalGauge { | ||
50 | this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; | 50 | this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; |
51 | this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString(); | 51 | this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString(); |
52 | 52 | ||
53 | - if (!settings.levelColors || settings.levelColors.length <= 0) { | ||
54 | - this.localSettings.levelColors = [keyColor]; | 53 | + this.localSettings.useFixedLevelColor = settings.useFixedLevelColor || false; |
54 | + if (!settings.useFixedLevelColor) { | ||
55 | + if (!settings.levelColors || settings.levelColors.length <= 0) { | ||
56 | + this.localSettings.levelColors = [keyColor]; | ||
57 | + } else { | ||
58 | + this.localSettings.levelColors = settings.levelColors.slice(); | ||
59 | + } | ||
55 | } else { | 60 | } else { |
56 | - this.localSettings.levelColors = settings.levelColors.slice(); | 61 | + this.localSettings.levelColors = [keyColor]; |
62 | + this.localSettings.fixedLevelColors = settings.fixedLevelColors || []; | ||
57 | } | 63 | } |
58 | 64 | ||
59 | this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : | 65 | this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : |
@@ -191,15 +197,137 @@ export default class TbCanvasDigitalGauge { | @@ -191,15 +197,137 @@ export default class TbCanvasDigitalGauge { | ||
191 | }; | 197 | }; |
192 | 198 | ||
193 | this.gauge = new CanvasDigitalGauge(gaugeData).draw(); | 199 | this.gauge = new CanvasDigitalGauge(gaugeData).draw(); |
200 | + this.init(); | ||
201 | + } | ||
202 | + | ||
203 | + init() { | ||
204 | + if (this.localSettings.useFixedLevelColor) { | ||
205 | + if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) { | ||
206 | + this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); | ||
207 | + this.updateLevelColors(this.localSettings.levelColors); | ||
208 | + } | ||
209 | + } | ||
210 | + } | ||
211 | + | ||
212 | + settingLevelColorsSubscribe(options) { | ||
213 | + let levelColorsDatasource = []; | ||
214 | + let predefineLevelColors = []; | ||
215 | + | ||
216 | + function setLevelColor(levelSetting, color) { | ||
217 | + if (levelSetting.valueSource === 'predefinedValue' && isFinite(levelSetting.value)) { | ||
218 | + predefineLevelColors.push({ | ||
219 | + value: levelSetting.value, | ||
220 | + color: color | ||
221 | + }) | ||
222 | + } else if (levelSetting.entityAlias && levelSetting.attribute) { | ||
223 | + let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias); | ||
224 | + if (!entityAliasId) { | ||
225 | + return; | ||
226 | + } | ||
227 | + | ||
228 | + let datasource = levelColorsDatasource.filter((datasource) => { | ||
229 | + return datasource.entityAliasId === entityAliasId; | ||
230 | + })[0]; | ||
231 | + | ||
232 | + let dataKey = { | ||
233 | + type: this.ctx.$scope.$injector.get('types').dataKeyType.attribute, | ||
234 | + name: levelSetting.attribute, | ||
235 | + label: levelSetting.attribute, | ||
236 | + settings: [{ | ||
237 | + color: color, | ||
238 | + index: predefineLevelColors.length | ||
239 | + }], | ||
240 | + _hash: Math.random() | ||
241 | + }; | ||
242 | + | ||
243 | + if (datasource) { | ||
244 | + let findDataKey = datasource.dataKeys.filter((dataKey) => { | ||
245 | + return dataKey.name === levelSetting.attribute; | ||
246 | + })[0]; | ||
247 | + | ||
248 | + if (findDataKey) { | ||
249 | + findDataKey.settings.push({ | ||
250 | + color: color, | ||
251 | + index: predefineLevelColors.length | ||
252 | + }); | ||
253 | + } else { | ||
254 | + datasource.dataKeys.push(dataKey) | ||
255 | + } | ||
256 | + } else { | ||
257 | + datasource = { | ||
258 | + type: this.ctx.$scope.$injector.get('types').datasourceType.entity, | ||
259 | + name: levelSetting.entityAlias, | ||
260 | + aliasName: levelSetting.entityAlias, | ||
261 | + entityAliasId: entityAliasId, | ||
262 | + dataKeys: [dataKey] | ||
263 | + }; | ||
264 | + levelColorsDatasource.push(datasource); | ||
265 | + } | ||
266 | + | ||
267 | + predefineLevelColors.push(null); | ||
268 | + } | ||
269 | + } | ||
270 | + | ||
271 | + for (let i = 0; i < options.length; i++) { | ||
272 | + let levelColor = options[i]; | ||
273 | + if (levelColor.from) { | ||
274 | + setLevelColor.call(this, levelColor.from, levelColor.color); | ||
275 | + } | ||
276 | + if (levelColor.to) { | ||
277 | + setLevelColor.call(this, levelColor.to, levelColor.color); | ||
278 | + } | ||
279 | + } | ||
194 | 280 | ||
281 | + this.subscribeLevelColorsAttributes(levelColorsDatasource); | ||
282 | + | ||
283 | + return predefineLevelColors; | ||
284 | + } | ||
285 | + | ||
286 | + updateLevelColors(levelColors) { | ||
287 | + this.gauge.options.levelColors = levelColors; | ||
288 | + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options); | ||
289 | + this.gauge.update(); | ||
290 | + } | ||
291 | + | ||
292 | + subscribeLevelColorsAttributes(datasources) { | ||
293 | + let TbCanvasDigitalGauge = this; | ||
294 | + let levelColorsSourcesSubscriptionOptions = { | ||
295 | + datasources: datasources, | ||
296 | + useDashboardTimewindow: false, | ||
297 | + type: this.ctx.$scope.$injector.get('types').widgetType.latest.value, | ||
298 | + callbacks: { | ||
299 | + onDataUpdated: (subscription) => { | ||
300 | + for (let i = 0; i < subscription.data.length; i++) { | ||
301 | + let keyData = subscription.data[i]; | ||
302 | + if (keyData && keyData.data && keyData.data[0]) { | ||
303 | + let attrValue = keyData.data[0][1]; | ||
304 | + if (isFinite(attrValue)) { | ||
305 | + for (let i = 0; i < keyData.dataKey.settings.length; i++) { | ||
306 | + let setting = keyData.dataKey.settings[i]; | ||
307 | + this.localSettings.levelColors[setting.index] = { | ||
308 | + value: attrValue, | ||
309 | + color: setting.color | ||
310 | + }; | ||
311 | + } | ||
312 | + } | ||
313 | + } | ||
314 | + } | ||
315 | + this.updateLevelColors(this.localSettings.levelColors); | ||
316 | + } | ||
317 | + } | ||
318 | + }; | ||
319 | + this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then( | ||
320 | + (subscription) => { | ||
321 | + TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription; | ||
322 | + } | ||
323 | + ); | ||
195 | } | 324 | } |
196 | 325 | ||
197 | update() { | 326 | update() { |
198 | if (this.ctx.data.length > 0) { | 327 | if (this.ctx.data.length > 0) { |
199 | var cellData = this.ctx.data[0]; | 328 | var cellData = this.ctx.data[0]; |
200 | if (cellData.data.length > 0) { | 329 | if (cellData.data.length > 0) { |
201 | - var tvPair = cellData.data[cellData.data.length - | ||
202 | - 1]; | 330 | + var tvPair = cellData.data[cellData.data.length - 1]; |
203 | var timestamp; | 331 | var timestamp; |
204 | if (this.localSettings.showTimestamp) { | 332 | if (this.localSettings.showTimestamp) { |
205 | timestamp = tvPair[0]; | 333 | timestamp = tvPair[0]; |
@@ -325,6 +453,11 @@ export default class TbCanvasDigitalGauge { | @@ -325,6 +453,11 @@ export default class TbCanvasDigitalGauge { | ||
325 | "type": "string", | 453 | "type": "string", |
326 | "default": null | 454 | "default": null |
327 | }, | 455 | }, |
456 | + "useFixedLevelColor": { | ||
457 | + "title": "Use precise value for the color indicator", | ||
458 | + "type": "boolean", | ||
459 | + "default": false | ||
460 | + }, | ||
328 | "levelColors": { | 461 | "levelColors": { |
329 | "title": "Colors of indicator, from lower to upper", | 462 | "title": "Colors of indicator, from lower to upper", |
330 | "type": "array", | 463 | "type": "array", |
@@ -333,6 +466,66 @@ export default class TbCanvasDigitalGauge { | @@ -333,6 +466,66 @@ export default class TbCanvasDigitalGauge { | ||
333 | "type": "string" | 466 | "type": "string" |
334 | } | 467 | } |
335 | }, | 468 | }, |
469 | + "fixedLevelColors": { | ||
470 | + "title": "The colors for the indicator using boundary values", | ||
471 | + "type": "array", | ||
472 | + "items": { | ||
473 | + "title": "levelColor", | ||
474 | + "type": "object", | ||
475 | + "properties": { | ||
476 | + "from": { | ||
477 | + "title": "From", | ||
478 | + "type": "object", | ||
479 | + "properties": { | ||
480 | + "valueSource": { | ||
481 | + "title": "[From] Value source", | ||
482 | + "type": "string", | ||
483 | + "default": "predefinedValue" | ||
484 | + }, | ||
485 | + "entityAlias": { | ||
486 | + "title": "[From] Source entity alias", | ||
487 | + "type": "string" | ||
488 | + }, | ||
489 | + "attribute": { | ||
490 | + "title": "[From] Source entity attribute", | ||
491 | + "type": "string" | ||
492 | + }, | ||
493 | + "value": { | ||
494 | + "title": "[From] Value (if predefined value is selected)", | ||
495 | + "type": "number" | ||
496 | + } | ||
497 | + } | ||
498 | + }, | ||
499 | + "to": { | ||
500 | + "title": "To", | ||
501 | + "type": "object", | ||
502 | + "properties": { | ||
503 | + "valueSource": { | ||
504 | + "title": "[To] Value source", | ||
505 | + "type": "string", | ||
506 | + "default": "predefinedValue" | ||
507 | + }, | ||
508 | + "entityAlias": { | ||
509 | + "title": "[To] Source entity alias", | ||
510 | + "type": "string" | ||
511 | + }, | ||
512 | + "attribute": { | ||
513 | + "title": "[To] Source entity attribute", | ||
514 | + "type": "string" | ||
515 | + }, | ||
516 | + "value": { | ||
517 | + "title": "[To] Value (if predefined value is selected)", | ||
518 | + "type": "number" | ||
519 | + } | ||
520 | + } | ||
521 | + }, | ||
522 | + "color": { | ||
523 | + "title": "Color", | ||
524 | + "type": "string" | ||
525 | + } | ||
526 | + } | ||
527 | + } | ||
528 | + }, | ||
336 | "animation": { | 529 | "animation": { |
337 | "title": "Enable animation", | 530 | "title": "Enable animation", |
338 | "type": "boolean", | 531 | "type": "boolean", |
@@ -521,8 +714,10 @@ export default class TbCanvasDigitalGauge { | @@ -521,8 +714,10 @@ export default class TbCanvasDigitalGauge { | ||
521 | "key": "gaugeColor", | 714 | "key": "gaugeColor", |
522 | "type": "color" | 715 | "type": "color" |
523 | }, | 716 | }, |
717 | + "useFixedLevelColor", | ||
524 | { | 718 | { |
525 | "key": "levelColors", | 719 | "key": "levelColors", |
720 | + "condition": "model.useFixedLevelColor !== true", | ||
526 | "items": [ | 721 | "items": [ |
527 | { | 722 | { |
528 | "key": "levelColors[]", | 723 | "key": "levelColors[]", |
@@ -530,6 +725,52 @@ export default class TbCanvasDigitalGauge { | @@ -530,6 +725,52 @@ export default class TbCanvasDigitalGauge { | ||
530 | } | 725 | } |
531 | ] | 726 | ] |
532 | }, | 727 | }, |
728 | + { | ||
729 | + "key": "fixedLevelColors", | ||
730 | + "condition": "model.useFixedLevelColor === true", | ||
731 | + "items": [ | ||
732 | + { | ||
733 | + "key": "fixedLevelColors[].from.valueSource", | ||
734 | + "type": "rc-select", | ||
735 | + "multiple": false, | ||
736 | + "items": [ | ||
737 | + { | ||
738 | + "value": "predefinedValue", | ||
739 | + "label": "Predefined value (Default)" | ||
740 | + }, | ||
741 | + { | ||
742 | + "value": "entityAttribute", | ||
743 | + "label": "Value taken from entity attribute" | ||
744 | + } | ||
745 | + ] | ||
746 | + }, | ||
747 | + "fixedLevelColors[].from.value", | ||
748 | + "fixedLevelColors[].from.entityAlias", | ||
749 | + "fixedLevelColors[].from.attribute", | ||
750 | + { | ||
751 | + "key": "fixedLevelColors[].to.valueSource", | ||
752 | + "type": "rc-select", | ||
753 | + "multiple": false, | ||
754 | + "items": [ | ||
755 | + { | ||
756 | + "value": "predefinedValue", | ||
757 | + "label": "Predefined value (Default)" | ||
758 | + }, | ||
759 | + { | ||
760 | + "value": "entityAttribute", | ||
761 | + "label": "Value taken from entity attribute" | ||
762 | + } | ||
763 | + ] | ||
764 | + }, | ||
765 | + "fixedLevelColors[].to.value", | ||
766 | + "fixedLevelColors[].to.entityAlias", | ||
767 | + "fixedLevelColors[].to.attribute", | ||
768 | + { | ||
769 | + "key": "fixedLevelColors[].color", | ||
770 | + "type": "color" | ||
771 | + } | ||
772 | + ] | ||
773 | + }, | ||
533 | "animation", | 774 | "animation", |
534 | "animationDuration", | 775 | "animationDuration", |
535 | { | 776 | { |