Showing
9 changed files
with
315 additions
and
139 deletions
... | ... | @@ -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 | { | ... | ... |