Commit c55ddd9f715574f2a78660155d297dc054da1441

Authored by Igor Kulikov
2 parents 63e0a0be 0e6b3b49

Merge with master

... ... @@ -14,24 +14,6 @@
14 14 -- limitations under the License.
15 15 --
16 16
17   --- call check_version();
18   -
19   -CREATE OR REPLACE PROCEDURE check_version(INOUT valid_version boolean) LANGUAGE plpgsql AS $BODY$
20   -DECLARE
21   - current_version integer;
22   -BEGIN
23   - RAISE NOTICE 'Check the current installed PostgreSQL version...';
24   - SELECT current_setting('server_version_num') INTO current_version;
25   - IF current_version > 110000 THEN
26   - RAISE NOTICE 'PostgreSQL version is valid!';
27   - RAISE NOTICE 'Schema update started...';
28   - SELECT true INTO valid_version;
29   - ELSE
30   - RAISE NOTICE 'Postgres version should be at least more than 10!';
31   - END IF;
32   -END;
33   -$BODY$;
34   -
35 17 -- call create_partition_ts_kv_table();
36 18
37 19 CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table() LANGUAGE plpgsql AS $$
... ...
... ... @@ -14,25 +14,6 @@
14 14 -- limitations under the License.
15 15 --
16 16
17   --- call check_version();
18   -
19   -CREATE OR REPLACE PROCEDURE check_version(INOUT valid_version boolean) LANGUAGE plpgsql AS $BODY$
20   -
21   -DECLARE
22   - current_version integer;
23   -BEGIN
24   - RAISE NOTICE 'Check the current installed PostgreSQL version...';
25   - SELECT current_setting('server_version_num') INTO current_version;
26   - IF current_version > 110000 THEN
27   - RAISE NOTICE 'PostgreSQL version is valid!';
28   - RAISE NOTICE 'Schema update started...';
29   - SELECT true INTO valid_version;
30   - ELSE
31   - RAISE NOTICE 'Postgres version should be at least more than 10!';
32   - END IF;
33   -END;
34   -$BODY$;
35   -
36 17 -- call create_new_ts_kv_table();
37 18
38 19 CREATE OR REPLACE PROCEDURE create_new_ts_kv_table() LANGUAGE plpgsql AS $$
... ...
... ... @@ -32,11 +32,8 @@ import java.sql.Statement;
32 32 public abstract class AbstractSqlTsDatabaseUpgradeService {
33 33
34 34 protected static final String CALL_REGEX = "call ";
35   - protected static final String CHECK_VERSION = "check_version(false)";
36   - protected static final String CHECK_VERSION_TO_DELETE = "check_version(INOUT valid_version boolean)";
37 35 protected static final String DROP_TABLE = "DROP TABLE ";
38 36 protected static final String DROP_PROCEDURE_IF_EXISTS = "DROP PROCEDURE IF EXISTS ";
39   - protected static final String DROP_PROCEDURE_CHECK_VERSION = DROP_PROCEDURE_IF_EXISTS + CHECK_VERSION_TO_DELETE;
40 37
41 38 @Value("${spring.datasource.url}")
42 39 protected String dbUrl;
... ... @@ -58,13 +55,14 @@ public abstract class AbstractSqlTsDatabaseUpgradeService {
58 55 }
59 56
60 57 protected boolean checkVersion(Connection conn) {
61   - log.info("Check the current PostgreSQL version...");
62 58 boolean versionValid = false;
63 59 try {
64 60 Statement statement = conn.createStatement();
65   - ResultSet resultSet = statement.executeQuery(CALL_REGEX + CHECK_VERSION);
  61 + ResultSet resultSet = statement.executeQuery("SELECT current_setting('server_version_num')");
66 62 resultSet.next();
67   - versionValid = resultSet.getBoolean(1);
  63 + if(resultSet.getLong(1) > 110000) {
  64 + versionValid = true;
  65 + }
68 66 statement.close();
69 67 } catch (Exception e) {
70 68 log.info("Failed to check current PostgreSQL version due to: {}", e.getMessage());
... ...
... ... @@ -70,16 +70,15 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
70 70 switch (fromVersion) {
71 71 case "2.4.3":
72 72 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
73   - log.info("Updating timeseries schema ...");
74   - log.info("Load upgrade functions ...");
75   - loadSql(conn);
  73 + log.info("Check the current PostgreSQL version...");
76 74 boolean versionValid = checkVersion(conn);
77 75 if (!versionValid) {
78   - log.info("PostgreSQL version should be at least more than 11!");
79   - log.info("Please upgrade your PostgreSQL and restart the script!");
  76 + throw new RuntimeException("PostgreSQL version should be at least more than 11, please upgrade your PostgreSQL and restart the script!");
80 77 } else {
81 78 log.info("PostgreSQL version is valid!");
82   - log.info("Updating schema ...");
  79 + log.info("Load upgrade functions ...");
  80 + loadSql(conn);
  81 + log.info("Updating timeseries schema ...");
83 82 executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE);
84 83 executeQuery(conn, CALL_CREATE_PARTITIONS);
85 84 executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
... ... @@ -91,7 +90,6 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
91 90 executeQuery(conn, DROP_TABLE_TS_KV_OLD);
92 91 executeQuery(conn, DROP_TABLE_TS_KV_LATEST_OLD);
93 92
94   - executeQuery(conn, DROP_PROCEDURE_CHECK_VERSION);
95 93 executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITION_TS_KV_TABLE);
96 94 executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITIONS);
97 95 executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE);
... ...
... ... @@ -73,16 +73,15 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
73 73 switch (fromVersion) {
74 74 case "2.4.3":
75 75 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
76   - log.info("Updating timescale schema ...");
77   - log.info("Load upgrade functions ...");
78   - loadSql(conn);
  76 + log.info("Check the current PostgreSQL version...");
79 77 boolean versionValid = checkVersion(conn);
80 78 if (!versionValid) {
81   - log.info("PostgreSQL version should be at least more than 11!");
82   - log.info("Please upgrade your PostgreSQL and restart the script!");
  79 + throw new RuntimeException("PostgreSQL version should be at least more than 11, please upgrade your PostgreSQL and restart the script!");
83 80 } else {
84 81 log.info("PostgreSQL version is valid!");
85   - log.info("Updating schema ...");
  82 + log.info("Load upgrade functions ...");
  83 + loadSql(conn);
  84 + log.info("Updating timescale schema ...");
86 85 executeQuery(conn, CALL_CREATE_TS_KV_LATEST_TABLE);
87 86 executeQuery(conn, CALL_CREATE_NEW_TENANT_TS_KV_TABLE);
88 87
... ... @@ -105,7 +104,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
105 104 executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN json_v json;");
106 105 executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN json_v json;");
107 106
108   - log.info("schema timeseries updated!");
  107 + log.info("schema timescale updated!");
109 108 }
110 109 }
111 110 break;
... ...
... ... @@ -16,16 +16,21 @@
16 16 package org.thingsboard.rule.engine.kafka;
17 17
18 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.lang3.BooleanUtils;
19 20 import org.apache.kafka.clients.producer.*;
  21 +import org.apache.kafka.common.header.Headers;
  22 +import org.apache.kafka.common.header.internals.RecordHeader;
  23 +import org.apache.kafka.common.header.internals.RecordHeaders;
20 24 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
21 25 import org.thingsboard.rule.engine.api.*;
22 26 import org.thingsboard.server.common.data.plugin.ComponentType;
23 27 import org.thingsboard.server.common.msg.TbMsg;
24 28 import org.thingsboard.server.common.msg.TbMsgMetaData;
25 29
  30 +import java.nio.charset.Charset;
  31 +import java.nio.charset.StandardCharsets;
26 32 import java.util.Properties;
27 33 import java.util.concurrent.ExecutionException;
28   -import java.util.concurrent.atomic.AtomicInteger;
29 34
30 35 @Slf4j
31 36 @RuleNode(
... ... @@ -46,8 +51,11 @@ public class TbKafkaNode implements TbNode {
46 51 private static final String PARTITION = "partition";
47 52 private static final String TOPIC = "topic";
48 53 private static final String ERROR = "error";
  54 + public static final String TB_MSG_MD_PREFIX = "tb_msg_md_";
49 55
50 56 private TbKafkaNodeConfiguration config;
  57 + private boolean addMetadataKeyValuesAsKafkaHeaders;
  58 + private Charset toBytesCharset;
51 59
52 60 private Producer<?, String> producer;
53 61
... ... @@ -66,8 +74,10 @@ public class TbKafkaNode implements TbNode {
66 74 properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, config.getBufferMemory());
67 75 if (config.getOtherProperties() != null) {
68 76 config.getOtherProperties()
69   - .forEach((k,v) -> properties.put(k, v));
  77 + .forEach(properties::put);
70 78 }
  79 + addMetadataKeyValuesAsKafkaHeaders = BooleanUtils.toBooleanDefaultIfNull(config.isAddMetadataKeyValuesAsKafkaHeaders(), false);
  80 + toBytesCharset = config.getKafkaHeadersCharset() != null ? Charset.forName(config.getKafkaHeadersCharset()) : StandardCharsets.UTF_8;
71 81 try {
72 82 this.producer = new KafkaProducer<>(properties);
73 83 } catch (Exception e) {
... ... @@ -79,16 +89,16 @@ public class TbKafkaNode implements TbNode {
79 89 public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
80 90 String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg.getMetaData());
81 91 try {
82   - producer.send(new ProducerRecord<>(topic, msg.getData()),
83   - (metadata, e) -> {
84   - if (metadata != null) {
85   - TbMsg next = processResponse(ctx, msg, metadata);
86   - ctx.tellNext(next, TbRelationTypes.SUCCESS);
87   - } else {
88   - TbMsg next = processException(ctx, msg, e);
89   - ctx.tellFailure(next, e);
90   - }
91   - });
  92 + if (!addMetadataKeyValuesAsKafkaHeaders) {
  93 + producer.send(new ProducerRecord<>(topic, msg.getData()),
  94 + (metadata, e) -> processRecord(ctx, msg, metadata, e));
  95 + } else {
  96 + Headers headers = new RecordHeaders();
  97 + msg.getMetaData().values().forEach((key, value) -> headers.add(new RecordHeader(TB_MSG_MD_PREFIX + key, value.getBytes(toBytesCharset))));
  98 + producer.send(new ProducerRecord<>(topic, null, null, null, msg.getData(), headers),
  99 + (metadata, e) -> processRecord(ctx, msg, metadata, e));
  100 + }
  101 +
92 102 } catch (Exception e) {
93 103 ctx.tellFailure(msg, e);
94 104 }
... ... @@ -105,6 +115,16 @@ public class TbKafkaNode implements TbNode {
105 115 }
106 116 }
107 117
  118 + private void processRecord(TbContext ctx, TbMsg msg, RecordMetadata metadata, Exception e) {
  119 + if (metadata != null) {
  120 + TbMsg next = processResponse(ctx, msg, metadata);
  121 + ctx.tellNext(next, TbRelationTypes.SUCCESS);
  122 + } else {
  123 + TbMsg next = processException(ctx, msg, e);
  124 + ctx.tellFailure(next, e);
  125 + }
  126 + }
  127 +
108 128 private TbMsg processResponse(TbContext ctx, TbMsg origMsg, RecordMetadata recordMetadata) {
109 129 TbMsgMetaData metaData = origMsg.getMetaData().copy();
110 130 metaData.putValue(OFFSET, String.valueOf(recordMetadata.offset()));
... ...
... ... @@ -36,6 +36,9 @@ public class TbKafkaNodeConfiguration implements NodeConfiguration<TbKafkaNodeCo
36 36 private String valueSerializer;
37 37 private Map<String, String> otherProperties;
38 38
  39 + private boolean addMetadataKeyValuesAsKafkaHeaders;
  40 + private String kafkaHeadersCharset;
  41 +
39 42 @Override
40 43 public TbKafkaNodeConfiguration defaultConfiguration() {
41 44 TbKafkaNodeConfiguration configuration = new TbKafkaNodeConfiguration();
... ... @@ -49,6 +52,8 @@ public class TbKafkaNodeConfiguration implements NodeConfiguration<TbKafkaNodeCo
49 52 configuration.setKeySerializer(StringSerializer.class.getName());
50 53 configuration.setValueSerializer(StringSerializer.class.getName());
51 54 configuration.setOtherProperties(Collections.emptyMap());
  55 + configuration.setAddMetadataKeyValuesAsKafkaHeaders(false);
  56 + configuration.setKafkaHeadersCharset("UTF-8");
52 57 return configuration;
53 58 }
54 59 }
... ...
... ... @@ -51,6 +51,10 @@ let defaultDigitalGaugeOptions = Object.assign({}, canvasGauges.GenericOptions,
51 51
52 52 neonGlowBrightness: 0,
53 53
  54 + colorTicks: 'gray',
  55 + tickWidth: 4,
  56 + ticks: [],
  57 +
54 58 isMobile: false
55 59
56 60 });
... ... @@ -133,6 +137,13 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge {
133 137 }
134 138 }
135 139
  140 + options.ticksValue = [];
  141 + for(let i = 0; i < options.ticks.length; i++){
  142 + if(options.ticks[i] !== null){
  143 + options.ticksValue.push(CanvasDigitalGauge.normalizeValue(options.ticks[i], options.minValue, options.maxValue))
  144 + }
  145 + }
  146 +
136 147 if (options.neonGlowBrightness) {
137 148 options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
138 149 options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString();
... ... @@ -729,6 +740,48 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is
729 740 context.stroke();
730 741 }
731 742
  743 +function drawTickArc(context, tickValues, Cx, Cy, Ri, Rm, Ro, startAngle, endAngle, color, tickWidth) {
  744 + if(!tickValues.length) {
  745 + return;
  746 + }
  747 +
  748 + const strokeWidth = Ro - Ri;
  749 + context.beginPath();
  750 + context.lineWidth = tickWidth;
  751 + context.strokeStyle = color;
  752 + for (let i = 0; i < tickValues.length; i++) {
  753 + var angle = startAngle + tickValues[i] * endAngle;
  754 + var x1 = Cx + (Ri + strokeWidth) * Math.cos(angle);
  755 + var y1 = Cy + (Ri + strokeWidth) * Math.sin(angle);
  756 + var x2 = Cx + Ri * Math.cos(angle);
  757 + var y2 = Cy + Ri * Math.sin(angle);
  758 + context.moveTo(x1, y1);
  759 + context.lineTo(x2, y2);
  760 + }
  761 + context.stroke();
  762 +}
  763 +
  764 +function drawTickBar(context, tickValues, startX, startY, distanceBar, strokeWidth, isVertical, color, tickWidth) {
  765 + if(!tickValues.length) {
  766 + return;
  767 + }
  768 +
  769 + context.beginPath();
  770 + context.lineWidth = tickWidth;
  771 + context.strokeStyle = color;
  772 + for (let i = 0; i < tickValues.length; i++) {
  773 + let tickValue = tickValues[i] * distanceBar;
  774 + if (isVertical) {
  775 + context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar);
  776 + context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar);
  777 + } else {
  778 + context.moveTo(startX + tickValue, startY);
  779 + context.lineTo(startX + tickValue, startY + strokeWidth);
  780 + }
  781 + }
  782 + context.stroke();
  783 +}
  784 +
732 785 function drawProgress(context, options, progress) {
733 786 var neonColor;
734 787 if (options.neonGlowBrightness) {
... ... @@ -759,6 +812,7 @@ function drawProgress(context, options, progress) {
759 812 if (options.neonGlowBrightness && !options.isMobile) {
760 813 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle);
761 814 }
  815 + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle, options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth);
762 816 } else if (options.gaugeType === 'arc') {
763 817 if (options.neonGlowBrightness) {
764 818 context.strokeStyle = neonColor;
... ... @@ -769,6 +823,7 @@ function drawProgress(context, options, progress) {
769 823 if (options.neonGlowBrightness && !options.isMobile) {
770 824 drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false);
771 825 }
  826 + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth);
772 827 } else if (options.gaugeType === 'horizontalBar') {
773 828 if (options.neonGlowBrightness) {
774 829 context.strokeStyle = neonColor;
... ... @@ -781,6 +836,7 @@ function drawProgress(context, options, progress) {
781 836 drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2,
782 837 neonColor, strokeWidth, false);
783 838 }
  839 + drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, false, options.colorTicks, options.tickWidth);
784 840 } else if (options.gaugeType === 'verticalBar') {
785 841 if (options.neonGlowBrightness) {
786 842 context.strokeStyle = neonColor;
... ... @@ -793,6 +849,7 @@ function drawProgress(context, options, progress) {
793 849 drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress,
794 850 neonColor, strokeWidth, true);
795 851 }
  852 + drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth, true, options.colorTicks, options.tickWidth);
796 853 }
797 854 }
798 855
... ...
... ... @@ -62,6 +62,12 @@ export default class TbCanvasDigitalGauge {
62 62 this.localSettings.fixedLevelColors = settings.fixedLevelColors || [];
63 63 }
64 64
  65 + this.localSettings.showTicks = settings.showTicks || false;
  66 + this.localSettings.ticks = [];
  67 + this.localSettings.ticksValue = settings.ticksValue || [];
  68 + this.localSettings.tickWidth = settings.tickWidth || 4;
  69 + this.localSettings.colorTicks = settings.colorTicks || '#666';
  70 +
65 71 this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals :
66 72 ((angular.isDefined(settings.decimals) && settings.decimals !== null)
67 73 ? settings.decimals : ctx.decimals);
... ... @@ -145,6 +151,10 @@ export default class TbCanvasDigitalGauge {
145 151 gaugeColor: this.localSettings.gaugeColor,
146 152 levelColors: this.localSettings.levelColors,
147 153
  154 + colorTicks: this.localSettings.colorTicks,
  155 + tickWidth: this.localSettings.tickWidth,
  156 + ticks: this.localSettings.ticks,
  157 +
148 158 title: this.localSettings.title,
149 159
150 160 fontTitleSize: this.localSettings.titleFont.size,
... ... @@ -204,9 +214,81 @@ export default class TbCanvasDigitalGauge {
204 214 if (this.localSettings.useFixedLevelColor) {
205 215 if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
206 216 this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
207   - this.updateLevelColors(this.localSettings.levelColors);
208 217 }
209 218 }
  219 + if (this.localSettings.showTicks) {
  220 + if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) {
  221 + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue);
  222 + }
  223 + }
  224 + this.updateSetting();
  225 + }
  226 +
  227 + static generateDatasorce(ctx, datasources, entityAlias, attribute, settings){
  228 + let entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias);
  229 + if (!entityAliasId) {
  230 + throw new Error('Not valid entity aliase name ' + entityAlias);
  231 + }
  232 +
  233 + let datasource = datasources.filter((datasource) => {
  234 + return datasource.entityAliasId === entityAliasId;
  235 + })[0];
  236 +
  237 + let dataKey = {
  238 + type: ctx.$scope.$injector.get('types').dataKeyType.attribute,
  239 + name: attribute,
  240 + label: attribute,
  241 + settings: [settings],
  242 + _hash: Math.random()
  243 + };
  244 +
  245 + if (datasource) {
  246 + let findDataKey = datasource.dataKeys.filter((dataKey) => {
  247 + return dataKey.name === attribute;
  248 + })[0];
  249 +
  250 + if (findDataKey) {
  251 + findDataKey.settings.push(settings);
  252 + } else {
  253 + datasource.dataKeys.push(dataKey)
  254 + }
  255 + } else {
  256 + datasource = {
  257 + type: ctx.$scope.$injector.get('types').datasourceType.entity,
  258 + name: entityAlias,
  259 + aliasName: entityAlias,
  260 + entityAliasId: entityAliasId,
  261 + dataKeys: [dataKey]
  262 + };
  263 + datasources.push(datasource);
  264 + }
  265 +
  266 + return datasources;
  267 + }
  268 +
  269 + settingTicksSubscribe(options) {
  270 + let ticksDatasource = [];
  271 + let predefineTicks = [];
  272 +
  273 + for (let i = 0; i < options.length; i++) {
  274 + let tick = options[i];
  275 + if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) {
  276 + predefineTicks.push(tick.value)
  277 + } else if (tick.entityAlias && tick.attribute) {
  278 + try {
  279 + ticksDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length);
  280 + } catch (e) {
  281 + continue;
  282 + }
  283 + predefineTicks.push(null);
  284 + }
  285 + }
  286 +
  287 + this.subscribeAttributes(ticksDatasource, 'ticks').then((subscription) => {
  288 + this.ticksSourcesSubscription = subscription;
  289 + });
  290 +
  291 + return predefineTicks;
210 292 }
211 293
212 294 settingLevelColorsSubscribe(options) {
... ... @@ -220,50 +302,14 @@ export default class TbCanvasDigitalGauge {
220 302 color: color
221 303 })
222 304 } 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: [{
  305 + try {
  306 + levelColorsDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, levelColorsDatasource, levelSetting.entityAlias, levelSetting.attribute, {
237 307 color: color,
238 308 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);
  309 + });
  310 + } catch (e) {
  311 + return;
265 312 }
266   -
267 313 predefineLevelColors.push(null);
268 314 }
269 315 }
... ... @@ -278,49 +324,63 @@ export default class TbCanvasDigitalGauge {
278 324 }
279 325 }
280 326
281   - this.subscribeLevelColorsAttributes(levelColorsDatasource);
  327 + this.subscribeAttributes(levelColorsDatasource, 'levelColors').then((subscription) => {
  328 + this.levelColorSourcesSubscription = subscription;
  329 + });
282 330
283 331 return predefineLevelColors;
284 332 }
285 333
286   - updateLevelColors(levelColors) {
287   - this.gauge.options.levelColors = levelColors;
288   - this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
289   - this.gauge.update();
290   - }
  334 + subscribeAttributes(datasources, typeAttributes) {
  335 + if (!datasources.length) {
  336 + return this.ctx.$scope.$injector.get('$q').when(null);
  337 + }
291 338
292   - subscribeLevelColorsAttributes(datasources) {
293   - let TbCanvasDigitalGauge = this;
294 339 let levelColorsSourcesSubscriptionOptions = {
295 340 datasources: datasources,
296 341 useDashboardTimewindow: false,
297 342 type: this.ctx.$scope.$injector.get('types').widgetType.latest.value,
298 343 callbacks: {
299 344 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);
  345 + this.updateAttribute(subscription.data, typeAttributes);
316 346 }
317 347 }
318 348 };
319   - this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then(
320   - (subscription) => {
321   - TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription;
  349 +
  350 + return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true);
  351 + }
  352 +
  353 + updateAttribute(data, typeAttributes) {
  354 + for (let i = 0; i < data.length; i++) {
  355 + let keyData = data[i];
  356 + if (keyData && keyData.data && keyData.data[0]) {
  357 + let attrValue = keyData.data[0][1];
  358 + if (isFinite(attrValue)) {
  359 + for (let i = 0; i < keyData.dataKey.settings.length; i++) {
  360 + let setting = keyData.dataKey.settings[i];
  361 + switch (typeAttributes) {
  362 + case 'levelColors':
  363 + this.localSettings.levelColors[setting.index] = {
  364 + value: attrValue,
  365 + color: setting.color
  366 + };
  367 + break;
  368 + case 'ticks':
  369 + this.localSettings.ticks[setting] = attrValue;
  370 + break;
  371 + }
  372 + }
  373 + }
322 374 }
323   - );
  375 + }
  376 + this.updateSetting();
  377 + }
  378 +
  379 + updateSetting() {
  380 + this.gauge.options.ticks = this.localSettings.ticks;
  381 + this.gauge.options.levelColors = this.localSettings.levelColors;
  382 + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
  383 + this.gauge.update();
324 384 }
325 385
326 386 update() {
... ... @@ -526,6 +586,48 @@ export default class TbCanvasDigitalGauge {
526 586 }
527 587 }
528 588 },
  589 + "showTicks": {
  590 + "title": "Show ticks",
  591 + "type": "boolean",
  592 + "default": false
  593 + },
  594 + "tickWidth": {
  595 + "title": "Width ticks",
  596 + "type": "number",
  597 + "default": 4
  598 + },
  599 + "colorTicks": {
  600 + "title": "Color ticks",
  601 + "type": "string",
  602 + "default": "#666"
  603 + },
  604 + "ticksValue": {
  605 + "title": "The ticks predefined value",
  606 + "type": "array",
  607 + "items": {
  608 + "title": "tickValue",
  609 + "type": "object",
  610 + "properties": {
  611 + "valueSource": {
  612 + "title": "Value source",
  613 + "type": "string",
  614 + "default": "predefinedValue"
  615 + },
  616 + "entityAlias": {
  617 + "title": "Source entity alias",
  618 + "type": "string"
  619 + },
  620 + "attribute": {
  621 + "title": "Source entity attribute",
  622 + "type": "string"
  623 + },
  624 + "value": {
  625 + "title": "Value (if predefined value is selected)",
  626 + "type": "number"
  627 + }
  628 + }
  629 + }
  630 + },
529 631 "animation": {
530 632 "title": "Enable animation",
531 633 "type": "boolean",
... ... @@ -771,6 +873,40 @@ export default class TbCanvasDigitalGauge {
771 873 }
772 874 ]
773 875 },
  876 + "showTicks",
  877 + {
  878 + "key": "tickWidth",
  879 + "condition": "model.showTicks === true"
  880 + },
  881 + {
  882 + "key": "colorTicks",
  883 + "condition": "model.showTicks === true",
  884 + "type": "color"
  885 + },
  886 + {
  887 + "key": "ticksValue",
  888 + "condition": "model.showTicks === true",
  889 + "items": [
  890 + {
  891 + "key": "ticksValue[].valueSource",
  892 + "type": "rc-select",
  893 + "multiple": false,
  894 + "items": [
  895 + {
  896 + "value": "predefinedValue",
  897 + "label": "Predefined value (Default)"
  898 + },
  899 + {
  900 + "value": "entityAttribute",
  901 + "label": "Value taken from entity attribute"
  902 + }
  903 + ]
  904 + },
  905 + "ticksValue[].value",
  906 + "ticksValue[].entityAlias",
  907 + "ticksValue[].attribute"
  908 + ]
  909 + },
774 910 "animation",
775 911 "animationDuration",
776 912 {
... ...