From 34dbc81de6890a37a28043377f0a10d3ffab54de Mon Sep 17 00:00:00 2001 From: Dmytro Shvaika Date: Tue, 1 Sep 2020 15:47:06 +0300 Subject: [PATCH 01/13] fix drop partitions by max ttl procedure --- .../schema_update_psql_drop_partitions.sql | 1 + .../install/ThingsboardInstallService.java | 5 +++++ .../CassandraTsDatabaseUpgradeService.java | 1 + .../install/PsqlTsDatabaseUpgradeService.java | 6 ++++++ .../TimescaleTsDatabaseUpgradeService.java | 2 ++ .../service/ttl/AbstractCleanUpService.java | 18 +++++++----------- .../ttl/events/EventsCleanUpService.java | 2 +- .../PsqlTimeseriesCleanUpService.java | 3 ++- .../TimescaleTimeseriesCleanUpService.java | 3 ++- dao/src/main/resources/sql/schema-ts-psql.sql | 1 + 10 files changed, 28 insertions(+), 14 deletions(-) diff --git a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql index 0916c241a1..41e1cfbb7a 100644 --- a/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql +++ b/application/src/main/data/upgrade/2.4.3/schema_update_psql_drop_partitions.sql @@ -64,6 +64,7 @@ BEGIN AND tablename like 'ts_kv_' || '%' AND tablename != 'ts_kv_latest' AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' LOOP IF partition != partition_by_max_ttl_date THEN IF partition_year IS NOT NULL THEN diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index e281c0958e..01ad8a29e9 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -146,6 +146,11 @@ public class ThingsboardInstallService { databaseTsUpgradeService.upgradeDatabase("2.5.0"); } + case "2.5.4": + log.info("Upgrading ThingsBoard from version 2.5.4 to 2.5.5 ..."); + if (databaseTsUpgradeService != null) { + databaseTsUpgradeService.upgradeDatabase("2.5.4"); + } log.info("Updating system data..."); diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java index 103e8090d9..07b8522323 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java @@ -49,6 +49,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase log.info("Schema updated."); break; case "2.5.0": + case "2.5.4": break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java index 7a8174af16..396f84664a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java @@ -195,6 +195,12 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); } break; + case "2.5.4": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Load Drop Partitions functions ..."); + loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java index d8f7ea61f9..a929a51fb5 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java @@ -177,6 +177,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); } break; + case "2.5.4": + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java index 4fc4df0048..e81788958d 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java @@ -38,19 +38,15 @@ public abstract class AbstractCleanUpService { @Value("${spring.datasource.password}") protected String dbPassword; - protected long executeQuery(Connection conn, String query) { - long removed = 0L; - try { - Statement statement = conn.createStatement(); + protected long executeQuery(Connection conn, String query) throws SQLException { + try (Statement statement = conn.createStatement()) { ResultSet resultSet = statement.executeQuery(query); - getWarnings(statement); + if (log.isDebugEnabled()) { + getWarnings(statement); + } resultSet.next(); - removed = resultSet.getLong(1); - log.debug("Successfully executed query: {}", query); - } catch (SQLException e) { - log.debug("Failed to execute query: {} due to: {}", query, e.getMessage()); + return resultSet.getLong(1); } - return removed; } protected void getWarnings(Statement statement) throws SQLException { @@ -65,6 +61,6 @@ public abstract class AbstractCleanUpService { } } - protected abstract void doCleanUp(Connection connection); + protected abstract void doCleanUp(Connection connection) throws SQLException; } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java index 5b094c5c0e..ca52bca7e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java @@ -54,7 +54,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { } @Override - protected void doCleanUp(Connection connection) { + protected void doCleanUp(Connection connection) throws SQLException { long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);"); log.info("Total events removed by TTL: [{}]", totalEventsRemoved); } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java index cd403ee3b8..2464ab4677 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/PsqlTimeseriesCleanUpService.java @@ -22,6 +22,7 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.PsqlTsDao; import java.sql.Connection; +import java.sql.SQLException; @PsqlTsDao @Service @@ -32,7 +33,7 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi private String partitionType; @Override - protected void doCleanUp(Connection connection) { + protected void doCleanUp(Connection connection) throws SQLException { long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);"); log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved); long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID_STR + "'," + systemTtl + ", 0);"); diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java index f5898b9b20..8bdeea46ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/TimescaleTimeseriesCleanUpService.java @@ -21,6 +21,7 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.TimescaleDBTsDao; import java.sql.Connection; +import java.sql.SQLException; @TimescaleDBTsDao @Service @@ -28,7 +29,7 @@ import java.sql.Connection; public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { @Override - protected void doCleanUp(Connection connection) { + protected void doCleanUp(Connection connection) throws SQLException { long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID_STR + "'," + systemTtl + ", 0);"); log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); } diff --git a/dao/src/main/resources/sql/schema-ts-psql.sql b/dao/src/main/resources/sql/schema-ts-psql.sql index 28420a8957..80f111869d 100644 --- a/dao/src/main/resources/sql/schema-ts-psql.sql +++ b/dao/src/main/resources/sql/schema-ts-psql.sql @@ -105,6 +105,7 @@ BEGIN AND tablename like 'ts_kv_' || '%' AND tablename != 'ts_kv_latest' AND tablename != 'ts_kv_dictionary' + AND tablename != 'ts_kv_indefinite' LOOP IF partition != partition_by_max_ttl_date THEN IF partition_year IS NOT NULL THEN From f16672cbf69717f8506937ec20a87dee4d7052f3 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 2 Sep 2020 18:12:08 +0300 Subject: [PATCH 02/13] added partition property for kafka --- application/src/main/resources/thingsboard.yml | 10 +++++----- .../server/queue/kafka/TbKafkaAdmin.java | 10 +++++++++- docker/queue-kafka.env | 1 + msa/js-executor/config/default.yml | 2 +- msa/js-executor/queue/kafkaTemplate.js | 18 +++++++++++++++--- .../src/main/resources/tb-coap-transport.yml | 10 +++++----- .../src/main/resources/tb-http-transport.yml | 10 +++++----- .../src/main/resources/tb-mqtt-transport.yml | 10 +++++----- 8 files changed, 46 insertions(+), 25 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ac76b67cd3..6400a6bbf0 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -617,11 +617,11 @@ queue: security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other: topic-properties: - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" aws_sqs: use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java index b92b094af1..bee293bd6d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java @@ -37,6 +37,7 @@ public class TbKafkaAdmin implements TbQueueAdmin { private final AdminClient client; private final Map topicConfigs; private final Set topics = ConcurrentHashMap.newKeySet(); + private final int numPartitions; private final short replicationFactor; @@ -50,6 +51,13 @@ public class TbKafkaAdmin implements TbQueueAdmin { log.error("Failed to get all topics.", e); } + String numPartitionsStr = topicConfigs.get("partitions"); + if (numPartitionsStr != null) { + numPartitions = Integer.parseInt(numPartitionsStr); + topicConfigs.remove("partitions"); + } else { + numPartitions = 1; + } replicationFactor = settings.getReplicationFactor(); } @@ -59,7 +67,7 @@ public class TbKafkaAdmin implements TbQueueAdmin { return; } try { - NewTopic newTopic = new NewTopic(topic, 1, replicationFactor).configs(topicConfigs); + NewTopic newTopic = new NewTopic(topic, numPartitions, replicationFactor).configs(topicConfigs); createTopic(newTopic).values().get(topic).get(); topics.add(topic); } catch (ExecutionException ee) { diff --git a/docker/queue-kafka.env b/docker/queue-kafka.env index 63107942fb..0207d64ef5 100644 --- a/docker/queue-kafka.env +++ b/docker/queue-kafka.env @@ -1,2 +1,3 @@ TB_QUEUE_TYPE=kafka TB_KAFKA_SERVERS=kafka:9092 +TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES=retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100 diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml index fac1ac7e8a..e6ec6f5b36 100644 --- a/msa/js-executor/config/default.yml +++ b/msa/js-executor/config/default.yml @@ -25,7 +25,7 @@ kafka: # Kafka Bootstrap Servers servers: "localhost:9092" replication_factor: "1" - topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600" + topic_properties: "retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100" use_confluent_cloud: false confluent: sasl: diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 0420173188..2672a8df6a 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -34,7 +34,7 @@ function KafkaProducer() { this.send = async (responseTopic, scriptId, rawResponse, headers) => { if (!topics.includes(responseTopic)) { - let createResponseTopicResult = await createTopic(responseTopic); + let createResponseTopicResult = await createTopic(responseTopic, 1); topics.push(responseTopic); if (createResponseTopicResult) { logger.info('Created new topic: %s', requestTopic); @@ -88,7 +88,18 @@ function KafkaProducer() { kafkaAdmin = kafkaClient.admin(); await kafkaAdmin.connect(); - let createRequestTopicResult = await createTopic(requestTopic); + let partitions = 1; + + for (let i = 0; i < configEntries.length; i++) { + let param = configEntries[i]; + if (param.name === 'partitions') { + partitions = param.value; + configEntries.splice(i, 1); + break; + } + } + + let createRequestTopicResult = await createTopic(requestTopic, partitions); if (createRequestTopicResult) { logger.info('Created new topic: %s', requestTopic); @@ -121,10 +132,11 @@ function KafkaProducer() { } })(); -function createTopic(topic) { +function createTopic(topic, partitions) { return kafkaAdmin.createTopics({ topics: [{ topic: topic, + numPartitions: partitions, replicationFactor: replicationFactor, configEntries: configEntries }] diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml index f04a7559b8..3f57b32fab 100644 --- a/transport/coap/src/main/resources/tb-coap-transport.yml +++ b/transport/coap/src/main/resources/tb-coap-transport.yml @@ -77,11 +77,11 @@ queue: security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other: topic-properties: - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" aws_sqs: use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml index a97a58cd85..77d5f30fa7 100644 --- a/transport/http/src/main/resources/tb-http-transport.yml +++ b/transport/http/src/main/resources/tb-http-transport.yml @@ -70,11 +70,11 @@ queue: security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other: topic-properties: - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" aws_sqs: use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index b3804e3531..bc18112c8e 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -98,11 +98,11 @@ queue: security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" other: topic-properties: - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}" - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}" + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}" aws_sqs: use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" From 8c089460845f899946d56c95febcbaad32cacc5e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 8 Sep 2020 17:38:16 +0300 Subject: [PATCH 03/13] Fixed not predefined legend config direction in State chart --- .../src/main/data/json/system/widget_bundles/charts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json index 26292c60b3..22c9d20a23 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -166,7 +166,7 @@ "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", "settingsSchema": "{}", "dataKeySettingsSchema": "{}", - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" } } ] From 33ac57a1a85cbf7f5bb5fb27850ec25c01b6ca69 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Wed, 9 Sep 2020 23:46:10 +0300 Subject: [PATCH 04/13] Added sorting of widget bundles by name --- .../home/pages/widget/widgets-bundles-table-config.resolver.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts index 5edd4c0825..c59dd6cc63 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widgets-bundles-table-config.resolver.ts @@ -37,6 +37,7 @@ import { getCurrentAuthUser } from '@app/core/auth/auth.selectors'; import { Authority } from '@shared/models/authority.enum'; import { DialogService } from '@core/services/dialog.service'; import { ImportExportService } from '@home/components/import-export/import-export.service'; +import { Direction } from "@shared/models/page/sort-order"; @Injectable() export class WidgetsBundlesTableConfigResolver implements Resolve> { @@ -55,6 +56,7 @@ export class WidgetsBundlesTableConfigResolver implements Resolve widgetsBundle ? widgetsBundle.title : ''; From 04b4a76048f3e5ee550f69a94b8d746da15c91d8 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 10 Sep 2020 17:23:42 +0300 Subject: [PATCH 05/13] Removed reset form from update entity --- .../src/app/modules/home/components/entity/entity.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/entity.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity.component.ts index 68e4ccc41e..2f6ee2fb8b 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity.component.ts @@ -65,7 +65,6 @@ export abstract class EntityComponent, set entity(entity: T) { this.entityValue = entity; if (this.entityForm) { - this.entityForm.reset(undefined, {emitEvent: false}); this.entityForm.markAsPristine(); this.updateForm(entity); } @@ -124,7 +123,7 @@ export abstract class EntityComponent, if (isString(obj[curr])) { acc[curr] = obj[curr].trim(); } else if (isObject(obj[curr])) { - acc[curr] = this.deepTrim(obj[curr]) + acc[curr] = this.deepTrim(obj[curr]); } else { acc[curr] = obj[curr]; } From b090081e09cfffac40be4fd36475a8541a398fce Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 10 Sep 2020 19:09:03 +0300 Subject: [PATCH 06/13] Updated order set value in entity-panel --- .../home/components/entity/entity-details-panel.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts index f6ef733560..8eb046c384 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts @@ -251,13 +251,14 @@ export class EntityDetailsPanelComponent extends PageComponent implements OnInit } onToggleEditMode(isEdit: boolean) { - this.isEdit = isEdit; - if (!this.isEdit) { + if (!isEdit) { this.entityComponent.entity = this.entity; if (this.entityTabsComponent) { this.entityTabsComponent.entity = this.entity; } + this.isEdit = isEdit; } else { + this.isEdit = isEdit; this.editingEntity = deepClone(this.entity); this.entityComponent.entity = this.editingEntity; if (this.entityTabsComponent) { From ac0f4bc72298bd3ed9ae9b81e3ce29d5ffe29c70 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 10 Sep 2020 11:09:58 +0300 Subject: [PATCH 07/13] Sort entities before batch update (for SQL) --- .../src/main/resources/thingsboard.yml | 2 ++ .../server/dao/sql/TbSqlBlockingQueue.java | 10 +++++++-- .../dao/sql/TbSqlBlockingQueueParams.java | 1 + .../dao/sql/TbSqlBlockingQueueWrapper.java | 13 ++++++++++-- .../server/dao/sql/TbSqlQueue.java | 3 ++- .../dao/sql/attributes/JpaAttributeDao.java | 12 ++++++++++- ...stractChunkedAggregationTimeseriesDao.java | 13 ++++++++---- .../dao/sqlts/AbstractSqlTimeseriesDao.java | 3 +++ .../dao/sqlts/SqlTimeseriesLatestDao.java | 21 ++++++++++++------- .../timescale/TimescaleTimeseriesDao.java | 14 +++++++------ 10 files changed, 69 insertions(+), 23 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 4345e5a757..448d2f4e9d 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -284,6 +284,8 @@ sql: batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}" stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}" batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:4}" + # Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks + batch_sort: "${SQL_BATCH_SORT:false}" # Specify whether to remove null characters from strValue of attributes and timeseries before insert remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}" # Specify whether to log database queries and their parameters generated by entity query repository diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java index 22fda66759..6a3a66a320 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java @@ -22,6 +22,7 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.stats.MessagesStats; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; @@ -30,6 +31,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j public class TbSqlBlockingQueue implements TbSqlQueue { @@ -46,7 +48,7 @@ public class TbSqlBlockingQueue implements TbSqlQueue { } @Override - public void init(ScheduledLogExecutorComponent logExecutor, Consumer> saveFunction, int index) { + public void init(ScheduledLogExecutorComponent logExecutor, Consumer> saveFunction, Comparator batchUpdateComparator, int index) { executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("sql-queue-" + index + "-" + params.getLogName().toLowerCase())); executor.submit(() -> { String logName = params.getLogName(); @@ -65,7 +67,11 @@ public class TbSqlBlockingQueue implements TbSqlQueue { queue.drainTo(entities, batchSize - 1); boolean fullPack = entities.size() == batchSize; log.debug("[{}] Going to save {} entities", logName, entities.size()); - saveFunction.accept(entities.stream().map(TbSqlQueueElement::getEntity).collect(Collectors.toList())); + Stream entitiesStream = entities.stream().map(TbSqlQueueElement::getEntity); + saveFunction.accept( + (params.isBatchSortEnabled() ? entitiesStream.sorted(batchUpdateComparator) : entitiesStream) + .collect(Collectors.toList()) + ); entities.forEach(v -> v.getFuture().set(null)); stats.incrementSuccessful(entities.size()); if (!fullPack) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueParams.java b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueParams.java index a63461787e..6100405711 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueParams.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueParams.java @@ -31,4 +31,5 @@ public class TbSqlBlockingQueueParams { private final long maxDelay; private final long statsPrintIntervalMs; private final String statsNamePrefix; + private final boolean batchSortEnabled; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueWrapper.java b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueWrapper.java index d9596a6efd..884d4c7a3d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueWrapper.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueueWrapper.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.stats.MessagesStats; import org.thingsboard.server.common.stats.StatsFactory; +import java.util.Comparator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; @@ -36,12 +37,20 @@ public class TbSqlBlockingQueueWrapper { private final int maxThreads; private final StatsFactory statsFactory; - public void init(ScheduledLogExecutorComponent logExecutor, Consumer> saveFunction) { + /** + * Starts TbSqlBlockingQueues. + * + * @param logExecutor executor that will be printing logs and statistics + * @param saveFunction function to save entities in database + * @param batchUpdateComparator comparator to sort entities by primary key to avoid deadlocks in cluster mode + * NOTE: you must use all of primary key parts in your comparator + */ + public void init(ScheduledLogExecutorComponent logExecutor, Consumer> saveFunction, Comparator batchUpdateComparator) { for (int i = 0; i < maxThreads; i++) { MessagesStats stats = statsFactory.createMessagesStats(params.getStatsNamePrefix() + ".queue." + i); TbSqlBlockingQueue queue = new TbSqlBlockingQueue<>(params, stats); queues.add(queue); - queue.init(logExecutor, saveFunction, i); + queue.init(logExecutor, saveFunction, batchUpdateComparator, i); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlQueue.java b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlQueue.java index c3955a811c..135e636ea5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlQueue.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlQueue.java @@ -17,12 +17,13 @@ package org.thingsboard.server.dao.sql; import com.google.common.util.concurrent.ListenableFuture; +import java.util.Comparator; import java.util.List; import java.util.function.Consumer; public interface TbSqlQueue { - void init(ScheduledLogExecutorComponent logExecutor, Consumer> saveFunction, int queueIndex); + void init(ScheduledLogExecutorComponent logExecutor, Consumer> saveFunction, Comparator batchUpdateComparator, int queueIndex); void destroy(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 2803dec8dd..69616155d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -38,6 +38,7 @@ import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.Collection; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -71,6 +72,9 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl @Value("${sql.attributes.batch_threads:4}") private int batchThreads; + @Value("${sql.batch_sort:false}") + private boolean batchSortEnabled; + private TbSqlBlockingQueueWrapper queue; @PostConstruct @@ -81,11 +85,17 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl .maxDelay(maxDelay) .statsPrintIntervalMs(statsPrintIntervalMs) .statsNamePrefix("attributes") + .batchSortEnabled(batchSortEnabled) .build(); Function hashcodeFunction = entity -> entity.getId().getEntityId().hashCode(); queue = new TbSqlBlockingQueueWrapper<>(params, hashcodeFunction, batchThreads, statsFactory); - queue.init(logExecutor, v -> attributeKvInsertRepository.saveOrUpdate(v)); + queue.init(logExecutor, v -> attributeKvInsertRepository.saveOrUpdate(v), + Comparator.comparing((AttributeKvEntity attributeKvEntity) -> attributeKvEntity.getId().getEntityId()) + .thenComparing(attributeKvEntity -> attributeKvEntity.getId().getEntityType().name()) + .thenComparing(attributeKvEntity -> attributeKvEntity.getId().getAttributeType()) + .thenComparing(attributeKvEntity -> attributeKvEntity.getId().getAttributeKey()) + ); } @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractChunkedAggregationTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractChunkedAggregationTimeseriesDao.java index 079bf1b660..3cee730ee3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractChunkedAggregationTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractChunkedAggregationTimeseriesDao.java @@ -21,6 +21,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.thingsboard.server.common.data.id.EntityId; @@ -31,6 +32,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper; @@ -40,9 +42,7 @@ import org.thingsboard.server.dao.timeseries.TimeseriesDao; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; @@ -68,11 +68,16 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq .maxDelay(tsMaxDelay) .statsPrintIntervalMs(tsStatsPrintIntervalMs) .statsNamePrefix("ts") + .batchSortEnabled(batchSortEnabled) .build(); Function hashcodeFunction = entity -> entity.getEntityId().hashCode(); tsQueue = new TbSqlBlockingQueueWrapper<>(tsParams, hashcodeFunction, tsBatchThreads, statsFactory); - tsQueue.init(logExecutor, v -> insertRepository.saveOrUpdate(v)); + tsQueue.init(logExecutor, v -> insertRepository.saveOrUpdate(v), + Comparator.comparing((Function) AbstractTsKvEntity::getEntityId) + .thenComparing(AbstractTsKvEntity::getKey) + .thenComparing(AbstractTsKvEntity::getTs) + ); } @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index 9ab8aa3dfc..8dcefeca61 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -53,6 +53,9 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries @Value("${sql.timescale.batch_threads:4}") protected int timescaleBatchThreads; + @Value("${sql.batch_sort:false}") + protected boolean batchSortEnabled; + protected ListenableFuture> processFindAllAsync(TenantId tenantId, EntityId entityId, List queries) { List>> futures = queries .stream() diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java index ee7c02d558..075571308a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; @@ -50,12 +51,10 @@ import org.thingsboard.server.dao.util.SqlTsLatestAnyDao; import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.ExecutionException; +import java.util.function.Function; +import java.util.stream.Collectors; @Slf4j @Component @@ -90,6 +89,9 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme @Value("${sql.ts_latest.batch_threads:4}") private int tsLatestBatchThreads; + @Value("${sql.batch_sort:false}") + protected boolean batchSortEnabled; + @Autowired protected ScheduledLogExecutorComponent logExecutor; @@ -104,6 +106,7 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme .maxDelay(tsLatestMaxDelay) .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs) .statsNamePrefix("ts.latest") + .batchSortEnabled(false) .build(); java.util.function.Function hashcodeFunction = entity -> entity.getEntityId().hashCode(); @@ -118,9 +121,13 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme trueLatest.put(key, ts); } }); - List latestEntities = new ArrayList<>(trueLatest.values()); + List latestEntities = batchSortEnabled ? + trueLatest.values().stream().sorted(Comparator.comparing((Function) AbstractTsKvEntity::getEntityId) + .thenComparingInt(AbstractTsKvEntity::getKey) + ) + .collect(Collectors.toList()) : new ArrayList<>(trueLatest.values()); insertLatestTsRepository.saveOrUpdate(latestEntities); - }); + }, (l, r) -> 0); } @PreDestroy diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java index 71647f88f9..6d5923c508 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java @@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper; @@ -43,11 +44,7 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -78,12 +75,17 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements .maxDelay(tsMaxDelay) .statsPrintIntervalMs(tsStatsPrintIntervalMs) .statsNamePrefix("ts.timescale") + .batchSortEnabled(batchSortEnabled) .build(); Function hashcodeFunction = entity -> entity.getEntityId().hashCode(); tsQueue = new TbSqlBlockingQueueWrapper<>(tsParams, hashcodeFunction, timescaleBatchThreads, statsFactory); - tsQueue.init(logExecutor, v -> insertRepository.saveOrUpdate(v)); + tsQueue.init(logExecutor, v -> insertRepository.saveOrUpdate(v), + Comparator.comparing((Function) AbstractTsKvEntity::getEntityId) + .thenComparing(AbstractTsKvEntity::getKey) + .thenComparing(AbstractTsKvEntity::getTs) + ); } @PreDestroy From 081c83b7251f96f38f989b5302f4200fbef61218 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 11 Sep 2020 12:07:29 +0300 Subject: [PATCH 08/13] Refactored saving latest ts --- .../server/dao/sqlts/SqlTimeseriesLatestDao.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java index 075571308a..f6e5e8aa40 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java @@ -116,16 +116,13 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme Map trueLatest = new HashMap<>(); v.forEach(ts -> { TsKey key = new TsKey(ts.getEntityId(), ts.getKey()); - TsKvLatestEntity old = trueLatest.get(key); - if (old == null || old.getTs() < ts.getTs()) { - trueLatest.put(key, ts); - } + trueLatest.merge(key, ts, (oldTs, newTs) -> oldTs.getTs() < newTs.getTs() ? newTs : oldTs); }); - List latestEntities = batchSortEnabled ? - trueLatest.values().stream().sorted(Comparator.comparing((Function) AbstractTsKvEntity::getEntityId) - .thenComparingInt(AbstractTsKvEntity::getKey) - ) - .collect(Collectors.toList()) : new ArrayList<>(trueLatest.values()); + List latestEntities = new ArrayList<>(trueLatest.values()); + if (batchSortEnabled) { + latestEntities.sort(Comparator.comparing((Function) AbstractTsKvEntity::getEntityId) + .thenComparingInt(AbstractTsKvEntity::getKey)); + } insertLatestTsRepository.saveOrUpdate(latestEntities); }, (l, r) -> 0); } From 3a830eeb9ac9747618200ce249f6669063fb7a35 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 11 Sep 2020 19:27:29 +0300 Subject: [PATCH 09/13] UI: Device profile alarm rules --- .../filter/filter-text.component.html | 19 ++++ .../filter/filter-text.component.scss | 79 ++++++++++++++ .../filter/filter-text.component.ts | 101 ++++++++++++++++++ .../home/components/home-components.module.ts | 3 + .../alarm/alarm-rule-condition.component.html | 32 +++--- .../alarm/alarm-rule-condition.component.scss | 17 ++- .../alarm/alarm-rule-condition.component.ts | 23 ++-- .../profile/alarm/alarm-rule.component.html | 88 +++++++-------- .../profile/alarm/alarm-rule.component.scss | 5 + .../alarm/create-alarm-rules.component.html | 6 +- .../alarm/create-alarm-rules.component.ts | 2 +- .../alarm/device-profile-alarm.component.html | 4 + .../device-profile-alarms.component.html | 3 +- .../device-profile-data.component.html | 2 +- .../app/shared/models/query/query.models.ts | 94 ++++++++++++++++ .../assets/locale/locale.constant-en_US.json | 12 ++- 16 files changed, 416 insertions(+), 74 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/filter/filter-text.component.html create mode 100644 ui-ngx/src/app/modules/home/components/filter/filter-text.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/filter/filter-text.component.ts diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-text.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-text.component.html new file mode 100644 index 0000000000..8aeacc51a2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/filter/filter-text.component.html @@ -0,0 +1,19 @@ + +
diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-text.component.scss b/ui-ngx/src/app/modules/home/components/filter/filter-text.component.scss new file mode 100644 index 0000000000..84fabbf213 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/filter/filter-text.component.scss @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .tb-filter-text { + overflow-y: auto; + &.disabled { + opacity: 0.7; + } + &.required { + color: #f44336; + } + } +} + +:host ::ng-deep { + .tb-filter-text { + line-height: 1.8em; + span { + display: inline-block; + vertical-align: middle; + line-height: 1.4em; + } + .tb-filter-predicate { + padding-right: 4px; + padding-left: 4px; + } + .tb-filter-entity-key, .tb-filter-value, .tb-filter-dynamic-source { + font-weight: bold; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + padding-left: 4px; + padding-right: 4px; + } + .tb-filter-entity-key, .tb-filter-value { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 150px; + } + .tb-filter-dynamic-source { + } + .tb-filter-entity-key { + color: #305680; + } + .tb-filter-value { + color: #ff5722; + } + .tb-filter-simple-operation { + font-size: 0.9em; + } + .tb-filter-complex-operation { + text-transform: uppercase; + font-weight: bold; + } + .tb-filter-dynamic-value { + .tb-filter-dynamic-source, .tb-filter-value { + color: #0c959c; + } + } + .tb-filter-bracket { + .tb-left-bracket, .tb-right-bracket { + font-size: 1.2em; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-text.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-text.component.ts new file mode 100644 index 0000000000..e6eb5955b0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/filter/filter-text.component.ts @@ -0,0 +1,101 @@ +/// +/// Copyright © 2016-2020 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { KeyFilter, keyFiltersToText } from '@shared/models/query/query.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; + +@Component({ + selector: 'tb-filter-text', + templateUrl: './filter-text.component.html', + styleUrls: ['./filter-text.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FilterTextComponent), + multi: true + } + ] +}) +export class FilterTextComponent implements ControlValueAccessor, OnInit { + + private requiredValue: boolean; + get required(): boolean { + return this.requiredValue; + } + @Input() + set required(value: boolean) { + this.requiredValue = coerceBooleanProperty(value); + } + + @Input() + disabled: boolean; + + @Input() + noFilterText = this.translate.instant('filter.no-filter-text'); + + @Input() + addFilterPrompt = this.translate.instant('filter.add-filter-prompt'); + + requiredClass = false; + + private filterText: string; + + private propagateChange = (v: any) => { }; + + constructor(private dialog: MatDialog, + private fb: FormBuilder, + private translate: TranslateService, + private datePipe: DatePipe) { + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + ngOnInit() { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } + + writeValue(value: Array): void { + this.updateFilterText(value); + } + + private updateFilterText(value: Array) { + this.requiredClass = false; + if (value && value.length) { + this.filterText = keyFiltersToText(this.translate, this.datePipe, value); + } else { + if (this.required && !this.disabled) { + this.filterText = this.addFilterPrompt; + this.requiredClass = true; + } else { + this.filterText = this.noFilterText; + } + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index 9421a2c9f8..d9771d844e 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -104,6 +104,7 @@ import { CreateAlarmRulesComponent } from './profile/alarm/create-alarm-rules.co import { AlarmRuleComponent } from './profile/alarm/alarm-rule.component'; import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-condition.component'; import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-key-filters-dialog.component'; +import { FilterTextComponent } from './filter/filter-text.component'; @NgModule({ declarations: @@ -165,6 +166,7 @@ import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-k FilterDialogComponent, FiltersDialogComponent, FilterSelectComponent, + FilterTextComponent, FiltersEditComponent, FiltersEditPanelComponent, UserFilterDialogComponent, @@ -245,6 +247,7 @@ import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-k FilterDialogComponent, FiltersDialogComponent, FilterSelectComponent, + FilterTextComponent, FiltersEditComponent, UserFilterDialogComponent, TenantProfileAutocompleteComponent, diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.html index 9a3dd2e6fa..2160b6b04a 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.html @@ -15,16 +15,22 @@ limitations under the License. --> - - - - - {{ disabled ? 'more_vert' : 'edit' }} - - + diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.scss b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.scss index c6146a53e6..1d654bc0f4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.scss @@ -14,9 +14,24 @@ * limitations under the License. */ :host { - a.mat-icon-button { + display: flex; + a.mat-button { &:hover, &:focus { border-bottom: none; } } + .tb-alarm-rule-condition { + padding: 8px; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + cursor: pointer; + } +} + +:host ::ng-deep { + .tb-alarm-rule-condition { + .tb-filter-text { + max-height: 200px; + } + } } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.ts index 306aeee3aa..7416b944eb 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-condition.component.ts @@ -30,6 +30,8 @@ import { AlarmRuleKeyFiltersDialogComponent, AlarmRuleKeyFiltersDialogData } from './alarm-rule-key-filters-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; @Component({ selector: 'tb-alarm-rule-condition', @@ -60,7 +62,9 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit private propagateChange = (v: any) => { }; constructor(private dialog: MatDialog, - private fb: FormBuilder) { + private fb: FormBuilder, + private translate: TranslateService, + private datePipe: DatePipe) { } registerOnChange(fn: any): void { @@ -76,6 +80,11 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; + if (this.disabled) { + this.alarmRuleConditionControl.disable({emitEvent: false}); + } else { + this.alarmRuleConditionControl.enable({emitEvent: false}); + } } writeValue(value: Array): void { @@ -83,8 +92,12 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit this.updateConditionInfo(); } + public conditionSet() { + return this.modelValue && this.modelValue.length; + } + public validate(c: FormControl) { - return (this.modelValue && this.modelValue.length) ? null : { + return this.conditionSet() ? null : { alarmRuleCondition: { valid: false, }, @@ -112,11 +125,7 @@ export class AlarmRuleConditionComponent implements ControlValueAccessor, OnInit } private updateConditionInfo() { - if (this.modelValue && this.modelValue.length) { - this.alarmRuleConditionControl.patchValue('Condition set'); - } else { - this.alarmRuleConditionControl.patchValue(null); - } + this.alarmRuleConditionControl.patchValue(this.modelValue); } private updateModel() { diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html index 3b7354c05a..2ba8ab183f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html @@ -16,56 +16,56 @@ -->
-
-
+
+ + +
- -
- - -
-
-
- +
device-profile.condition-duration
+
-
- - - - - {{ 'device-profile.condition-duration-value-required' | translate }} - - - {{ 'device-profile.condition-duration-value-range' | translate }} - - - {{ 'device-profile.condition-duration-value-range' | translate }} - - - - - - - {{ timeUnitTranslations.get(timeUnit) | translate }} - - - - {{ 'device-profile.condition-duration-time-unit-required' | translate }} - - +
+ +
+ + + + + {{ 'device-profile.condition-duration-value-required' | translate }} + + + {{ 'device-profile.condition-duration-value-range' | translate }} + + + {{ 'device-profile.condition-duration-value-range' | translate }} + + + + + + + {{ timeUnitTranslations.get(timeUnit) | translate }} + + + + {{ 'device-profile.condition-duration-time-unit-required' | translate }} + + +
@@ -73,7 +73,7 @@
-
device-profile.advanced-settings
+
device-profile.alarm-rule-details
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.scss b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.scss index 00885ed393..ed8d08806f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.scss @@ -14,6 +14,11 @@ * limitations under the License. */ :host { + .tb-condition-duration { + padding: 8px; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + } .mat-expansion-panel.advanced-settings { box-shadow: none; border: none; diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html index 9a5835d556..40b4fd0c35 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html @@ -36,7 +36,7 @@
-
+
+ device-profile.no-create-alarm-rules +
+
+ device-profile.no-clear-alarm-rule +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html index 4b159c5a62..daae838285 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-data.component.html @@ -39,7 +39,7 @@ required> - +
{{'device-profile.alarm-rules' | translate: diff --git a/ui-ngx/src/app/shared/models/query/query.models.ts b/ui-ngx/src/app/shared/models/query/query.models.ts index e6019fe023..e9419a709d 100644 --- a/ui-ngx/src/app/shared/models/query/query.models.ts +++ b/ui-ngx/src/app/shared/models/query/query.models.ts @@ -25,6 +25,8 @@ import { PageData } from '@shared/models/page/page-data'; import { isDefined, isEqual } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { AlarmInfo, AlarmSearchStatus, AlarmSeverity } from '../alarm.models'; +import { Filter } from '@material-ui/icons'; +import { DatePipe } from '@angular/common'; export enum EntityKeyType { ATTRIBUTE = 'ATTRIBUTE', @@ -358,6 +360,98 @@ export interface FiltersInfo { datasourceFilters: {[datasourceIndex: number]: FilterInfo}; } +export function keyFiltersToText(translate: TranslateService, datePipe: DatePipe, keyFilters: Array): string { + const filtersText = keyFilters.map(keyFilter => + keyFilterToText(translate, datePipe, keyFilter, + keyFilters.length > 1 ? ComplexOperation.AND : undefined)); + let result: string; + if (filtersText.length > 1) { + const andText = translate.instant('filter.operation.and'); + result = filtersText.join(' ' + andText + ' '); + } else { + result = filtersText[0]; + } + return result; +} + +export function keyFilterToText(translate: TranslateService, datePipe: DatePipe, keyFilter: KeyFilter, + parentComplexOperation?: ComplexOperation): string { + const keyFilterPredicate = keyFilter.predicate; + return keyFilterPredicateToText(translate, datePipe, keyFilter, keyFilterPredicate, parentComplexOperation); +} + +export function keyFilterPredicateToText(translate: TranslateService, + datePipe: DatePipe, + keyFilter: KeyFilter, + keyFilterPredicate: KeyFilterPredicate, + parentComplexOperation?: ComplexOperation): string { + if (keyFilterPredicate.type === FilterPredicateType.COMPLEX) { + const complexPredicate = keyFilterPredicate as ComplexFilterPredicate; + const complexOperation = complexPredicate.operation; + const complexPredicatesText = + complexPredicate.predicates.map(predicate => keyFilterPredicateToText(translate, datePipe, keyFilter, predicate, complexOperation)); + if (complexPredicatesText.length > 1) { + const operationText = translate.instant(complexOperationTranslationMap.get(complexOperation)); + let result = complexPredicatesText.join(' ' + operationText + ' '); + if (complexOperation === ComplexOperation.OR && parentComplexOperation && ComplexOperation.OR !== parentComplexOperation) { + result = `(${result})`; + } + return result; + } else { + return complexPredicatesText[0]; + } + } else { + return simpleKeyFilterPredicateToText(translate, datePipe, keyFilter, keyFilterPredicate); + } +} + +function simpleKeyFilterPredicateToText(translate: TranslateService, + datePipe: DatePipe, + keyFilter: KeyFilter, + keyFilterPredicate: StringFilterPredicate | + NumericFilterPredicate | + BooleanFilterPredicate): string { + const key = keyFilter.key.key; + let operation: string; + let value: string; + const val = keyFilterPredicate.value; + const dynamicValue = !!val.dynamicValue && !!val.dynamicValue.sourceType; + if (dynamicValue) { + value = '' + + translate.instant(dynamicValueSourceTypeTranslationMap.get(val.dynamicValue.sourceType)) + ''; + value += '.' + val.dynamicValue.sourceAttribute + ''; + } + switch (keyFilterPredicate.type) { + case FilterPredicateType.STRING: + operation = translate.instant(stringOperationTranslationMap.get(keyFilterPredicate.operation)); + if (keyFilterPredicate.ignoreCase) { + operation += ' ' + translate.instant('filter.ignore-case'); + } + if (!dynamicValue) { + value = `'${keyFilterPredicate.value.defaultValue}'`; + } + break; + case FilterPredicateType.NUMERIC: + operation = translate.instant(numericOperationTranslationMap.get(keyFilterPredicate.operation)); + if (!dynamicValue) { + if (keyFilter.valueType === EntityKeyValueType.DATE_TIME) { + value = datePipe.transform(keyFilterPredicate.value.defaultValue, 'yyyy-MM-dd HH:mm'); + } else { + value = keyFilterPredicate.value.defaultValue + ''; + } + } + break; + case FilterPredicateType.BOOLEAN: + operation = translate.instant(booleanOperationTranslationMap.get(keyFilterPredicate.operation)); + value = translate.instant(keyFilterPredicate.value.defaultValue ? 'value.true' : 'value.false'); + break; + } + if (!dynamicValue) { + value = `${value}`; + } + return `${key} ${operation} ${value}`; +} + export function keyFilterInfosToKeyFilters(keyFilterInfos: Array): Array { const keyFilters: Array = []; for (const keyFilterInfo of keyFilterInfos) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 75165699fe..302d62133e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -813,15 +813,16 @@ "alarm-rules": "Alarm rules ({{count}})", "add-alarm-rule": "Add alarm rule", "edit-alarm-rule": "Edit alarm rule", - "alarm-rule-details": "Alarm rule details", "alarm-type": "Alarm type", "alarm-type-required": "Alarm type is required.", "alarm-type-pattern-hint": "Alarm type pattern, use ${metaKeyName} to substitute variables from metadata", "create-alarm-pattern": "Create {{alarmType}} alarm", "create-alarm-rules": "Create alarm rules", + "no-create-alarm-rules": "No create conditions configured", "clear-alarm-rule": "Clear alarm rule", - "add-create-alarm-rule": "Add create alarm rule", - "add-clear-alarm-rule": "Add clear alarm rule", + "no-clear-alarm-rule": "No clear condition configured", + "add-create-alarm-rule": "Add create condition", + "add-clear-alarm-rule": "Add clear condition", "select-alarm-severity": "Select alarm severity", "alarm-severity-required": "Alarm severity is required.", "condition-duration": "Condition duration", @@ -831,6 +832,7 @@ "condition-duration-value-required": "Duration value is required.", "condition-duration-time-unit-required": "Time unit is required.", "advanced-settings": "Advanced settings", + "alarm-rule-details": "Details", "propagate-alarm": "Propagate alarm", "alarm-details": "Alarm details", "alarm-rule-condition": "Alarm rule condition", @@ -1277,6 +1279,8 @@ "filter": "Filter", "editable": "Editable", "no-filters-found": "No filters found.", + "no-filter-text": "No filter specified", + "add-filter-prompt": "Please add filter", "no-filter-matching": "'{{filter}}' not found.", "create-new-filter": "Create a new one!", "filter-required": "Filter is required.", @@ -1295,7 +1299,7 @@ "and": "and", "or": "or" }, - "ignore-case": "Ignore case", + "ignore-case": "ignore case", "value": "Value", "remove-filter": "Remove filter", "no-filters": "No filters configured", From 6edf736e154dc5d55fc84e9243ee4ecfc1cf3fc1 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 14 Sep 2020 11:45:07 +0300 Subject: [PATCH 10/13] Version set to 3.1.2-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- dao/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 40 files changed, 41 insertions(+), 41 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index f81ae293d2..b6cdf9187d 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index b69d0c7e62..b5d04fbe8c 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 2c42f494d1..975827b994 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 18f4a26200..0d8233ce2d 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 121fb19af1..eb5ffc0555 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 196ec8d2b8..42d51380df 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index e1b47bcded..ea56755253 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 0ab8859238..7967fb67bf 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 221f126469..e29374eff2 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 9e2ba641dd..d79515a89c 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 3b09bdff40..7e8c32212e 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index d104406726..21cf8a3768 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 352ac7f38a..2d7b9c9cee 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 25dbf9d35e..5baba0d2ed 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index a2f973029e..048f43cc1e 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard dao diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 21811582a9..210fc55b72 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index b0f3af4fe3..c6cf4a1edf 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "3.1.1", + "version": "3.1.2", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.js", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 0f9c6dac86..af387fc59b 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/pom.xml b/msa/pom.xml index bdcc73e441..4cf7e6d43b 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 282bbf6f97..32b9873527 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index b1d373c231..5c122ecae3 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 7b3a9d8824..70e737c68c 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index cad26dc670..bcc81c87f1 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index ebb25456b7..bdd9b62c8d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index eccfcfb1bf..81300a347b 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index a39acf8e0e..0c5f82cbd5 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "3.1.1", + "version": "3.1.2", "description": "ThingsBoard Web UI Microservice", "main": "server.js", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 7fb6a99896..3b37627255 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 1aa0fc49cf..b26f7841bd 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard netty-mqtt - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 4fb089b93b..4b2a83325c 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index 1b989fb7b0..70f4ffb8f1 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 8c3014ac80..6b89625471 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 011d1a8dbc..4b56e71c3f 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 9df49dcd78..a06c714017 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 8fe3bc66b9..2521ed4aaf 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 2558c4d841..877df7e4ff 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 2f3e423b7f..e1f0334092 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index 30e112a3ce..c9480bf6fd 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 7e1a6bd998..80b26d9ba4 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index ef12a0d8de..df948ca4b3 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "3.1.1", + "version": "3.1.2", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 80e13e995d..8d913a1d87 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard org.thingsboard From 745648a5adc6c9a0df4e6b4e184904111bdf7586 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 14 Sep 2020 14:24:09 +0300 Subject: [PATCH 11/13] Version set to 3.1.2-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- dao/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- ui-ngx/pom.xml | 2 +- 37 files changed, 38 insertions(+), 38 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index c2ae7eccc2..df17f3c2ec 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 9258ee75a4..b5d04fbe8c 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 650b05c04e..975827b994 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index eb7a3b226c..0d8233ce2d 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 17a0679630..eb5ffc0555 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index 52685d3891..42d51380df 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index aaf46710f7..ea56755253 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 10c3ccb040..7967fb67bf 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 3b04a20abe..e29374eff2 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index 040f7c372e..d79515a89c 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 8a02c940a7..7e8c32212e 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index fbdb10d081..21cf8a3768 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 41813bb006..0c2ed12345 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index 076eff152a..5baba0d2ed 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index ec4960eb5f..048f43cc1e 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard dao diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 74948b0e84..210fc55b72 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index ea286c58ba..af387fc59b 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/pom.xml b/msa/pom.xml index 7419141995..4cf7e6d43b 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 2d42d8f6dd..32b9873527 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index f41a3afd1e..5c122ecae3 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 0b3ab420e8..70e737c68c 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index 5621fd6bdc..bcc81c87f1 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index 24f8350706..bdd9b62c8d 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index e000241608..81300a347b 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index f9ef249ce1..3b37627255 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index 58524a1a08..b26f7841bd 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard netty-mqtt - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index 0225caa662..4b2a83325c 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index a7b97610d1..70f4ffb8f1 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 197c4e500d..6b89625471 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 1a4c0b8506..4b56e71c3f 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index c4d2b66d15..a06c714017 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 8d596c8493..2521ed4aaf 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index 1b7abf06db..877df7e4ff 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index f5ae869c75..e1f0334092 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index a1a45f54b8..c9480bf6fd 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 429c953a46..80b26d9ba4 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard transport diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index 14b9317e4a..8d913a1d87 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.2.0-SNAPSHOT + 3.1.2-SNAPSHOT thingsboard org.thingsboard From c4e67215969501693d89bc6a8659dbadc98fb458 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 14 Sep 2020 15:33:01 +0300 Subject: [PATCH 12/13] UI: Device profile alarm rules improvements --- .../boolean-filter-predicate.component.html | 3 +- .../boolean-filter-predicate.component.ts | 2 + ...lex-filter-predicate-dialog.component.html | 1 + ...mplex-filter-predicate-dialog.component.ts | 1 + .../complex-filter-predicate.component.ts | 5 +- .../filter-predicate-list.component.html | 1 + .../filter/filter-predicate-list.component.ts | 5 +- .../filter-predicate-value.component.html | 2 +- .../filter-predicate-value.component.ts | 14 +- .../filter/filter-predicate.component.html | 15 ++- .../filter/filter-predicate.component.ts | 2 + .../filter/key-filter-dialog.component.html | 1 + .../filter/key-filter-dialog.component.ts | 1 + .../filter/key-filter-list.component.html | 126 ++++++++++-------- .../filter/key-filter-list.component.scss | 13 ++ .../filter/key-filter-list.component.ts | 21 ++- .../numeric-filter-predicate.component.html | 3 +- .../numeric-filter-predicate.component.ts | 2 + .../string-filter-predicate.component.html | 3 +- .../string-filter-predicate.component.ts | 2 + ...arm-rule-key-filters-dialog.component.html | 1 + .../alarm/create-alarm-rules.component.html | 3 +- .../alarm/create-alarm-rules.component.ts | 17 +++ .../alarm/device-profile-alarm.component.html | 24 +++- .../alarm/device-profile-alarm.component.ts | 33 +++++ .../app/shared/models/query/query.models.ts | 3 + .../assets/locale/locale.constant-en_US.json | 3 + 27 files changed, 233 insertions(+), 74 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html index 5e4c597447..d05e4b3775 100644 --- a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.html @@ -24,7 +24,8 @@ - diff --git a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts index e7e1dd5c81..aa264697b6 100644 --- a/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/boolean-filter-predicate.component.ts @@ -39,6 +39,8 @@ export class BooleanFilterPredicateComponent implements ControlValueAccessor, On @Input() disabled: boolean; + @Input() allowUserDynamicSource = true; + valueTypeEnum = EntityKeyValueType; booleanFilterPredicateFormGroup: FormGroup; diff --git a/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html b/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html index a2e01977ea..4502e9da67 100644 --- a/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.html @@ -38,6 +38,7 @@ diff --git a/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.ts b/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.ts index 623ca4f968..8fe9e5a1b6 100644 --- a/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate-dialog.component.ts @@ -36,6 +36,7 @@ export interface ComplexFilterPredicateDialogData { isAdd: boolean; valueType: EntityKeyValueType; displayUserParameters: boolean; + allowUserDynamicSource: boolean; } @Component({ diff --git a/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.ts index 654eecdefd..87bad30d5a 100644 --- a/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/complex-filter-predicate.component.ts @@ -50,6 +50,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On @Input() displayUserParameters = true; + @Input() allowUserDynamicSource = true; + private propagateChange = null; private complexFilterPredicate: ComplexFilterPredicateInfo; @@ -86,7 +88,8 @@ export class ComplexFilterPredicateComponent implements ControlValueAccessor, On valueType: this.valueType, isAdd: false, key: this.key, - displayUserParameters: this.displayUserParameters + displayUserParameters: this.displayUserParameters, + allowUserDynamicSource: this.allowUserDynamicSource } }).afterClosed().subscribe( (result) => { diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html index 2da9a68e4d..ffef5e2349 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.html @@ -52,6 +52,7 @@ fxFlex [valueType]="valueType" [displayUserParameters]="displayUserParameters" + [allowUserDynamicSource]="allowUserDynamicSource" [key]="key" [formControl]="predicateControl"> diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts index c5eb501b16..2e59e85123 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-list.component.ts @@ -64,6 +64,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni @Input() displayUserParameters = true; + @Input() allowUserDynamicSource = true; + filterListFormGroup: FormGroup; valueTypeEnum = EntityKeyValueType; @@ -156,7 +158,8 @@ export class FilterPredicateListComponent implements ControlValueAccessor, OnIni valueType: this.valueType, key: this.key, isAdd: true, - displayUserParameters: this.displayUserParameters + displayUserParameters: this.displayUserParameters, + allowUserDynamicSource: this.allowUserDynamicSource } }).afterClosed().pipe( map((result) => { diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html index e9e38c71f5..524285ea3f 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.html @@ -55,7 +55,7 @@ {{'filter.no-dynamic-value' | translate}} - {{dynamicValueSourceTypeTranslations.get(dynamicValueSourceTypeEnum[sourceType]) | translate}} + {{dynamicValueSourceTypeTranslations.get(sourceType) | translate}} diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts index 548137976e..aa430e596e 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate-value.component.ts @@ -46,13 +46,23 @@ export class FilterPredicateValueComponent implements ControlValueAccessor, OnIn @Input() disabled: boolean; + @Input() + set allowUserDynamicSource(allow: boolean) { + this.dynamicValueSourceTypes = [DynamicValueSourceType.CURRENT_TENANT, + DynamicValueSourceType.CURRENT_CUSTOMER]; + if (allow) { + this.dynamicValueSourceTypes.push(DynamicValueSourceType.CURRENT_USER); + } + } + @Input() valueType: EntityKeyValueType; valueTypeEnum = EntityKeyValueType; - dynamicValueSourceTypes = Object.keys(DynamicValueSourceType); - dynamicValueSourceTypeEnum = DynamicValueSourceType; + dynamicValueSourceTypes: DynamicValueSourceType[] = [DynamicValueSourceType.CURRENT_TENANT, + DynamicValueSourceType.CURRENT_CUSTOMER, DynamicValueSourceType.CURRENT_USER]; + dynamicValueSourceTypeTranslations = dynamicValueSourceTypeTranslationMap; filterPredicateValueFormGroup: FormGroup; diff --git a/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.html b/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.html index abe231219b..3b9d89008b 100644 --- a/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/filter-predicate.component.html @@ -19,20 +19,27 @@
- + - + - +
- - - -
filter.key-filters
-
-
-
- -
- - -   -   -
-
- -
-
-
- filter.operation.and + + + + +
filter.key-filters
+
+
+
+ +
+ + +   +  
-
-
-
{{ keyFilterControl.value.key.key }}
-
{{ entityKeyTypeTranslations.get(keyFilterControl.value.key.type) }}
- - +
+ +
+
+
+ filter.operation.and +
+
+
+
{{ keyFilterControl.value.key.key }}
+
{{ entityKeyTypeTranslations.get(keyFilterControl.value.key.type) }}
+ + +
+
-
+ filter.no-key-filters +
+
+ +
+ + + + +
filter.preview
+
+
+
+
- filter.no-key-filters -
-
- -
- + +
diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.scss b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.scss index 3951b5d640..bc47f04915 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.scss +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.scss @@ -26,4 +26,17 @@ color: #666; font-weight: 500; } + .tb-filter-preview { + padding: 8px; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + } +} + +:host ::ng-deep { + .tb-filter-preview { + .tb-filter-text { + max-height: 200px; + } + } } diff --git a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts index f78effb739..2779a35be9 100644 --- a/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/key-filter-list.component.ts @@ -19,13 +19,18 @@ import { AbstractControl, ControlValueAccessor, FormArray, - FormBuilder, + FormBuilder, FormControl, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; -import { EntityKeyType, entityKeyTypeTranslationMap, KeyFilterInfo } from '@shared/models/query/query.models'; +import { + EntityKeyType, + entityKeyTypeTranslationMap, + KeyFilter, + KeyFilterInfo, keyFilterInfosToKeyFilters +} from '@shared/models/query/query.models'; import { MatDialog } from '@angular/material/dialog'; import { deepClone } from '@core/utils'; import { KeyFilterDialogComponent, KeyFilterDialogData } from '@home/components/filter/key-filter-dialog.component'; @@ -48,12 +53,16 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { @Input() displayUserParameters = true; + @Input() allowUserDynamicSource = true; + @Input() telemetryKeysOnly = false; keyFilterListFormGroup: FormGroup; entityKeyTypeTranslations = entityKeyTypeTranslationMap; + keyFiltersControl: FormControl; + private propagateChange = null; private valueChangeSubscription: Subscription = null; @@ -66,6 +75,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { this.keyFilterListFormGroup = this.fb.group({}); this.keyFilterListFormGroup.addControl('keyFilters', this.fb.array([])); + this.keyFiltersControl = this.fb.control(null); } keyFiltersFormArray(): FormArray { @@ -83,8 +93,10 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { this.disabled = isDisabled; if (this.disabled) { this.keyFilterListFormGroup.disable({emitEvent: false}); + this.keyFiltersControl.disable({emitEvent: false}); } else { this.keyFilterListFormGroup.enable({emitEvent: false}); + this.keyFiltersControl.enable({emitEvent: false}); } } @@ -107,6 +119,8 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { } else { this.keyFilterListFormGroup.enable({emitEvent: false}); } + const keyFiltersArray = keyFilterInfosToKeyFilters(keyFilters); + this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false}); } public removeKeyFilter(index: number) { @@ -155,6 +169,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { isAdd, readonly: this.disabled, displayUserParameters: this.displayUserParameters, + allowUserDynamicSource: this.allowUserDynamicSource, telemetryKeysOnly: this.telemetryKeysOnly } }).afterClosed(); @@ -167,5 +182,7 @@ export class KeyFilterListComponent implements ControlValueAccessor, OnInit { } else { this.propagateChange(null); } + const keyFiltersArray = keyFilterInfosToKeyFilters(keyFilters); + this.keyFiltersControl.patchValue(keyFiltersArray, {emitEvent: false}); } } diff --git a/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html b/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html index 7a4a9c552b..71cbbd0335 100644 --- a/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.html @@ -24,7 +24,8 @@ - diff --git a/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts index e9e4f89c3b..32d5779c63 100644 --- a/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/numeric-filter-predicate.component.ts @@ -40,6 +40,8 @@ export class NumericFilterPredicateComponent implements ControlValueAccessor, On @Input() disabled: boolean; + @Input() allowUserDynamicSource = true; + @Input() valueType: EntityKeyValueType; numericFilterPredicateFormGroup: FormGroup; diff --git a/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html b/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html index ef9f2c01e7..92d470c6b8 100644 --- a/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html +++ b/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.html @@ -28,7 +28,8 @@
- diff --git a/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts b/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts index f115748e48..dffd52b274 100644 --- a/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts +++ b/ui-ngx/src/app/modules/home/components/filter/string-filter-predicate.component.ts @@ -40,6 +40,8 @@ export class StringFilterPredicateComponent implements ControlValueAccessor, OnI @Input() disabled: boolean; + @Input() allowUserDynamicSource = true; + valueTypeEnum = EntityKeyValueType; stringFilterPredicateFormGroup: FormGroup; diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-key-filters-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-key-filters-dialog.component.html index 3712ddff39..26defe62ff 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-key-filters-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule-key-filters-dialog.component.html @@ -32,6 +32,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html index 40b4fd0c35..c3ac14c757 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html @@ -25,7 +25,8 @@ - + {{ alarmSeverityTranslationMap.get(alarmSeverityEnum[alarmSeverity]) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts index 50604b9eab..caebff80b4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts @@ -61,6 +61,8 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, createAlarmRulesFormGroup: FormGroup; + private usedSeverities: AlarmSeverity[] = []; + private valueChangeSubscription: Subscription = null; private propagateChange = (v: any) => { }; @@ -121,6 +123,7 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, this.valueChangeSubscription = this.createAlarmRulesFormGroup.valueChanges.subscribe(() => { this.updateModel(); }); + this.updateUsedSeverities(); } public removeCreateAlarmRule(index: number) { @@ -149,12 +152,26 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, }; } + public isDisabledSeverity(severity: AlarmSeverity, index: number): boolean { + const usedIndex = this.usedSeverities.indexOf(severity); + return usedIndex > -1 && usedIndex !== index; + } + + private updateUsedSeverities() { + this.usedSeverities = []; + const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; + value.forEach((rule, index) => { + this.usedSeverities[index] = AlarmSeverity[rule.severity]; + }); + } + private updateModel() { const value: {severity: string, alarmRule: AlarmRule}[] = this.createAlarmRulesFormGroup.get('createAlarmRules').value; const createAlarmRules: {[severity: string]: AlarmRule} = {}; value.forEach(v => { createAlarmRules[v.severity] = v.alarmRule; }); + this.updateUsedSeverities(); this.propagateChange(createAlarmRules); } } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html index f85fdc99d6..d827245abf 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html @@ -26,6 +26,7 @@ {{'device-profile.alarm-type' | translate}} @@ -90,9 +91,28 @@
- + {{ 'device-profile.propagate-alarm' | translate }} -
TODO: Propagate relation types
+
+ + device-profile.alarm-rule-relation-types-list + + + {{key}} + close + + + + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts index 9ab4e39e27..8befb6204d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts @@ -27,6 +27,8 @@ import { } from '@angular/forms'; import { AlarmRule, DeviceProfileAlarm } from '@shared/models/device.models'; import { MatDialog } from '@angular/material/dialog'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { MatChipInputEvent } from '@angular/material/chips'; @Component({ selector: 'tb-device-profile-alarm', @@ -53,6 +55,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit @Output() removeAlarm = new EventEmitter(); + separatorKeysCodes = [ENTER, COMMA, SEMICOLON]; + expanded = false; private modelValue: DeviceProfileAlarm; @@ -124,6 +128,35 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit }; } + removeRelationType(key: string): void { + const keys: string[] = this.alarmFormGroup.get('propagateRelationTypes').value; + const index = keys.indexOf(key); + if (index >= 0) { + keys.splice(index, 1); + this.alarmFormGroup.get('propagateRelationTypes').setValue(keys, {emitEvent: true}); + } + } + + addRelationType(event: MatChipInputEvent): void { + const input = event.input; + let value = event.value; + if ((value || '').trim()) { + value = value.trim(); + let keys: string[] = this.alarmFormGroup.get('propagateRelationTypes').value; + if (!keys || keys.indexOf(value) === -1) { + if (!keys) { + keys = []; + } + keys.push(value); + this.alarmFormGroup.get('propagateRelationTypes').setValue(keys, {emitEvent: true}); + } + } + if (input) { + input.value = ''; + } + } + + private updateModel() { const value = this.alarmFormGroup.value; this.modelValue = {...this.modelValue, ...value}; diff --git a/ui-ngx/src/app/shared/models/query/query.models.ts b/ui-ngx/src/app/shared/models/query/query.models.ts index e9419a709d..3d3223bf34 100644 --- a/ui-ngx/src/app/shared/models/query/query.models.ts +++ b/ui-ngx/src/app/shared/models/query/query.models.ts @@ -453,6 +453,9 @@ function simpleKeyFilterPredicateToText(translate: TranslateService, } export function keyFilterInfosToKeyFilters(keyFilterInfos: Array): Array { + if (!keyFilterInfos) { + return []; + } const keyFilters: Array = []; for (const keyFilterInfo of keyFilterInfos) { const key = keyFilterInfo.key; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 302d62133e..a3b7d8c1d5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -834,6 +834,8 @@ "advanced-settings": "Advanced settings", "alarm-rule-details": "Details", "propagate-alarm": "Propagate alarm", + "alarm-rule-relation-types-list": "Relation types to propagate", + "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", "alarm-details": "Alarm details", "alarm-rule-condition": "Alarm rule condition", "enter-alarm-rule-condition-prompt": "Please add alarm rule condition", @@ -1302,6 +1304,7 @@ "ignore-case": "ignore case", "value": "Value", "remove-filter": "Remove filter", + "preview": "Filter preview", "no-filters": "No filters configured", "add-filter": "Add filter", "add-complex-filter": "Add complex filter", From 3dc7fde4dbe67aa39b2a8aca1789e7459c2251d7 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 14 Sep 2020 19:23:18 +0300 Subject: [PATCH 13/13] UI: Add device profile wizard --- .../home/components/home-components.module.ts | 7 +- .../add-device-profile-dialog.component.html | 114 ++++++++++++ .../add-device-profile-dialog.component.scss | 38 ++++ .../add-device-profile-dialog.component.ts | 173 ++++++++++++++++++ .../profile/alarm/alarm-rule.component.html | 8 +- .../alarm/create-alarm-rules.component.html | 2 +- .../alarm/create-alarm-rules.component.ts | 3 + .../alarm/device-profile-alarm.component.html | 2 +- .../alarm/device-profile-alarm.component.ts | 19 +- .../device-profile-alarms.component.html | 4 + .../alarm/device-profile-alarms.component.ts | 8 +- .../device-profile-autocomplete.component.ts | 43 +++-- .../profile/device-profile.component.html | 2 +- .../device-profiles-table-config.resolver.ts | 21 ++- .../assets/locale/locale.constant-en_US.json | 1 + 15 files changed, 409 insertions(+), 36 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html create mode 100644 ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts diff --git a/ui-ngx/src/app/modules/home/components/home-components.module.ts b/ui-ngx/src/app/modules/home/components/home-components.module.ts index d9771d844e..0f8b4a88cf 100644 --- a/ui-ngx/src/app/modules/home/components/home-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/home-components.module.ts @@ -105,6 +105,7 @@ import { AlarmRuleComponent } from './profile/alarm/alarm-rule.component'; import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-condition.component'; import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-key-filters-dialog.component'; import { FilterTextComponent } from './filter/filter-text.component'; +import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component'; @NgModule({ declarations: @@ -192,7 +193,8 @@ import { FilterTextComponent } from './filter/filter-text.component'; DeviceProfileAlarmsComponent, DeviceProfileDataComponent, DeviceProfileComponent, - DeviceProfileDialogComponent + DeviceProfileDialogComponent, + AddDeviceProfileDialogComponent ], imports: [ CommonModule, @@ -269,7 +271,8 @@ import { FilterTextComponent } from './filter/filter-text.component'; DeviceProfileAlarmsComponent, DeviceProfileDataComponent, DeviceProfileComponent, - DeviceProfileDialogComponent + DeviceProfileDialogComponent, + AddDeviceProfileDialogComponent ], providers: [ WidgetComponentService, diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html new file mode 100644 index 0000000000..5e1c4adfd6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html @@ -0,0 +1,114 @@ + +
+ +

device-profile.add

+ + +
+ + +
+
+ + +
+ {{ 'device-profile.device-profile-details' | translate }} +
+ + device-profile.name + + + {{ 'device-profile.name-required' | translate }} + + + + + + device-profile.type + + + {{deviceProfileTypeTranslations.get(type) | translate}} + + + + {{ 'device-profile.type-required' | translate }} + + + + device-profile.description + + +
+
+
+ +
+ {{ 'device-profile.transport-configuration' | translate }} + + device-profile.transport-type + + + {{deviceTransportTypeTranslations.get(type) | translate}} + + + + {{ 'device-profile.transport-type-required' | translate }} + + + + +
+
+ +
+ {{'device-profile.alarm-rules' | translate: + {count: alarmRulesFormGroup.get('alarms').value ? + alarmRulesFormGroup.get('alarms').value.length : 0} }} + + +
+
+
+
+
+ + +
+ + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss new file mode 100644 index 0000000000..cafcce74b6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.scss @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .mat-dialog-content { + display: flex; + flex-direction: column; + overflow: hidden; + + .mat-stepper-horizontal { + display: flex; + flex-direction: column; + overflow: hidden; + } + } +} + +:host ::ng-deep { + .mat-dialog-content { + .mat-stepper-horizontal { + .mat-horizontal-content-container { + overflow: auto; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts new file mode 100644 index 0000000000..476c69488f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts @@ -0,0 +1,173 @@ +/// +/// Copyright © 2016-2020 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + Component, + ComponentFactoryResolver, + Inject, + Injector, + SkipSelf, + ViewChild +} from '@angular/core'; +import { ErrorStateMatcher } from '@angular/material/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Router } from '@angular/router'; +import { + createDeviceProfileConfiguration, + createDeviceProfileTransportConfiguration, + DeviceProfile, + DeviceProfileType, + deviceProfileTypeTranslationMap, + DeviceTransportType, + deviceTransportTypeTranslationMap +} from '@shared/models/device.models'; +import { DeviceProfileService } from '@core/http/device-profile.service'; +import { EntityType } from '@shared/models/entity-type.models'; +import { MatHorizontalStepper } from '@angular/material/stepper'; +import { RuleChainId } from '@shared/models/id/rule-chain-id'; + +export interface AddDeviceProfileDialogData { + deviceProfileName: string; +} + +@Component({ + selector: 'tb-add-device-profile-dialog', + templateUrl: './add-device-profile-dialog.component.html', + providers: [], + styleUrls: ['./add-device-profile-dialog.component.scss'] +}) +export class AddDeviceProfileDialogComponent extends + DialogComponent implements AfterViewInit { + + @ViewChild('addDeviceProfileStepper', {static: true}) addDeviceProfileStepper: MatHorizontalStepper; + + selectedIndex = 0; + + entityType = EntityType; + + deviceProfileTypes = Object.keys(DeviceProfileType); + + deviceProfileTypeTranslations = deviceProfileTypeTranslationMap; + + deviceTransportTypes = Object.keys(DeviceTransportType); + + deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; + + deviceProfileDetailsFormGroup: FormGroup; + + transportConfigFormGroup: FormGroup; + + alarmRulesFormGroup: FormGroup; + + constructor(protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: AddDeviceProfileDialogData, + public dialogRef: MatDialogRef, + private componentFactoryResolver: ComponentFactoryResolver, + private injector: Injector, + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, + private deviceProfileService: DeviceProfileService, + private fb: FormBuilder) { + super(store, router, dialogRef); + this.deviceProfileDetailsFormGroup = this.fb.group( + { + name: [data.deviceProfileName, [Validators.required]], + type: [DeviceProfileType.DEFAULT, [Validators.required]], + defaultRuleChainId: [null, []], + description: ['', []] + } + ); + this.transportConfigFormGroup = this.fb.group( + { + transportType: [DeviceTransportType.DEFAULT, [Validators.required]], + transportConfiguration: [createDeviceProfileTransportConfiguration(DeviceTransportType.DEFAULT), + [Validators.required]] + } + ); + this.transportConfigFormGroup.get('transportType').valueChanges.subscribe(() => { + this.deviceProfileTransportTypeChanged(); + }); + + this.alarmRulesFormGroup = this.fb.group( + { + alarms: [null] + } + ); + } + + private deviceProfileTransportTypeChanged() { + const deviceTransportType: DeviceTransportType = this.transportConfigFormGroup.get('transportType').value; + this.transportConfigFormGroup.patchValue( + {transportConfiguration: createDeviceProfileTransportConfiguration(deviceTransportType)}); + } + + ngAfterViewInit(): void { + } + + cancel(): void { + this.dialogRef.close(null); + } + + previousStep() { + this.addDeviceProfileStepper.previous(); + } + + nextStep() { + if (this.selectedIndex < 2) { + this.addDeviceProfileStepper.next(); + } else { + this.add(); + } + } + + selectedForm(): FormGroup { + switch (this.selectedIndex) { + case 0: + return this.deviceProfileDetailsFormGroup; + case 1: + return this.transportConfigFormGroup; + case 2: + return this.alarmRulesFormGroup; + } + } + + private add(): void { + const deviceProfile: DeviceProfile = { + name: this.deviceProfileDetailsFormGroup.get('name').value, + type: this.deviceProfileDetailsFormGroup.get('type').value, + transportType: this.transportConfigFormGroup.get('transportType').value, + description: this.deviceProfileDetailsFormGroup.get('description').value, + profileData: { + configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), + transportConfiguration: this.transportConfigFormGroup.get('transportConfiguration').value, + alarms: this.alarmRulesFormGroup.get('alarms').value + } + }; + if (this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value) { + deviceProfile.defaultRuleChainId = new RuleChainId(this.deviceProfileDetailsFormGroup.get('defaultRuleChainId').value); + } + this.deviceProfileService.saveDeviceProfile(deviceProfile).subscribe( + (savedDeviceProfile) => { + this.dialogRef.close(savedDeviceProfile); + } + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html index 2ba8ab183f..ef69037727 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/alarm-rule.component.html @@ -32,12 +32,12 @@
- -
+ +
{{ timeUnitTranslations.get(timeUnit) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html index c3ac14c757..121df96386 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.html @@ -46,7 +46,7 @@ remove_circle_outline
-
+
device-profile.no-create-alarm-rules
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts index caebff80b4..6dac1f341e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/create-alarm-rules.component.ts @@ -124,6 +124,9 @@ export class CreateAlarmRulesComponent implements ControlValueAccessor, OnInit, this.updateModel(); }); this.updateUsedSeverities(); + if (!this.disabled && !this.createAlarmRulesFormGroup.valid) { + this.updateModel(); + } } public removeCreateAlarmRule(index: number) { diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html index d827245abf..d4a72da751 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.html @@ -67,7 +67,7 @@ remove_circle_outline
-
+
device-profile.no-clear-alarm-rule
diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts index 8befb6204d..7fa60e79d5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarm.component.ts @@ -63,7 +63,8 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit alarmFormGroup: FormGroup; - private propagateChange = (v: any) => { }; + private propagateChange = null; + private propagateChangePending = false; constructor(private dialog: MatDialog, private fb: FormBuilder) { @@ -71,6 +72,12 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit registerOnChange(fn: any): void { this.propagateChange = fn; + if (this.propagateChangePending) { + this.propagateChangePending = false; + setTimeout(() => { + this.propagateChange(this.modelValue); + }, 0); + } } registerOnTouched(fn: any): void { @@ -100,11 +107,15 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit } writeValue(value: DeviceProfileAlarm): void { + this.propagateChangePending = false; this.modelValue = value; if (!this.modelValue.alarmType) { this.expanded = true; } this.alarmFormGroup.reset(this.modelValue || undefined, {emitEvent: false}); + if (!this.disabled && !this.alarmFormGroup.valid) { + this.updateModel(); + } } public addClearAlarmRule() { @@ -160,6 +171,10 @@ export class DeviceProfileAlarmComponent implements ControlValueAccessor, OnInit private updateModel() { const value = this.alarmFormGroup.value; this.modelValue = {...this.modelValue, ...value}; - this.propagateChange(this.modelValue); + if (this.propagateChange) { + this.propagateChange(this.modelValue); + } else { + this.propagateChangePending = true; + } } } diff --git a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html index d07e8b0fed..7bd7c546db 100644 --- a/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/alarm/device-profile-alarms.component.html @@ -25,6 +25,10 @@
+
+ device-profile.no-alarm-rules +