diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java index e7b0113dbe..75499b9a8d 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java @@ -43,6 +43,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; @Slf4j public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor { + private static final String UNKNOWN_NAME = "Unknown"; private final String ruleChainName; private final TbApiUsageReportClient apiUsageClient; private final DefaultTbContext defaultCtx; @@ -57,7 +58,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor highPriorityQueue = new ConcurrentLinkedQueue<>(); - private static final int MAX_DOWNLINK_ATTEMPTS = 10; // max number of attemps to send downlink message if edge connected + private static final int MAX_DOWNLINK_ATTEMPTS = 10; // max number of attempts to send downlink message if edge connected private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; private static final String QUEUE_START_SEQ_ID_ATTR_KEY = "queueStartSeqId"; diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index dd01313364..203ab39781 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -122,7 +122,21 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService updateSchema("3.6.4", 3006004, "3.7.0", 3007000, null); break; case "3.7.0": - updateSchema("3.7.0", 3007000, "3.7.1", 3007001, null); + updateSchema("3.7.0", 3007000, "3.7.1", 3007001, connection -> { + try { + connection.createStatement().execute("UPDATE rule_node SET " + + "configuration = CASE " + + " WHEN (configuration::jsonb ->> 'persistAlarmRulesState') = 'false'" + + " THEN (configuration::jsonb || '{\"fetchAlarmRulesStateOnStart\": \"false\"}'::jsonb)::varchar " + + " ELSE configuration " + + "END, " + + "configuration_version = 1 " + + "WHERE type = 'org.thingsboard.rule.engine.profile.TbDeviceProfileNode' " + + "AND configuration_version < 1;"); + } catch (Exception e) { + log.warn("Failed to execute update script for device profile rule nodes due to: ", e); + } + }); break; case "3.7.1": updateSchema("3.7.1", 3007001, "3.7.2", 3007002, null); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index 1e701c7b5c..4043b519da 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -101,9 +101,9 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer private ExecutorService tsCallBackExecutor; private ScheduledExecutorService staleSessionCleanupExecutor; - @Value("${server.ws.rate_limits.subscriptions_per_tenant:2000:60}") + @Value("${server.ws.rate_limits.subscriptions_per_tenant:}") private String subscriptionsPerTenantRateLimit; - @Value("${server.ws.rate_limits.subscriptions_per_user:500:60}") + @Value("${server.ws.rate_limits.subscriptions_per_user:}") private String subscriptionsPerUserRateLimit; public DefaultTbLocalSubscriptionService(AttributesService attrService, TimeseriesService tsService, TbServiceInfoProvider serviceInfoProvider, @@ -334,7 +334,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer } private void onTimeSeriesUpdate(UUID entityId, List data, TbCallback callback) { - entityUpdates.get(entityId).timeSeriesUpdateTs = System.currentTimeMillis(); + getEntityUpdatesInfo(entityId).timeSeriesUpdateTs = System.currentTimeMillis(); processSubscriptionData(entityId, sub -> TbSubscriptionType.TIMESERIES.equals(sub.getType()), s -> { @@ -371,7 +371,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer } private void onAttributesUpdate(UUID entityId, String scope, List data, TbCallback callback) { - entityUpdates.get(entityId).attributesUpdateTs = System.currentTimeMillis(); + getEntityUpdatesInfo(entityId).attributesUpdateTs = System.currentTimeMillis(); processSubscriptionData(entityId, sub -> TbSubscriptionType.ATTRIBUTES.equals(sub.getType()), s -> { @@ -639,4 +639,8 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer throw new TbRateLimitsException(message); } + private TbEntityUpdatesInfo getEntityUpdatesInfo(UUID entityId) { + return entityUpdates.computeIfAbsent(entityId, id -> new TbEntityUpdatesInfo(0)); + } + } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e1893511f3..536c35e816 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -84,9 +84,9 @@ server: auth_timeout_ms: "${TB_SERVER_WS_AUTH_TIMEOUT_MS:10000}" rate_limits: # Per-tenant rate limit for WS subscriptions - subscriptions_per_tenant: "${TB_SERVER_WS_SUBSCRIPTIONS_PER_TENANT_RATE_LIMIT:2000:60}" + subscriptions_per_tenant: "${TB_SERVER_WS_SUBSCRIPTIONS_PER_TENANT_RATE_LIMIT:}" # Per-user rate limit for WS subscriptions - subscriptions_per_user: "${TB_SERVER_WS_SUBSCRIPTIONS_PER_USER_RATE_LIMIT:500:60}" + subscriptions_per_user: "${TB_SERVER_WS_SUBSCRIPTIONS_PER_USER_RATE_LIMIT:}" rest: server_side_rpc: # Minimum value of the server-side RPC timeout. May override value provided in the REST API call. @@ -1020,6 +1020,8 @@ transport: # MQTT disconnect timeout in milliseconds. The time to wait for the client to disconnect after the server sends a disconnect message. disconnect_timeout: "${MQTT_DISCONNECT_TIMEOUT:1000}" msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before the device connected state. This limit works on the low level before TenantProfileLimits mechanism + # Interval of periodic report of the gateway metrics + gateway_metrics_report_interval_sec: "${MQTT_GATEWAY_METRICS_REPORT_INTERVAL_SEC:60}" netty: # Netty leak detector level leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java index 7c8cdb5d6e..5fd68417dd 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java @@ -20,22 +20,34 @@ import io.netty.handler.codec.mqtt.MqttQoS; import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties; +import org.thingsboard.server.transport.mqtt.gateway.GatewayMetricsService; +import org.thingsboard.server.common.msg.gateway.metrics.GatewayMetadata; +import org.thingsboard.server.transport.mqtt.gateway.metrics.GatewayMetricsState; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestCallback; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_ATTRIBUTES_TOPIC; import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC; @@ -43,6 +55,7 @@ import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVIC import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_TELEMETRY_TOPIC; import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_CONNECT_TOPIC; import static org.thingsboard.server.common.data.device.profile.MqttTopics.GATEWAY_TELEMETRY_TOPIC; +import static org.thingsboard.server.transport.mqtt.gateway.GatewayMetricsService.GATEWAY_METRICS; @Slf4j public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqttIntegrationTest { @@ -53,6 +66,9 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt protected static final String MALFORMED_JSON_PAYLOAD = "{\"key1\":, \"key2\":true, \"key3\": 3.0, \"key4\": 4," + " \"key5\": {\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}}"; + @SpyBean + GatewayMetricsService gatewayMetricsService; + @Before public void beforeTest() throws Exception { MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder() @@ -106,13 +122,98 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt String deviceName = "Device A"; Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), - 20, - 100); + 20, + 100); assertNotNull(device); client.disconnect(); } + @Test + public void testPushMetricsGateway() throws Exception { + MqttTestConfigProperties configProperties = MqttTestConfigProperties.builder() + .gatewayName("Test metrics gateway") + .build(); + processBeforeTest(configProperties); + + Map> gwLatencies = new HashMap<>(); + Map> transportLatencies = new HashMap<>(); + + publishLatency(gwLatencies, transportLatencies, 5); + + gatewayMetricsService.reportMetrics(); + + List actualKeys = getActualKeysList(savedGateway.getId(), List.of(GATEWAY_METRICS)); + assertEquals(GATEWAY_METRICS, actualKeys.get(0)); + + String telemetryUrl = String.format("/api/plugins/telemetry/DEVICE/%s/values/timeseries?startTs=%d&endTs=%d&keys=%s", savedGateway.getId(), 0, System.currentTimeMillis(), GATEWAY_METRICS); + + Map>> gatewayTelemetry = doGetAsyncTyped(telemetryUrl, new TypeReference<>() {}); + Map latencyCheckTelemetry = gatewayTelemetry.get(GATEWAY_METRICS).get(0); + Map latencyCheckValue = JacksonUtil.fromString((String) latencyCheckTelemetry.get("value"), new TypeReference<>() {}); + assertNotNull(latencyCheckValue); + + gwLatencies.forEach((connectorName, gwLatencyList) -> { + long avgGwLatency = (long) gwLatencyList.stream().mapToLong(Long::longValue).average().getAsDouble(); + long minGwLatency = gwLatencyList.stream().mapToLong(Long::longValue).min().getAsLong(); + long maxGwLatency = gwLatencyList.stream().mapToLong(Long::longValue).max().getAsLong(); + + List transportLatencyList = transportLatencies.get(connectorName); + assertNotNull(transportLatencyList); + + long avgTransportLatency = (long) transportLatencyList.stream().mapToLong(Long::longValue).average().getAsDouble(); + long minTransportLatency = transportLatencyList.stream().mapToLong(Long::longValue).min().getAsLong(); + long maxTransportLatency = transportLatencyList.stream().mapToLong(Long::longValue).max().getAsLong(); + + GatewayMetricsState.ConnectorMetricsResult connectorLatencyResult = latencyCheckValue.get(connectorName); + assertNotNull(connectorLatencyResult); + checkConnectorLatencyResult(connectorLatencyResult, avgGwLatency, minGwLatency, maxGwLatency, avgTransportLatency, minTransportLatency, maxTransportLatency); + }); + } + + private void publishLatency(Map> gwLatencies, Map> transportLatencies, int n) throws Exception { + Random random = new Random(); + for (int i = 0; i < n; i++) { + long publishedTs = System.currentTimeMillis() - 10; + long gatewayLatencyA = random.nextLong(100, 500); + var firstData = new GatewayMetadata("connectorA", publishedTs - gatewayLatencyA, publishedTs); + gwLatencies.computeIfAbsent("connectorA", key -> new ArrayList<>()).add(gatewayLatencyA); + long gatewayLatencyB = random.nextLong(120, 450); + var secondData = new GatewayMetadata("connectorB", publishedTs - gatewayLatencyB, publishedTs); + gwLatencies.computeIfAbsent("connectorB", key -> new ArrayList<>()).add(gatewayLatencyB); + + List expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); + String deviceName1 = "Device A"; + String deviceName2 = "Device B"; + String firstMetadata = JacksonUtil.writeValueAsString(firstData); + String secondMetadata = JacksonUtil.writeValueAsString(secondData); + String payload = getGatewayTelemetryJsonPayloadWithMetadata(deviceName1, deviceName2, "10000", "20000", firstMetadata, secondMetadata); + processGatewayTelemetryTest(GATEWAY_TELEMETRY_TOPIC, expectedKeys, payload.getBytes(), deviceName1, deviceName2); + + ArgumentCaptor transportReceiveTsCaptorA = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor transportReceiveTsCaptorB = ArgumentCaptor.forClass(Long.class); + verify(gatewayMetricsService).process(any(), eq(savedGateway.getId()), eq(List.of(firstData)), transportReceiveTsCaptorA.capture()); + verify(gatewayMetricsService).process(any(), eq(savedGateway.getId()), eq(List.of(secondData)), transportReceiveTsCaptorB.capture()); + Long transportReceiveTsA = transportReceiveTsCaptorA.getValue(); + Long transportReceiveTsB = transportReceiveTsCaptorB.getValue(); + Long transportLatencyA = transportReceiveTsA - publishedTs; + Long transportLatencyB = transportReceiveTsB - publishedTs; + transportLatencies.computeIfAbsent("connectorA", key -> new ArrayList<>()).add(transportLatencyA); + transportLatencies.computeIfAbsent("connectorB", key -> new ArrayList<>()).add(transportLatencyB); + } + } + + private void checkConnectorLatencyResult(GatewayMetricsState.ConnectorMetricsResult result, long avgGwLatency, long minGwLatency, long maxGwLatency, + long avgTransportLatency, long minTransportLatency, long maxTransportLatency) { + assertNotNull(result); + assertEquals(avgGwLatency, result.avgGwLatency()); + assertEquals(minGwLatency, result.minGwLatency()); + assertEquals(maxGwLatency, result.maxGwLatency()); + assertEquals(avgTransportLatency, result.avgTransportLatency()); + assertEquals(minTransportLatency, result.minTransportLatency()); + assertEquals(maxTransportLatency, result.maxTransportLatency()); + } + protected void processJsonPayloadTelemetryTest(String topic, List expectedKeys, byte[] payload, boolean withTs) throws Exception { processTelemetryTest(topic, expectedKeys, payload, withTs, false); } @@ -143,7 +244,8 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt long end = System.currentTimeMillis() + 5000; Map>> values = null; while (start <= end) { - values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() {}); + values = doGetAsyncTyped(getTelemetryValuesUrl, new TypeReference<>() { + }); boolean valid = values.size() == expectedKeys.size(); if (valid) { for (String key : expectedKeys) { @@ -182,7 +284,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt MqttTestClient client = new MqttTestClient(); client.connectAndWait(gatewayAccessToken); client.publishAndWait(topic, payload); - client.disconnect(); + client.disconnectAndWait(); Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class), 20, @@ -239,6 +341,16 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt return "{\"" + deviceA + "\": " + payload + ", \"" + deviceB + "\": " + payload + "}"; } + protected String getGatewayTelemetryJsonPayloadWithMetadata(String deviceA, String deviceB, String firstTsValue, String secondTsValue, String firstMetadata, String secondMetadata) { + String payloadA = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + ", \"metadata\":" + firstMetadata + "}, " + + "{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]"; + + String payloadB = "[{\"ts\": " + firstTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + ", \"metadata\":" + secondMetadata + "}, " + + "{\"ts\": " + secondTsValue + ", \"values\": " + PAYLOAD_VALUES_STR + "}]"; + + return "{\"" + deviceA + "\": " + payloadA + ", \"" + deviceB + "\": " + payloadB + "}"; + } + private String getTelemetryValuesUrl(DeviceId deviceId, Set actualKeySet) { return "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?startTs=0&endTs=25000&keys=" + String.join(",", actualKeySet); } diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java index 776d5657ce..ee4af20639 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java @@ -240,7 +240,7 @@ public final class TbActorMailbox implements TbActorCtx { highPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason)); normalPriorityMsgs.forEach(msg -> msg.onTbActorStopped(stopReason)); } catch (Throwable t) { - log.warn("[{}] Failed to destroy actor: {}", selfId, t); + log.warn("[{}] Failed to destroy actor: ", selfId, t); } }); } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/gateway/metrics/GatewayMetadata.java b/common/message/src/main/java/org/thingsboard/server/common/msg/gateway/metrics/GatewayMetadata.java new file mode 100644 index 0000000000..cc905f9383 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/gateway/metrics/GatewayMetadata.java @@ -0,0 +1,19 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.common.msg.gateway.metrics; + +public record GatewayMetadata(String connector, long receivedTs, long publishedTs) { +} diff --git a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java index d1283d3ba0..f4abf5f3a0 100644 --- a/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java +++ b/common/proto/src/main/java/org/thingsboard/server/common/adaptor/JsonConverter.java @@ -34,6 +34,8 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.gateway.metrics.GatewayMetadata; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; @@ -83,6 +85,49 @@ public class JsonConverter { return convertToTelemetryProto(jsonElement, System.currentTimeMillis()); } + public static TbPair> convertToGatewayTelemetry(JsonElement jsonElement, long systemTs) { + List metadataResult = null; + PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); + if (jsonElement.isJsonArray()) { + var ja = jsonElement.getAsJsonArray(); + for (int i = 0; i < ja.size(); i++) { + var je = ja.get(i); + if (je.isJsonObject()) { + JsonObject jo = je.getAsJsonObject(); + JsonElement metadataElem = jo.remove("metadata"); + if (metadataElem != null) { + if (metadataResult == null) { + metadataResult = new ArrayList<>(); + } + if (metadataElem.isJsonObject()) { + JsonObject metadataObj = metadataElem.getAsJsonObject(); + var connector = getAndValidateMetadataElement(metadataObj, "connector").getAsString(); + var receivedTs = getAndValidateMetadataElement(metadataObj, "receivedTs").getAsLong(); + var publishedTs = getAndValidateMetadataElement(metadataObj, "publishedTs").getAsLong(); + metadataResult.add(new GatewayMetadata(connector, receivedTs, publishedTs)); + } else { + throw new JsonSyntaxException("Can't parse gateway metadata: " + metadataElem); + } + } + parseObject(systemTs, null, builder, jo); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); + } + } + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); + } + return TbPair.of(builder.build(), metadataResult); + } + + private static JsonElement getAndValidateMetadataElement(JsonObject metadata, String elementName) { + var element = metadata.get(elementName); + if (element == null || element.isJsonNull()) { + throw new JsonSyntaxException(String.format("Can't parse gateway element in metadata: [%s][%s]", metadata, elementName)); + } + return element; + } + private static void convertToTelemetry(JsonElement jsonElement, long systemTs, Map> result, PostTelemetryMsg.Builder builder) { if (jsonElement.isJsonObject()) { parseObject(systemTs, result, builder, jsonElement.getAsJsonObject()); diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java index d8a409e704..8bd15a1ccf 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/TopicService.java @@ -21,8 +21,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; @Service public class TopicService { @@ -30,9 +30,9 @@ public class TopicService { @Value("${queue.prefix:}") private String prefix; - private final Map tbCoreNotificationTopics = new HashMap<>(); - private final Map tbRuleEngineNotificationTopics = new HashMap<>(); - private final Map tbEdgeNotificationTopics = new HashMap<>(); + private final ConcurrentMap tbCoreNotificationTopics = new ConcurrentHashMap<>(); + private final ConcurrentMap tbRuleEngineNotificationTopics = new ConcurrentHashMap<>(); + private final ConcurrentMap tbEdgeNotificationTopics = new ConcurrentHashMap<>(); /** * Each Service should start a consumer for messages that target individual service instance based on serviceId. diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index b8fd79be4b..d78a4afbc2 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -22,12 +22,12 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportTenantProfileCache; import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor; +import org.thingsboard.server.transport.mqtt.gateway.GatewayMetricsService; import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicInteger; @@ -37,7 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ @Slf4j @Component -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')") +@TbMqttTransportComponent public class MqttTransportContext extends TransportContext { @Getter @@ -56,6 +56,10 @@ public class MqttTransportContext extends TransportContext { @Autowired private TransportTenantProfileCache tenantProfileCache; + @Getter + @Autowired + private GatewayMetricsService gatewayMetricsService; + @Getter @Value("${transport.mqtt.netty.max_payload_size}") private Integer maxPayloadSize; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index f839ed4b18..d7f64e58ee 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -1464,7 +1464,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional deviceProfileOpt) { deviceSessionCtx.onDeviceUpdate(sessionInfo, device, deviceProfileOpt); if (gatewaySessionHandler != null) { - gatewaySessionHandler.onDeviceUpdate(sessionInfo, device, deviceProfileOpt); + gatewaySessionHandler.onGatewayUpdate(sessionInfo, device, deviceProfileOpt); } } @@ -1473,6 +1473,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement context.onAuthFailure(address); ChannelHandlerContext ctx = deviceSessionCtx.getChannel(); closeCtx(ctx, MqttReasonCodes.Disconnect.ADMINISTRATIVE_ACTION); + if (gatewaySessionHandler != null) { + gatewaySessionHandler.onGatewayDelete(deviceId); + } } public void sendErrorRpcResponse(TransportProtos.SessionInfoProto sessionInfo, int requestId, ThingsboardErrorCode result, String errorMsg) { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java index ffc7e9e561..b8d1dbe582 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java @@ -39,7 +39,7 @@ import java.net.InetSocketAddress; * @author Andrew Shvayka */ @Service("MqttTransportService") -@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')") +@TbMqttTransportComponent @Slf4j public class MqttTransportService implements TbTransportService { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/TbMqttTransportComponent.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/TbMqttTransportComponent.java new file mode 100644 index 0000000000..da0047cc7e --- /dev/null +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/TbMqttTransportComponent.java @@ -0,0 +1,26 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.transport.mqtt; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true')") +public @interface TbMqttTransportComponent { +} diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/gateway/GatewayMetricsService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/gateway/GatewayMetricsService.java new file mode 100644 index 0000000000..4f67866d06 --- /dev/null +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/gateway/GatewayMetricsService.java @@ -0,0 +1,113 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.transport.mqtt.gateway; + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.common.transport.TransportServiceCallback; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.scheduler.SchedulerComponent; +import org.thingsboard.server.transport.mqtt.TbMqttTransportComponent; +import org.thingsboard.server.common.msg.gateway.metrics.GatewayMetadata; +import org.thingsboard.server.transport.mqtt.gateway.metrics.GatewayMetricsState; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Service +@TbMqttTransportComponent +public class GatewayMetricsService { + + public static final String GATEWAY_METRICS = "gatewayMetrics"; + + @Value("${transport.mqtt.gateway_metrics_report_interval_sec:60}") + private int metricsReportIntervalSec; + + @Autowired + private SchedulerComponent scheduler; + + @Autowired + private TransportService transportService; + + private Map states = new ConcurrentHashMap<>(); + + @PostConstruct + private void init() { + scheduler.scheduleAtFixedRate(this::reportMetrics, metricsReportIntervalSec, metricsReportIntervalSec, TimeUnit.SECONDS); + } + + public void process(TransportProtos.SessionInfoProto sessionInfo, DeviceId gatewayId, List data, long serverReceiveTs) { + states.computeIfAbsent(gatewayId, k -> new GatewayMetricsState(sessionInfo)).update(data, serverReceiveTs); + } + + public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceId gatewayId) { + var state = states.get(gatewayId); + if (state != null) { + state.updateSessionInfo(sessionInfo); + } + } + + public void onDeviceDelete(DeviceId deviceId) { + states.remove(deviceId); + } + + public void reportMetrics() { + if (states.isEmpty()) { + return; + } + Map statesToReport = states; + states = new ConcurrentHashMap<>(); + + long ts = System.currentTimeMillis(); + + statesToReport.forEach((gatewayId, state) -> { + reportMetrics(state, ts); + }); + } + + private void reportMetrics(GatewayMetricsState state, long ts) { + if (state.isEmpty()) { + return; + } + var result = state.getStateResult(); + var kvProto = TransportProtos.KeyValueProto.newBuilder() + .setKey(GATEWAY_METRICS) + .setType(TransportProtos.KeyValueType.JSON_V) + .setJsonV(JacksonUtil.toString(result)) + .build(); + + TransportProtos.TsKvListProto tsKvList = TransportProtos.TsKvListProto.newBuilder() + .setTs(ts) + .addKv(kvProto) + .build(); + + TransportProtos.PostTelemetryMsg telemetryMsg = TransportProtos.PostTelemetryMsg.newBuilder() + .addTsKvList(tsKvList) + .build(); + + transportService.process(state.getSessionInfo(), telemetryMsg, TransportServiceCallback.EMPTY); + } + +} diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/gateway/metrics/GatewayMetricsState.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/gateway/metrics/GatewayMetricsState.java new file mode 100644 index 0000000000..a800c78e28 --- /dev/null +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/gateway/metrics/GatewayMetricsState.java @@ -0,0 +1,123 @@ +/** + * Copyright © 2016-2024 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. + */ +package org.thingsboard.server.transport.mqtt.gateway.metrics; + +import lombok.Getter; +import org.thingsboard.server.common.msg.gateway.metrics.GatewayMetadata; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class GatewayMetricsState { + + private final Map connectors; + private final Lock updateLock; + + @Getter + private volatile TransportProtos.SessionInfoProto sessionInfo; + + public GatewayMetricsState(TransportProtos.SessionInfoProto sessionInfo) { + this.connectors = new HashMap<>(); + this.updateLock = new ReentrantLock(); + this.sessionInfo = sessionInfo; + } + + public void updateSessionInfo(TransportProtos.SessionInfoProto sessionInfo) { + this.sessionInfo = sessionInfo; + } + + public void update(List metricsData, long serverReceiveTs) { + updateLock.lock(); + try { + metricsData.forEach(data -> { + connectors.computeIfAbsent(data.connector(), k -> new ConnectorMetricsState()).update(data, serverReceiveTs); + }); + } finally { + updateLock.unlock(); + } + } + + public Map getStateResult() { + Map result = new HashMap<>(); + updateLock.lock(); + try { + connectors.forEach((name, state) -> result.put(name, state.getResult())); + connectors.clear(); + } finally { + updateLock.unlock(); + } + + return result; + } + + public boolean isEmpty() { + return connectors.isEmpty(); + } + + private static class ConnectorMetricsState { + private final AtomicInteger count; + private final AtomicLong gwLatencySum; + private final AtomicLong transportLatencySum; + private volatile long minGwLatency; + private volatile long maxGwLatency; + private volatile long minTransportLatency; + private volatile long maxTransportLatency; + + private ConnectorMetricsState() { + this.count = new AtomicInteger(0); + this.gwLatencySum = new AtomicLong(0); + this.transportLatencySum = new AtomicLong(0); + } + + private void update(GatewayMetadata metricsData, long serverReceiveTs) { + long gwLatency = metricsData.publishedTs() - metricsData.receivedTs(); + long transportLatency = serverReceiveTs - metricsData.publishedTs(); + count.incrementAndGet(); + gwLatencySum.addAndGet(gwLatency); + transportLatencySum.addAndGet(transportLatency); + if (minGwLatency == 0 || minGwLatency > gwLatency) { + minGwLatency = gwLatency; + } + if (maxGwLatency < gwLatency) { + maxGwLatency = gwLatency; + } + if (minTransportLatency == 0 || minTransportLatency > transportLatency) { + minTransportLatency = transportLatency; + } + if (maxTransportLatency < transportLatency) { + maxTransportLatency = transportLatency; + } + } + + private ConnectorMetricsResult getResult() { + long count = this.count.get(); + long avgGwLatency = gwLatencySum.get() / count; + long avgTransportLatency = transportLatencySum.get() / count; + return new ConnectorMetricsResult(avgGwLatency, minGwLatency, maxGwLatency, avgTransportLatency, minTransportLatency, maxTransportLatency); + } + } + + public record ConnectorMetricsResult(long avgGwLatency, long minGwLatency, long maxGwLatency, + long avgTransportLatency, long minTransportLatency, long maxTransportLatency) { + } + +} diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java index 3b3aaef983..2e205ff840 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/AbstractGatewaySessionHandler.java @@ -48,6 +48,8 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.util.TbPair; +import org.thingsboard.server.common.msg.gateway.metrics.GatewayMetadata; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; @@ -62,6 +64,7 @@ import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor; +import org.thingsboard.server.transport.mqtt.gateway.GatewayMetricsService; import org.thingsboard.server.transport.mqtt.util.sparkplug.SparkplugConnectionState; import java.util.ArrayList; @@ -114,6 +117,7 @@ public abstract class AbstractGatewaySessionHandler mqttQoSMap; protected final ChannelHandlerContext channel; protected final DeviceSessionCtx deviceSessionCtx; + protected final GatewayMetricsService gatewayMetricsService; @Getter @Setter @@ -131,6 +135,7 @@ public abstract class AbstractGatewaySessionHandler createWeakMap() { @@ -380,7 +385,13 @@ public abstract class AbstractGatewaySessionHandler> gatewayPayloadPair = JsonConverter.convertToGatewayTelemetry(msg.getAsJsonArray(), systemTs); + TransportProtos.PostTelemetryMsg postTelemetryMsg = gatewayPayloadPair.getFirst(); + List metadata = gatewayPayloadPair.getSecond(); + if (!CollectionUtils.isEmpty(metadata)) { + gatewayMetricsService.process(deviceSessionCtx.getSessionInfo(), gateway.getDeviceId(), metadata, systemTs); + } transportService.process(deviceCtx.getSessionInfo(), postTelemetryMsg, getPubAckCallback(channel, deviceName, msgId, postTelemetryMsg)); } catch (Throwable e) { log.warn("[{}][{}][{}] Failed to convert telemetry: [{}]", gateway.getTenantId(), gateway.getDeviceId(), deviceName, msg, e); diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index 9aa228afff..5dce8e8893 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -17,14 +17,21 @@ package org.thingsboard.server.transport.mqtt.session; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.mqtt.MqttPublishMessage; +import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.auth.GetOrCreateDeviceFromGatewayResponse; +import org.thingsboard.server.gen.transport.TransportProtos; +import java.util.Optional; import java.util.UUID; /** * Created by nickAS21 on 26.12.22 */ +@Slf4j public class GatewaySessionHandler extends AbstractGatewaySessionHandler { public GatewaySessionHandler(DeviceSessionCtx deviceSessionCtx, UUID sessionId, boolean overwriteDevicesActivity) { @@ -51,7 +58,16 @@ public class GatewaySessionHandler extends AbstractGatewaySessionHandler deviceProfileOpt) { + this.onDeviceUpdate(sessionInfo, device, deviceProfileOpt); + gatewayMetricsService.onDeviceUpdate(sessionInfo, gateway.getDeviceId()); + } + + public void onGatewayDelete(DeviceId deviceId) { + gatewayMetricsService.onDeviceDelete(deviceId); } } diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml index 140839ccc3..c9fd10a99d 100644 --- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml +++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml @@ -146,6 +146,8 @@ transport: # MQTT disconnect timeout in milliseconds. The time to wait for the client to disconnect after the server sends a disconnect message. disconnect_timeout: "${MQTT_DISCONNECT_TIMEOUT:1000}" msg_queue_size_per_device_limit: "${MQTT_MSG_QUEUE_SIZE_PER_DEVICE_LIMIT:100}" # messages await in the queue before device connected state. This limit works on low level before TenantProfileLimits mechanism + # Interval of periodic report of the gateway metrics + gateway_metrics_report_interval_sec: "${MQTT_GATEWAY_METRICS_REPORT_INTERVAL_SEC:60}" netty: # Netty leak detector level leak_detector_level: "${NETTY_LEAK_DETECTOR_LVL:DISABLED}" diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 916b72cc15..74c29bf00e 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -32,7 +32,6 @@ "@flowjs/ngx-flow": "~0.6.0", "@geoman-io/leaflet-geoman-free": "2.14.2", "@iplab/ngx-color-picker": "^15.0.2", - "@juggle/resize-observer": "^3.4.0", "@mat-datetimepicker/core": "~11.0.3", "@material-ui/core": "4.12.3", "@material-ui/icons": "4.11.2", diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts index bd866df54e..41e9527a1d 100644 --- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts @@ -87,7 +87,6 @@ import { import { deepClone } from '@core/utils'; import { Filters } from '@shared/models/query/query.models'; import { hidePageSizePixelValue } from '@shared/models/constants'; -import { ResizeObserver } from '@juggle/resize-observer'; import { DeleteTimeseriesPanelComponent } from '@home/components/attribute/delete-timeseries-panel.component'; import { FormBuilder } from '@angular/forms'; diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 9b5bd7d3ca..bb83f1e1bf 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -150,7 +150,6 @@ import { TbPopoverService } from '@shared/components/popover.service'; import { catchError, distinctUntilChanged, map, skip, tap } from 'rxjs/operators'; import { LayoutFixedSize, LayoutWidthType } from '@home/components/dashboard-page/layout/layout.models'; import { TbPopoverComponent } from '@shared/components/popover.component'; -import { ResizeObserver } from '@juggle/resize-observer'; import { HasDirtyFlag } from '@core/guards/confirm-on-exit.guard'; import { MoveWidgetsDialogComponent, diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts index a5d411face..46255cfc57 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.ts @@ -53,7 +53,6 @@ import { Widget, WidgetPosition } from '@app/shared/models/widget.models'; import { MatMenuTrigger } from '@angular/material/menu'; import { SafeStyle } from '@angular/platform-browser'; import { distinct, take } from 'rxjs/operators'; -import { ResizeObserver } from '@juggle/resize-observer'; import { UtilsService } from '@core/services/utils.service'; import { WidgetComponentAction, WidgetComponentActionType } from '@home/components/widget/widget-container.component'; import { TbPopoverComponent } from '@shared/components/popover.component'; diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 6872b09d36..75f44be0b3 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -64,7 +64,6 @@ import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { isDefined, isEqual, isNotEmptyStr, isUndefined } from '@core/utils'; import { HasUUID } from '@shared/models/id/has-uuid'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { EntitiesTableAction, IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models'; import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; diff --git a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts index d226459160..670eafc63f 100644 --- a/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts @@ -49,7 +49,6 @@ import { EntityId } from '@shared/models/id/entity-id'; import { RelationsDatasource } from '../../models/datasource/relation-datasource'; import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component'; import { hidePageSizePixelValue } from '@shared/models/constants'; -import { ResizeObserver } from '@juggle/resize-observer'; import { FormBuilder } from '@angular/forms'; @Component({ diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts index c4cafab70a..a432d4898f 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts @@ -41,7 +41,6 @@ import { EntityVersion, VersionCreationResult, VersionLoadResult } from '@shared import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { BranchAutocompleteComponent } from '@shared/components/vc/branch-autocomplete.component'; diff --git a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts index f33c690739..92be318fc2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/action/manage-widget-actions.component.ts @@ -52,7 +52,6 @@ import { WidgetActionDialogData } from '@home/components/widget/action/widget-action-dialog.component'; import { deepClone } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts index 1d1aa533cd..e4a9393dff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts @@ -104,7 +104,6 @@ import { import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { entityFields } from '@shared/models/entity.models'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { ALARM_ASSIGNEE_PANEL_DATA, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts index 2d6525e6f7..025d55ef11 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -49,7 +49,6 @@ import { import { DataKey } from '@shared/models/widget.models'; import { formatNumberValue, formatValue, isDefined, isDefinedAndNotNull, isNumeric } from '@core/utils'; import { map } from 'rxjs/operators'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-card-widget.component.ts index 5bd6411b86..af5c19c4d6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-card-widget.component.ts @@ -41,7 +41,6 @@ import { resolveCssSize, textStyle } from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts index 9a47a5db9d..f20c0b1313 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/label-value-card-widget.component.ts @@ -40,7 +40,6 @@ import { resolveCssSize, textStyle } from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts index 96bdff9afc..b3f88ae3ec 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/progress-bar-widget.component.ts @@ -42,7 +42,6 @@ import { } from '@shared/models/widget-settings.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; import { progressBarDefaultSettings, ProgressBarLayout, ProgressBarWidgetSettings } from './progress-bar-widget.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/unread-notification-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/unread-notification-widget.component.ts index dea274d118..3873754572 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/unread-notification-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/unread-notification-widget.component.ts @@ -30,7 +30,6 @@ import { import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; import { isDefined } from '@core/utils'; import { backgroundStyle, ComponentStyle, overlayStyle, textStyle } from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { BehaviorSubject, fromEvent, Observable, ReplaySubject, Subscription } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts index 96ec634362..3494b7e6dc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-card-widget.component.ts @@ -44,7 +44,6 @@ import { import { valueCardDefaultSettings, ValueCardLayout, ValueCardWidgetSettings } from './value-card-widget.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; import { Observable } from 'rxjs'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts index 8511944868..1525f53256 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts @@ -41,7 +41,6 @@ import { textStyle } from '@shared/models/widget-settings.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; -import { ResizeObserver } from '@juggle/resize-observer'; import { valueChartCardDefaultSettings, ValueChartCardLayout, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts index 54d674d636..422baa9288 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.component.ts @@ -37,7 +37,6 @@ import { WidgetContext } from '@home/models/widget-component.models'; import { Observable } from 'rxjs'; import { backgroundStyle, ComponentStyle, overlayStyle, textStyle } from '@shared/models/widget-settings.models'; import { TbLatestChart } from '@home/components/widget/lib/chart/latest-chart'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { WidgetComponent } from '@home/components/widget/widget.component'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts index e041a69084..19b8a29084 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/latest-chart.ts @@ -14,7 +14,6 @@ /// limitations under the License. /// -import { ResizeObserver } from '@juggle/resize-observer'; import { ECharts, echartsModule, EChartsOption } from '@home/components/widget/lib/chart/echarts-widget.models'; import { LatestChartDataItem, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts index c5d09cfa6e..9e32fdeea2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -45,7 +45,6 @@ import { updateDarkMode, updateXAxisTimeWindow } from '@home/components/widget/lib/chart/time-series-chart.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { calculateAxisSize, ECharts, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts index 060139c121..b8977e36df 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/count/count-widget.component.ts @@ -41,7 +41,6 @@ import { CountWidgetSettings } from '@home/components/widget/lib/count/count-widget.models'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { ResizeObserver } from '@juggle/resize-observer'; import { UtilsService } from '@core/services/utils.service'; const layoutHeight = 36; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts index ab25a96af1..7a80f0c4ae 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts @@ -102,7 +102,6 @@ import { sortItems } from '@shared/models/page/page-link'; import { entityFields } from '@shared/models/entity.models'; import { DatePipe } from '@angular/common'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { AggregationType } from '@shared/models/time/time.models'; import { FormBuilder } from '@angular/forms'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts index 149583a149..4f8b1ee34b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts @@ -58,7 +58,6 @@ import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetr import { forkJoin, Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { ImportExportService } from '@shared/import-export/import-export.service'; -import { ResizeObserver } from '@juggle/resize-observer'; // @dynamic @Component({ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts index b17b8ef28a..2537f415da 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts @@ -20,7 +20,6 @@ import { AttributeService } from '@core/http/attribute.service'; import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { WidgetContext } from '@home/models/widget-component.models'; import { TbFlot } from '@home/components/widget/lib/flot-widget'; -import { ResizeObserver } from '@juggle/resize-observer'; import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { UtilsService } from '@core/services/utils.service'; import { DatasourceType, LegendConfig, LegendData, LegendPosition, widgetType } from '@shared/models/widget.models'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts index 263b6edbb1..b5d0a02982 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/battery-level-widget.component.ts @@ -45,7 +45,6 @@ import { BatteryLevelLayout, BatteryLevelWidgetSettings } from '@home/components/widget/lib/indicator/battery-level-widget.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts index 3a898f0f82..acb60af335 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/signal-strength-widget.component.ts @@ -57,7 +57,6 @@ import { } from '@shared/models/widget-settings.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; import { formatValue, isDefinedAndNotNull, isNumeric, isUndefinedOrNull } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { Element, G, Svg, SVG } from '@svgdotjs/svg.js'; import { signalBarActive, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.component.ts index d8d5b48ff6..6fa9df1c2b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/indicator/status-widget.component.ts @@ -41,7 +41,6 @@ import { resolveCssSize, textStyle } from '@shared/models/widget-settings.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { ValueType } from '@shared/models/constants'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts index afc508d3d2..0c223b17bf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/mobile-app-qrcode-widget.component.ts @@ -25,7 +25,6 @@ import { UtilsService } from '@core/services/utils.service'; import { Observable, Subject } from 'rxjs'; import { MINUTE } from '@shared/models/time/time.models'; import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts index ecd3ad952f..10f541c26c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/multiple-input-widget.component.ts @@ -40,7 +40,6 @@ import { AttributeService } from '@core/http/attribute.service'; import { AttributeData, AttributeScope, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; import { forkJoin, Observable, Subject } from 'rxjs'; import { EntityId } from '@shared/models/id/entity-id'; -import { ResizeObserver } from '@juggle/resize-observer'; import { takeUntil } from 'rxjs/operators'; import { JsonObjectEditDialogComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts index 4387bb71e5..584ddc0ae7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/knob.component.ts @@ -23,7 +23,6 @@ import { AppState } from '@core/core.state'; import { isDefined, isNumber } from '@core/utils'; import { CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import tinycolor from 'tinycolor2'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ColorProcessor, gradientColor } from '@shared/models/widget-settings.models'; import GenericOptions = CanvasGauges.GenericOptions; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.ts index 0fceda2bd0..aebf2d9d86 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/led-indicator.component.ts @@ -25,7 +25,6 @@ import { UtilsService } from '@core/services/utils.service'; import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { DatasourceType, widgetType } from '@shared/models/widget.models'; import { EntityType } from '@shared/models/entity-type.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import Timeout = NodeJS.Timeout; const checkStatusPollingInterval = 10000; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts index 37879144cd..61051b3119 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts @@ -73,7 +73,6 @@ import { PersistentFilterPanelData } from '@home/components/widget/lib/rpc/persistent-filter-panel.component'; import { PersistentAddDialogComponent } from '@home/components/widget/lib/rpc/persistent-add-dialog.component'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { HttpErrorResponse } from '@angular/common/http'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts index 7c2250ce39..abf97356dd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.component.ts @@ -28,7 +28,6 @@ import { import { BasicActionWidgetComponent, ValueSetter } from '@home/components/widget/lib/action/action-widget.models'; import { backgroundStyle, ComponentStyle, overlayStyle } from '@shared/models/widget-settings.models'; import { Observable } from 'rxjs'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { ValueType } from '@shared/models/constants'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.ts index 5e8f802aa1..a48479637b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/round-switch.component.ts @@ -24,7 +24,6 @@ import { isDefined } from '@core/utils'; import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { DatasourceType, widgetType } from '@shared/models/widget.models'; import { EntityType } from '@shared/models/entity-type.models'; -import { ResizeObserver } from '@juggle/resize-observer'; type RetrieveValueMethod = 'rpc' | 'attribute' | 'timeseries'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts index 63ed57628b..cbe8cd2119 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/single-switch-widget.component.ts @@ -40,7 +40,6 @@ import { textStyle } from '@shared/models/widget-settings.models'; import { Observable } from 'rxjs'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { ValueType } from '@shared/models/constants'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts index a3d81bbe0d..6171a56a7e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/slider-widget.component.ts @@ -34,7 +34,6 @@ import { textStyle } from '@shared/models/widget-settings.models'; import { Observable } from 'rxjs'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; import { ValueType } from '@shared/models/constants'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts index 0681c0267f..007eb37929 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/switch.component.ts @@ -25,7 +25,6 @@ import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from import { DatasourceType, widgetType } from '@shared/models/widget.models'; import { EntityType } from '@shared/models/entity-type.models'; import { MatSlideToggle } from '@angular/material/slide-toggle'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ThemePalette } from '@angular/material/core'; const switchAspectRation = 2.7893; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts index 010cb9d6bc..aebb2b427d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol-widget.component.ts @@ -120,6 +120,7 @@ export class ScadaSymbolWidgetComponent implements OnInit, AfterViewInit, OnDest onScadaSymbolObjectLoadingState(loading: boolean) { this.loadingSubject.next(loading); + this.cd.detectChanges(); } onScadaSymbolObjectError(error: string) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts index 8e5993902d..0418665d69 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/scada/scada-symbol.models.ts @@ -15,7 +15,7 @@ /// import { ValueType } from '@shared/models/constants'; -import { Box, Element, Runner, SVG, Svg, Text, Timeline } from '@svgdotjs/svg.js'; +import { Box, Element, Runner, Style, SVG, Svg, Text, Timeline } from '@svgdotjs/svg.js'; import '@svgdotjs/svg.panzoom.js'; import { DataToValueType, @@ -41,7 +41,6 @@ import { ColorProcessor, constantColor, Font } from '@shared/models/widget-setti import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { WidgetAction, WidgetActionType, widgetActionTypeTranslationMap } from '@shared/models/widget.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { catchError, map, take, takeUntil } from 'rxjs/operators'; import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; import { MatIconRegistry } from '@angular/material/icon'; @@ -59,6 +58,7 @@ export interface ScadaSymbolApi { enable: (element: Element | Element[]) => void; callAction: (event: Event, behaviorId: string, value?: any, observer?: Partial>) => void; setValue: (valueId: string, value: any) => void; + cssAnimate: CssScadaSymbolAnimations; } export interface ScadaSymbolContext { @@ -622,7 +622,8 @@ export class ScadaSymbolObject { disable: this.disableElement.bind(this), enable: this.enableElement.bind(this), callAction: this.callAction.bind(this), - setValue: this.setValue.bind(this) + setValue: this.setValue.bind(this), + cssAnimate: new CssScadaSymbolAnimations(this.svgShape) }, tags: {}, properties: {}, @@ -1009,3 +1010,301 @@ export class ScadaSymbolObject { } } } + +const scadaSymbolAnimationId = 'scadaSymbolAnimation'; + +class CssScadaSymbolAnimations { + constructor(private svgShape: Svg) {} + + public rotate(element: Element, rotation: number, duration = 1000, loop = false): CssScadaSymbolAnimation { + this.checkOldAnimation(element); + return this.setupAnimation(element, + new RotateCssScadaSymbolAnimation(this.svgShape, element, loop, rotation || 0)).duration(duration); + } + + public move(element: Element, deltaX: number, deltaY: number, duration = 1000, loop = false): CssScadaSymbolAnimation { + this.checkOldAnimation(element); + return this.setupAnimation(element, + new MoveCssScadaSymbolAnimation(this.svgShape, element, loop, deltaX, deltaY).duration(duration)); + } + + public attr(element: Element, attrName: string, value: any, duration = 1000, loop = false): CssScadaSymbolAnimation { + this.checkOldAnimation(element); + return this.setupAnimation(element, + new AttrsCssScadaSymbolAnimation(this.svgShape, element, loop, {[attrName]: value}).duration(duration)); + } + + public attrs(element: Element, attr: any, duration = 1000, loop = false): CssScadaSymbolAnimation { + this.checkOldAnimation(element); + return this.setupAnimation(element, + new AttrsCssScadaSymbolAnimation(this.svgShape, element, loop, attr).duration(duration)); + } + + public animation(element: Element): CssScadaSymbolAnimation | undefined { + return element.remember(scadaSymbolAnimationId); + } + + private checkOldAnimation(element: Element) { + const previousAnimation: CssScadaSymbolAnimation = element.remember(scadaSymbolAnimationId); + if (previousAnimation) { + previousAnimation.destroy(); + } + } + + private setupAnimation(element: Element, animation: CssScadaSymbolAnimation): CssScadaSymbolAnimation { + animation.init(); + element.remember(scadaSymbolAnimationId, animation); + return animation; + } +} + +interface ScadaSymbolAnimationKeyframe { + stop: string; + style: any; +} + +abstract class CssScadaSymbolAnimation { + + private _animationName: string; + private _animationStyle: Style; + + private _running = true; + private _speed = 1; + private _duration = 1000; + private _easing = 'linear'; + + protected constructor(protected svgShape: Svg, + protected element: Element, + protected loop: boolean) { + } + + public init() { + this.prepareAnimation(); + } + + public start() { + if (!this._running) { + this.updateAnimationStyle('animation-play-state', 'running'); + this._running = true; + } + } + + public pause() { + if (this._running) { + this.updateAnimationStyle('animation-play-state', 'paused'); + this._running = false; + } + } + + public running(): boolean { + return this._running; + } + + public destroy() { + if (this._animationStyle) { + this._animationStyle.remove(); + this.element.removeClass(this._animationName); + this._animationStyle = null; + this._animationName = null; + } + } + + public duration(duration: number): CssScadaSymbolAnimation { + this._duration = duration; + this.updateAnimationStyle(this.loop ? 'animation-duration' : 'transition-duration', + Math.round(this._duration / this._speed) + 'ms'); + return this; + } + + public speed(speed: number): CssScadaSymbolAnimation { + this._speed = speed; + this.updateAnimationStyle(this.loop ? 'animation-duration' : 'transition-duration', + Math.round(this._duration / this._speed) + 'ms'); + return this; + } + + public easing(easing: string): CssScadaSymbolAnimation { + this._easing = easing; + this.updateAnimationStyle(this.loop ? 'animation-timing-function' : 'transition-timing-function', this._easing); + return this; + } + + private prepareAnimation() { + this._animationName = 'animation_' + generateElementId(); + this._animationStyle = this.svgShape.style(); + let styles: any; + if (this.loop) { + styles = { + 'animation-name': this._animationName, + 'animation-duration': this._duration + 'ms', + 'animation-timing-function': this._easing, + 'animation-iteration-count': 'infinite', + 'animation-fill-mode': 'forwards', + 'animation-play-state': 'running', + ...this.animationStyles() + }; + } else { + styles = { + 'transition-property': this.transitionProperties(), + 'transition-duration': this._duration + 'ms', + 'transition-timing-function': this._easing, + ...this.animationStyles() + }; + } + this._animationStyle.rule('.' + this._animationName, styles); + + if (this.loop) { + const keyframes = this.animationKeyframes(); + let keyframesCss = `\n@keyframes ${this._animationName} {\n`; + for (const keyframe of keyframes) { + let keyframeCss = ` ${keyframe.stop} {\n`; + for (const i of Object.keys(keyframe.style)) { + keyframeCss += ' ' + i + ':' + keyframe.style[i] + ';\n'; + } + keyframeCss += ' }\n'; + keyframesCss += keyframeCss; + } + keyframesCss += '}'; + this._animationStyle.addText(keyframesCss); + } + this.element.addClass(this._animationName); + if (!this.loop) { + this.doTransform(); + } + } + + private updateAnimationStyle(attrName: string, value: any) { + if (this._animationStyle) { + const styleText = this._animationStyle.node.innerHTML; + const attrValueRegex = new RegExp(`${attrName}:([^;]+);`); + this._animationStyle.node.innerHTML = styleText.replace(attrValueRegex, `${attrName}:${value};`); + } + } + + protected animationStyles(): any { + return {}; + } + + protected abstract animationKeyframes(): ScadaSymbolAnimationKeyframe[]; + + protected abstract transitionProperties(): string; + + protected doTransform() { + } + +} + +class RotateCssScadaSymbolAnimation extends CssScadaSymbolAnimation { + + constructor(protected svgShape: Svg, + protected element: Element, + protected loop: boolean, + private rotation: number) { + super(svgShape, element, loop); + } + + protected animationStyles(): any { + return { + 'transform-origin': `${this.element.cx()}px ${this.element.cy()}px` + }; + } + + protected animationKeyframes(): ScadaSymbolAnimationKeyframe[] { + const transform = this.element.transform(); + return [ + { + stop: '0%', + style: { + transform: `translate(${transform.translateX}px, ${transform.translateY}px) rotate(${transform.rotate}deg)` + } + }, + { + stop: '100%', + style: { + transform: `translate(${transform.translateX}px, ${transform.translateY}px) rotate(${this.rotation}deg)` + } + } + ]; + } + + protected transitionProperties(): string { + return 'transform'; + } + + protected doTransform() { + const transform = this.element.transform(); + this.element.attr({transform: `translate(${transform.translateX} ${transform.translateY}) rotate(${this.rotation})`}); + } + +} + +class MoveCssScadaSymbolAnimation extends CssScadaSymbolAnimation { + + constructor(protected svgShape: Svg, + protected element: Element, + protected loop: boolean, + private deltaX: number, + private deltaY: number) { + super(svgShape, element, loop); + } + + protected animationStyles(): any { + return { + 'transform-origin': `${this.element.cx()}px ${this.element.cy()}px` + }; + } + + protected animationKeyframes(): ScadaSymbolAnimationKeyframe[] { + const transform = this.element.transform(); + return [ + { + stop: '0%', + style: { + transform: `translate(${transform.translateX}px, ${transform.translateY}px)` + } + }, + { + stop: '100%', + style: { + transform: `translate(${transform.translateX+this.deltaX}px, ${transform.translateY+this.deltaY}px)` + } + } + ]; + } + + protected transitionProperties(): string { + return 'transform'; + } + + protected doTransform() { + this.element.relative(this.deltaX, this.deltaY); + } + +} + +class AttrsCssScadaSymbolAnimation extends CssScadaSymbolAnimation { + + constructor(protected svgShape: Svg, + protected element: Element, + protected loop: boolean, + private attr: any) { + super(svgShape, element, loop); + } + + protected animationStyles(): any { + return {}; + } + + protected animationKeyframes(): ScadaSymbolAnimationKeyframe[] { + return []; + } + + protected transitionProperties(): string { + return Object.keys(this.attr).join(' '); + } + + protected doTransform() { + this.element.attr(this.attr); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts index 55f010cff3..f2ce5ba7fe 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/custom-action-pretty-resources-tabs.component.ts @@ -34,7 +34,6 @@ import { AppState } from '@core/core.state'; import { CustomActionDescriptor } from '@shared/models/widget.models'; import { Ace } from 'ace-builds'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { CustomPrettyActionEditorCompleter } from '@home/components/widget/lib/settings/common/action/custom-action.models'; import { Observable } from 'rxjs/internal/Observable'; import { forkJoin, from } from 'rxjs'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts index b21614cb50..6b7e80a269 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts @@ -85,7 +85,6 @@ import { Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; import { DatePipe } from '@angular/common'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { DISPLAY_COLUMNS_PANEL_DATA, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts index 79c180cfbc..62d1818acc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/trip-animation/trip-animation.component.ts @@ -51,7 +51,6 @@ import { parseFunction, safeExecute } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface'; interface DataMap { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts index 929f41fe48..5098bc6b29 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/weather/wind-speed-direction-widget.component.ts @@ -43,7 +43,6 @@ import { overlayStyle } from '@shared/models/widget-settings.models'; import { formatValue, isDefinedAndNotNull, isNumeric } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { Path, Svg, SVG, Text } from '@svgdotjs/svg.js'; import { DataKey } from '@shared/models/widget.models'; import { Observable } from 'rxjs'; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index 5bd4910af7..51ca44c159 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -100,7 +100,6 @@ import { DashboardService } from '@core/http/dashboard.service'; import { WidgetSubscription } from '@core/api/widget-subscription'; import { EntityService } from '@core/http/entity.service'; import { ServicesMap } from '@home/models/services.map'; -import { ResizeObserver } from '@juggle/resize-observer'; import { EntityDataService } from '@core/api/entity-data.service'; import { TranslateService } from '@ngx-translate/core'; import { NotificationType } from '@core/notification/notification.models'; diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts index 9c1ad96894..769671c0ac 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.models.ts @@ -17,7 +17,6 @@ import { ImageResourceInfo } from '@shared/models/resource.models'; import * as svgjs from '@svgdotjs/svg.js'; import { Box, Element, Rect, Style, SVG, Svg, Timeline } from '@svgdotjs/svg.js'; -import { ResizeObserver } from '@juggle/resize-observer'; import { ViewContainerRef } from '@angular/core'; import { forkJoin, from } from 'rxjs'; import { diff --git a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts index 3ca2036369..9bd2145c91 100644 --- a/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts +++ b/ui-ngx/src/app/modules/home/pages/widget/widget-editor.component.ts @@ -60,7 +60,6 @@ import { SaveWidgetTypeAsDialogResult } from '@home/pages/widget/save-widget-type-as-dialog.component'; import { forkJoin, mergeMap, of, Subscription, throwError } from 'rxjs'; -import { ResizeObserver } from '@juggle/resize-observer'; import { widgetEditorCompleter } from '@home/pages/widget/widget-editor.models'; import { Observable } from 'rxjs/internal/Observable'; import { catchError, map, tap } from 'rxjs/operators'; diff --git a/ui-ngx/src/app/shared/components/button/widget-button.component.ts b/ui-ngx/src/app/shared/components/button/widget-button.component.ts index db6584d96f..bfb81b89b7 100644 --- a/ui-ngx/src/app/shared/components/button/widget-button.component.ts +++ b/ui-ngx/src/app/shared/components/button/widget-button.component.ts @@ -36,7 +36,6 @@ import { import { coerceBoolean } from '@shared/decorators/coercion'; import { ComponentStyle, iconStyle, validateCssSize } from '@shared/models/widget-settings.models'; import { UtilsService } from '@core/services/utils.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { Observable, of } from 'rxjs'; import { WidgetContext } from '@home/models/widget-component.models'; import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; diff --git a/ui-ngx/src/app/shared/components/css.component.ts b/ui-ngx/src/app/shared/components/css.component.ts index 176a09c8f3..05b43d5863 100644 --- a/ui-ngx/src/app/shared/components/css.component.ts +++ b/ui-ngx/src/app/shared/components/css.component.ts @@ -34,7 +34,6 @@ import { AppState } from '@core/core.state'; import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { beautifyCss } from '@shared/models/beautify.models'; @Component({ diff --git a/ui-ngx/src/app/shared/components/fab-toolbar.component.ts b/ui-ngx/src/app/shared/components/fab-toolbar.component.ts index 1d89c5172c..72529189dd 100644 --- a/ui-ngx/src/app/shared/components/fab-toolbar.component.ts +++ b/ui-ngx/src/app/shared/components/fab-toolbar.component.ts @@ -29,7 +29,6 @@ import { } from '@angular/core'; import { WINDOW } from '@core/services/window.service'; import { _Constructor, CanColor, mixinColor, ThemePalette } from '@angular/material/core'; -import { ResizeObserver } from '@juggle/resize-observer'; export declare type FabToolbarDirection = 'left' | 'right'; diff --git a/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts index e876b06694..632351f8ed 100644 --- a/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts +++ b/ui-ngx/src/app/shared/components/grid/scroll-grid.component.ts @@ -34,7 +34,6 @@ import { import { BreakpointObserver } from '@angular/cdk/layout'; import { isObject } from '@app/core/utils'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { ResizeObserver } from '@juggle/resize-observer'; export type ItemSizeFunction = (itemWidth: number) => number; diff --git a/ui-ngx/src/app/shared/components/html.component.ts b/ui-ngx/src/app/shared/components/html.component.ts index d51458a816..da7e1fde9b 100644 --- a/ui-ngx/src/app/shared/components/html.component.ts +++ b/ui-ngx/src/app/shared/components/html.component.ts @@ -34,7 +34,6 @@ import { AppState } from '@core/core.state'; import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { beautifyHtml } from '@shared/models/beautify.models'; @Component({ diff --git a/ui-ngx/src/app/shared/components/image/image-gallery.component.ts b/ui-ngx/src/app/shared/components/image/image-gallery.component.ts index 24a4c3dde7..4a41e5ce86 100644 --- a/ui-ngx/src/app/shared/components/image/image-gallery.component.ts +++ b/ui-ngx/src/app/shared/components/image/image-gallery.component.ts @@ -48,7 +48,6 @@ import { AppState } from '@core/core.state'; import { DialogService } from '@core/services/dialog.service'; import { FormBuilder } from '@angular/forms'; import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { ResizeObserver } from '@juggle/resize-observer'; import { hidePageSizePixelValue } from '@shared/models/constants'; import { coerceBoolean } from '@shared/decorators/coercion'; import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; diff --git a/ui-ngx/src/app/shared/components/js-func.component.ts b/ui-ngx/src/app/shared/components/js-func.component.ts index e15535391f..8255ef405c 100644 --- a/ui-ngx/src/app/shared/components/js-func.component.ts +++ b/ui-ngx/src/app/shared/components/js-func.component.ts @@ -36,7 +36,6 @@ import { UtilsService } from '@core/services/utils.service'; import { guid, isUndefined } from '@app/core/utils'; import { TranslateService } from '@ngx-translate/core'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { TbEditorCompleter } from '@shared/models/ace/completion.models'; import { beautifyJs } from '@shared/models/beautify.models'; import { ScriptLanguage } from '@shared/models/rule-node.models'; diff --git a/ui-ngx/src/app/shared/components/json-content.component.ts b/ui-ngx/src/app/shared/components/json-content.component.ts index d79feacfbc..fb444da03d 100644 --- a/ui-ngx/src/app/shared/components/json-content.component.ts +++ b/ui-ngx/src/app/shared/components/json-content.component.ts @@ -35,7 +35,6 @@ import { AppState } from '@core/core.state'; import { ContentType, contentTypesMap } from '@shared/models/constants'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { guid } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { getAce } from '@shared/models/ace/ace.models'; import { beautifyJs } from '@shared/models/beautify.models'; import { coerceBoolean } from '@shared/decorators/coercion'; diff --git a/ui-ngx/src/app/shared/components/json-object-edit.component.ts b/ui-ngx/src/app/shared/components/json-object-edit.component.ts index 60411fc521..ea8bda0004 100644 --- a/ui-ngx/src/app/shared/components/json-object-edit.component.ts +++ b/ui-ngx/src/app/shared/components/json-object-edit.component.ts @@ -39,7 +39,6 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { guid, isDefinedAndNotNull, isObject, isUndefined } from '@core/utils'; -import { ResizeObserver } from '@juggle/resize-observer'; import { getAce } from '@shared/models/ace/ace.models'; import { coerceBoolean } from '@shared/decorators/coercion'; diff --git a/ui-ngx/src/app/shared/components/markdown-editor.component.ts b/ui-ngx/src/app/shared/components/markdown-editor.component.ts index bcff3c8e09..c71991a6a2 100644 --- a/ui-ngx/src/app/shared/components/markdown-editor.component.ts +++ b/ui-ngx/src/app/shared/components/markdown-editor.component.ts @@ -28,7 +28,6 @@ import { import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Ace } from 'ace-builds'; import { getAce } from '@shared/models/ace/ace.models'; -import { ResizeObserver } from '@juggle/resize-observer'; import { coerceBoolean } from '@shared/decorators/coercion'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; diff --git a/ui-ngx/src/app/shared/components/protobuf-content.component.ts b/ui-ngx/src/app/shared/components/protobuf-content.component.ts index 8e2f0874a2..cb3a3e14ff 100644 --- a/ui-ngx/src/app/shared/components/protobuf-content.component.ts +++ b/ui-ngx/src/app/shared/components/protobuf-content.component.ts @@ -26,7 +26,6 @@ import { import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Ace } from 'ace-builds'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { guid } from '@core/utils'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Store } from '@ngrx/store'; diff --git a/ui-ngx/src/app/shared/components/svg-xml.component.ts b/ui-ngx/src/app/shared/components/svg-xml.component.ts index ce57b8d998..ff236c5707 100644 --- a/ui-ngx/src/app/shared/components/svg-xml.component.ts +++ b/ui-ngx/src/app/shared/components/svg-xml.component.ts @@ -34,7 +34,6 @@ import { AppState } from '@core/core.state'; import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; -import { ResizeObserver } from '@juggle/resize-observer'; import { coerceBoolean } from '@shared/decorators/coercion'; @Component({ diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index 0f46efe018..51bfe8f56e 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -1697,11 +1697,6 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@juggle/resize-observer@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" - integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== - "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"