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 | 92 | .andExpect(status().isBadRequest()) |
93 | 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 | 96 | @Test |
109 | 97 | public void testSendTestMail() throws Exception { |
110 | 98 | loginSysAdmin(); | ... | ... |
... | ... | @@ -73,9 +73,6 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { |
73 | 73 | if (!existentAdminSettings.getKey().equals(adminSettings.getKey())) { |
74 | 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 | 15 | */ |
16 | 16 | package org.thingsboard.server.dao.sql.attributes; |
17 | 17 | |
18 | +import org.springframework.data.jpa.repository.Modifying; | |
18 | 19 | import org.springframework.data.jpa.repository.Query; |
19 | 20 | import org.springframework.data.repository.CrudRepository; |
20 | 21 | import org.springframework.data.repository.query.Param; |
22 | +import org.springframework.transaction.annotation.Transactional; | |
21 | 23 | import org.thingsboard.server.common.data.EntityType; |
22 | 24 | import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; |
23 | 25 | import org.thingsboard.server.dao.model.sql.AttributeKvEntity; |
... | ... | @@ -34,5 +36,16 @@ public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, |
34 | 36 | List<AttributeKvEntity> findAllByEntityTypeAndEntityIdAndAttributeType(@Param("entityType") EntityType entityType, |
35 | 37 | @Param("entityId") String entityId, |
36 | 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 | 138 | |
139 | 139 | @Override |
140 | 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 | 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 | 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 | +} | |
\ No newline at end of file | ... | ... |
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 | 58 | @Autowired |
59 | 59 | private RelationRepository relationRepository; |
60 | 60 | |
61 | + @Autowired | |
62 | + private RelationInsertRepository relationInsertRepository; | |
63 | + | |
61 | 64 | @Override |
62 | 65 | public ListenableFuture<List<EntityRelation>> findAllByFrom(TenantId tenantId, EntityId from, RelationTypeGroup typeGroup) { |
63 | 66 | return service.submit(() -> DaoUtil.convertDataList( |
... | ... | @@ -119,12 +122,12 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple |
119 | 122 | |
120 | 123 | @Override |
121 | 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 | 128 | @Override |
126 | 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 | 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 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -67,13 +67,4 @@ public abstract class BaseAdminSettingsServiceTest extends AbstractServiceTest { |
67 | 67 | adminSettings.setKey("newKey"); |
68 | 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 | 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 | 30 | $translate('admin.test-mail-sent').then(function (translation) { |
31 | 31 | vm.testMailSent = translation; | ... | ... |
... | ... | @@ -104,26 +104,32 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { |
104 | 104 | } |
105 | 105 | |
106 | 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 | 110 | options.colorsRange = []; |
109 | 111 | if (options.neonGlowBrightness) { |
110 | 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 | 121 | pct: percentage, |
124 | 122 | color: tColor.toRgb(), |
125 | 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 | 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 | 157 | destroy() { |
141 | 158 | this.contextValueClone = null; |
142 | 159 | this.elementValueClone = null; | ... | ... |
... | ... | @@ -50,10 +50,16 @@ export default class TbCanvasDigitalGauge { |
50 | 50 | this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; |
51 | 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 | 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 | 65 | this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : |
... | ... | @@ -191,15 +197,137 @@ export default class TbCanvasDigitalGauge { |
191 | 197 | }; |
192 | 198 | |
193 | 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 | 326 | update() { |
198 | 327 | if (this.ctx.data.length > 0) { |
199 | 328 | var cellData = this.ctx.data[0]; |
200 | 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 | 331 | var timestamp; |
204 | 332 | if (this.localSettings.showTimestamp) { |
205 | 333 | timestamp = tvPair[0]; |
... | ... | @@ -325,6 +453,11 @@ export default class TbCanvasDigitalGauge { |
325 | 453 | "type": "string", |
326 | 454 | "default": null |
327 | 455 | }, |
456 | + "useFixedLevelColor": { | |
457 | + "title": "Use precise value for the color indicator", | |
458 | + "type": "boolean", | |
459 | + "default": false | |
460 | + }, | |
328 | 461 | "levelColors": { |
329 | 462 | "title": "Colors of indicator, from lower to upper", |
330 | 463 | "type": "array", |
... | ... | @@ -333,6 +466,66 @@ export default class TbCanvasDigitalGauge { |
333 | 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 | 529 | "animation": { |
337 | 530 | "title": "Enable animation", |
338 | 531 | "type": "boolean", |
... | ... | @@ -521,8 +714,10 @@ export default class TbCanvasDigitalGauge { |
521 | 714 | "key": "gaugeColor", |
522 | 715 | "type": "color" |
523 | 716 | }, |
717 | + "useFixedLevelColor", | |
524 | 718 | { |
525 | 719 | "key": "levelColors", |
720 | + "condition": "model.useFixedLevelColor !== true", | |
526 | 721 | "items": [ |
527 | 722 | { |
528 | 723 | "key": "levelColors[]", |
... | ... | @@ -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 | 774 | "animation", |
534 | 775 | "animationDuration", |
535 | 776 | { | ... | ... |