Merge with master
This commit is contained in:
commit
c55ddd9f71
@ -14,24 +14,6 @@
|
|||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
--
|
--
|
||||||
|
|
||||||
-- call check_version();
|
|
||||||
|
|
||||||
CREATE OR REPLACE PROCEDURE check_version(INOUT valid_version boolean) LANGUAGE plpgsql AS $BODY$
|
|
||||||
DECLARE
|
|
||||||
current_version integer;
|
|
||||||
BEGIN
|
|
||||||
RAISE NOTICE 'Check the current installed PostgreSQL version...';
|
|
||||||
SELECT current_setting('server_version_num') INTO current_version;
|
|
||||||
IF current_version > 110000 THEN
|
|
||||||
RAISE NOTICE 'PostgreSQL version is valid!';
|
|
||||||
RAISE NOTICE 'Schema update started...';
|
|
||||||
SELECT true INTO valid_version;
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'Postgres version should be at least more than 10!';
|
|
||||||
END IF;
|
|
||||||
END;
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
-- call create_partition_ts_kv_table();
|
-- call create_partition_ts_kv_table();
|
||||||
|
|
||||||
CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table() LANGUAGE plpgsql AS $$
|
CREATE OR REPLACE PROCEDURE create_partition_ts_kv_table() LANGUAGE plpgsql AS $$
|
||||||
|
|||||||
@ -14,25 +14,6 @@
|
|||||||
-- limitations under the License.
|
-- limitations under the License.
|
||||||
--
|
--
|
||||||
|
|
||||||
-- call check_version();
|
|
||||||
|
|
||||||
CREATE OR REPLACE PROCEDURE check_version(INOUT valid_version boolean) LANGUAGE plpgsql AS $BODY$
|
|
||||||
|
|
||||||
DECLARE
|
|
||||||
current_version integer;
|
|
||||||
BEGIN
|
|
||||||
RAISE NOTICE 'Check the current installed PostgreSQL version...';
|
|
||||||
SELECT current_setting('server_version_num') INTO current_version;
|
|
||||||
IF current_version > 110000 THEN
|
|
||||||
RAISE NOTICE 'PostgreSQL version is valid!';
|
|
||||||
RAISE NOTICE 'Schema update started...';
|
|
||||||
SELECT true INTO valid_version;
|
|
||||||
ELSE
|
|
||||||
RAISE NOTICE 'Postgres version should be at least more than 10!';
|
|
||||||
END IF;
|
|
||||||
END;
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
-- call create_new_ts_kv_table();
|
-- call create_new_ts_kv_table();
|
||||||
|
|
||||||
CREATE OR REPLACE PROCEDURE create_new_ts_kv_table() LANGUAGE plpgsql AS $$
|
CREATE OR REPLACE PROCEDURE create_new_ts_kv_table() LANGUAGE plpgsql AS $$
|
||||||
|
|||||||
@ -32,11 +32,8 @@ import java.sql.Statement;
|
|||||||
public abstract class AbstractSqlTsDatabaseUpgradeService {
|
public abstract class AbstractSqlTsDatabaseUpgradeService {
|
||||||
|
|
||||||
protected static final String CALL_REGEX = "call ";
|
protected static final String CALL_REGEX = "call ";
|
||||||
protected static final String CHECK_VERSION = "check_version(false)";
|
|
||||||
protected static final String CHECK_VERSION_TO_DELETE = "check_version(INOUT valid_version boolean)";
|
|
||||||
protected static final String DROP_TABLE = "DROP TABLE ";
|
protected static final String DROP_TABLE = "DROP TABLE ";
|
||||||
protected static final String DROP_PROCEDURE_IF_EXISTS = "DROP PROCEDURE IF EXISTS ";
|
protected static final String DROP_PROCEDURE_IF_EXISTS = "DROP PROCEDURE IF EXISTS ";
|
||||||
protected static final String DROP_PROCEDURE_CHECK_VERSION = DROP_PROCEDURE_IF_EXISTS + CHECK_VERSION_TO_DELETE;
|
|
||||||
|
|
||||||
@Value("${spring.datasource.url}")
|
@Value("${spring.datasource.url}")
|
||||||
protected String dbUrl;
|
protected String dbUrl;
|
||||||
@ -58,13 +55,14 @@ public abstract class AbstractSqlTsDatabaseUpgradeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean checkVersion(Connection conn) {
|
protected boolean checkVersion(Connection conn) {
|
||||||
log.info("Check the current PostgreSQL version...");
|
|
||||||
boolean versionValid = false;
|
boolean versionValid = false;
|
||||||
try {
|
try {
|
||||||
Statement statement = conn.createStatement();
|
Statement statement = conn.createStatement();
|
||||||
ResultSet resultSet = statement.executeQuery(CALL_REGEX + CHECK_VERSION);
|
ResultSet resultSet = statement.executeQuery("SELECT current_setting('server_version_num')");
|
||||||
resultSet.next();
|
resultSet.next();
|
||||||
versionValid = resultSet.getBoolean(1);
|
if(resultSet.getLong(1) > 110000) {
|
||||||
|
versionValid = true;
|
||||||
|
}
|
||||||
statement.close();
|
statement.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.info("Failed to check current PostgreSQL version due to: {}", e.getMessage());
|
log.info("Failed to check current PostgreSQL version due to: {}", e.getMessage());
|
||||||
|
|||||||
@ -70,16 +70,15 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
|
|||||||
switch (fromVersion) {
|
switch (fromVersion) {
|
||||||
case "2.4.3":
|
case "2.4.3":
|
||||||
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
|
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
|
||||||
log.info("Updating timeseries schema ...");
|
log.info("Check the current PostgreSQL version...");
|
||||||
log.info("Load upgrade functions ...");
|
|
||||||
loadSql(conn);
|
|
||||||
boolean versionValid = checkVersion(conn);
|
boolean versionValid = checkVersion(conn);
|
||||||
if (!versionValid) {
|
if (!versionValid) {
|
||||||
log.info("PostgreSQL version should be at least more than 11!");
|
throw new RuntimeException("PostgreSQL version should be at least more than 11, please upgrade your PostgreSQL and restart the script!");
|
||||||
log.info("Please upgrade your PostgreSQL and restart the script!");
|
|
||||||
} else {
|
} else {
|
||||||
log.info("PostgreSQL version is valid!");
|
log.info("PostgreSQL version is valid!");
|
||||||
log.info("Updating schema ...");
|
log.info("Load upgrade functions ...");
|
||||||
|
loadSql(conn);
|
||||||
|
log.info("Updating timeseries schema ...");
|
||||||
executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE);
|
executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE);
|
||||||
executeQuery(conn, CALL_CREATE_PARTITIONS);
|
executeQuery(conn, CALL_CREATE_PARTITIONS);
|
||||||
executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
|
executeQuery(conn, CALL_CREATE_TS_KV_DICTIONARY_TABLE);
|
||||||
@ -91,7 +90,6 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
|
|||||||
executeQuery(conn, DROP_TABLE_TS_KV_OLD);
|
executeQuery(conn, DROP_TABLE_TS_KV_OLD);
|
||||||
executeQuery(conn, DROP_TABLE_TS_KV_LATEST_OLD);
|
executeQuery(conn, DROP_TABLE_TS_KV_LATEST_OLD);
|
||||||
|
|
||||||
executeQuery(conn, DROP_PROCEDURE_CHECK_VERSION);
|
|
||||||
executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITION_TS_KV_TABLE);
|
executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITION_TS_KV_TABLE);
|
||||||
executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITIONS);
|
executeQuery(conn, DROP_PROCEDURE_CREATE_PARTITIONS);
|
||||||
executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE);
|
executeQuery(conn, DROP_PROCEDURE_CREATE_TS_KV_DICTIONARY_TABLE);
|
||||||
|
|||||||
@ -73,16 +73,15 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
|
|||||||
switch (fromVersion) {
|
switch (fromVersion) {
|
||||||
case "2.4.3":
|
case "2.4.3":
|
||||||
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
|
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
|
||||||
log.info("Updating timescale schema ...");
|
log.info("Check the current PostgreSQL version...");
|
||||||
log.info("Load upgrade functions ...");
|
|
||||||
loadSql(conn);
|
|
||||||
boolean versionValid = checkVersion(conn);
|
boolean versionValid = checkVersion(conn);
|
||||||
if (!versionValid) {
|
if (!versionValid) {
|
||||||
log.info("PostgreSQL version should be at least more than 11!");
|
throw new RuntimeException("PostgreSQL version should be at least more than 11, please upgrade your PostgreSQL and restart the script!");
|
||||||
log.info("Please upgrade your PostgreSQL and restart the script!");
|
|
||||||
} else {
|
} else {
|
||||||
log.info("PostgreSQL version is valid!");
|
log.info("PostgreSQL version is valid!");
|
||||||
log.info("Updating schema ...");
|
log.info("Load upgrade functions ...");
|
||||||
|
loadSql(conn);
|
||||||
|
log.info("Updating timescale schema ...");
|
||||||
executeQuery(conn, CALL_CREATE_TS_KV_LATEST_TABLE);
|
executeQuery(conn, CALL_CREATE_TS_KV_LATEST_TABLE);
|
||||||
executeQuery(conn, CALL_CREATE_NEW_TENANT_TS_KV_TABLE);
|
executeQuery(conn, CALL_CREATE_NEW_TENANT_TS_KV_TABLE);
|
||||||
|
|
||||||
@ -105,7 +104,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
|
|||||||
executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN json_v json;");
|
executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN json_v json;");
|
||||||
executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN json_v json;");
|
executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN json_v json;");
|
||||||
|
|
||||||
log.info("schema timeseries updated!");
|
log.info("schema timescale updated!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -16,16 +16,21 @@
|
|||||||
package org.thingsboard.rule.engine.kafka;
|
package org.thingsboard.rule.engine.kafka;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.kafka.clients.producer.*;
|
import org.apache.kafka.clients.producer.*;
|
||||||
|
import org.apache.kafka.common.header.Headers;
|
||||||
|
import org.apache.kafka.common.header.internals.RecordHeader;
|
||||||
|
import org.apache.kafka.common.header.internals.RecordHeaders;
|
||||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
||||||
import org.thingsboard.rule.engine.api.*;
|
import org.thingsboard.rule.engine.api.*;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||||
import org.thingsboard.server.common.msg.TbMsg;
|
import org.thingsboard.server.common.msg.TbMsg;
|
||||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RuleNode(
|
@RuleNode(
|
||||||
@ -46,8 +51,11 @@ public class TbKafkaNode implements TbNode {
|
|||||||
private static final String PARTITION = "partition";
|
private static final String PARTITION = "partition";
|
||||||
private static final String TOPIC = "topic";
|
private static final String TOPIC = "topic";
|
||||||
private static final String ERROR = "error";
|
private static final String ERROR = "error";
|
||||||
|
public static final String TB_MSG_MD_PREFIX = "tb_msg_md_";
|
||||||
|
|
||||||
private TbKafkaNodeConfiguration config;
|
private TbKafkaNodeConfiguration config;
|
||||||
|
private boolean addMetadataKeyValuesAsKafkaHeaders;
|
||||||
|
private Charset toBytesCharset;
|
||||||
|
|
||||||
private Producer<?, String> producer;
|
private Producer<?, String> producer;
|
||||||
|
|
||||||
@ -66,8 +74,10 @@ public class TbKafkaNode implements TbNode {
|
|||||||
properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, config.getBufferMemory());
|
properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, config.getBufferMemory());
|
||||||
if (config.getOtherProperties() != null) {
|
if (config.getOtherProperties() != null) {
|
||||||
config.getOtherProperties()
|
config.getOtherProperties()
|
||||||
.forEach((k,v) -> properties.put(k, v));
|
.forEach(properties::put);
|
||||||
}
|
}
|
||||||
|
addMetadataKeyValuesAsKafkaHeaders = BooleanUtils.toBooleanDefaultIfNull(config.isAddMetadataKeyValuesAsKafkaHeaders(), false);
|
||||||
|
toBytesCharset = config.getKafkaHeadersCharset() != null ? Charset.forName(config.getKafkaHeadersCharset()) : StandardCharsets.UTF_8;
|
||||||
try {
|
try {
|
||||||
this.producer = new KafkaProducer<>(properties);
|
this.producer = new KafkaProducer<>(properties);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -79,16 +89,16 @@ public class TbKafkaNode implements TbNode {
|
|||||||
public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
|
public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
|
||||||
String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg.getMetaData());
|
String topic = TbNodeUtils.processPattern(config.getTopicPattern(), msg.getMetaData());
|
||||||
try {
|
try {
|
||||||
producer.send(new ProducerRecord<>(topic, msg.getData()),
|
if (!addMetadataKeyValuesAsKafkaHeaders) {
|
||||||
(metadata, e) -> {
|
producer.send(new ProducerRecord<>(topic, msg.getData()),
|
||||||
if (metadata != null) {
|
(metadata, e) -> processRecord(ctx, msg, metadata, e));
|
||||||
TbMsg next = processResponse(ctx, msg, metadata);
|
} else {
|
||||||
ctx.tellNext(next, TbRelationTypes.SUCCESS);
|
Headers headers = new RecordHeaders();
|
||||||
} else {
|
msg.getMetaData().values().forEach((key, value) -> headers.add(new RecordHeader(TB_MSG_MD_PREFIX + key, value.getBytes(toBytesCharset))));
|
||||||
TbMsg next = processException(ctx, msg, e);
|
producer.send(new ProducerRecord<>(topic, null, null, null, msg.getData(), headers),
|
||||||
ctx.tellFailure(next, e);
|
(metadata, e) -> processRecord(ctx, msg, metadata, e));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
ctx.tellFailure(msg, e);
|
ctx.tellFailure(msg, e);
|
||||||
}
|
}
|
||||||
@ -105,6 +115,16 @@ public class TbKafkaNode implements TbNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processRecord(TbContext ctx, TbMsg msg, RecordMetadata metadata, Exception e) {
|
||||||
|
if (metadata != null) {
|
||||||
|
TbMsg next = processResponse(ctx, msg, metadata);
|
||||||
|
ctx.tellNext(next, TbRelationTypes.SUCCESS);
|
||||||
|
} else {
|
||||||
|
TbMsg next = processException(ctx, msg, e);
|
||||||
|
ctx.tellFailure(next, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private TbMsg processResponse(TbContext ctx, TbMsg origMsg, RecordMetadata recordMetadata) {
|
private TbMsg processResponse(TbContext ctx, TbMsg origMsg, RecordMetadata recordMetadata) {
|
||||||
TbMsgMetaData metaData = origMsg.getMetaData().copy();
|
TbMsgMetaData metaData = origMsg.getMetaData().copy();
|
||||||
metaData.putValue(OFFSET, String.valueOf(recordMetadata.offset()));
|
metaData.putValue(OFFSET, String.valueOf(recordMetadata.offset()));
|
||||||
|
|||||||
@ -36,6 +36,9 @@ public class TbKafkaNodeConfiguration implements NodeConfiguration<TbKafkaNodeCo
|
|||||||
private String valueSerializer;
|
private String valueSerializer;
|
||||||
private Map<String, String> otherProperties;
|
private Map<String, String> otherProperties;
|
||||||
|
|
||||||
|
private boolean addMetadataKeyValuesAsKafkaHeaders;
|
||||||
|
private String kafkaHeadersCharset;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TbKafkaNodeConfiguration defaultConfiguration() {
|
public TbKafkaNodeConfiguration defaultConfiguration() {
|
||||||
TbKafkaNodeConfiguration configuration = new TbKafkaNodeConfiguration();
|
TbKafkaNodeConfiguration configuration = new TbKafkaNodeConfiguration();
|
||||||
@ -49,6 +52,8 @@ public class TbKafkaNodeConfiguration implements NodeConfiguration<TbKafkaNodeCo
|
|||||||
configuration.setKeySerializer(StringSerializer.class.getName());
|
configuration.setKeySerializer(StringSerializer.class.getName());
|
||||||
configuration.setValueSerializer(StringSerializer.class.getName());
|
configuration.setValueSerializer(StringSerializer.class.getName());
|
||||||
configuration.setOtherProperties(Collections.emptyMap());
|
configuration.setOtherProperties(Collections.emptyMap());
|
||||||
|
configuration.setAddMetadataKeyValuesAsKafkaHeaders(false);
|
||||||
|
configuration.setKafkaHeadersCharset("UTF-8");
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,10 @@ let defaultDigitalGaugeOptions = Object.assign({}, canvasGauges.GenericOptions,
|
|||||||
|
|
||||||
neonGlowBrightness: 0,
|
neonGlowBrightness: 0,
|
||||||
|
|
||||||
|
colorTicks: 'gray',
|
||||||
|
tickWidth: 4,
|
||||||
|
ticks: [],
|
||||||
|
|
||||||
isMobile: false
|
isMobile: false
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -133,6 +137,13 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.ticksValue = [];
|
||||||
|
for(let i = 0; i < options.ticks.length; i++){
|
||||||
|
if(options.ticks[i] !== null){
|
||||||
|
options.ticksValue.push(CanvasDigitalGauge.normalizeValue(options.ticks[i], options.minValue, options.maxValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.neonGlowBrightness) {
|
if (options.neonGlowBrightness) {
|
||||||
options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
|
options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString();
|
||||||
options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString();
|
options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString();
|
||||||
@ -729,6 +740,48 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is
|
|||||||
context.stroke();
|
context.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawTickArc(context, tickValues, Cx, Cy, Ri, Rm, Ro, startAngle, endAngle, color, tickWidth) {
|
||||||
|
if(!tickValues.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const strokeWidth = Ro - Ri;
|
||||||
|
context.beginPath();
|
||||||
|
context.lineWidth = tickWidth;
|
||||||
|
context.strokeStyle = color;
|
||||||
|
for (let i = 0; i < tickValues.length; i++) {
|
||||||
|
var angle = startAngle + tickValues[i] * endAngle;
|
||||||
|
var x1 = Cx + (Ri + strokeWidth) * Math.cos(angle);
|
||||||
|
var y1 = Cy + (Ri + strokeWidth) * Math.sin(angle);
|
||||||
|
var x2 = Cx + Ri * Math.cos(angle);
|
||||||
|
var y2 = Cy + Ri * Math.sin(angle);
|
||||||
|
context.moveTo(x1, y1);
|
||||||
|
context.lineTo(x2, y2);
|
||||||
|
}
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTickBar(context, tickValues, startX, startY, distanceBar, strokeWidth, isVertical, color, tickWidth) {
|
||||||
|
if(!tickValues.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.lineWidth = tickWidth;
|
||||||
|
context.strokeStyle = color;
|
||||||
|
for (let i = 0; i < tickValues.length; i++) {
|
||||||
|
let tickValue = tickValues[i] * distanceBar;
|
||||||
|
if (isVertical) {
|
||||||
|
context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar);
|
||||||
|
context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar);
|
||||||
|
} else {
|
||||||
|
context.moveTo(startX + tickValue, startY);
|
||||||
|
context.lineTo(startX + tickValue, startY + strokeWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
function drawProgress(context, options, progress) {
|
function drawProgress(context, options, progress) {
|
||||||
var neonColor;
|
var neonColor;
|
||||||
if (options.neonGlowBrightness) {
|
if (options.neonGlowBrightness) {
|
||||||
@ -759,6 +812,7 @@ function drawProgress(context, options, progress) {
|
|||||||
if (options.neonGlowBrightness && !options.isMobile) {
|
if (options.neonGlowBrightness && !options.isMobile) {
|
||||||
drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle);
|
drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle);
|
||||||
}
|
}
|
||||||
|
drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle, options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth);
|
||||||
} else if (options.gaugeType === 'arc') {
|
} else if (options.gaugeType === 'arc') {
|
||||||
if (options.neonGlowBrightness) {
|
if (options.neonGlowBrightness) {
|
||||||
context.strokeStyle = neonColor;
|
context.strokeStyle = neonColor;
|
||||||
@ -769,6 +823,7 @@ function drawProgress(context, options, progress) {
|
|||||||
if (options.neonGlowBrightness && !options.isMobile) {
|
if (options.neonGlowBrightness && !options.isMobile) {
|
||||||
drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false);
|
drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false);
|
||||||
}
|
}
|
||||||
|
drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth);
|
||||||
} else if (options.gaugeType === 'horizontalBar') {
|
} else if (options.gaugeType === 'horizontalBar') {
|
||||||
if (options.neonGlowBrightness) {
|
if (options.neonGlowBrightness) {
|
||||||
context.strokeStyle = neonColor;
|
context.strokeStyle = neonColor;
|
||||||
@ -781,6 +836,7 @@ function drawProgress(context, options, progress) {
|
|||||||
drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2,
|
drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2,
|
||||||
neonColor, strokeWidth, false);
|
neonColor, strokeWidth, false);
|
||||||
}
|
}
|
||||||
|
drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, false, options.colorTicks, options.tickWidth);
|
||||||
} else if (options.gaugeType === 'verticalBar') {
|
} else if (options.gaugeType === 'verticalBar') {
|
||||||
if (options.neonGlowBrightness) {
|
if (options.neonGlowBrightness) {
|
||||||
context.strokeStyle = neonColor;
|
context.strokeStyle = neonColor;
|
||||||
@ -793,6 +849,7 @@ function drawProgress(context, options, progress) {
|
|||||||
drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress,
|
drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress,
|
||||||
neonColor, strokeWidth, true);
|
neonColor, strokeWidth, true);
|
||||||
}
|
}
|
||||||
|
drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth, true, options.colorTicks, options.tickWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,12 @@ export default class TbCanvasDigitalGauge {
|
|||||||
this.localSettings.fixedLevelColors = settings.fixedLevelColors || [];
|
this.localSettings.fixedLevelColors = settings.fixedLevelColors || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.localSettings.showTicks = settings.showTicks || false;
|
||||||
|
this.localSettings.ticks = [];
|
||||||
|
this.localSettings.ticksValue = settings.ticksValue || [];
|
||||||
|
this.localSettings.tickWidth = settings.tickWidth || 4;
|
||||||
|
this.localSettings.colorTicks = settings.colorTicks || '#666';
|
||||||
|
|
||||||
this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals :
|
this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals :
|
||||||
((angular.isDefined(settings.decimals) && settings.decimals !== null)
|
((angular.isDefined(settings.decimals) && settings.decimals !== null)
|
||||||
? settings.decimals : ctx.decimals);
|
? settings.decimals : ctx.decimals);
|
||||||
@ -145,6 +151,10 @@ export default class TbCanvasDigitalGauge {
|
|||||||
gaugeColor: this.localSettings.gaugeColor,
|
gaugeColor: this.localSettings.gaugeColor,
|
||||||
levelColors: this.localSettings.levelColors,
|
levelColors: this.localSettings.levelColors,
|
||||||
|
|
||||||
|
colorTicks: this.localSettings.colorTicks,
|
||||||
|
tickWidth: this.localSettings.tickWidth,
|
||||||
|
ticks: this.localSettings.ticks,
|
||||||
|
|
||||||
title: this.localSettings.title,
|
title: this.localSettings.title,
|
||||||
|
|
||||||
fontTitleSize: this.localSettings.titleFont.size,
|
fontTitleSize: this.localSettings.titleFont.size,
|
||||||
@ -204,9 +214,81 @@ export default class TbCanvasDigitalGauge {
|
|||||||
if (this.localSettings.useFixedLevelColor) {
|
if (this.localSettings.useFixedLevelColor) {
|
||||||
if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
|
if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) {
|
||||||
this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
|
this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors);
|
||||||
this.updateLevelColors(this.localSettings.levelColors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.localSettings.showTicks) {
|
||||||
|
if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) {
|
||||||
|
this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.updateSetting();
|
||||||
|
}
|
||||||
|
|
||||||
|
static generateDatasorce(ctx, datasources, entityAlias, attribute, settings){
|
||||||
|
let entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias);
|
||||||
|
if (!entityAliasId) {
|
||||||
|
throw new Error('Not valid entity aliase name ' + entityAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
let datasource = datasources.filter((datasource) => {
|
||||||
|
return datasource.entityAliasId === entityAliasId;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
let dataKey = {
|
||||||
|
type: ctx.$scope.$injector.get('types').dataKeyType.attribute,
|
||||||
|
name: attribute,
|
||||||
|
label: attribute,
|
||||||
|
settings: [settings],
|
||||||
|
_hash: Math.random()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (datasource) {
|
||||||
|
let findDataKey = datasource.dataKeys.filter((dataKey) => {
|
||||||
|
return dataKey.name === attribute;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (findDataKey) {
|
||||||
|
findDataKey.settings.push(settings);
|
||||||
|
} else {
|
||||||
|
datasource.dataKeys.push(dataKey)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
datasource = {
|
||||||
|
type: ctx.$scope.$injector.get('types').datasourceType.entity,
|
||||||
|
name: entityAlias,
|
||||||
|
aliasName: entityAlias,
|
||||||
|
entityAliasId: entityAliasId,
|
||||||
|
dataKeys: [dataKey]
|
||||||
|
};
|
||||||
|
datasources.push(datasource);
|
||||||
|
}
|
||||||
|
|
||||||
|
return datasources;
|
||||||
|
}
|
||||||
|
|
||||||
|
settingTicksSubscribe(options) {
|
||||||
|
let ticksDatasource = [];
|
||||||
|
let predefineTicks = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < options.length; i++) {
|
||||||
|
let tick = options[i];
|
||||||
|
if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) {
|
||||||
|
predefineTicks.push(tick.value)
|
||||||
|
} else if (tick.entityAlias && tick.attribute) {
|
||||||
|
try {
|
||||||
|
ticksDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length);
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
predefineTicks.push(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.subscribeAttributes(ticksDatasource, 'ticks').then((subscription) => {
|
||||||
|
this.ticksSourcesSubscription = subscription;
|
||||||
|
});
|
||||||
|
|
||||||
|
return predefineTicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
settingLevelColorsSubscribe(options) {
|
settingLevelColorsSubscribe(options) {
|
||||||
@ -220,50 +302,14 @@ export default class TbCanvasDigitalGauge {
|
|||||||
color: color
|
color: color
|
||||||
})
|
})
|
||||||
} else if (levelSetting.entityAlias && levelSetting.attribute) {
|
} else if (levelSetting.entityAlias && levelSetting.attribute) {
|
||||||
let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias);
|
try {
|
||||||
if (!entityAliasId) {
|
levelColorsDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, levelColorsDatasource, levelSetting.entityAlias, levelSetting.attribute, {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let datasource = levelColorsDatasource.filter((datasource) => {
|
|
||||||
return datasource.entityAliasId === entityAliasId;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
let dataKey = {
|
|
||||||
type: this.ctx.$scope.$injector.get('types').dataKeyType.attribute,
|
|
||||||
name: levelSetting.attribute,
|
|
||||||
label: levelSetting.attribute,
|
|
||||||
settings: [{
|
|
||||||
color: color,
|
color: color,
|
||||||
index: predefineLevelColors.length
|
index: predefineLevelColors.length
|
||||||
}],
|
});
|
||||||
_hash: Math.random()
|
} catch (e) {
|
||||||
};
|
return;
|
||||||
|
|
||||||
if (datasource) {
|
|
||||||
let findDataKey = datasource.dataKeys.filter((dataKey) => {
|
|
||||||
return dataKey.name === levelSetting.attribute;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (findDataKey) {
|
|
||||||
findDataKey.settings.push({
|
|
||||||
color: color,
|
|
||||||
index: predefineLevelColors.length
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
datasource.dataKeys.push(dataKey)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
datasource = {
|
|
||||||
type: this.ctx.$scope.$injector.get('types').datasourceType.entity,
|
|
||||||
name: levelSetting.entityAlias,
|
|
||||||
aliasName: levelSetting.entityAlias,
|
|
||||||
entityAliasId: entityAliasId,
|
|
||||||
dataKeys: [dataKey]
|
|
||||||
};
|
|
||||||
levelColorsDatasource.push(datasource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
predefineLevelColors.push(null);
|
predefineLevelColors.push(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,49 +324,63 @@ export default class TbCanvasDigitalGauge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.subscribeLevelColorsAttributes(levelColorsDatasource);
|
this.subscribeAttributes(levelColorsDatasource, 'levelColors').then((subscription) => {
|
||||||
|
this.levelColorSourcesSubscription = subscription;
|
||||||
|
});
|
||||||
|
|
||||||
return predefineLevelColors;
|
return predefineLevelColors;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLevelColors(levelColors) {
|
subscribeAttributes(datasources, typeAttributes) {
|
||||||
this.gauge.options.levelColors = levelColors;
|
if (!datasources.length) {
|
||||||
this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
|
return this.ctx.$scope.$injector.get('$q').when(null);
|
||||||
this.gauge.update();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
subscribeLevelColorsAttributes(datasources) {
|
|
||||||
let TbCanvasDigitalGauge = this;
|
|
||||||
let levelColorsSourcesSubscriptionOptions = {
|
let levelColorsSourcesSubscriptionOptions = {
|
||||||
datasources: datasources,
|
datasources: datasources,
|
||||||
useDashboardTimewindow: false,
|
useDashboardTimewindow: false,
|
||||||
type: this.ctx.$scope.$injector.get('types').widgetType.latest.value,
|
type: this.ctx.$scope.$injector.get('types').widgetType.latest.value,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
onDataUpdated: (subscription) => {
|
onDataUpdated: (subscription) => {
|
||||||
for (let i = 0; i < subscription.data.length; i++) {
|
this.updateAttribute(subscription.data, typeAttributes);
|
||||||
let keyData = subscription.data[i];
|
|
||||||
if (keyData && keyData.data && keyData.data[0]) {
|
|
||||||
let attrValue = keyData.data[0][1];
|
|
||||||
if (isFinite(attrValue)) {
|
|
||||||
for (let i = 0; i < keyData.dataKey.settings.length; i++) {
|
|
||||||
let setting = keyData.dataKey.settings[i];
|
|
||||||
this.localSettings.levelColors[setting.index] = {
|
|
||||||
value: attrValue,
|
|
||||||
color: setting.color
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.updateLevelColors(this.localSettings.levelColors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then(
|
|
||||||
(subscription) => {
|
return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true);
|
||||||
TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription;
|
}
|
||||||
|
|
||||||
|
updateAttribute(data, typeAttributes) {
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
let keyData = data[i];
|
||||||
|
if (keyData && keyData.data && keyData.data[0]) {
|
||||||
|
let attrValue = keyData.data[0][1];
|
||||||
|
if (isFinite(attrValue)) {
|
||||||
|
for (let i = 0; i < keyData.dataKey.settings.length; i++) {
|
||||||
|
let setting = keyData.dataKey.settings[i];
|
||||||
|
switch (typeAttributes) {
|
||||||
|
case 'levelColors':
|
||||||
|
this.localSettings.levelColors[setting.index] = {
|
||||||
|
value: attrValue,
|
||||||
|
color: setting.color
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'ticks':
|
||||||
|
this.localSettings.ticks[setting] = attrValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
this.updateSetting();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSetting() {
|
||||||
|
this.gauge.options.ticks = this.localSettings.ticks;
|
||||||
|
this.gauge.options.levelColors = this.localSettings.levelColors;
|
||||||
|
this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options);
|
||||||
|
this.gauge.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@ -526,6 +586,48 @@ export default class TbCanvasDigitalGauge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"showTicks": {
|
||||||
|
"title": "Show ticks",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"tickWidth": {
|
||||||
|
"title": "Width ticks",
|
||||||
|
"type": "number",
|
||||||
|
"default": 4
|
||||||
|
},
|
||||||
|
"colorTicks": {
|
||||||
|
"title": "Color ticks",
|
||||||
|
"type": "string",
|
||||||
|
"default": "#666"
|
||||||
|
},
|
||||||
|
"ticksValue": {
|
||||||
|
"title": "The ticks predefined value",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"title": "tickValue",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"valueSource": {
|
||||||
|
"title": "Value source",
|
||||||
|
"type": "string",
|
||||||
|
"default": "predefinedValue"
|
||||||
|
},
|
||||||
|
"entityAlias": {
|
||||||
|
"title": "Source entity alias",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"attribute": {
|
||||||
|
"title": "Source entity attribute",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"title": "Value (if predefined value is selected)",
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"animation": {
|
"animation": {
|
||||||
"title": "Enable animation",
|
"title": "Enable animation",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -771,6 +873,40 @@ export default class TbCanvasDigitalGauge {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"showTicks",
|
||||||
|
{
|
||||||
|
"key": "tickWidth",
|
||||||
|
"condition": "model.showTicks === true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "colorTicks",
|
||||||
|
"condition": "model.showTicks === true",
|
||||||
|
"type": "color"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ticksValue",
|
||||||
|
"condition": "model.showTicks === true",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"key": "ticksValue[].valueSource",
|
||||||
|
"type": "rc-select",
|
||||||
|
"multiple": false,
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"value": "predefinedValue",
|
||||||
|
"label": "Predefined value (Default)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "entityAttribute",
|
||||||
|
"label": "Value taken from entity attribute"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ticksValue[].value",
|
||||||
|
"ticksValue[].entityAlias",
|
||||||
|
"ticksValue[].attribute"
|
||||||
|
]
|
||||||
|
},
|
||||||
"animation",
|
"animation",
|
||||||
"animationDuration",
|
"animationDuration",
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user