From c676ebb2675a9a3d055b86ee51b9da40a34878fc Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 16 Sep 2024 17:38:26 +0300 Subject: [PATCH 001/110] Move lastLoginTs and failedLoginAttempts from user's additionalInfo --- .../main/data/upgrade/3.7.1/schema_update.sql | 25 +++++++++++ .../server/controller/UserController.java | 7 +++- .../edge/EdgeEventSourcingListener.java | 3 -- .../system/DefaultSystemSecurityService.java | 2 +- .../server/dao/user/UserService.java | 2 +- .../common/data/security/UserCredentials.java | 2 + .../server/dao/model/ModelConstants.java | 3 +- .../dao/model/sql/UserCredentialsEntity.java | 17 +++++--- .../dao/sql/user/JpaUserCredentialsDao.java | 15 +++++++ .../sql/user/UserCredentialsRepository.java | 18 ++++++++ .../server/dao/user/UserCredentialsDao.java | 6 +++ .../server/dao/user/UserServiceImpl.java | 42 +++++-------------- 12 files changed, 97 insertions(+), 45 deletions(-) create mode 100644 application/src/main/data/upgrade/3.7.1/schema_update.sql diff --git a/application/src/main/data/upgrade/3.7.1/schema_update.sql b/application/src/main/data/upgrade/3.7.1/schema_update.sql new file mode 100644 index 0000000000..240daa18d5 --- /dev/null +++ b/application/src/main/data/upgrade/3.7.1/schema_update.sql @@ -0,0 +1,25 @@ +-- +-- 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. +-- + +ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS last_login_ts BIGINT; +UPDATE user_credentials c SET last_login_ts = (SELECT (additional_info::json ->> 'lastLoginTs')::bigint FROM tb_user u WHERE u.id = c.user_id) + WHERE last_login_ts IS NULL; + +ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS failed_login_attempts INT; +UPDATE user_credentials c SET failed_login_attempts = (SELECT (additional_info::json ->> 'failedLoginAttempts')::int FROM tb_user u WHERE u.id = c.user_id) + WHERE failed_login_attempts IS NULL; + +UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts')::text WHERE additional_info IS NOT NULL AND additional_info != 'null'; diff --git a/application/src/main/java/org/thingsboard/server/controller/UserController.java b/application/src/main/java/org/thingsboard/server/controller/UserController.java index a71bd4dd38..df7f47c378 100644 --- a/application/src/main/java/org/thingsboard/server/controller/UserController.java +++ b/application/src/main/java/org/thingsboard/server/controller/UserController.java @@ -109,6 +109,8 @@ import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARA import static org.thingsboard.server.controller.ControllerConstants.USER_TEXT_SEARCH_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; import static org.thingsboard.server.dao.entity.BaseEntityService.NULL_CUSTOMER_ID; +import static org.thingsboard.server.dao.user.UserServiceImpl.LAST_LOGIN_TS; +import static org.thingsboard.server.dao.user.UserServiceImpl.USER_CREDENTIALS_ENABLED; @RequiredArgsConstructor @RestController @@ -151,9 +153,10 @@ public class UserController extends BaseController { processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); - if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { - additionalInfo.put("userCredentialsEnabled", true); + if (userCredentials.isEnabled() && !additionalInfo.has(USER_CREDENTIALS_ENABLED)) { + additionalInfo.put(USER_CREDENTIALS_ENABLED, true); } + additionalInfo.put(LAST_LOGIN_TS, userCredentials.getLastLoginTs()); } return user; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index 17ceb17ed1..907872d503 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -50,7 +50,6 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.RelationActionEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.dao.user.UserServiceImpl; /** * This event listener does not support async event processing because relay on ThreadLocal @@ -231,8 +230,6 @@ public class EdgeEventSourcingListener { user.setAdditionalInfo(null); } if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) { - additionalInfo.remove(UserServiceImpl.FAILED_LOGIN_ATTEMPTS); - additionalInfo.remove(UserServiceImpl.LAST_LOGIN_TS); if (additionalInfo.isEmpty()) { user.setAdditionalInfo(null); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index 6516bd7ce6..c969fa71ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -265,7 +265,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { } } if (actionType == ActionType.LOGIN && e == null) { - userService.setLastLoginTs(user.getTenantId(), user.getId()); + userService.updateLastLoginTs(user.getTenantId(), user.getId()); } auditLogService.logEntityAction( user.getTenantId(), user.getCustomerId(), user.getId(), diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index 8f22812cfc..586ae50f5d 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -97,7 +97,7 @@ public interface UserService extends EntityDaoService { int increaseFailedLoginAttempts(TenantId tenantId, UserId userId); - void setLastLoginTs(TenantId tenantId, UserId userId); + void updateLastLoginTs(TenantId tenantId, UserId userId); void saveMobileSession(TenantId tenantId, UserId userId, String mobileToken, MobileSessionInfo sessionInfo); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java index 1104ae2949..4a882795db 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/UserCredentials.java @@ -40,6 +40,8 @@ public class UserCredentials extends BaseDataWithAdditionalInfo private Long resetTokenExpTime; @Convert(converter = JsonConverter.class) - @Column(name = ModelConstants.USER_CREDENTIALS_ADDITIONAL_PROPERTY) + @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; + @Column(name = ModelConstants.USER_CREDENTIALS_LAST_LOGIN_TS_PROPERTY) + private Long lastLoginTs; + + @Column(name = ModelConstants.USER_CREDENTIALS_FAILED_LOGIN_ATTEMPTS_PROPERTY) + private Integer failedLoginAttempts; + public UserCredentialsEntity() { super(); } public UserCredentialsEntity(UserCredentials userCredentials) { - if (userCredentials.getId() != null) { - this.setUuid(userCredentials.getId().getId()); - } - this.setCreatedTime(userCredentials.getCreatedTime()); + super(userCredentials); if (userCredentials.getUserId() != null) { this.userId = userCredentials.getUserId().getId(); } @@ -82,6 +85,8 @@ public final class UserCredentialsEntity extends BaseSqlEntity this.resetToken = userCredentials.getResetToken(); this.resetTokenExpTime = userCredentials.getResetTokenExpTime(); this.additionalInfo = userCredentials.getAdditionalInfo(); + this.lastLoginTs = userCredentials.getLastLoginTs(); + this.failedLoginAttempts = userCredentials.getFailedLoginAttempts(); } @Override @@ -98,6 +103,8 @@ public final class UserCredentialsEntity extends BaseSqlEntity userCredentials.setResetToken(resetToken); userCredentials.setResetTokenExpTime(resetTokenExpTime); userCredentials.setAdditionalInfo(additionalInfo); + userCredentials.setLastLoginTs(lastLoginTs); + userCredentials.setFailedLoginAttempts(failedLoginAttempts); return userCredentials; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java index 1f502db792..f277475896 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserCredentialsDao.java @@ -69,4 +69,19 @@ public class JpaUserCredentialsDao extends JpaAbstractDao { void removeByUserId(TenantId tenantId, UserId userId); + void setLastLoginTs(TenantId tenantId, UserId userId, long lastLoginTs); + + int incrementFailedLoginAttempts(TenantId tenantId, UserId userId); + + void setFailedLoginAttempts(TenantId tenantId, UserId userId, int failedLoginAttempts); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index ba0d3d69f3..c927c3e465 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -18,8 +18,6 @@ package org.thingsboard.server.dao.user; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.BooleanNode; -import com.fasterxml.jackson.databind.node.IntNode; -import com.fasterxml.jackson.databind.node.LongNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; @@ -86,16 +84,13 @@ import static org.thingsboard.server.dao.service.Validator.validateString; public class UserServiceImpl extends AbstractCachedEntityService implements UserService { public static final String USER_PASSWORD_HISTORY = "userPasswordHistory"; - + public static final String USER_CREDENTIALS_ENABLED = "userCredentialsEnabled"; public static final String LAST_LOGIN_TS = "lastLoginTs"; - public static final String FAILED_LOGIN_ATTEMPTS = "failedLoginAttempts"; private static final int DEFAULT_TOKEN_LENGTH = 30; public static final String INCORRECT_USER_ID = "Incorrect userId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - private static final String USER_CREDENTIALS_ENABLED = "userCredentialsEnabled"; - @Value("${security.user_login_case_sensitive:true}") private boolean userLoginCaseSensitive; @@ -428,6 +423,7 @@ public class UserServiceImpl extends AbstractCachedEntityService Date: Tue, 17 Sep 2024 13:20:07 +0300 Subject: [PATCH 002/110] Update user_credentials in schema-entities.sql --- dao/src/main/resources/sql/schema-entities.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 9c95f385f8..c553562007 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -497,7 +497,9 @@ CREATE TABLE IF NOT EXISTS user_credentials ( reset_token varchar(255) UNIQUE, reset_token_exp_time BIGINT, user_id uuid UNIQUE, - additional_info varchar DEFAULT '{}' + additional_info varchar DEFAULT '{}', + last_login_ts BIGINT, + failed_login_attempts INT ); CREATE TABLE IF NOT EXISTS widget_type ( From 345c1e5a31779b1f0b8a03381f5319f5464f564a Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 17 Sep 2024 19:05:03 +0300 Subject: [PATCH 003/110] Fix incrementFailedLoginAttemptsByUserId --- .../org/thingsboard/server/controller/TwoFactorAuthTest.java | 4 +++- .../server/dao/sql/user/UserCredentialsRepository.java | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java index 63703ae27b..7fa848f95d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java @@ -319,7 +319,9 @@ public class TwoFactorAuthTest extends AbstractControllerTest { assertThat(successfulLogInAuditLog.getActionStatus()).isEqualTo(ActionStatus.SUCCESS); assertThat(successfulLogInAuditLog.getUserName()).isEqualTo(username); }); - assertThat(userService.findUserById(tenantId, user.getId()).getAdditionalInfo() + + loginTenantAdmin(); + assertThat(doGet("/api/user/" + user.getId(), User.class).getAdditionalInfo() .get("lastLoginTs").asLong()) .isGreaterThan(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(3)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java index 60cdffb410..4aca40647c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserCredentialsRepository.java @@ -43,7 +43,6 @@ public interface UserCredentialsRepository extends JpaRepository Date: Tue, 17 Sep 2024 18:34:48 +0200 Subject: [PATCH 004/110] fixed concurrent modification in TbSubscriptionsInfo --- .../server/service/subscription/TbSubscriptionsInfo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java index 48464d5297..a65fc0bedb 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java @@ -20,6 +20,7 @@ import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import lombok.ToString; +import java.util.HashSet; import java.util.Set; /** @@ -48,7 +49,7 @@ public class TbSubscriptionsInfo { } protected TbSubscriptionsInfo copy(int seqNumber) { - return new TbSubscriptionsInfo(notifications, alarms, tsAllKeys, tsKeys, attrAllKeys, attrKeys, seqNumber); + return new TbSubscriptionsInfo(notifications, alarms, tsAllKeys, tsKeys != null ? new HashSet<>(tsKeys) : null, attrAllKeys, attrKeys != null ? new HashSet<>(attrKeys) : null, seqNumber); } } From 8d04c5040e5e5f83750ba204c3e6f87b3cdcfcd4 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 18 Sep 2024 22:06:28 +0200 Subject: [PATCH 005/110] implemented removing all subscriptions by batch --- .../DefaultTbLocalSubscriptionService.java | 39 +++++++++-- .../subscription/TbEntityLocalSubsInfo.java | 64 +++++++++++++++++-- 2 files changed, 91 insertions(+), 12 deletions(-) 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 4043b519da..0a47f5f375 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 @@ -282,7 +282,6 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer if (sessionSubscriptions != null) { TbSubscription subscription = sessionSubscriptions.remove(subscriptionId); if (subscription != null) { - if (sessionSubscriptions.isEmpty()) { subscriptionsBySessionId.remove(sessionId); } @@ -304,22 +303,26 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer @Override public void cancelAllSessionSubscriptions(TenantId tenantId, String sessionId) { log.debug("[{}][{}] Going to remove session subscriptions.", tenantId, sessionId); - List results = new ArrayList<>(); Lock subsLock = getSubsLock(tenantId); subsLock.lock(); try { Map> sessionSubscriptions = subscriptionsBySessionId.remove(sessionId); if (sessionSubscriptions != null) { - for (TbSubscription subscription : sessionSubscriptions.values()) { - results.add(modifySubscription(tenantId, subscription.getEntityId(), subscription, false)); - } + Map>> entitySubscriptions = + sessionSubscriptions.values().stream().collect(Collectors.groupingBy(TbSubscription::getEntityId)); + + entitySubscriptions.forEach((entityId, subscriptions) -> { + TbEntitySubEvent event = removeAllSubscriptions(tenantId, entityId, subscriptions); + if (event != null) { + pushSubscriptionsEvent(tenantId, entityId, event); + } + }); } else { log.debug("[{}][{}] No session subscriptions found!", tenantId, sessionId); } } finally { subsLock.unlock(); } - results.stream().filter(SubscriptionModificationResult::hasEvent).forEach(this::pushSubscriptionEvent); } @Override @@ -500,6 +503,30 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer return new SubscriptionModificationResult(tenantId, entityId, subscription, missedUpdatesCandidate, event); } + private TbEntitySubEvent removeAllSubscriptions(TenantId tenantId, EntityId entityId, List> subscriptions) { + TbEntitySubEvent event = null; + try { + TbEntityLocalSubsInfo entitySubs = subscriptionsByEntityId.get(entityId.getId()); + event = entitySubs.removeAll(subscriptions); + if (entitySubs.isEmpty()) { + subscriptionsByEntityId.remove(entityId.getId()); + entityUpdates.remove(entityId.getId()); + } + } catch (Exception e) { + log.warn("[{}][{}] Failed to remove all subscriptions {} due to ", tenantId, entityId, subscriptions, e); + } + return event; + } + + private void pushSubscriptionsEvent(TenantId tenantId, EntityId entityId, TbEntitySubEvent event) { + try { + log.trace("[{}][{}] Event: {}", tenantId, entityId, event); + pushSubEventToManagerService(tenantId, entityId, event); + } catch (Exception e) { + log.warn("[{}][{}] Failed to push subscription event {} due to ", tenantId, entityId, event, e); + } + } + private void pushSubscriptionEvent(SubscriptionModificationResult modificationResult) { try { TbEntitySubEvent event = modificationResult.getEvent(); diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java index f5a5639ec0..a865faeb1a 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -129,13 +130,64 @@ public class TbEntityLocalSubsInfo { if (!subs.remove(sub)) { return null; } - if (subs.isEmpty()) { + if (isEmpty()) { return toEvent(ComponentLifecycleEvent.DELETED); } - TbSubscriptionsInfo oldState = state.copy(); - TbSubscriptionsInfo newState = new TbSubscriptionsInfo(); + TbSubscriptionType type = sub.getType(); + TbSubscriptionsInfo newState = state.copy(); + updateNewState(newState, type); + return updateState(Set.of(type), newState); + } + + public TbEntitySubEvent removeAll(List> subsToRemove) { + Set changedTypes = new HashSet<>(); + TbSubscriptionsInfo newState = state.copy(); + for (TbSubscription sub : subsToRemove) { + log.trace("[{}][{}][{}] Removing: {}", tenantId, entityId, sub.getSubscriptionId(), sub); + if (!subs.remove(sub)) { + continue; + } + if (isEmpty()) { + return toEvent(ComponentLifecycleEvent.DELETED); + } + TbSubscriptionType type = sub.getType(); + if (changedTypes.contains(type)) { + continue; + } + + updateNewState(newState, type); + changedTypes.add(type); + } + + return updateState(changedTypes, newState); + } + + private void updateNewState(TbSubscriptionsInfo state, TbSubscriptionType type) { + switch (type) { + case NOTIFICATIONS: + case NOTIFICATIONS_COUNT: + state.notifications = false; + break; + case ALARMS: + state.alarms = false; + break; + case ATTRIBUTES: + state.attrAllKeys = false; + state.attrKeys = null; + break; + case TIMESERIES: + state.tsAllKeys = false; + state.tsKeys = null; + } + } + + private TbEntitySubEvent updateState(Set updatedTypes, TbSubscriptionsInfo newState) { for (TbSubscription subscription : subs) { - switch (subscription.getType()) { + TbSubscriptionType type = subscription.getType(); + if (!updatedTypes.contains(type)) { + continue; + } + switch (type) { case NOTIFICATIONS: case NOTIFICATIONS_COUNT: if (!newState.notifications) { @@ -173,7 +225,7 @@ public class TbEntityLocalSubsInfo { break; } } - if (newState.equals(oldState)) { + if (newState.equals(state)) { return null; } else { this.state = newState; @@ -196,7 +248,7 @@ public class TbEntityLocalSubsInfo { public boolean isEmpty() { - return state.isEmpty(); + return subs.isEmpty(); } public TbSubscription registerPendingSubscription(TbSubscription subscription, TbEntitySubEvent event) { From 20aca9466fc7a0d0514a6ac24d933d9aab618cd9 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 30 Sep 2024 12:53:19 +0300 Subject: [PATCH 006/110] Add tests for failed login and lastLoginTs --- .../server/controller/AuthControllerTest.java | 82 +++++++++++++------ 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java index 817ded33e7..62b8188cfd 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java @@ -27,6 +27,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.UserActivationLink; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.model.SecuritySettings; @@ -67,31 +68,30 @@ public class AuthControllerTest extends AbstractControllerTest { .andExpect(status().isUnauthorized()); loginSysAdmin(); - doGet("/api/auth/user") - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name()))) - .andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL))); + User user = getCurrentUser(); + assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN); + assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL); loginTenantAdmin(); - doGet("/api/auth/user") - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name()))) - .andExpect(jsonPath("$.email", is(TENANT_ADMIN_EMAIL))); + user = getCurrentUser(); + assertThat(user.getAuthority()).isEqualTo(Authority.TENANT_ADMIN); + assertThat(user.getEmail()).isEqualTo(TENANT_ADMIN_EMAIL); loginCustomerUser(); - doGet("/api/auth/user") - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authority", is(Authority.CUSTOMER_USER.name()))) - .andExpect(jsonPath("$.email", is(CUSTOMER_USER_EMAIL))); + user = getCurrentUser(); + assertThat(user.getAuthority()).isEqualTo(Authority.CUSTOMER_USER); + assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL); + assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue(); + user = getUser(customerUserId); + assertThat(user.getAdditionalInfo().get("lastLoginTs").asLong()).isCloseTo(System.currentTimeMillis(), within(10000L)); } @Test public void testLoginLogout() throws Exception { loginSysAdmin(); - doGet("/api/auth/user") - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name()))) - .andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL))); + User user = getCurrentUser(); + assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN); + assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL); TimeUnit.SECONDS.sleep(1); //We need to make sure that event for invalidating token was successfully processed @@ -102,19 +102,45 @@ public class AuthControllerTest extends AbstractControllerTest { resetTokens(); } + @Test + public void testFailedLogin() throws Exception { + int maxFailedLoginAttempts = 3; + loginSysAdmin(); + updateSecuritySettings(securitySettings -> { + securitySettings.setMaxFailedLoginAttempts(maxFailedLoginAttempts); + }); + loginTenantAdmin(); + + for (int i = 0; i < maxFailedLoginAttempts; i++) { + String error = getErrorMessage(doPost("/api/auth/login", + new LoginRequest(CUSTOMER_USER_EMAIL, "IncorrectPassword")) + .andExpect(status().isUnauthorized())); + assertThat(error).containsIgnoringCase("invalid username or password"); + } + + User user = getUser(customerUserId); + assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue(); + + String error = getErrorMessage(doPost("/api/auth/login", + new LoginRequest(CUSTOMER_USER_EMAIL, "IncorrectPassword4")) + .andExpect(status().isUnauthorized())); + assertThat(error).containsIgnoringCase("account is locked"); + + user = getUser(customerUserId); + assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isFalse(); + } + @Test public void testRefreshToken() throws Exception { loginSysAdmin(); - doGet("/api/auth/user") - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name()))) - .andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL))); + User user = getCurrentUser(); + assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN); + assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL); refreshToken(); - doGet("/api/auth/user") - .andExpect(status().isOk()) - .andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name()))) - .andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL))); + user = getCurrentUser(); + assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN); + assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL); } @Test @@ -277,6 +303,14 @@ public class AuthControllerTest extends AbstractControllerTest { doPost("/api/admin/securitySettings", securitySettings).andExpect(status().isOk()); } + private User getCurrentUser() throws Exception { + return doGet("/api/auth/user", User.class); + } + + private User getUser(UserId id) throws Exception { + return doGet("/api/user/" + id, User.class); + } + private String getActivationLink(User user) throws Exception { return doGet("/api/user/" + user.getId() + "/activationLink", String.class); } From 29a41e5306e86505b95c5b8c842222637016da17 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 30 Sep 2024 14:10:05 +0200 Subject: [PATCH 007/110] added corresponding tests --- .../subscription/TbEntityLocalSubsInfo.java | 2 +- .../TbEntityLocalSubsInfoTest.java | 177 ++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java index a865faeb1a..12786c65ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java @@ -139,7 +139,7 @@ public class TbEntityLocalSubsInfo { return updateState(Set.of(type), newState); } - public TbEntitySubEvent removeAll(List> subsToRemove) { + public TbEntitySubEvent removeAll(List> subsToRemove) { Set changedTypes = new HashSet<>(); TbSubscriptionsInfo newState = state.copy(); for (TbSubscription sub : subsToRemove) { diff --git a/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java b/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java new file mode 100644 index 0000000000..88f29308ab --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java @@ -0,0 +1,177 @@ +/** + * 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.service.subscription; + +import org.junit.jupiter.api.Test; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class TbEntityLocalSubsInfoTest { + + @Test + void addTest() { + Set expectedSubs = new HashSet<>(); + TbEntityLocalSubsInfo subsInfo = createSubsInfo(); + TenantId tenantId = subsInfo.getTenantId(); + EntityId entityId = subsInfo.getEntityId(); + TbAttributeSubscription attrSubscription1 = TbAttributeSubscription.builder() + .sessionId("session1") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key1", 1L, "key2", 2L)) + .build(); + expectedSubs.add(attrSubscription1); + TbEntitySubEvent created = subsInfo.add(attrSubscription1); + assertFalse(subsInfo.isEmpty()); + assertNotNull(created); + assertEquals(expectedSubs, subsInfo.getSubs()); + checkEvent(created, expectedSubs, ComponentLifecycleEvent.CREATED); + + assertNull(subsInfo.add(attrSubscription1)); + + TbAttributeSubscription attrSubscription2 = TbAttributeSubscription.builder() + .sessionId("session2") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key3", 3L, "key4", 4L)) + .build(); + expectedSubs.add(attrSubscription2); + TbEntitySubEvent updated = subsInfo.add(attrSubscription2); + assertNotNull(updated); + + assertEquals(expectedSubs, subsInfo.getSubs()); + checkEvent(updated, expectedSubs, ComponentLifecycleEvent.UPDATED); + } + + @Test + void removeTest() { + Set expectedSubs = new HashSet<>(); + TbEntityLocalSubsInfo subsInfo = createSubsInfo(); + TenantId tenantId = subsInfo.getTenantId(); + EntityId entityId = subsInfo.getEntityId(); + TbAttributeSubscription attrSubscription1 = TbAttributeSubscription.builder() + .sessionId("session1") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key1", 1L, "key2", 2L)) + .build(); + + TbAttributeSubscription attrSubscription2 = TbAttributeSubscription.builder() + .sessionId("session2") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key3", 3L, "key4", 4L)) + .build(); + + expectedSubs.add(attrSubscription1); + expectedSubs.add(attrSubscription2); + + subsInfo.add(attrSubscription1); + subsInfo.add(attrSubscription2); + + assertEquals(expectedSubs, subsInfo.getSubs()); + + TbEntitySubEvent updatedEvent = subsInfo.remove(attrSubscription1); + expectedSubs.remove(attrSubscription1); + assertNotNull(updatedEvent); + assertEquals(expectedSubs, subsInfo.getSubs()); + checkEvent(updatedEvent, expectedSubs, ComponentLifecycleEvent.UPDATED); + + TbEntitySubEvent deletedEvent = subsInfo.remove(attrSubscription2); + expectedSubs.remove(attrSubscription2); + assertNotNull(deletedEvent); + assertEquals(expectedSubs, subsInfo.getSubs()); + checkEvent(deletedEvent, expectedSubs, ComponentLifecycleEvent.DELETED); + + assertTrue(subsInfo.isEmpty()); + } + + @Test + void removeAllTest() { + List subs = new ArrayList<>(); + TbEntityLocalSubsInfo subsInfo = createSubsInfo(); + TenantId tenantId = subsInfo.getTenantId(); + EntityId entityId = subsInfo.getEntityId(); + TbAttributeSubscription attrSubscription1 = TbAttributeSubscription.builder() + .sessionId("session1") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key1", 1L, "key2", 2L)) + .build(); + + TbAttributeSubscription attrSubscription2 = TbAttributeSubscription.builder() + .sessionId("session2") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key3", 3L, "key4", 4L)) + .build(); + + subs.add(attrSubscription1); + subs.add(attrSubscription2); + + subsInfo.add(attrSubscription1); + subsInfo.add(attrSubscription2); + + assertFalse(subsInfo.isEmpty()); + + TbEntitySubEvent deletedEvent = subsInfo.removeAll(subs); + assertNotNull(deletedEvent); + checkEvent(deletedEvent, subs, ComponentLifecycleEvent.DELETED); + + assertTrue(subsInfo.isEmpty()); + } + + private TbEntityLocalSubsInfo createSubsInfo() { + return new TbEntityLocalSubsInfo(new TenantId(UUID.randomUUID()), new DeviceId(UUID.randomUUID())); + } + + private void checkEvent(TbEntitySubEvent event, Collection expectedSubs, ComponentLifecycleEvent expectedType) { + assertEquals(expectedType, event.getType()); + TbSubscriptionsInfo info = event.getInfo(); + if (event.getType() == ComponentLifecycleEvent.DELETED) { + assertNull(info); + return; + } + assertNotNull(info); + assertFalse(info.notifications); + assertFalse(info.alarms); + assertFalse(info.attrAllKeys); + assertFalse(info.tsAllKeys); + assertNull(info.tsKeys); + assertEquals(getAttrKeys(expectedSubs), info.attrKeys); + } + + private Set getAttrKeys(Collection attributeSubscriptions) { + return attributeSubscriptions.stream().map(s -> s.getKeyStates().keySet()).flatMap(Collection::stream).collect(Collectors.toSet()); + } +} From 8d6768009d0435a960d42ad8e6108ef726bf97e3 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 2 Oct 2024 13:12:53 +0300 Subject: [PATCH 008/110] lwm2m: update version: leshan = M15, californium = 3.12.1 --- .../lwm2m/attributes/LwM2mAttributesTest.java | 4 +- .../lwm2m/client/TbLwm2mObjectEnabler.java | 75 +++++++++---------- ...ntegrationDiscoverWriteAttributesTest.java | 44 ++++++----- ...LwM2mCredentialsSecurityInfoValidator.java | 2 +- .../device/DeviceCredentialsServiceImpl.java | 2 +- .../validator/DeviceProfileDataValidator.java | 2 +- pom.xml | 4 +- 7 files changed, 67 insertions(+), 66 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/LwM2mAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/LwM2mAttributesTest.java index 73f8346566..ad23ed9f0c 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/LwM2mAttributesTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/attributes/LwM2mAttributesTest.java @@ -49,13 +49,13 @@ public class LwM2mAttributesTest { @ParameterizedTest(name = "Tests {index} : {0}") @MethodSource("doesntSupportAttributesWithoutValue") public void check_attribute_can_not_be_created_without_value(LwM2mAttributeModel model) { - assertThrows(UnsupportedOperationException.class, () -> LwM2mAttributes.create(model)); + assertThrows(IllegalArgumentException.class, () -> LwM2mAttributes.create(model)); } @ParameterizedTest(name = "Tests {index} : {0}") @MethodSource("doesntSupportAttributesWithValueNull") public void check_attribute_can_not_be_created_with_null(LwM2mAttributeModel model) { - assertThrows(NullPointerException.class, () -> LwM2mAttributes.create(model, null)); + assertThrows(IllegalArgumentException.class, () -> LwM2mAttributes.create(model, null)); } private static Stream supportNullAttributes() throws InvalidAttributeException { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java index f548cc30e2..a7bdae3321 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java @@ -69,7 +69,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -88,6 +87,7 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab private LinkFormatHelper tbLinkFormatHelper; protected Map lwM2mAttributes; + public TbLwm2mObjectEnabler(int id, ObjectModel objectModel, Map instances, LwM2mInstanceEnablerFactory instanceFactory, ContentFormat defaultContentFormat) { super(id, objectModel); @@ -586,10 +586,10 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab * - Less Than lt (def = -- ) Float Resource Numerical&Readable Resource * - Step st (def = -- ) Float Resource Numerical&Readable Resource */ - public WriteAttributesResponse doWriteAttributes(LwM2mServer server, WriteAttributesRequest request) { + public WriteAttributesResponse doWriteAttributes(LwM2mServer server, WriteAttributesRequest request) { LwM2mPath lwM2mPath = request.getPath(); LwM2mAttributeSet attributeSet = lwM2mAttributes.get(lwM2mPath); - Map > attributes = new HashMap<>(); + Map> attributes = new HashMap<>(); for (LwM2mAttribute attr : request.getAttributes().getLwM2mAttributes()) { if (attr.getName().equals("pmax") || attr.getName().equals("pmin")) { @@ -606,12 +606,12 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab } } } - if (attributes.size()>0){ + if (attributes.size() > 0) { if (attributeSet == null) { attributeSet = new LwM2mAttributeSet(attributes.values()); } else { Iterable> lwM2mAttributeIterable = attributeSet.getLwM2mAttributes(); - Map > attributesOld = new HashMap<>(); + Map> attributesOld = new HashMap<>(); for (LwM2mAttribute attr : lwM2mAttributeIterable) { attributesOld.put(attr.getName(), attr); } @@ -643,7 +643,7 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab LwM2mPath path = request.getPath(); if (path.isObject()) { - LwM2mLink[] ObjectLinks = linkUpdateAttributes(this.tbLinkFormatHelper.getObjectDescription(this, null), server); + LwM2mLink[] ObjectLinks = linkAddUpdateAttributes(this.tbLinkFormatHelper.getObjectDescription(server, this, null), server); return DiscoverResponse.success(ObjectLinks); } else if (path.isObjectInstance()) { @@ -651,7 +651,7 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab if (!getAvailableInstanceIds().contains(path.getObjectInstanceId())) return DiscoverResponse.notFound(); - LwM2mLink[] instanceLink = linkUpdateAttributes(this.tbLinkFormatHelper.getInstanceDescription(this, path.getObjectInstanceId(), null), server); + LwM2mLink[] instanceLink = linkAddUpdateAttributes(this.tbLinkFormatHelper.getInstanceDescription(server, this, path.getObjectInstanceId(), null), server); return DiscoverResponse.success(instanceLink); } else if (path.isResource()) { @@ -666,46 +666,43 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab if (!getAvailableResourceIds(path.getObjectInstanceId()).contains(path.getResourceId())) return DiscoverResponse.notFound(); - LwM2mLink resourceLink = linkAddAttribute( - this.tbLinkFormatHelper.getResourceDescription(this, path.getObjectInstanceId(), path.getResourceId(), null), - server); - return DiscoverResponse.success(new LwM2mLink[] { resourceLink }); + LwM2mLink[] resourceLink = linkAddUpdateAttributes(this.tbLinkFormatHelper.getResourceDescription(server, + this, path.getObjectInstanceId(), path.getResourceId(), null), server); + return DiscoverResponse.success(resourceLink); } return DiscoverResponse.badRequest(null); } - private LwM2mLink[] linkUpdateAttributes(LwM2mLink[] links, LwM2mServer server) { - return Arrays.stream(links) - .map(link -> linkAddAttribute(link, server)) - .toArray(LwM2mLink[]::new); - } - - private LwM2mLink linkAddAttribute(LwM2mLink link, LwM2mServer server) { + private LwM2mLink[] linkAddUpdateAttributes(LwM2mLink[] links, LwM2mServer server) { + ArrayList resourceLinkList = new ArrayList<>(); + for (LwM2mLink link : links) { - LwM2mAttributeSet lwM2mAttributeSetDop = null; - if (this.lwM2mAttributes.get(link.getPath())!= null){ - lwM2mAttributeSetDop = this.lwM2mAttributes.get(link.getPath()); - } - LwM2mAttribute resourceAttributeDim = getResourceAttributes (server, link.getPath()); + LwM2mAttributeSet lwM2mAttributeSetDop = null; + if (this.lwM2mAttributes.get(link.getPath()) != null) { + lwM2mAttributeSetDop = this.lwM2mAttributes.get(link.getPath()); + } + LwM2mAttribute resourceAttributeDim = getResourceAttributes(server, link.getPath()); - Map > attributes = new HashMap<>(); - if (link.getAttributes() != null) { - for (LwM2mAttribute attr : link.getAttributes().getLwM2mAttributes()) { - attributes.put(attr.getName(), attr); + Map> attributes = new HashMap<>(); + if (link.getAttributes() != null) { + for (LwM2mAttribute attr : link.getAttributes().getLwM2mAttributes()) { + attributes.put(attr.getName(), attr); + } } - } - if (lwM2mAttributeSetDop != null) { - for (LwM2mAttribute attr : lwM2mAttributeSetDop.getLwM2mAttributes()) { - attributes.put(attr.getName(), attr); + if (lwM2mAttributeSetDop != null) { + for (LwM2mAttribute attr : lwM2mAttributeSetDop.getLwM2mAttributes()) { + attributes.put(attr.getName(), attr); + } } + if (resourceAttributeDim != null) { + attributes.put(resourceAttributeDim.getName(), resourceAttributeDim); + } + resourceLinkList.add(new LwM2mLink(link.getRootPath(), link.getPath(), attributes.values())); } - if (resourceAttributeDim != null) { - attributes.put(resourceAttributeDim.getName(), resourceAttributeDim); - } - return new LwM2mLink(link.getRootPath(), link.getPath(), attributes.values()); + return resourceLinkList.toArray(LwM2mLink[]::new); } - protected LwM2mAttribute getResourceAttributes (LwM2mServer server, LwM2mPath path) { + protected LwM2mAttribute getResourceAttributes(LwM2mServer server, LwM2mPath path) { ResourceModel resourceModel = getObjectModel().resources.get(path.getResourceId()); if (path.isResource() && resourceModel.multiple) { return getResourceAttributeDim(path, server); @@ -717,13 +714,13 @@ public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyab LwM2mInstanceEnabler instance = instances.get(path.getObjectInstanceId()); try { ReadResponse readResponse = instance.read(server, path.getResourceId()); - if (readResponse.getCode().getCode()==205 && readResponse.getContent() instanceof LwM2mMultipleResource) { - long valueDim = ((LwM2mMultipleResource)readResponse.getContent()).getInstances().size(); + if (readResponse.getCode().getCode() == 205 && readResponse.getContent() instanceof LwM2mMultipleResource) { + long valueDim = ((LwM2mMultipleResource) readResponse.getContent()).getInstances().size(); return LwM2mAttributes.create(LwM2mAttributes.DIMENSION, valueDim); } else { return null; } - } catch (Exception e ){ + } catch (Exception e) { return null; } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java index d7807ee24a..c44048849e 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java @@ -49,6 +49,7 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL String actual = rpcActualResult.get("error").asText(); assertTrue(actual.equals(expected)); } + /** * WriteAttributes {"id":"/3_1.2/0/6","attributes":{"pmax":100, "pmin":10}} * if not implemented: @@ -82,7 +83,7 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL @Test public void testWriteAttributesResourceServerUriWithParametersById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectInstanceIdVer_1; + String expectedPath = objectInstanceIdVer_1; String actualResult = sendRPCReadById(expectedPath); String expectedValue = "{\"uri\":\"coaps://localhost:5690\"}"; actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); @@ -108,13 +109,13 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL * "ver" only for objectId */ @Test - public void testReadDIM_3_0_6_Only_R () throws Exception { + public void testReadDIM_3_0_6_Only_R() throws Exception { String path = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6; String actualResult = sendDiscover(path); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String expected = ";dim=3"; - assertTrue(rpcActualResult.get("value").asText().equals(expected)); + assertTrue(rpcActualResult.get("value").asText().contains(expected)); } @@ -126,7 +127,7 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL * "ver" only for objectId */ @Test - public void testReadVer () throws Exception { + public void testReadVer() throws Exception { String path = objectIdVer_3; String actualResult = sendDiscover(path); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -149,7 +150,7 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); - // result changed + // result changed actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); @@ -175,7 +176,7 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL * <3/0/6>;dim=8,<3/0/7>;gt=50;lt=42.2;st=0.5,<3/0/8>;... */ @Test - public void testWriteAttributesPeriodLtGt () throws Exception { + public void testWriteAttributesPeriodLtGt() throws Exception { String expectedPath = objectInstanceIdVer_3; String expectedValue = "{\"pmax\":60}"; String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); @@ -187,30 +188,33 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_7; - expectedValue ="{\"gt\":50, \"lt\":42.2, \"st\":0.5}"; + expectedValue = "{\"gt\":50.0, \"lt\":42.2, \"st\":0.5}"; actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); - // ObjectId + // ObjectId expectedPath = objectIdVer_3; actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - // String expected = ";ver=1.2,;pmax=60,,,,,;dim=3,;st=0.5;lt=42.2;gt=50.0,,,,;dim=1,,,,,,,,,"; + String actualValue = rpcActualResult.get("value").asText(); String expected = ";ver=1.2,;pmax=65"; - assertTrue(rpcActualResult.get("value").asText().contains(expected)); - expected = ";dim=3,;st=0.5;lt=42.2;gt=50.0"; - assertTrue(rpcActualResult.get("value").asText().contains(expected)); - // ObjectInstanceId + assertTrue(actualValue.contains(expected)); + expected = ";dim=3"; + assertTrue(actualValue.contains(expected)); + expected = ";st=0.5;lt=42.2;gt=50"; + assertTrue(actualValue.contains(expected)); + // ObjectInstanceId expectedPath = objectInstanceIdVer_3; actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + actualValue = rpcActualResult.get("value").asText(); expected = ";pmax=65"; - assertTrue(rpcActualResult.get("value").asText().contains(expected)); - expected = ";dim=3,;st=0.5;lt=42.2;gt=50.0"; - assertTrue(rpcActualResult.get("value").asText().contains(expected)); - // ResourceId + assertTrue(actualValue.contains(expected)); + expected = ";dim=3,;st=0.5;lt=42.2;gt=50"; + assertTrue(actualValue.contains(expected)); + // ResourceId expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6; actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -221,10 +225,10 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - expected = ";st=0.5;lt=42.2;gt=50.0"; + expected = ";st=0.5;lt=42.2;gt=50"; assertTrue(rpcActualResult.get("value").asText().contains(expected)); - // ResourceInstanceId - expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6+ "/1"; + // ResourceInstanceId + expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6 + "/1"; actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.INTERNAL_SERVER_ERROR.getName(), rpcActualResult.get("result").asText()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java index a44c6adb6e..66b072fdaa 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/LwM2mCredentialsSecurityInfoValidator.java @@ -18,7 +18,7 @@ package org.thingsboard.server.transport.lwm2m.secure; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.DecoderException; -import org.eclipse.leshan.core.util.SecurityUtil; +import org.eclipse.leshan.core.security.util.SecurityUtil; import org.eclipse.leshan.server.security.SecurityInfo; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java index e958313d67..cce114f361 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceCredentialsServiceImpl.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.SecurityMode; -import org.eclipse.leshan.core.util.SecurityUtil; +import org.eclipse.leshan.core.security.util.SecurityUtil; import org.hibernate.exception.ConstraintViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java index 2d2c7bf21c..bfff00f6b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.service.validator; import com.google.protobuf.Descriptors; import com.google.protobuf.DynamicMessage; import lombok.extern.slf4j.Slf4j; -import org.eclipse.leshan.core.util.SecurityUtil; +import org.eclipse.leshan.core.security.util.SecurityUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; diff --git a/pom.xml b/pom.xml index 5e4cc50c7c..ea50c0fddb 100755 --- a/pom.xml +++ b/pom.xml @@ -74,8 +74,8 @@ 1.7.0 4.4.0 2.2.14 - 3.11.0 - 2.0.0-M14 + 3.12.1 + 2.0.0-M15 2.10.1 2.3.32 2.0.1 From 873ad00792730a29f87325b74c86dd51cc3dc5b9 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 2 Oct 2024 16:30:17 +0200 Subject: [PATCH 009/110] minor refactoring and test improvements due to comments --- .../subscription/TbEntityLocalSubsInfo.java | 6 ++-- .../TbEntityLocalSubsInfoTest.java | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java index 12786c65ee..ee20843538 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfo.java @@ -135,7 +135,7 @@ public class TbEntityLocalSubsInfo { } TbSubscriptionType type = sub.getType(); TbSubscriptionsInfo newState = state.copy(); - updateNewState(newState, type); + clearState(newState, type); return updateState(Set.of(type), newState); } @@ -155,14 +155,14 @@ public class TbEntityLocalSubsInfo { continue; } - updateNewState(newState, type); + clearState(newState, type); changedTypes.add(type); } return updateState(changedTypes, newState); } - private void updateNewState(TbSubscriptionsInfo state, TbSubscriptionType type) { + private void clearState(TbSubscriptionsInfo state, TbSubscriptionType type) { switch (type) { case NOTIFICATIONS: case NOTIFICATIONS_COUNT: diff --git a/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java b/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java index 88f29308ab..e9b95ec832 100644 --- a/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java +++ b/application/src/test/java/org/thingsboard/server/service/subscription/TbEntityLocalSubsInfoTest.java @@ -21,7 +21,6 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -36,10 +35,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -class TbEntityLocalSubsInfoTest { +public class TbEntityLocalSubsInfoTest { @Test - void addTest() { + public void addTest() { Set expectedSubs = new HashSet<>(); TbEntityLocalSubsInfo subsInfo = createSubsInfo(); TenantId tenantId = subsInfo.getTenantId(); @@ -74,7 +73,7 @@ class TbEntityLocalSubsInfoTest { } @Test - void removeTest() { + public void removeTest() { Set expectedSubs = new HashSet<>(); TbEntityLocalSubsInfo subsInfo = createSubsInfo(); TenantId tenantId = subsInfo.getTenantId(); @@ -117,8 +116,7 @@ class TbEntityLocalSubsInfoTest { } @Test - void removeAllTest() { - List subs = new ArrayList<>(); + public void removeAllTest() { TbEntityLocalSubsInfo subsInfo = createSubsInfo(); TenantId tenantId = subsInfo.getTenantId(); EntityId entityId = subsInfo.getEntityId(); @@ -136,17 +134,28 @@ class TbEntityLocalSubsInfoTest { .keyStates(Map.of("key3", 3L, "key4", 4L)) .build(); - subs.add(attrSubscription1); - subs.add(attrSubscription2); + TbAttributeSubscription attrSubscription3 = TbAttributeSubscription.builder() + .sessionId("session3") + .tenantId(tenantId) + .entityId(entityId) + .keyStates(Map.of("key5", 5L, "key6", 6L)) + .build(); subsInfo.add(attrSubscription1); subsInfo.add(attrSubscription2); + subsInfo.add(attrSubscription3); + + assertFalse(subsInfo.isEmpty()); + + TbEntitySubEvent updatedEvent = subsInfo.removeAll(List.of(attrSubscription1, attrSubscription2)); + assertNotNull(updatedEvent); + checkEvent(updatedEvent, Set.of(attrSubscription3), ComponentLifecycleEvent.UPDATED); assertFalse(subsInfo.isEmpty()); - TbEntitySubEvent deletedEvent = subsInfo.removeAll(subs); + TbEntitySubEvent deletedEvent = subsInfo.removeAll(List.of(attrSubscription3)); assertNotNull(deletedEvent); - checkEvent(deletedEvent, subs, ComponentLifecycleEvent.DELETED); + checkEvent(deletedEvent, null, ComponentLifecycleEvent.DELETED); assertTrue(subsInfo.isEmpty()); } @@ -155,7 +164,7 @@ class TbEntityLocalSubsInfoTest { return new TbEntityLocalSubsInfo(new TenantId(UUID.randomUUID()), new DeviceId(UUID.randomUUID())); } - private void checkEvent(TbEntitySubEvent event, Collection expectedSubs, ComponentLifecycleEvent expectedType) { + private void checkEvent(TbEntitySubEvent event, Set expectedSubs, ComponentLifecycleEvent expectedType) { assertEquals(expectedType, event.getType()); TbSubscriptionsInfo info = event.getInfo(); if (event.getType() == ComponentLifecycleEvent.DELETED) { @@ -171,7 +180,7 @@ class TbEntityLocalSubsInfoTest { assertEquals(getAttrKeys(expectedSubs), info.attrKeys); } - private Set getAttrKeys(Collection attributeSubscriptions) { + private Set getAttrKeys(Set attributeSubscriptions) { return attributeSubscriptions.stream().map(s -> s.getKeyStates().keySet()).flatMap(Collection::stream).collect(Collectors.toSet()); } } From b81d2ab0c6e73413d0ff1e9691fefc78acd372ef Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 3 Oct 2024 09:27:00 +0300 Subject: [PATCH 010/110] lwm2m: fix bug test sendCollected --- .../lwm2m/client/LwM2MTestClient.java | 6 +- .../lwm2m/client/LwM2mTemperatureSensor.java | 46 ++++---- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 33 ++++-- ...wm2mIntegrationReadCollectedValueTest.java | 103 ++++++++++++++++++ .../rpc/sql/RpcLwm2mIntegrationReadTest.java | 70 +----------- 5 files changed, 158 insertions(+), 100 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 655edc6db6..796f3e09c5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -137,6 +137,8 @@ public class LwM2MTestClient { private Map clientDtlsCid; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mClientContext clientContext; + private LwM2mTemperatureSensor lwM2mTemperatureSensor12; + public void init(Security security, Security securityBs, int port, boolean isRpc, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mClientContext clientContext, boolean isWriteAttribute, Integer cIdLength, boolean queueMode, @@ -189,7 +191,7 @@ public class LwM2MTestClient { locationParams.getPos(); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0); - LwM2mTemperatureSensor lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); + lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12); List enablers = initializer.createAll(); @@ -315,7 +317,6 @@ public class LwM2MTestClient { clientDtlsCid = new HashMap<>(); clientStates.add(ON_INIT); leshanClient = builder.build(); - lwM2mTemperatureSensor12.setLeshanClient(leshanClient); LwM2mClientObserver observer = new LwM2mClientObserver() { @Override @@ -452,6 +453,7 @@ public class LwM2MTestClient { if (isStartLw) { this.awaitClientAfterStartConnectLw(); } + lwM2mTemperatureSensor12.setLeshanClient(leshanClient); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java index 4f594ed4c0..35d36749c9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java @@ -50,14 +50,17 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr private double maxMeasuredValue = currentTemp; private LeshanClient leshanClient; - private int cntRead_5700; private int cntIdentitySystem; protected static final Random RANDOM = new Random(); private static final List supportedResources = Arrays.asList(5601, 5602, 5700, 5701); - public LwM2mTemperatureSensor() { + private LwM2mServer registeredServer; + private ManualDataSender sender; + + private int resourceIdForSendCollected = 5700; + public LwM2mTemperatureSensor() { } public LwM2mTemperatureSensor(ScheduledExecutorService executorService, Integer id) { @@ -72,26 +75,33 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr @Override public synchronized ReadResponse read(LwM2mServer identity, int resourceId) { - log.info("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); + log.trace("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); + if (this.registeredServer == null) { + try { + Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0 = Instant.now().toEpochMilli(); + this.registeredServer = this.leshanClient.getRegisteredServers().values().iterator().next(); + this.sender = (ManualDataSender) this.leshanClient.getSendService().getDataSender(ManualDataSender.DEFAULT_NAME); + this.sender.collectData(Arrays.asList(getPathForCollectedValue(resourceIdForSendCollected))); + } catch (Exception e) { + log.error("[{}] Sender for SendCollected", e.toString()); + e.printStackTrace(); + } + } switch (resourceId) { case 5601: return ReadResponse.success(resourceId, getTwoDigitValue(minMeasuredValue)); case 5602: return ReadResponse.success(resourceId, getTwoDigitValue(maxMeasuredValue)); case 5700: - if (identity == LwM2mServer.SYSTEM) { // return value for ForCollectedValue + if (identity == LwM2mServer.SYSTEM) { + double val5700 = cntIdentitySystem == 0 ? RESOURCE_ID_3303_12_5700_VALUE_0 : RESOURCE_ID_3303_12_5700_VALUE_1; cntIdentitySystem++; - return ReadResponse.success(resourceId, cntIdentitySystem == 1 ? - RESOURCE_ID_3303_12_5700_VALUE_0 : RESOURCE_ID_3303_12_5700_VALUE_1); - } - cntRead_5700++; - if (cntRead_5700 == 1) { // read value after start - return ReadResponse.success(resourceId, getTwoDigitValue(currentTemp)); + return ReadResponse.success(resourceId, val5700); } else { - if (this.getId() == 12 && this.leshanClient != null) { + if (cntIdentitySystem == 1 && this.getId() == 12 && this.leshanClient != null) { sendCollected(); } - return ReadResponse.success(resourceId, getTwoDigitValue(currentTemp)); + return super.read(identity, resourceId); } case 5701: return ReadResponse.success(resourceId, UNIT_CELSIUS); @@ -163,14 +173,10 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr private void sendCollected() { try { - int resourceId = 5700; - LwM2mServer registeredServer = this.leshanClient.getRegisteredServers().values().iterator().next(); - ManualDataSender sender = this.leshanClient.getSendService().getDataSender(ManualDataSender.DEFAULT_NAME, - ManualDataSender.class); - sender.collectData(Arrays.asList(getPathForCollectedValue(resourceId))); - Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0 = Instant.now().toEpochMilli(); - Thread.sleep(RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS); - sender.collectData(Arrays.asList(getPathForCollectedValue(resourceId))); + if ((Instant.now().toEpochMilli() - Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0) < RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS) { + Thread.sleep(RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS); + } + sender.collectData(Arrays.asList(getPathForCollectedValue(resourceIdForSendCollected))); Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1 = Instant.now().toEpochMilli(); sender.sendCollectedData(registeredServer, ContentFormat.SENML_JSON, 1000, false); } catch (InterruptedException e) { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index d0b86fdab0..bd3ca0642a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -71,7 +71,7 @@ import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fr public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { protected final LinkParser linkParser = new DefaultLwM2mLinkParser(); - protected String OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC; + protected String CONFIG_PROFILE_WITH_PARAMS_RPC; public Set expectedObjects; public Set expectedObjectIdVers; public Set expectedInstances; @@ -116,10 +116,10 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationWriteCborTest")){ supportFormatOnly_SenMLJSON_SenMLCBOR = true; } - initRpc(); + initRpc(false); } - private void initRpc () throws Exception { + protected void initRpc(boolean isCollected) throws Exception { String endpoint = DEVICE_ENDPOINT_RPC_PREF + endpointSequence.incrementAndGet(); createNewClient(SECURITY_NO_SEC, null, true, endpoint); expectedObjects = ConcurrentHashMap.newKeySet(); @@ -157,15 +157,14 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg id_3_0_9 = fromVersionedIdToObjectId(idVer_3_0_9); idVer_19_0_0 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0; - OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC = + String ATTRIBUTES_TELEMETRY_WITH_PARAMS_RPC_WITH_OBSERVE = " {\n" + " \"keyName\": {\n" + " \"" + idVer_3_0_9 + "\": \"" + RESOURCE_ID_NAME_3_9 + "\",\n" + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_14 + "\": \"" + RESOURCE_ID_NAME_3_14 + "\",\n" + " \"" + idVer_19_0_0 + "\": \"" + RESOURCE_ID_NAME_19_0_0 + "\",\n" + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\": \"" + RESOURCE_ID_NAME_19_1_0 + "\",\n" + - " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2 + "\": \"" + RESOURCE_ID_NAME_19_0_2 + "\",\n" + - " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\": \"" + RESOURCE_ID_NAME_3303_12_5700 + "\"\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2 + "\": \"" + RESOURCE_ID_NAME_19_0_2 + "\"\n" + " },\n" + " \"observe\": [\n" + " \"" + idVer_3_0_9 + "\",\n" + @@ -180,13 +179,29 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg " \"telemetry\": [\n" + " \"" + idVer_3_0_9 + "\",\n" + " \"" + idVer_19_0_0 + "\",\n" + - " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\",\n" + - " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\"\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\"\n" + " ],\n" + " \"attributeLwm2m\": {}\n" + " }"; - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC, getBootstrapServerCredentialsNoSec(NONE)); + String TELEMETRY_WITH_PARAMS_RPC_WITHOUT_OBSERVE = + " {\n" + + " \"keyName\": {\n" + + " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\": \"" + RESOURCE_ID_NAME_3303_12_5700 + "\"\n" + + " },\n" + + " \"observe\": [\n" + + " ],\n" + + " \"attribute\": [\n" + + " ],\n" + + " \"telemetry\": [\n" + + " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }" ; + + CONFIG_PROFILE_WITH_PARAMS_RPC = isCollected ? TELEMETRY_WITH_PARAMS_RPC_WITHOUT_OBSERVE : ATTRIBUTES_TELEMETRY_WITH_PARAMS_RPC_WITH_OBSERVE; + + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(CONFIG_PROFILE_WITH_PARAMS_RPC, getBootstrapServerCredentialsNoSec(NONE)); createDeviceProfile(transportConfiguration); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(endpoint)); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java new file mode 100644 index 0000000000..d76f2f5400 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java @@ -0,0 +1,103 @@ +/** + * 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.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; +import java.util.concurrent.atomic.AtomicReference; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3303_12_5700; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS; + +@Slf4j +public class RpcLwm2mIntegrationReadCollectedValueTest extends AbstractRpcLwM2MIntegrationTest { + + @Before + public void startInitRPC() throws Exception { + initRpc(true); + } + + /** + * Read {"id":"/3303/12/5700"} + * Trigger a Send operation from the client with multiple values for the same resource as a payload + * acked "[{"bn":"/3303/12/5700","bt":1724".. 116 bytes] + * 2 values for the resource /3303/12/5700 should be stored with: + * - timestamps1 = Instance.now() + RESOURCE_ID_VALUE_3303_12_5700_1 + * - timestamps2 = (timestamps1 + 3 sec) + RESOURCE_ID_VALUE_3303_12_5700_2 + * @throws Exception + */ + @Test + public void testReadSingleResource_sendFromClient_CollectedValue() throws Exception { + // init test + int cntValues = 2; + int resourceId = 5700; + String expectedIdVer = objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + resourceId; + sendRPCById(expectedIdVer); + + // verify time start/end send CollectedValue; + await().atMost(40, SECONDS).until(() -> RESOURCE_ID_3303_12_5700_TS_0 > 0 + && RESOURCE_ID_3303_12_5700_TS_1 > 0); + + // verify result read: verify count value: 1-2: send CollectedValue; + AtomicReference actualValues = new AtomicReference<>(); + await().atMost(40, SECONDS).until(() -> { + actualValues.set(doGetAsync( + "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + + RESOURCE_ID_NAME_3303_12_5700 + + "&startTs=" + (RESOURCE_ID_3303_12_5700_TS_0 - RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS) + + "&endTs=" + (RESOURCE_ID_3303_12_5700_TS_1 + RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS) + + "&interval=0&limit=100&useStrictDataTypes=false", + ObjectNode.class)); + return actualValues.get() != null && actualValues.get().size() > 0 + && actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700).size() >= cntValues && verifyTs(actualValues); + }); + } + + private boolean verifyTs(AtomicReference actualValues) { + String expectedVal_0 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_0); + String expectedVal_1 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_1); + ArrayNode actual = (ArrayNode) actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700); + long actualTS0 = 0; + long actualTS1 = 0; + for (JsonNode tsNode : actual) { + if (tsNode.get("value").asText().equals(expectedVal_0)) { + actualTS0 = tsNode.get("ts").asLong(); + } else if (tsNode.get("value").asText().equals(expectedVal_1)) { + actualTS1 = tsNode.get("ts").asLong(); + } + } + return actualTS0 >= RESOURCE_ID_3303_12_5700_TS_0 + && actualTS1 <= RESOURCE_ID_3303_12_5700_TS_1 + && (actualTS1 - actualTS0) >= RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS; + } + + private String sendRPCById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java index 1ab4893ec4..b8835d427d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -15,23 +15,15 @@ */ package org.thingsboard.server.transport.lwm2m.rpc.sql; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections4.map.HashedMap; import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Before; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; -import java.time.Instant; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.awaitility.Awaitility.await; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -39,24 +31,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_11; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_3; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_1_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3303_12_5700; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_9; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS; @Slf4j public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest { @@ -228,59 +213,6 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest assertTrue(actualValues.contains(expected19_1_0)); } - - /** - * Read {"id":"/3303/12/5700"} - * Trigger a Send operation from the client with multiple values for the same resource as a payload - * acked "[{"bn":"/3303/12/5700","bt":1724".. 116 bytes] - * 2 values for the resource /3303/12/5700 should be stored with: - * - timestamps1 = Instance.now() + RESOURCE_ID_VALUE_3303_12_5700_1 - * - timestamps2 = (timestamps1 + 3 sec) + RESOURCE_ID_VALUE_3303_12_5700_2 - * @throws Exception - */ - @Test - public void testReadSingleResource_sendFromClient_CollectedValue() throws Exception { - // init test - long startTs = Instant.now().toEpochMilli(); - int cntValues = 4; - int resourceId = 5700; - String expectedIdVer = objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + resourceId; - sendRPCById(expectedIdVer); - // verify result read: verify count value: 1-2: send CollectedValue; 3 - response for read; - long endTs = Instant.now().toEpochMilli() + RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS * 4; - String expectedVal_1 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_0); - String expectedVal_2 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_1); - AtomicReference actualValues = new AtomicReference<>(); - await().atMost(40, SECONDS).until(() -> { - actualValues.set(doGetAsync( - "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" - + RESOURCE_ID_NAME_3303_12_5700 - + "&startTs=" + startTs - + "&endTs=" + endTs - + "&interval=0&limit=100&useStrictDataTypes=false", - ObjectNode.class)); - // verify cntValues - return actualValues.get() != null && actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700).size() == cntValues; - }); - // verify ts - ArrayNode actual = (ArrayNode) actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700); - Map keyTsMaps = new HashedMap(); - for (JsonNode tsNode: actual) { - if (tsNode.get("value").asText().equals(expectedVal_1) || tsNode.get("value").asText().equals(expectedVal_2)) { - keyTsMaps.put(tsNode.get("value").asText(), tsNode.get("ts").asLong()); - } - } - assertTrue(keyTsMaps.size() == 2); - long actualTS0 = keyTsMaps.get(expectedVal_1).longValue(); - long actualTS1 = keyTsMaps.get(expectedVal_2).longValue(); - assertTrue(actualTS0 > 0); - assertTrue(actualTS1 > 0); - assertTrue(actualTS1 > actualTS0); - assertTrue((actualTS1 - actualTS0) >= RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS); - assertTrue(actualTS0 <= RESOURCE_ID_3303_12_5700_TS_0); - assertTrue(actualTS1 <= RESOURCE_ID_3303_12_5700_TS_1); - } - /** * ReadComposite {"keys":["batteryLevel", "UtfOffset", "dataDescription"]} */ From 13c49d16899814b4285589f43c56e4dec1f5da96 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 3 Oct 2024 13:14:30 +0300 Subject: [PATCH 011/110] Get rid of "userCredentialsEnabled" in user's additional info --- .../main/data/upgrade/3.8.0/schema_update.sql | 3 ++- .../server/controller/BaseController.java | 19 +++++++++++-------- .../edge/EdgeEventSourcingListener.java | 1 - .../server/controller/AuthControllerTest.java | 2 +- .../thingsboard/server/edge/UserEdgeTest.java | 16 ++++++++-------- .../server/dao/user/UserServiceImpl.java | 12 ++---------- 6 files changed, 24 insertions(+), 29 deletions(-) diff --git a/application/src/main/data/upgrade/3.8.0/schema_update.sql b/application/src/main/data/upgrade/3.8.0/schema_update.sql index 240daa18d5..1084dd374f 100644 --- a/application/src/main/data/upgrade/3.8.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.8.0/schema_update.sql @@ -22,4 +22,5 @@ ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS failed_login_attempts INT; UPDATE user_credentials c SET failed_login_attempts = (SELECT (additional_info::json ->> 'failedLoginAttempts')::int FROM tb_user u WHERE u.id = c.user_id) WHERE failed_login_attempts IS NULL; -UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts')::text WHERE additional_info IS NOT NULL AND additional_info != 'null'; +UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts' - 'userCredentialsEnabled')::text + WHERE additional_info IS NOT NULL AND additional_info != 'null'; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 70565d975b..387b39707b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -38,6 +38,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.common.util.DonAsynchron; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; @@ -193,7 +194,6 @@ import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_DASH import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD; import static org.thingsboard.server.controller.UserController.YOU_DON_T_HAVE_PERMISSION_TO_PERFORM_THIS_OPERATION; import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.user.UserServiceImpl.LAST_LOGIN_TS; @TbCoreComponent public abstract class BaseController { @@ -878,15 +878,18 @@ public abstract class BaseController { } protected void checkUserInfo(User user) throws ThingsboardException { + ObjectNode info; if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) { - checkDashboardInfo(additionalInfo); - - UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); - if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { - additionalInfo.put("userCredentialsEnabled", true); - } - additionalInfo.put(LAST_LOGIN_TS, userCredentials.getLastLoginTs()); + info = additionalInfo; + checkDashboardInfo(info); + } else { + info = JacksonUtil.newObjectNode(); + user.setAdditionalInfo(info); } + + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); + info.put("userCredentialsEnabled", userCredentials.isEnabled()); + info.put("lastLoginTs", userCredentials.getLastLoginTs()); } protected void checkDashboardInfo(JsonNode additionalInfo) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index 907872d503..893bd13171 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -225,7 +225,6 @@ public class EdgeEventSourcingListener { } private void cleanUpUserAdditionalInfo(User user) { - // reset FAILED_LOGIN_ATTEMPTS and LAST_LOGIN_TS - edge is not interested in this information if (user.getAdditionalInfo() instanceof NullNode) { user.setAdditionalInfo(null); } diff --git a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java index 62b8188cfd..73182587fb 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AuthControllerTest.java @@ -81,8 +81,8 @@ public class AuthControllerTest extends AbstractControllerTest { user = getCurrentUser(); assertThat(user.getAuthority()).isEqualTo(Authority.CUSTOMER_USER); assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL); - assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue(); user = getUser(customerUserId); + assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue(); assertThat(user.getAdditionalInfo().get("lastLoginTs").asLong()).isCloseTo(System.currentTimeMillis(), within(10000L)); } diff --git a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java index 1c3b5ccb7a..82d202533f 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -47,7 +47,7 @@ public class UserEdgeTest extends AbstractEdgeTest { @Test public void testCreateUpdateDeleteTenantUser() throws Exception { // create user - edgeImitator.expectMessageAmount(6); + edgeImitator.expectMessageAmount(4); User newTenantAdmin = new User(); newTenantAdmin.setAuthority(Authority.TENANT_ADMIN); newTenantAdmin.setTenantId(tenantId); @@ -55,9 +55,9 @@ public class UserEdgeTest extends AbstractEdgeTest { newTenantAdmin.setFirstName("Boris"); newTenantAdmin.setLastName("Johnson"); User savedTenantAdmin = createUser(newTenantAdmin, "tenant"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 6 messages - x2 user update msg and x4 user credentials update msgs (create + authenticate user) - Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); - Assert.assertEquals(4, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); + Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x1 user update msg and x3 user credentials update msgs (create + authenticate user) + Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); + Assert.assertEquals(3, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); @@ -133,7 +133,7 @@ public class UserEdgeTest extends AbstractEdgeTest { Assert.assertTrue(edgeImitator.waitForMessages()); // create user - edgeImitator.expectMessageAmount(6); + edgeImitator.expectMessageAmount(4); User customerUser = new User(); customerUser.setAuthority(Authority.CUSTOMER_USER); customerUser.setTenantId(tenantId); @@ -142,9 +142,9 @@ public class UserEdgeTest extends AbstractEdgeTest { customerUser.setFirstName("John"); customerUser.setLastName("Edwards"); User savedCustomerUser = createUser(customerUser, "customer"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 6 messages - x2 user update msg and x4 user credentials update msgs (create + authenticate user) - Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); - Assert.assertEquals(4, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); + Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x1 user update msg and x3 user credentials update msgs (create + authenticate user) + Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); + Assert.assertEquals(3, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index c927c3e465..dc758b6b5d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.user; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; @@ -84,8 +83,6 @@ import static org.thingsboard.server.dao.service.Validator.validateString; public class UserServiceImpl extends AbstractCachedEntityService implements UserService { public static final String USER_PASSWORD_HISTORY = "userPasswordHistory"; - public static final String USER_CREDENTIALS_ENABLED = "userCredentialsEnabled"; - public static final String LAST_LOGIN_TS = "lastLoginTs"; private static final int DEFAULT_TOKEN_LENGTH = 30; public static final String INCORRECT_USER_ID = "Incorrect userId "; @@ -430,17 +427,12 @@ public class UserServiceImpl extends AbstractCachedEntityService INCORRECT_USER_ID + id); UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId()); userCredentials.setEnabled(enabled); - saveUserCredentials(tenantId, userCredentials); - - User user = findUserById(tenantId, userId); - user.setAdditionalInfoField(USER_CREDENTIALS_ENABLED, BooleanNode.valueOf(enabled)); - saveUser(tenantId, user); if (enabled) { - resetFailedLoginAttempts(tenantId, userId); + userCredentials.setFailedLoginAttempts(0); } + saveUserCredentials(tenantId, userCredentials); } - @Override public void resetFailedLoginAttempts(TenantId tenantId, UserId userId) { log.trace("Executing resetFailedLoginAttempts [{}]", userId); From 1f8c521ba41fe80fe9a804a687bdf35f4767e076 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Thu, 3 Oct 2024 13:35:43 +0300 Subject: [PATCH 012/110] Update UserEdgeTest --- .../org/thingsboard/server/edge/UserEdgeTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java index 82d202533f..52204f8fde 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -47,7 +47,7 @@ public class UserEdgeTest extends AbstractEdgeTest { @Test public void testCreateUpdateDeleteTenantUser() throws Exception { // create user - edgeImitator.expectMessageAmount(4); + edgeImitator.expectMessageAmount(3); User newTenantAdmin = new User(); newTenantAdmin.setAuthority(Authority.TENANT_ADMIN); newTenantAdmin.setTenantId(tenantId); @@ -55,9 +55,9 @@ public class UserEdgeTest extends AbstractEdgeTest { newTenantAdmin.setFirstName("Boris"); newTenantAdmin.setLastName("Johnson"); User savedTenantAdmin = createUser(newTenantAdmin, "tenant"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x1 user update msg and x3 user credentials update msgs (create + authenticate user) + Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user) Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); - Assert.assertEquals(3, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); + Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); @@ -133,7 +133,7 @@ public class UserEdgeTest extends AbstractEdgeTest { Assert.assertTrue(edgeImitator.waitForMessages()); // create user - edgeImitator.expectMessageAmount(4); + edgeImitator.expectMessageAmount(3); User customerUser = new User(); customerUser.setAuthority(Authority.CUSTOMER_USER); customerUser.setTenantId(tenantId); @@ -142,9 +142,9 @@ public class UserEdgeTest extends AbstractEdgeTest { customerUser.setFirstName("John"); customerUser.setLastName("Edwards"); User savedCustomerUser = createUser(customerUser, "customer"); - Assert.assertTrue(edgeImitator.waitForMessages()); // wait 4 messages - x1 user update msg and x3 user credentials update msgs (create + authenticate user) + Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user) Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); - Assert.assertEquals(3, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); + Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); Optional userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class); Assert.assertTrue(userUpdateMsgOpt.isPresent()); UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get(); From f8ac65754f352dcac558996db8daa6ac27cc7b05 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 4 Oct 2024 12:06:54 +0300 Subject: [PATCH 013/110] Fix UserControllerTest --- .../org/thingsboard/server/controller/UserControllerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java index 17085f3ca9..c42ecc4545 100644 --- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java @@ -113,6 +113,7 @@ public class UserControllerTest extends AbstractControllerTest { Assert.assertEquals(email, savedUser.getEmail()); User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class); + foundUser.setAdditionalInfo(savedUser.getAdditionalInfo()); Assert.assertEquals(foundUser, savedUser); testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser, @@ -265,6 +266,7 @@ public class UserControllerTest extends AbstractControllerTest { User savedUser = doPost("/api/user", user, User.class); User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class); Assert.assertNotNull(foundUser); + foundUser.setAdditionalInfo(savedUser.getAdditionalInfo()); Assert.assertEquals(savedUser, foundUser); } From 4b53e81dafcfb554e883c7c635a46b255c6bfaae Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 3 Oct 2024 09:27:00 +0300 Subject: [PATCH 014/110] lwm2m: fix bug test sendCollected --- .../lwm2m/client/LwM2MTestClient.java | 6 +- .../lwm2m/client/LwM2mTemperatureSensor.java | 46 ++++---- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 33 ++++-- ...wm2mIntegrationReadCollectedValueTest.java | 103 ++++++++++++++++++ .../rpc/sql/RpcLwm2mIntegrationReadTest.java | 70 +----------- 5 files changed, 158 insertions(+), 100 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 655edc6db6..796f3e09c5 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -137,6 +137,8 @@ public class LwM2MTestClient { private Map clientDtlsCid; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mClientContext clientContext; + private LwM2mTemperatureSensor lwM2mTemperatureSensor12; + public void init(Security security, Security securityBs, int port, boolean isRpc, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mClientContext clientContext, boolean isWriteAttribute, Integer cIdLength, boolean queueMode, @@ -189,7 +191,7 @@ public class LwM2MTestClient { locationParams.getPos(); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0); - LwM2mTemperatureSensor lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); + lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12); List enablers = initializer.createAll(); @@ -315,7 +317,6 @@ public class LwM2MTestClient { clientDtlsCid = new HashMap<>(); clientStates.add(ON_INIT); leshanClient = builder.build(); - lwM2mTemperatureSensor12.setLeshanClient(leshanClient); LwM2mClientObserver observer = new LwM2mClientObserver() { @Override @@ -452,6 +453,7 @@ public class LwM2MTestClient { if (isStartLw) { this.awaitClientAfterStartConnectLw(); } + lwM2mTemperatureSensor12.setLeshanClient(leshanClient); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java index 4f594ed4c0..35d36749c9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java @@ -50,14 +50,17 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr private double maxMeasuredValue = currentTemp; private LeshanClient leshanClient; - private int cntRead_5700; private int cntIdentitySystem; protected static final Random RANDOM = new Random(); private static final List supportedResources = Arrays.asList(5601, 5602, 5700, 5701); - public LwM2mTemperatureSensor() { + private LwM2mServer registeredServer; + private ManualDataSender sender; + + private int resourceIdForSendCollected = 5700; + public LwM2mTemperatureSensor() { } public LwM2mTemperatureSensor(ScheduledExecutorService executorService, Integer id) { @@ -72,26 +75,33 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr @Override public synchronized ReadResponse read(LwM2mServer identity, int resourceId) { - log.info("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); + log.trace("Read on Temperature resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId); + if (this.registeredServer == null) { + try { + Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0 = Instant.now().toEpochMilli(); + this.registeredServer = this.leshanClient.getRegisteredServers().values().iterator().next(); + this.sender = (ManualDataSender) this.leshanClient.getSendService().getDataSender(ManualDataSender.DEFAULT_NAME); + this.sender.collectData(Arrays.asList(getPathForCollectedValue(resourceIdForSendCollected))); + } catch (Exception e) { + log.error("[{}] Sender for SendCollected", e.toString()); + e.printStackTrace(); + } + } switch (resourceId) { case 5601: return ReadResponse.success(resourceId, getTwoDigitValue(minMeasuredValue)); case 5602: return ReadResponse.success(resourceId, getTwoDigitValue(maxMeasuredValue)); case 5700: - if (identity == LwM2mServer.SYSTEM) { // return value for ForCollectedValue + if (identity == LwM2mServer.SYSTEM) { + double val5700 = cntIdentitySystem == 0 ? RESOURCE_ID_3303_12_5700_VALUE_0 : RESOURCE_ID_3303_12_5700_VALUE_1; cntIdentitySystem++; - return ReadResponse.success(resourceId, cntIdentitySystem == 1 ? - RESOURCE_ID_3303_12_5700_VALUE_0 : RESOURCE_ID_3303_12_5700_VALUE_1); - } - cntRead_5700++; - if (cntRead_5700 == 1) { // read value after start - return ReadResponse.success(resourceId, getTwoDigitValue(currentTemp)); + return ReadResponse.success(resourceId, val5700); } else { - if (this.getId() == 12 && this.leshanClient != null) { + if (cntIdentitySystem == 1 && this.getId() == 12 && this.leshanClient != null) { sendCollected(); } - return ReadResponse.success(resourceId, getTwoDigitValue(currentTemp)); + return super.read(identity, resourceId); } case 5701: return ReadResponse.success(resourceId, UNIT_CELSIUS); @@ -163,14 +173,10 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr private void sendCollected() { try { - int resourceId = 5700; - LwM2mServer registeredServer = this.leshanClient.getRegisteredServers().values().iterator().next(); - ManualDataSender sender = this.leshanClient.getSendService().getDataSender(ManualDataSender.DEFAULT_NAME, - ManualDataSender.class); - sender.collectData(Arrays.asList(getPathForCollectedValue(resourceId))); - Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0 = Instant.now().toEpochMilli(); - Thread.sleep(RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS); - sender.collectData(Arrays.asList(getPathForCollectedValue(resourceId))); + if ((Instant.now().toEpochMilli() - Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0) < RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS) { + Thread.sleep(RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS); + } + sender.collectData(Arrays.asList(getPathForCollectedValue(resourceIdForSendCollected))); Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1 = Instant.now().toEpochMilli(); sender.sendCollectedData(registeredServer, ContentFormat.SENML_JSON, 1000, false); } catch (InterruptedException e) { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index d0b86fdab0..bd3ca0642a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -71,7 +71,7 @@ import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fr public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { protected final LinkParser linkParser = new DefaultLwM2mLinkParser(); - protected String OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC; + protected String CONFIG_PROFILE_WITH_PARAMS_RPC; public Set expectedObjects; public Set expectedObjectIdVers; public Set expectedInstances; @@ -116,10 +116,10 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationWriteCborTest")){ supportFormatOnly_SenMLJSON_SenMLCBOR = true; } - initRpc(); + initRpc(false); } - private void initRpc () throws Exception { + protected void initRpc(boolean isCollected) throws Exception { String endpoint = DEVICE_ENDPOINT_RPC_PREF + endpointSequence.incrementAndGet(); createNewClient(SECURITY_NO_SEC, null, true, endpoint); expectedObjects = ConcurrentHashMap.newKeySet(); @@ -157,15 +157,14 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg id_3_0_9 = fromVersionedIdToObjectId(idVer_3_0_9); idVer_19_0_0 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0; - OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC = + String ATTRIBUTES_TELEMETRY_WITH_PARAMS_RPC_WITH_OBSERVE = " {\n" + " \"keyName\": {\n" + " \"" + idVer_3_0_9 + "\": \"" + RESOURCE_ID_NAME_3_9 + "\",\n" + " \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_14 + "\": \"" + RESOURCE_ID_NAME_3_14 + "\",\n" + " \"" + idVer_19_0_0 + "\": \"" + RESOURCE_ID_NAME_19_0_0 + "\",\n" + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\": \"" + RESOURCE_ID_NAME_19_1_0 + "\",\n" + - " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2 + "\": \"" + RESOURCE_ID_NAME_19_0_2 + "\",\n" + - " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\": \"" + RESOURCE_ID_NAME_3303_12_5700 + "\"\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2 + "\": \"" + RESOURCE_ID_NAME_19_0_2 + "\"\n" + " },\n" + " \"observe\": [\n" + " \"" + idVer_3_0_9 + "\",\n" + @@ -180,13 +179,29 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg " \"telemetry\": [\n" + " \"" + idVer_3_0_9 + "\",\n" + " \"" + idVer_19_0_0 + "\",\n" + - " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\",\n" + - " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\"\n" + + " \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\"\n" + " ],\n" + " \"attributeLwm2m\": {}\n" + " }"; - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS_RPC, getBootstrapServerCredentialsNoSec(NONE)); + String TELEMETRY_WITH_PARAMS_RPC_WITHOUT_OBSERVE = + " {\n" + + " \"keyName\": {\n" + + " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\": \"" + RESOURCE_ID_NAME_3303_12_5700 + "\"\n" + + " },\n" + + " \"observe\": [\n" + + " ],\n" + + " \"attribute\": [\n" + + " ],\n" + + " \"telemetry\": [\n" + + " \"" + objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + RESOURCE_ID_5700 + "\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }" ; + + CONFIG_PROFILE_WITH_PARAMS_RPC = isCollected ? TELEMETRY_WITH_PARAMS_RPC_WITHOUT_OBSERVE : ATTRIBUTES_TELEMETRY_WITH_PARAMS_RPC_WITH_OBSERVE; + + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(CONFIG_PROFILE_WITH_PARAMS_RPC, getBootstrapServerCredentialsNoSec(NONE)); createDeviceProfile(transportConfiguration); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(endpoint)); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java new file mode 100644 index 0000000000..d76f2f5400 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadCollectedValueTest.java @@ -0,0 +1,103 @@ +/** + * 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.lwm2m.rpc.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; +import java.util.concurrent.atomic.AtomicReference; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_0; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_1; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3303_12_5700; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS; + +@Slf4j +public class RpcLwm2mIntegrationReadCollectedValueTest extends AbstractRpcLwM2MIntegrationTest { + + @Before + public void startInitRPC() throws Exception { + initRpc(true); + } + + /** + * Read {"id":"/3303/12/5700"} + * Trigger a Send operation from the client with multiple values for the same resource as a payload + * acked "[{"bn":"/3303/12/5700","bt":1724".. 116 bytes] + * 2 values for the resource /3303/12/5700 should be stored with: + * - timestamps1 = Instance.now() + RESOURCE_ID_VALUE_3303_12_5700_1 + * - timestamps2 = (timestamps1 + 3 sec) + RESOURCE_ID_VALUE_3303_12_5700_2 + * @throws Exception + */ + @Test + public void testReadSingleResource_sendFromClient_CollectedValue() throws Exception { + // init test + int cntValues = 2; + int resourceId = 5700; + String expectedIdVer = objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + resourceId; + sendRPCById(expectedIdVer); + + // verify time start/end send CollectedValue; + await().atMost(40, SECONDS).until(() -> RESOURCE_ID_3303_12_5700_TS_0 > 0 + && RESOURCE_ID_3303_12_5700_TS_1 > 0); + + // verify result read: verify count value: 1-2: send CollectedValue; + AtomicReference actualValues = new AtomicReference<>(); + await().atMost(40, SECONDS).until(() -> { + actualValues.set(doGetAsync( + "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + + RESOURCE_ID_NAME_3303_12_5700 + + "&startTs=" + (RESOURCE_ID_3303_12_5700_TS_0 - RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS) + + "&endTs=" + (RESOURCE_ID_3303_12_5700_TS_1 + RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS) + + "&interval=0&limit=100&useStrictDataTypes=false", + ObjectNode.class)); + return actualValues.get() != null && actualValues.get().size() > 0 + && actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700).size() >= cntValues && verifyTs(actualValues); + }); + } + + private boolean verifyTs(AtomicReference actualValues) { + String expectedVal_0 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_0); + String expectedVal_1 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_1); + ArrayNode actual = (ArrayNode) actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700); + long actualTS0 = 0; + long actualTS1 = 0; + for (JsonNode tsNode : actual) { + if (tsNode.get("value").asText().equals(expectedVal_0)) { + actualTS0 = tsNode.get("ts").asLong(); + } else if (tsNode.get("value").asText().equals(expectedVal_1)) { + actualTS1 = tsNode.get("ts").asLong(); + } + } + return actualTS0 >= RESOURCE_ID_3303_12_5700_TS_0 + && actualTS1 <= RESOURCE_ID_3303_12_5700_TS_1 + && (actualTS1 - actualTS0) >= RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS; + } + + private String sendRPCById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); + } +} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java index 1ab4893ec4..b8835d427d 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java @@ -15,23 +15,15 @@ */ package org.thingsboard.server.transport.lwm2m.rpc.sql; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections4.map.HashedMap; import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.node.LwM2mPath; +import org.junit.Before; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; -import java.time.Instant; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.awaitility.Awaitility.await; import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -39,24 +31,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_11; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_TS_1; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_3; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_1_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3303_12_5700; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_9; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_0; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3303_12_5700_VALUE_1; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS; @Slf4j public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest { @@ -228,59 +213,6 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest assertTrue(actualValues.contains(expected19_1_0)); } - - /** - * Read {"id":"/3303/12/5700"} - * Trigger a Send operation from the client with multiple values for the same resource as a payload - * acked "[{"bn":"/3303/12/5700","bt":1724".. 116 bytes] - * 2 values for the resource /3303/12/5700 should be stored with: - * - timestamps1 = Instance.now() + RESOURCE_ID_VALUE_3303_12_5700_1 - * - timestamps2 = (timestamps1 + 3 sec) + RESOURCE_ID_VALUE_3303_12_5700_2 - * @throws Exception - */ - @Test - public void testReadSingleResource_sendFromClient_CollectedValue() throws Exception { - // init test - long startTs = Instant.now().toEpochMilli(); - int cntValues = 4; - int resourceId = 5700; - String expectedIdVer = objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12 + "/" + resourceId; - sendRPCById(expectedIdVer); - // verify result read: verify count value: 1-2: send CollectedValue; 3 - response for read; - long endTs = Instant.now().toEpochMilli() + RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS * 4; - String expectedVal_1 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_0); - String expectedVal_2 = String.valueOf(RESOURCE_ID_3303_12_5700_VALUE_1); - AtomicReference actualValues = new AtomicReference<>(); - await().atMost(40, SECONDS).until(() -> { - actualValues.set(doGetAsync( - "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" - + RESOURCE_ID_NAME_3303_12_5700 - + "&startTs=" + startTs - + "&endTs=" + endTs - + "&interval=0&limit=100&useStrictDataTypes=false", - ObjectNode.class)); - // verify cntValues - return actualValues.get() != null && actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700).size() == cntValues; - }); - // verify ts - ArrayNode actual = (ArrayNode) actualValues.get().get(RESOURCE_ID_NAME_3303_12_5700); - Map keyTsMaps = new HashedMap(); - for (JsonNode tsNode: actual) { - if (tsNode.get("value").asText().equals(expectedVal_1) || tsNode.get("value").asText().equals(expectedVal_2)) { - keyTsMaps.put(tsNode.get("value").asText(), tsNode.get("ts").asLong()); - } - } - assertTrue(keyTsMaps.size() == 2); - long actualTS0 = keyTsMaps.get(expectedVal_1).longValue(); - long actualTS1 = keyTsMaps.get(expectedVal_2).longValue(); - assertTrue(actualTS0 > 0); - assertTrue(actualTS1 > 0); - assertTrue(actualTS1 > actualTS0); - assertTrue((actualTS1 - actualTS0) >= RESOURCE_ID_VALUE_3303_12_5700_DELTA_TS); - assertTrue(actualTS0 <= RESOURCE_ID_3303_12_5700_TS_0); - assertTrue(actualTS1 <= RESOURCE_ID_3303_12_5700_TS_1); - } - /** * ReadComposite {"keys":["batteryLevel", "UtfOffset", "dataDescription"]} */ From 8f3abdf1397e3ba4780200d5798325c608398c9f Mon Sep 17 00:00:00 2001 From: nick Date: Sun, 6 Oct 2024 22:04:26 +0300 Subject: [PATCH 015/110] lwm2m: update tests lwm2mAttributes --- .../lwm2m/AbstractLwM2MIntegrationTest.java | 3 +- .../lwm2m/client/LwM2MTestClient.java | 4 +- .../lwm2m/client/SimpleLwM2MDevice.java | 73 +- .../lwm2m/client/TbLwm2mObjectEnabler.java | 729 ------------------ .../lwm2m/client/TbObjectsInitializer.java | 71 -- .../rpc/AbstractRpcLwM2MIntegrationTest.java | 3 - .../sql/RpcLwm2mIntegrationDiscoverTest.java | 12 + ...ntegrationDiscoverWriteAttributesTest.java | 175 ++--- 8 files changed, 120 insertions(+), 950 deletions(-) delete mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java delete mode 100644 application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbObjectsInitializer.java diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index af5be0dc77..1acf5165fd 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -177,7 +177,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte protected LwM2MTestClient lwM2MTestClient; private String[] resources; protected String deviceId; - protected boolean isWriteAttribute = false; protected boolean supportFormatOnly_SenMLJSON_SenMLCBOR = false; @Before @@ -319,7 +318,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte try (ServerSocket socket = new ServerSocket(0)) { int clientPort = socket.getLocalPort(); lwM2MTestClient.init(security, securityBs, clientPort, isRpc, - this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest, isWriteAttribute, + this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest, clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR); } } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java index 796f3e09c5..fab682f854 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java @@ -141,7 +141,7 @@ public class LwM2MTestClient { public void init(Security security, Security securityBs, int port, boolean isRpc, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, - LwM2mClientContext clientContext, boolean isWriteAttribute, Integer cIdLength, boolean queueMode, + LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode, boolean supportFormatOnly_SenMLJSON_SenMLCBOR) throws InvalidDDFFileException, IOException { Assert.assertNull("client already initialized", leshanClient); this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler; @@ -151,7 +151,7 @@ public class LwM2MTestClient { models.addAll(ObjectLoader.loadDdfFile(LwM2MTestClient.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName), resourceName)); } LwM2mModel model = new StaticModel(models); - ObjectsInitializer initializer = isWriteAttribute ? new TbObjectsInitializer(model) : new ObjectsInitializer(model); + ObjectsInitializer initializer = new ObjectsInitializer(model); if (securityBs != null && security != null) { // SECURITY security.setId(serverId); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java index 370c2249df..a6a850a897 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java @@ -20,7 +20,7 @@ import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.servers.LwM2mServer; import org.eclipse.leshan.core.Destroyable; import org.eclipse.leshan.core.model.ObjectModel; -import org.eclipse.leshan.core.model.ResourceModel; +import org.eclipse.leshan.core.model.ResourceModel.Type; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.request.argument.Arguments; import org.eclipse.leshan.core.response.ExecuteResponse; @@ -30,7 +30,6 @@ import org.eclipse.leshan.core.response.WriteResponse; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.PrimitiveIterator; @@ -46,9 +45,46 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl private static final Random RANDOM = new Random(); private static final int min = 5; private static final int max = 50; - private static final PrimitiveIterator.OfInt randomIterator = new Random().ints(min,max + 1).iterator(); + private static final PrimitiveIterator.OfInt randomIterator = new Random().ints(min, max + 1).iterator(); private static final List supportedResources = Arrays.asList(0, 1, 2, 3, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21); - + /** + * 0: DC power + * 1: Internal Battery + * 2: External Battery + * 3: Fuel Cell + * 4: Power over Ethernet + * 5: USB + * 6: AC (Mains) power + * 7: Solar + */ + private static final Map availablePowerSources = + Map.of(0, 0L, 1, 1L, 2, 7L); + private static Map powerSourceVoltage = + Map.of(0, 12000L, 1, 12400L, 7, 14600L); //mV + private static Map powerSourceCurrent = + Map.of(0, 72000L, 1, 2000L, 7, 25000L); // mA + + /** + * 0=No error + * 1=Low battery power + * 2=External power supply off + * 3=GPS module failure + * 4=Low received signal strength + * 5=Out of memory + * 6=SMS failure + * 7=IP connectivity failure + * 8=Peripheral malfunction + * 9..15=Reserved for future use + * 16..32=Device specific error codes + * + * When the single Device Object Instance is initiated, there is only one error code Resource Instance whose value is equal to 0 that means no error. + * When the first error happens, the LwM2M Client changes error code Resource Instance to any non-zero value to indicate the error type. + * When any other error happens, a new error code Resource Instance is created. + * When an error associated with a Resource Instance is no longer present, that Resource Instance is deleted. + * When the single existing error is no longer present, the LwM2M Client returns to the original no error state where Instance 0 has value 0. + */ + private static Map errorCode = + Map.of(0, 0L); // 0-32 public SimpleLwM2MDevice() { } @@ -81,15 +117,17 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl case 3: return ReadResponse.success(resourceId, getFirmwareVersion()); case 6: - return ReadResponse.success(resourceId, getAvailablePowerSources(), ResourceModel.Type.INTEGER); + return ReadResponse.success(resourceId, getAvailablePowerSources(), Type.INTEGER); + case 7: + return ReadResponse.success(resourceId, getPowerSourceVoltage(), Type.INTEGER); + case 8: + return ReadResponse.success(resourceId, getPowerSourceCurrent(), Type.INTEGER); case 9: return ReadResponse.success(resourceId, getBatteryLevel()); case 10: return ReadResponse.success(resourceId, getMemoryFree()); case 11: - Map errorCodes = new HashMap<>(); - errorCodes.put(0, getErrorCode()); - return ReadResponse.success(resourceId, errorCodes, ResourceModel.Type.INTEGER); + return ReadResponse.success(resourceId, getErrorCodes(), Type.INTEGER); case 14: return ReadResponse.success(resourceId, getUtcOffset()); case 15: @@ -156,16 +194,19 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl return "1.0.2"; } - private long getErrorCode() { - return 0; + private Map getAvailablePowerSources() { + return availablePowerSources; } - private Map getAvailablePowerSources() { - Map availablePowerSources = new HashMap<>(); - availablePowerSources.put(0, 1L); - availablePowerSources.put(1, 2L); - availablePowerSources.put(2, 5L); - return availablePowerSources; + private Map getPowerSourceVoltage() { + return powerSourceVoltage; + } + private Map getPowerSourceCurrent() { + return powerSourceCurrent; + } + + private Map getErrorCodes() { + return errorCode; } private int getBatteryLevel() { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java deleted file mode 100644 index a7bdae3321..0000000000 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbLwm2mObjectEnabler.java +++ /dev/null @@ -1,729 +0,0 @@ -/** - * 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.lwm2m.client; - -import org.eclipse.leshan.client.LwM2mClient; -import org.eclipse.leshan.client.resource.BaseObjectEnabler; -import org.eclipse.leshan.client.resource.DummyInstanceEnabler; -import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; -import org.eclipse.leshan.client.resource.LwM2mInstanceEnablerFactory; -import org.eclipse.leshan.client.resource.listener.ResourceListener; -import org.eclipse.leshan.client.servers.LwM2mServer; -import org.eclipse.leshan.client.servers.ServersInfoExtractor; -import org.eclipse.leshan.client.util.LinkFormatHelper; -import org.eclipse.leshan.core.Destroyable; -import org.eclipse.leshan.core.LwM2mId; -import org.eclipse.leshan.core.Startable; -import org.eclipse.leshan.core.Stoppable; -import org.eclipse.leshan.core.link.lwm2m.LwM2mLink; -import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttribute; -import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributeSet; -import org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes; -import org.eclipse.leshan.core.model.ObjectModel; -import org.eclipse.leshan.core.model.ResourceModel; -import org.eclipse.leshan.core.node.LwM2mMultipleResource; -import org.eclipse.leshan.core.node.LwM2mObject; -import org.eclipse.leshan.core.node.LwM2mObjectInstance; -import org.eclipse.leshan.core.node.LwM2mPath; -import org.eclipse.leshan.core.node.LwM2mResource; -import org.eclipse.leshan.core.node.LwM2mResourceInstance; -import org.eclipse.leshan.core.request.BootstrapDeleteRequest; -import org.eclipse.leshan.core.request.BootstrapReadRequest; -import org.eclipse.leshan.core.request.BootstrapWriteRequest; -import org.eclipse.leshan.core.request.ContentFormat; -import org.eclipse.leshan.core.request.CreateRequest; -import org.eclipse.leshan.core.request.DeleteRequest; -import org.eclipse.leshan.core.request.DiscoverRequest; -import org.eclipse.leshan.core.request.DownlinkRequest; -import org.eclipse.leshan.core.request.ExecuteRequest; -import org.eclipse.leshan.core.request.ObserveRequest; -import org.eclipse.leshan.core.request.ReadRequest; -import org.eclipse.leshan.core.request.WriteAttributesRequest; -import org.eclipse.leshan.core.request.WriteRequest; -import org.eclipse.leshan.core.request.WriteRequest.Mode; -import org.eclipse.leshan.core.response.BootstrapDeleteResponse; -import org.eclipse.leshan.core.response.BootstrapReadResponse; -import org.eclipse.leshan.core.response.BootstrapWriteResponse; -import org.eclipse.leshan.core.response.CreateResponse; -import org.eclipse.leshan.core.response.DeleteResponse; -import org.eclipse.leshan.core.response.DiscoverResponse; -import org.eclipse.leshan.core.response.ExecuteResponse; -import org.eclipse.leshan.core.response.ObserveResponse; -import org.eclipse.leshan.core.response.ReadResponse; -import org.eclipse.leshan.core.response.WriteAttributesResponse; -import org.eclipse.leshan.core.response.WriteResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -public class TbLwm2mObjectEnabler extends BaseObjectEnabler implements Destroyable, Startable, Stoppable { - - private static Logger LOG = LoggerFactory.getLogger(DummyInstanceEnabler.class); - - protected Map instances; - - protected LwM2mInstanceEnablerFactory instanceFactory; - protected ContentFormat defaultContentFormat; - - private LinkFormatHelper tbLinkFormatHelper; - protected Map lwM2mAttributes; - - public TbLwm2mObjectEnabler(int id, ObjectModel objectModel, Map instances, - LwM2mInstanceEnablerFactory instanceFactory, ContentFormat defaultContentFormat) { - super(id, objectModel); - this.instances = new HashMap<>(instances); - ; - this.instanceFactory = instanceFactory; - this.defaultContentFormat = defaultContentFormat; - for (Entry entry : this.instances.entrySet()) { - instances.put(entry.getKey(), entry.getValue()); - listenInstance(entry.getValue(), entry.getKey()); - } - this.lwM2mAttributes = new HashMap<>(); - } - - public TbLwm2mObjectEnabler(int id, ObjectModel objectModel) { - super(id, objectModel); - } - - @Override - public synchronized List getAvailableInstanceIds() { - List ids = new ArrayList<>(instances.keySet()); - Collections.sort(ids); - return ids; - } - - @Override - public synchronized List getAvailableResourceIds(int instanceId) { - LwM2mInstanceEnabler instanceEnabler = instances.get(instanceId); - if (instanceEnabler != null) { - return instanceEnabler.getAvailableResourceIds(getObjectModel()); - } else { - return Collections.emptyList(); - } - } - - public synchronized void addInstance(int instanceId, LwM2mInstanceEnabler newInstance) { - instances.put(instanceId, newInstance); - listenInstance(newInstance, instanceId); - fireInstancesAdded(instanceId); - } - - public synchronized LwM2mInstanceEnabler getInstance(int instanceId) { - return instances.get(instanceId); - } - - public synchronized LwM2mInstanceEnabler removeInstance(int instanceId) { - LwM2mInstanceEnabler removedInstance = instances.remove(instanceId); - if (removedInstance != null) { - fireInstancesRemoved(removedInstance.getId()); - } - return removedInstance; - } - - @Override - protected CreateResponse doCreate(LwM2mServer server, CreateRequest request) { - if (!getObjectModel().multiple && instances.size() > 0) { - return CreateResponse.badRequest("an instance already exist for this single instance object"); - } - - if (request.unknownObjectInstanceId()) { - // create instance - LwM2mInstanceEnabler newInstance = createInstance(server, getObjectModel().multiple ? null : 0, - request.getResources()); - - // add new instance to this object - instances.put(newInstance.getId(), newInstance); - listenInstance(newInstance, newInstance.getId()); - fireInstancesAdded(newInstance.getId()); - - return CreateResponse - .success(new LwM2mPath(request.getPath().getObjectId(), newInstance.getId()).toString()); - } else { - List instanceNodes = request.getObjectInstances(); - - // checks single object instances - if (!getObjectModel().multiple) { - if (request.getObjectInstances().size() > 1) { - return CreateResponse.badRequest("can not create several instances on this single instance object"); - } - if (request.getObjectInstances().get(0).getId() != 0) { - return CreateResponse.badRequest("single instance object must use 0 as ID"); - } - } - // ensure instance does not already exists - for (LwM2mObjectInstance instance : instanceNodes) { - if (instances.containsKey(instance.getId())) { - return CreateResponse.badRequest(String.format("instance %d already exists", instance.getId())); - } - } - - // create the new instances - int[] instanceIds = new int[request.getObjectInstances().size()]; - int i = 0; - for (LwM2mObjectInstance instance : request.getObjectInstances()) { - // create instance - LwM2mInstanceEnabler newInstance = createInstance(server, instance.getId(), - instance.getResources().values()); - - // add new instance to this object - instances.put(newInstance.getId(), newInstance); - listenInstance(newInstance, newInstance.getId()); - - // store instance ids - instanceIds[i] = newInstance.getId(); - i++; - } - fireInstancesAdded(instanceIds); - return CreateResponse.success(); - } - } - - protected LwM2mInstanceEnabler createInstance(LwM2mServer server, Integer instanceId, - Collection resources) { - // create the new instance - LwM2mInstanceEnabler newInstance = instanceFactory.create(getObjectModel(), instanceId, instances.keySet()); - newInstance.setLwM2mClient(getLwm2mClient()); - - // add/write resource - for (LwM2mResource resource : resources) { - newInstance.write(server, true, resource.getId(), resource); - } - - return newInstance; - } - - @Override - protected ReadResponse doRead(LwM2mServer server, ReadRequest request) { - LwM2mPath path = request.getPath(); - - // Manage Object case - if (path.isObject()) { - List lwM2mObjectInstances = new ArrayList<>(); - for (LwM2mInstanceEnabler instance : instances.values()) { - ReadResponse response = instance.read(server); - if (response.isSuccess()) { - lwM2mObjectInstances.add((LwM2mObjectInstance) response.getContent()); - } - } - return ReadResponse.success(new LwM2mObject(getId(), lwM2mObjectInstances)); - } - - // Manage Instance case - LwM2mInstanceEnabler instance = instances.get(path.getObjectInstanceId()); - if (instance == null) - return ReadResponse.notFound(); - - if (path.getResourceId() == null) { - return instance.read(server); - } - - // Manage Resource case - if (path.getResourceInstanceId() == null) { - return instance.read(server, path.getResourceId()); - } - - // Manage Resource Instance case - return instance.read(server, path.getResourceId(), path.getResourceInstanceId()); - } - - @Override - protected BootstrapReadResponse doRead(LwM2mServer server, BootstrapReadRequest request) { - // Basic implementation we delegate to classic Read Request - ReadResponse response = doRead(server, - new ReadRequest(request.getContentFormat(), request.getPath(), request.getCoapRequest())); - return new BootstrapReadResponse(response.getCode(), response.getContent(), response.getErrorMessage()); - } - - @Override - protected ObserveResponse doObserve(final LwM2mServer server, final ObserveRequest request) { - final LwM2mPath path = request.getPath(); - - // Manage Object case - if (path.isObject()) { - List lwM2mObjectInstances = new ArrayList<>(); - for (LwM2mInstanceEnabler instance : instances.values()) { - ReadResponse response = instance.observe(server); - if (response.isSuccess()) { - lwM2mObjectInstances.add((LwM2mObjectInstance) response.getContent()); - } - } - return ObserveResponse.success(new LwM2mObject(getId(), lwM2mObjectInstances)); - } - - // Manage Instance case - final LwM2mInstanceEnabler instance = instances.get(path.getObjectInstanceId()); - if (instance == null) - return ObserveResponse.notFound(); - - if (path.getResourceId() == null) { - return instance.observe(server); - } - - // Manage Resource case - if (path.getResourceInstanceId() == null) { - return instance.observe(server, path.getResourceId()); - } - - // Manage Resource Instance case - return instance.observe(server, path.getResourceId(), path.getResourceInstanceId()); - } - - @Override - protected WriteResponse doWrite(LwM2mServer server, WriteRequest request) { - LwM2mPath path = request.getPath(); - - // Manage Instance case - LwM2mInstanceEnabler instance = instances.get(path.getObjectInstanceId()); - if (instance == null) - return WriteResponse.notFound(); - - if (path.isObjectInstance()) { - return instance.write(server, request.isReplaceRequest(), (LwM2mObjectInstance) request.getNode()); - } - - // Manage Resource case - if (path.getResourceInstanceId() == null) { - return instance.write(server, request.isReplaceRequest(), path.getResourceId(), - (LwM2mResource) request.getNode()); - } - - // Manage Resource Instance case - return instance.write(server, false, path.getResourceId(), path.getResourceInstanceId(), - ((LwM2mResourceInstance) request.getNode())); - } - - @Override - protected BootstrapWriteResponse doWrite(LwM2mServer server, BootstrapWriteRequest request) { - LwM2mPath path = request.getPath(); - - // Manage Object case - if (path.isObject()) { - for (LwM2mObjectInstance instanceNode : ((LwM2mObject) request.getNode()).getInstances().values()) { - LwM2mInstanceEnabler instanceEnabler = instances.get(instanceNode.getId()); - if (instanceEnabler == null) { - doCreate(server, new CreateRequest(path.getObjectId(), instanceNode)); - } else { - doWrite(server, new WriteRequest(Mode.REPLACE, path.getObjectId(), instanceEnabler.getId(), - instanceNode.getResources().values())); - } - } - return BootstrapWriteResponse.success(); - } - - // Manage Instance case - if (path.isObjectInstance()) { - LwM2mObjectInstance instanceNode = (LwM2mObjectInstance) request.getNode(); - LwM2mInstanceEnabler instanceEnabler = instances.get(path.getObjectInstanceId()); - if (instanceEnabler == null) { - doCreate(server, new CreateRequest(path.getObjectId(), instanceNode)); - } else { - doWrite(server, new WriteRequest(Mode.REPLACE, request.getContentFormat(), path.getObjectId(), - path.getObjectInstanceId(), instanceNode.getResources().values())); - } - return BootstrapWriteResponse.success(); - } - - // Manage resource case - LwM2mResource resource = (LwM2mResource) request.getNode(); - LwM2mInstanceEnabler instanceEnabler = instances.get(path.getObjectInstanceId()); - if (instanceEnabler == null) { - doCreate(server, new CreateRequest(path.getObjectId(), - new LwM2mObjectInstance(path.getObjectInstanceId(), resource))); - } else { - instanceEnabler.write(server, true, path.getResourceId(), resource); - } - return BootstrapWriteResponse.success(); - } - - @Override - protected ExecuteResponse doExecute(LwM2mServer server, ExecuteRequest request) { - LwM2mPath path = request.getPath(); - LwM2mInstanceEnabler instance = instances.get(path.getObjectInstanceId()); - if (instance == null) { - return ExecuteResponse.notFound(); - } - return instance.execute(server, path.getResourceId(), request.getArguments()); - } - - @Override - protected DeleteResponse doDelete(LwM2mServer server, DeleteRequest request) { - LwM2mInstanceEnabler deletedInstance = instances.remove(request.getPath().getObjectInstanceId()); - if (deletedInstance != null) { - deletedInstance.onDelete(server); - fireInstancesRemoved(deletedInstance.getId()); - return DeleteResponse.success(); - } - return DeleteResponse.notFound(); - } - - @Override - public BootstrapDeleteResponse doDelete(LwM2mServer server, BootstrapDeleteRequest request) { - if (request.getPath().isRoot() || request.getPath().isObject()) { - if (id == LwM2mId.SECURITY) { - // For security object, we clean everything except bootstrap Server account. - - // Get bootstrap account and store removed instances ids - Entry bootstrapServerAccount = null; - int[] instanceIds = new int[instances.size()]; - int i = 0; - for (Entry instance : instances.entrySet()) { - if (ServersInfoExtractor.isBootstrapServer(instance.getValue())) { - bootstrapServerAccount = instance; - } else { - // Store instance ids - instanceIds[i] = instance.getKey(); - i++; - } - } - // Clear everything - instances.clear(); - - // Put bootstrap account again - if (bootstrapServerAccount != null) { - instances.put(bootstrapServerAccount.getKey(), bootstrapServerAccount.getValue()); - } - - fireInstancesRemoved(instanceIds); - return BootstrapDeleteResponse.success(); - } else if (id == LwM2mId.OSCORE) { - // For OSCORE object, we clean everything except OSCORE object link to bootstrap Server account. - - // Get bootstrap account - LwM2mObjectInstance bootstrapInstance = ServersInfoExtractor.getBootstrapSecurityInstance( - getLwm2mClient().getObjectTree().getObjectEnabler(LwM2mId.SECURITY)); - // Get OSCORE instance ID associated to it - Integer bootstrapOscoreInstanceId = bootstrapInstance != null - ? ServersInfoExtractor.getOscoreSecurityMode(bootstrapInstance) - : null; - - // if bootstrap server use OSCORE, - // search the OSCORE instance for this ID and store removed instances ids - if (bootstrapOscoreInstanceId != null) { - Entry bootstrapServerOscore = null; - int[] instanceIds = new int[instances.size()]; - int i = 0; - for (Entry instance : instances.entrySet()) { - if (bootstrapOscoreInstanceId.equals(instance.getKey())) { - bootstrapServerOscore = instance; - } else { - // Store instance ids - instanceIds[i] = instance.getKey(); - i++; - } - } - - // Clear everything - instances.clear(); - - // Put bootstrap OSCORE instance again - if (bootstrapServerOscore != null) { - instances.put(bootstrapServerOscore.getKey(), bootstrapServerOscore.getValue()); - } - fireInstancesRemoved(instanceIds); - return BootstrapDeleteResponse.success(); - } - // else delete everything. - } - - // In all other cases, just delete everything - instances.clear(); - // fired instances removed - int[] instanceIds = new int[instances.size()]; - int i = 0; - for (Entry instance : instances.entrySet()) { - instanceIds[i] = instance.getKey(); - i++; - } - fireInstancesRemoved(instanceIds); - - return BootstrapDeleteResponse.success(); - } else if (request.getPath().isObjectInstance()) { - if (id == LwM2mId.SECURITY) { - // For security object, deleting bootstrap Server account is not allowed - LwM2mInstanceEnabler instance = instances.get(request.getPath().getObjectInstanceId()); - if (instance == null) { - return BootstrapDeleteResponse - .badRequest(String.format("Instance %s not found", request.getPath())); - } else if (ServersInfoExtractor.isBootstrapServer(instance)) { - return BootstrapDeleteResponse.badRequest("bootstrap server can not be deleted"); - } - } else if (id == LwM2mId.OSCORE) { - // For OSCORE object, deleting instance linked to Bootstrap account is not allowed - - // Get bootstrap instance - LwM2mObjectInstance bootstrapInstance = ServersInfoExtractor.getBootstrapSecurityInstance( - getLwm2mClient().getObjectTree().getObjectEnabler(LwM2mId.SECURITY)); - // Get OSCORE instance ID associated to it - Integer bootstrapOscoreInstanceId = bootstrapInstance != null - ? ServersInfoExtractor.getOscoreSecurityMode(bootstrapInstance) - : null; - - if (bootstrapOscoreInstanceId != null - && bootstrapOscoreInstanceId.equals(request.getPath().getObjectInstanceId())) { - return BootstrapDeleteResponse - .badRequest("OSCORE instance linked to bootstrap server can not be deleted"); - } - } - if (null != instances.remove(request.getPath().getObjectInstanceId())) { - fireInstancesRemoved(request.getPath().getObjectInstanceId()); - return BootstrapDeleteResponse.success(); - } else { - return BootstrapDeleteResponse.badRequest(String.format("Instance %s not found", request.getPath())); - } - } - return BootstrapDeleteResponse.badRequest(String.format("unexcepted path %s", request.getPath())); - } - - protected void listenInstance(LwM2mInstanceEnabler instance, final int instanceId) { - instance.addResourceListener(new ResourceListener() { - @Override - public void resourceChanged(LwM2mPath... paths) { - for (LwM2mPath path : paths) { - if (!isValid(instanceId, path)) { - LOG.warn("InstanceEnabler ({}) of object ({}) try to raise a change of {} which seems invalid.", - instanceId, getId(), path); - } - } - fireResourcesChanged(paths); - } - }); - } - - protected boolean isValid(int instanceId, LwM2mPath pathToValidate) { - if (!(pathToValidate.isResource() || pathToValidate.isResourceInstance())) - return false; - - if (pathToValidate.getObjectId() != getId()) { - return false; - } - - if (pathToValidate.getObjectInstanceId() != instanceId) { - return false; - } - - return true; - } - - @Override - public ContentFormat getDefaultEncodingFormat(DownlinkRequest request) { - return defaultContentFormat; - } - - @Override - public void init(LwM2mClient client, LinkFormatHelper linkFormatHelper) { - super.init(client, linkFormatHelper); - this.tbLinkFormatHelper = linkFormatHelper; - for (LwM2mInstanceEnabler instanceEnabler : instances.values()) { - instanceEnabler.setLwM2mClient(client); - } - } - - @Override - public void destroy() { - for (LwM2mInstanceEnabler instanceEnabler : instances.values()) { - if (instanceEnabler instanceof Destroyable) { - ((Destroyable) instanceEnabler).destroy(); - } else if (instanceEnabler instanceof Stoppable) { - ((Stoppable) instanceEnabler).stop(); - } - } - } - - @Override - public void start() { - for (LwM2mInstanceEnabler instanceEnabler : instances.values()) { - if (instanceEnabler instanceof Startable) { - ((Startable) instanceEnabler).start(); - } - } - } - - @Override - public void stop() { - for (LwM2mInstanceEnabler instanceEnabler : instances.values()) { - if (instanceEnabler instanceof Stoppable) { - ((Stoppable) instanceEnabler).stop(); - } - } - } - - @Override - public synchronized WriteAttributesResponse writeAttributes(LwM2mServer server, WriteAttributesRequest request) { - // execute is not supported for bootstrap - if (server.isLwm2mBootstrapServer()) { - return WriteAttributesResponse.methodNotAllowed(); - } -// return WriteAttributesResponse.internalServerError("not implemented"); - return doWriteAttributes(server, request); - } - - /** - * Class Attributes - * - pmin (def = 0(sec)) Integer Resource/Object Instance/Object Readable Resource - * - pmax (def = -- ) Integer Resource/Object Instance/Object Readable Resource - * - Greater Than gt (def = -- ) Float Resource Numerical&Readable Resource - * - Less Than lt (def = -- ) Float Resource Numerical&Readable Resource - * - Step st (def = -- ) Float Resource Numerical&Readable Resource - */ - public WriteAttributesResponse doWriteAttributes(LwM2mServer server, WriteAttributesRequest request) { - LwM2mPath lwM2mPath = request.getPath(); - LwM2mAttributeSet attributeSet = lwM2mAttributes.get(lwM2mPath); - Map> attributes = new HashMap<>(); - - for (LwM2mAttribute attr : request.getAttributes().getLwM2mAttributes()) { - if (attr.getName().equals("pmax") || attr.getName().equals("pmin")) { - if (lwM2mPath.isObject() || lwM2mPath.isObjectInstance() || lwM2mPath.isResource()) { - attributes.put(attr.getName(), attr); - } else { - return WriteAttributesResponse.badRequest("Attribute " + attr.getName() + " can be used for only Resource/Object Instance/Object."); - } - } else if (attr.getName().equals("gt") || attr.getName().equals("lt") || attr.getName().equals("st")) { - if (lwM2mPath.isResource()) { - attributes.put(attr.getName(), attr); - } else { - return WriteAttributesResponse.badRequest("Attribute " + attr.getName() + " can be used for only Resource."); - } - } - } - if (attributes.size() > 0) { - if (attributeSet == null) { - attributeSet = new LwM2mAttributeSet(attributes.values()); - } else { - Iterable> lwM2mAttributeIterable = attributeSet.getLwM2mAttributes(); - Map> attributesOld = new HashMap<>(); - for (LwM2mAttribute attr : lwM2mAttributeIterable) { - attributesOld.put(attr.getName(), attr); - } - attributesOld.putAll(attributes); - attributeSet = new LwM2mAttributeSet(attributesOld.values()); - } - lwM2mAttributes.put(lwM2mPath, attributeSet); - return WriteAttributesResponse.success(); - } - return WriteAttributesResponse.internalServerError("not implemented"); - } - - @Override - public synchronized DiscoverResponse discover(LwM2mServer server, DiscoverRequest request) { - - if (server.isLwm2mBootstrapServer()) { - // discover is not supported for bootstrap - return DiscoverResponse.methodNotAllowed(); - } - - if (id == LwM2mId.SECURITY || id == LwM2mId.OSCORE) { - return DiscoverResponse.notFound(); - } - return doDiscover(server, request); - - } - - protected DiscoverResponse doDiscover(LwM2mServer server, DiscoverRequest request) { - - LwM2mPath path = request.getPath(); - if (path.isObject()) { - LwM2mLink[] ObjectLinks = linkAddUpdateAttributes(this.tbLinkFormatHelper.getObjectDescription(server, this, null), server); - return DiscoverResponse.success(ObjectLinks); - - } else if (path.isObjectInstance()) { - // Manage discover on instance - if (!getAvailableInstanceIds().contains(path.getObjectInstanceId())) - return DiscoverResponse.notFound(); - - LwM2mLink[] instanceLink = linkAddUpdateAttributes(this.tbLinkFormatHelper.getInstanceDescription(server, this, path.getObjectInstanceId(), null), server); - return DiscoverResponse.success(instanceLink); - - } else if (path.isResource()) { - // Manage discover on resource - if (!getAvailableInstanceIds().contains(path.getObjectInstanceId())) - return DiscoverResponse.notFound(); - - ResourceModel resourceModel = getObjectModel().resources.get(path.getResourceId()); - if (resourceModel == null) - return DiscoverResponse.notFound(); - - if (!getAvailableResourceIds(path.getObjectInstanceId()).contains(path.getResourceId())) - return DiscoverResponse.notFound(); - - LwM2mLink[] resourceLink = linkAddUpdateAttributes(this.tbLinkFormatHelper.getResourceDescription(server, - this, path.getObjectInstanceId(), path.getResourceId(), null), server); - return DiscoverResponse.success(resourceLink); - } - return DiscoverResponse.badRequest(null); - } - - private LwM2mLink[] linkAddUpdateAttributes(LwM2mLink[] links, LwM2mServer server) { - ArrayList resourceLinkList = new ArrayList<>(); - for (LwM2mLink link : links) { - - LwM2mAttributeSet lwM2mAttributeSetDop = null; - if (this.lwM2mAttributes.get(link.getPath()) != null) { - lwM2mAttributeSetDop = this.lwM2mAttributes.get(link.getPath()); - } - LwM2mAttribute resourceAttributeDim = getResourceAttributes(server, link.getPath()); - - Map> attributes = new HashMap<>(); - if (link.getAttributes() != null) { - for (LwM2mAttribute attr : link.getAttributes().getLwM2mAttributes()) { - attributes.put(attr.getName(), attr); - } - } - if (lwM2mAttributeSetDop != null) { - for (LwM2mAttribute attr : lwM2mAttributeSetDop.getLwM2mAttributes()) { - attributes.put(attr.getName(), attr); - } - } - if (resourceAttributeDim != null) { - attributes.put(resourceAttributeDim.getName(), resourceAttributeDim); - } - resourceLinkList.add(new LwM2mLink(link.getRootPath(), link.getPath(), attributes.values())); - } - return resourceLinkList.toArray(LwM2mLink[]::new); - } - - protected LwM2mAttribute getResourceAttributes(LwM2mServer server, LwM2mPath path) { - ResourceModel resourceModel = getObjectModel().resources.get(path.getResourceId()); - if (path.isResource() && resourceModel.multiple) { - return getResourceAttributeDim(path, server); - } - return null; - } - - protected LwM2mAttribute getResourceAttributeDim(LwM2mPath path, LwM2mServer server) { - LwM2mInstanceEnabler instance = instances.get(path.getObjectInstanceId()); - try { - ReadResponse readResponse = instance.read(server, path.getResourceId()); - if (readResponse.getCode().getCode() == 205 && readResponse.getContent() instanceof LwM2mMultipleResource) { - long valueDim = ((LwM2mMultipleResource) readResponse.getContent()).getInstances().size(); - return LwM2mAttributes.create(LwM2mAttributes.DIMENSION, valueDim); - } else { - return null; - } - } catch (Exception e) { - return null; - } - } - -} - diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbObjectsInitializer.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbObjectsInitializer.java deleted file mode 100644 index eff5ac962e..0000000000 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/TbObjectsInitializer.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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.lwm2m.client; - -import org.eclipse.leshan.client.resource.BaseInstanceEnablerFactory; -import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler; -import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; -import org.eclipse.leshan.client.resource.ObjectsInitializer; -import org.eclipse.leshan.core.model.LwM2mModel; -import org.eclipse.leshan.core.model.ObjectModel; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class TbObjectsInitializer extends ObjectsInitializer { - - - public TbObjectsInitializer(LwM2mModel model) { - super(model); - } - - public List create(int... objectId) { - List enablers = new ArrayList<>(); - for (int anObjectId : objectId) { - LwM2mObjectEnabler objectEnabler = create(anObjectId); - if (objectEnabler != null) - enablers.add(objectEnabler); - } - return enablers; - } - - public LwM2mObjectEnabler create(int objectId) { - ObjectModel objectModel = model.getObjectModel(objectId); - if (objectModel == null) { - throw new IllegalArgumentException( - "Cannot create object for id " + objectId + " because no model is defined for this id."); - } - return createNodeEnabler(objectModel); - } - - protected LwM2mObjectEnabler createNodeEnabler(ObjectModel objectModel) { - Map instances = new HashMap<>(); - LwM2mInstanceEnabler[] newInstances = createInstances(objectModel); - for (LwM2mInstanceEnabler instance : newInstances) { - // set id if not already set - if (instance.getId() == null) { - int id = BaseInstanceEnablerFactory.generateNewInstanceId(instances.keySet()); - instance.setId(id); - } - instance.setModel(objectModel); - instances.put(instance.getId(), instance); - } - return new TbLwm2mObjectEnabler(objectModel.id, objectModel, instances, getFactoryFor(objectModel), - getContentFormat(objectModel.id)); - } -} diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java index bd3ca0642a..dae47c039f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java @@ -110,9 +110,6 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg @Before public void startInitRPC() throws Exception { - if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationDiscoverWriteAttributesTest")){ - isWriteAttribute = true; - } if (this.getClass().getSimpleName().equals("RpcLwm2mIntegrationWriteCborTest")){ supportFormatOnly_SenMLJSON_SenMLCBOR = true; } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java index 9518010f6a..f34b32a97a 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java @@ -39,6 +39,7 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_6; public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegrationTest { @@ -171,6 +172,17 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText()); } + @Test + public void testDiscoverRequestCannotTargetResourceInstance_Return_INTERNAL_SERVER_ERROR() throws Exception { + // ResourceInstanceId + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6 + "/1"; + String actualResult = sendDiscover(expectedPath); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.INTERNAL_SERVER_ERROR.getName(), rpcActualResult.get("result").asText()); + String expected = "InvalidRequestException: Discover request cannot target resource instance path: /3/0/6/1"; + assertTrue(rpcActualResult.get("error").asText().contains(expected)); + } + private String sendDiscover(String path) throws Exception { String setRpcRequest = "{\"method\": \"Discover\", \"params\": {\"id\": \"" + path + "\"}}"; return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java index c44048849e..b3bff83426 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverWriteAttributesTest.java @@ -19,12 +19,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.eclipse.leshan.core.ResponseCode; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_6; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_7; @@ -32,69 +32,22 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcLwM2MIntegrationTest { /** - * WriteAttributes {"id":"/3_1.2/0/6","attributes":{"pmax":100, "pmin":10}} - * if not implemented: - * {"result":"INTERNAL_SERVER_ERROR","error":"not implemented"} - * if implemented: - * {"result":"BAD_REQUEST","error":"Attribute pmax can be used for only Resource/Object Instance/Object."} + * Class Attributes + * - dim (0-65535) Integer: Multiple-Instance Resource; R, Number of instances existing for a Multiple-Instance Resource + * Class Attributes + * - pmin (def = 0(sec)) Integer: Object; Object Instance; Resource; Resource Instance; RW, Readable Resource + * - pmax (def = -- ) Integer: Object; Object Instance; Resource; Resource Instance; RW, Readable Resource + * - Greater Than gt (def = -- ) Float: Resource; Resource Instance; RW, Numerical&Readable Resource + * - Less Than lt (def = -- ) Float: Resource; Resource Instance; RW, Numerical&Readable Resource + * - Step st (def = -- ) Float: Resource; Resource Instance; RW, Numerical&Readable Resource */ - @Test - public void testWriteAttributesResourceWithParametersByResourceInstanceId_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6 + "/1"; - String expectedValue = "{\"pmax\":100, \"pmin\":10}"; - String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); - String expected = "Attribute pmax can be used for only Resource/Object Instance/Object."; - String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); - } - /** - * WriteAttributes {"id":"/3_1.2/0/6","attributes":{"pmax":100, "pmin":10}} - * if not implemented: - * {"result":"INTERNAL_SERVER_ERROR","error":"not implemented"} - * if implemented: - * {"result":"BAD_REQUEST","error":"Attribute pmax can be used for only Resource/Object Instance/Object."} - */ - @Test - public void testWriteAttributeResourceDimWithParametersByResourceId_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6; - String expectedValue = "{\"dim\":3}"; - String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); - String expected = "Attribute dim is of class PROPERTIES but only NOTIFICATION attribute can be used in WRITE ATTRIBUTE request."; - String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); - } - - @Test - public void testWriteAttributesResourceVerWithParametersById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectIdVer_3; - String expectedValue = "{\"ver\":1.3}"; - String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); - String expected = "Attribute ver is of class PROPERTIES but only NOTIFICATION attribute can be used in WRITE ATTRIBUTE request."; - String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); - } - - @Test - public void testWriteAttributesResourceServerUriWithParametersById_Result_BAD_REQUEST() throws Exception { - String expectedPath = objectInstanceIdVer_1; - String actualResult = sendRPCReadById(expectedPath); - String expectedValue = "{\"uri\":\"coaps://localhost:5690\"}"; - actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); - String expected = "Attribute uri is of class PROPERTIES but only NOTIFICATION attribute can be used in WRITE ATTRIBUTE request."; - String actual = rpcActualResult.get("error").asText(); - assertTrue(actual.equals(expected)); - } /** + * Class Attributes + * Object Version ver Object + * Provide the version of the associated Object. + * "ver" only for objectId * Class Attributes * Dimension dim Integer [0:255] * Number of instances existing for a Multiple-Instance Resource @@ -106,46 +59,34 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL * Integer * 0..7 * WriteAttributes implemented: Discover {"id":"3/0/6"} -> 'dim' = 3 - * "ver" only for objectId */ @Test public void testReadDIM_3_0_6_Only_R() throws Exception { - String path = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6; - String actualResult = sendDiscover(path); - ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expected = ";dim=3"; - assertTrue(rpcActualResult.get("value").asText().contains(expected)); - - } - - - /** - * Class Attributes - * Object Version ver Object - * Provide the version of the associated Object. - * "ver" only for objectId - */ - @Test - public void testReadVer() throws Exception { String path = objectIdVer_3; String actualResult = sendDiscover(path); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String expected = ";ver=1.2"; assertTrue(rpcActualResult.get("value").asText().contains(expected)); + expected = ";dim=3"; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); + expected = ";dim=3"; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); + expected = ";dim=3"; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); + expected = ";dim=1"; + assertTrue(rpcActualResult.get("value").asText().contains(expected)); } /** * WriteAttributes {"id":"/3/0/14","attributes":{"pmax":100, "pmin":10}} - * if not implemented: - * {"result":"INTERNAL_SERVER_ERROR","error":"not implemented"} - * if implemented: * {"result":"CHANGED"} + * result changed: + * */ @Test public void testWriteAttributesResourceWithParametersById_Result_CHANGED() throws Exception { - String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14; + String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6; String expectedValue = "{\"pmax\":100, \"pmin\":10}"; String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); @@ -154,41 +95,33 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - String expected = ";pmax=100;pmin=10"; + String expected = ";pmax=100;pmin=10;dim=3"; assertTrue(rpcActualResult.get("value").asText().contains(expected)); } - /** - * Class Attributes - * Minimum/Maximum Period pmin/pmax - * Notes: The Minimum Period Attribute: - * -- indicates the minimum time in seconds the LwM2M Client MUST wait between two notifications. If a notification of an observed Resource is supposed to be generated but it is before pmin expiry, notification MUST be sent as soon as pmin expires. In the absence of this parameter, the Minimum Period is defined by the Default Minimum Period set in the LwM2M Server Account. - * Notes: The Maximum Period Attribute: - * -- indicates the maximum time in seconds the LwM2M Client MAY wait between two notifications. When this "Maximum Period" expires after the last notification, a new notification MUST be sent. In the absence of this parameter, the "Maximum Period" is defined by the Default Maximum Period when set in the LwM2M Server Account or considered as 0 otherwise. The value of 0, means pmax MUST be ignored. The maximum period parameter MUST be greater than the minimum period parameter otherwise pmax will be ignored for the Resource to which such inconsistent timing conditions are applied. - * Greater Than gt Resource - * Less Than lt Resource - * Step st Resource - * - * Object Id = 1 - * Default Minimum Period Id = 2 300 or 0 - * Default Maximum Period Id = 3 6000 or "-" - * ;pmax=65, , <3/0/2>, , , - * <3/0/6>;dim=8,<3/0/7>;gt=50;lt=42.2;st=0.5,<3/0/8>;... - */ @Test - public void testWriteAttributesPeriodLtGt() throws Exception { + public void testWriteAttributesResourceVerWithParametersById_Result_BAD_REQUEST() throws Exception { + String expectedPath = objectIdVer_3; + String expectedValue = "{\"ver\":1.3}"; + String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText()); + String expected = "Attribute ver is of class PROPERTIES but only NOTIFICATION attribute can be used in WRITE ATTRIBUTE request."; + String actual = rpcActualResult.get("error").asText(); + assertTrue(actual.equals(expected)); + } + + @Test + public void testWriteAttributesObjectInstanceResourcePeriodLtGt_Return_CHANGED() throws Exception { String expectedPath = objectInstanceIdVer_3; - String expectedValue = "{\"pmax\":60}"; + String expectedValue = "{\"pmax\":65, \"pmin\":5}"; String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); - expectedPath = objectInstanceIdVer_3; - expectedValue = "{\"pmax\":65}"; - actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); - rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_7; - expectedValue = "{\"gt\":50.0, \"lt\":42.2, \"st\":0.5}"; + String expectedValueStr = "gt=50;lt=42.2;st=0.5"; + JsonUtils.parse("{" + expectedValueStr + "}").toString(); + expectedValue = JsonUtils.parse("{" + expectedValueStr + "}").toString(); actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText()); @@ -198,11 +131,11 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); String actualValue = rpcActualResult.get("value").asText(); - String expected = ";ver=1.2,;pmax=65"; + String expected = ";ver=1.2,;pmax=65;pmin=5"; assertTrue(actualValue.contains(expected)); expected = ";dim=3"; assertTrue(actualValue.contains(expected)); - expected = ";st=0.5;lt=42.2;gt=50"; + expected = ";" + expectedValueStr + ";dim=3"; assertTrue(actualValue.contains(expected)); // ObjectInstanceId expectedPath = objectInstanceIdVer_3; @@ -210,30 +143,23 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); actualValue = rpcActualResult.get("value").asText(); - expected = ";pmax=65"; + expected = ";pmax=65;pmin=5"; assertTrue(actualValue.contains(expected)); - expected = ";dim=3,;st=0.5;lt=42.2;gt=50"; + expected = ";" + expectedValueStr + ";dim=3"; assertTrue(actualValue.contains(expected)); // ResourceId expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6; actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - expected = ";dim=3"; + expected = ";dim=3,,,"; assertTrue(rpcActualResult.get("value").asText().contains(expected)); expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_7; actualResult = sendDiscover(expectedPath); rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); - expected = ";st=0.5;lt=42.2;gt=50"; + expected = ";" + expectedValueStr; assertTrue(rpcActualResult.get("value").asText().contains(expected)); - // ResourceInstanceId - expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_6 + "/1"; - actualResult = sendDiscover(expectedPath); - rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); - assertEquals(ResponseCode.INTERNAL_SERVER_ERROR.getName(), rpcActualResult.get("result").asText()); - expected = "InvalidRequestException: Discover request cannot target resource instance path: /3/0/6/1"; - assertTrue(rpcActualResult.get("error").asText().contains(expected)); } private String sendRPCExecuteWithValueById(String path, String value) throws Exception { @@ -241,11 +167,6 @@ public class RpcLwm2mIntegrationDiscoverWriteAttributesTest extends AbstractRpcL return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); } - private String sendRPCReadById(String path) throws Exception { - String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; - return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); - } - private String sendDiscover(String path) throws Exception { String setRpcRequest = "{\"method\": \"Discover\", \"params\": {\"id\": \"" + path + "\"}}"; return doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setRpcRequest, String.class, status().isOk()); From 30344c9999c1b2b201de872517404baf8fca194d Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 7 Oct 2024 12:06:05 +0300 Subject: [PATCH 016/110] Deleted gateway components after migration to Gateway extension --- ...gateway-connector-basic-config.abstract.ts | 77 - ...ay-connector-version-processor.abstract.ts | 69 - .../modbus-version-processor.abstract.ts | 71 - .../mqtt-version-processor.abstract.ts | 101 -- .../opc-version-processor.abstract.ts | 56 - ...eway-advanced-configuration.component.html | 25 - ...eway-advanced-configuration.component.scss | 23 - ...ateway-advanced-configuration.component.ts | 96 -- ...gateway-basic-configuration.component.html | 814 ---------- ...gateway-basic-configuration.component.scss | 103 -- .../gateway-basic-configuration.component.ts | 568 ------- .../gateway-configuration.component.html | 64 - .../gateway-configuration.component.scss | 64 - .../gateway-configuration.component.ts | 396 ----- .../models/gateway-configuration.models.ts | 170 --- .../device-info-table.component.html | 99 -- .../device-info-table.component.scss | 57 - .../device-info-table.component.ts | 164 --- .../mapping-data-keys-panel.component.html | 235 --- .../mapping-data-keys-panel.component.scss | 60 - .../mapping-data-keys-panel.component.ts | 197 --- .../mapping-table.component.html | 125 -- .../mapping-table.component.scss | 101 -- .../mapping-table/mapping-table.component.ts | 323 ---- .../modbus-basic-config.abstract.ts | 76 - .../modbus-basic-config.component.html | 38 - .../modbus-basic-config.component.scss | 18 - .../modbus-basic-config.component.ts | 76 - .../modbus-legacy-basic-config.component.ts | 80 - .../modbus-data-keys-panel.component.html | 270 ---- .../modbus-data-keys-panel.component.scss | 45 - .../modbus-data-keys-panel.component.ts | 306 ---- .../modbus-master-table.component.html | 150 -- .../modbus-master-table.component.scss | 90 -- .../modbus-master-table.component.ts | 245 --- .../modbus-security-config.component.html | 66 - .../modbus-security-config.component.ts | 163 -- .../modbus-slave-config.component.html | 274 ---- .../modbus-slave-config.component.ts | 283 ---- .../modbus-legacy-slave-dialog.component.ts | 84 -- .../modbus-slave-dialog.abstract.ts | 208 --- .../modbus-slave-dialog.component.html | 352 ----- .../modbus-slave-dialog.component.scss | 36 - .../modbus-slave-dialog.component.ts | 87 -- .../modbus-values.component.html | 129 -- .../modbus-values.component.scss | 23 - .../modbus-values/modbus-values.component.ts | 240 --- .../mqtt-basic-config.abstract.ts | 93 -- .../mqtt-basic-config.component.html | 41 - .../mqtt-basic-config.component.scss | 23 - .../mqtt-basic-config.component.ts | 97 -- .../mqtt-legacy-basic-config.component.ts | 117 -- .../broker-config-control.component.html | 86 -- .../broker-config-control.component.ts | 124 -- .../workers-config-control.component.html | 63 - .../workers-config-control.component.ts | 108 -- .../opc-server-config.component.html | 148 -- .../opc-server-config.component.scss | 20 - .../opc-server-config.component.ts | 152 -- .../opc-ua-basic-config.component.html | 30 - .../opc-ua-basic-config.component.scss | 23 - .../opc-ua-basic-config.component.ts | 88 -- .../opc-ua-legacy-basic-config.component.ts | 88 -- .../report-strategy.component.html | 57 - .../report-strategy.component.ts | 174 --- .../rest-connector-security.component.html | 65 - .../rest-connector-security.component.scss | 29 - .../rest-connector-security.component.ts | 132 -- .../modbus-rpc-parameters.component.html | 81 - .../modbus-rpc-parameters.component.scss | 20 - .../modbus-rpc-parameters.component.ts | 166 --- .../mqtt-rpc-parameters.component.html | 48 - .../mqtt-rpc-parameters.component.scss | 24 - .../mqtt-rpc-parameters.component.ts | 139 -- .../opc-rpc-parameters.component.html | 93 -- .../opc-rpc-parameters.component.scss | 32 - .../opc-rpc-parameters.component.ts | 169 --- .../security-config.component.html | 128 -- .../security-config.component.scss | 20 - .../security-config.component.ts | 177 --- .../type-value-panel.component.html | 103 -- .../type-value-panel.component.scss | 49 - .../type-value-panel.component.ts | 160 -- .../device-gateway-command.component.html | 53 - .../device-gateway-command.component.scss | 75 - .../device-gateway-command.component.ts | 42 - .../add-connector-dialog.component.html | 117 -- .../add-connector-dialog.component.scss | 22 - .../dialog/add-connector-dialog.component.ts | 149 -- .../dialog/mapping-dialog.component.html | 766 ---------- .../dialog/mapping-dialog.component.scss | 86 -- .../dialog/mapping-dialog.component.ts | 421 ------ .../gateway/gateway-connectors.component.html | 323 ---- .../gateway/gateway-connectors.component.scss | 156 -- .../gateway/gateway-connectors.component.ts | 897 ----------- .../lib/gateway/gateway-form.component.html | 266 ---- .../lib/gateway/gateway-form.component.scss | 36 - .../lib/gateway/gateway-form.component.ts | 426 ------ .../widget/lib/gateway/gateway-form.models.ts | 380 ----- .../lib/gateway/gateway-logs.component.html | 57 - .../lib/gateway/gateway-logs.component.scss | 56 - .../lib/gateway/gateway-logs.component.ts | 200 --- .../gateway-remote-configuration-dialog.html | 52 - .../gateway-remote-configuration-dialog.ts | 57 - ...service-rpc-connector-template-dialog.html | 54 - ...y-service-rpc-connector-template-dialog.ts | 63 - ...ice-rpc-connector-templates.component.html | 67 - ...ice-rpc-connector-templates.component.scss | 113 -- ...rvice-rpc-connector-templates.component.ts | 85 -- ...teway-service-rpc-connector.component.html | 413 ------ ...teway-service-rpc-connector.component.scss | 67 - ...gateway-service-rpc-connector.component.ts | 345 ----- .../gateway-service-rpc.component.html | 88 -- .../gateway-service-rpc.component.scss | 87 -- .../gateway/gateway-service-rpc.component.ts | 230 --- .../gateway/gateway-statistics.component.html | 86 -- .../gateway/gateway-statistics.component.scss | 87 -- .../gateway/gateway-statistics.component.ts | 291 ---- .../lib/gateway/gateway-widget.models.ts | 1307 ----------------- .../gateway/pipes/gateway-help-link.pipe.ts | 41 - .../pipes/gateway-port-tooltip.pipe.ts | 42 - .../pipes/latest-version-config.pipe.ts | 32 - .../pipes/rpc-template-array-view.pipe.ts | 28 - .../gateway-connector-version-mapping.util.ts | 51 - .../utils/modbus-version-mapping.util.ts | 106 -- .../utils/mqtt-version-mapping.util.ts | 217 --- .../gateway/utils/opc-version-mapping.util.ts | 143 -- .../widget/widget-components.module.ts | 138 -- 128 files changed, 19382 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-version-processor.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/opc-version-processor.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/models/gateway-configuration.models.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-legacy-slave-dialog.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.models.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/latest-version-config.pipe.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/modbus-version-mapping.util.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/mqtt-version-mapping.util.ts delete mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/opc-version-mapping.util.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract.ts deleted file mode 100644 index 4c90eeb01c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract.ts +++ /dev/null @@ -1,77 +0,0 @@ -/// -/// 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. -/// - -import { AfterViewInit, Directive, EventEmitter, inject, Input, OnDestroy, Output, TemplateRef } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, ValidationErrors, Validator } from '@angular/forms'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; - -@Directive() -export abstract class GatewayConnectorBasicConfigDirective - implements AfterViewInit, ControlValueAccessor, Validator, OnDestroy { - - @Input() generalTabContent: TemplateRef; - @Output() initialized = new EventEmitter(); - - basicFormGroup: FormGroup; - - protected fb = inject(FormBuilder); - protected onChange!: (value: OutputBasicConfig) => void; - protected onTouched!: () => void; - protected destroy$ = new Subject(); - - constructor() { - this.basicFormGroup = this.initBasicFormGroup(); - - this.basicFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe((value) => this.onBasicFormGroupChange(value)); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - ngAfterViewInit(): void { - this.initialized.emit(); - } - - validate(): ValidationErrors | null { - return this.basicFormGroup.valid ? null : { basicFormGroup: { valid: false } }; - } - - registerOnChange(fn: (value: OutputBasicConfig) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(config: OutputBasicConfig): void { - this.basicFormGroup.setValue(this.mapConfigToFormValue(config), { emitEvent: false }); - } - - protected onBasicFormGroupChange(value: InputBasicConfig): void { - this.onChange(this.getMappedValue(value)); - this.onTouched(); - } - - protected abstract mapConfigToFormValue(config: OutputBasicConfig): InputBasicConfig; - protected abstract getMappedValue(config: InputBasicConfig): OutputBasicConfig; - protected abstract initBasicFormGroup(): FormGroup; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-version-processor.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-version-processor.abstract.ts deleted file mode 100644 index 93baf124a3..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/gateway-connector-version-processor.abstract.ts +++ /dev/null @@ -1,69 +0,0 @@ -/// -/// 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. -/// - -import { GatewayConnector, GatewayVersion } from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { - GatewayConnectorVersionMappingUtil -} from '@home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util'; - -export abstract class GatewayConnectorVersionProcessor { - gatewayVersion: number; - configVersion: number; - - protected constructor(protected gatewayVersionIn: string | number, protected connector: GatewayConnector) { - this.gatewayVersion = GatewayConnectorVersionMappingUtil.parseVersion(this.gatewayVersionIn); - this.configVersion = GatewayConnectorVersionMappingUtil.parseVersion(this.connector.configVersion); - } - - getProcessedByVersion(): GatewayConnector { - if (!this.isVersionUpdateNeeded()) { - return this.connector; - } - - return this.processVersionUpdate(); - } - - private processVersionUpdate(): GatewayConnector { - if (this.isVersionUpgradeNeeded()) { - return this.getUpgradedVersion(); - } else if (this.isVersionDowngradeNeeded()) { - return this.getDowngradedVersion(); - } - - return this.connector; - } - - private isVersionUpdateNeeded(): boolean { - if (!this.gatewayVersion) { - return false; - } - - return this.configVersion !== this.gatewayVersion; - } - - private isVersionUpgradeNeeded(): boolean { - return this.gatewayVersion >= GatewayConnectorVersionMappingUtil.parseVersion(GatewayVersion.Current) - && (!this.configVersion || this.configVersion < this.gatewayVersion); - } - - private isVersionDowngradeNeeded(): boolean { - return this.configVersion && this.configVersion >= GatewayConnectorVersionMappingUtil.parseVersion(GatewayVersion.Current) - && (this.configVersion > this.gatewayVersion); - } - - protected abstract getDowngradedVersion(): GatewayConnector; - protected abstract getUpgradedVersion(): GatewayConnector; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract.ts deleted file mode 100644 index 8b05572659..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract.ts +++ /dev/null @@ -1,71 +0,0 @@ -/// -/// 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. -/// - -import { - GatewayConnector, - LegacySlaveConfig, - ModbusBasicConfig, - ModbusBasicConfig_v3_5_2, - ModbusLegacyBasicConfig, - ModbusLegacySlave, - ModbusMasterConfig, - ModbusSlave, -} from '../gateway-widget.models'; -import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract'; -import { ModbusVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/modbus-version-mapping.util'; - -export class ModbusVersionProcessor extends GatewayConnectorVersionProcessor { - - constructor( - protected gatewayVersionIn: string, - protected connector: GatewayConnector - ) { - super(gatewayVersionIn, connector); - } - - getUpgradedVersion(): GatewayConnector { - const configurationJson = this.connector.configurationJson; - return { - ...this.connector, - configurationJson: { - master: configurationJson.master?.slaves - ? ModbusVersionMappingUtil.mapMasterToUpgradedVersion(configurationJson.master as ModbusMasterConfig) - : { slaves: [] }, - slave: configurationJson.slave - ? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(configurationJson.slave as ModbusLegacySlave) - : {} as ModbusSlave, - }, - configVersion: this.gatewayVersionIn - } as GatewayConnector; - } - - getDowngradedVersion(): GatewayConnector { - const configurationJson = this.connector.configurationJson; - return { - ...this.connector, - configurationJson: { - ...configurationJson, - slave: configurationJson.slave - ? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(configurationJson.slave as ModbusSlave) - : {} as ModbusLegacySlave, - master: configurationJson.master?.slaves - ? ModbusVersionMappingUtil.mapMasterToDowngradedVersion(configurationJson.master as ModbusMasterConfig) - : { slaves: [] }, - }, - configVersion: this.gatewayVersionIn - } as GatewayConnector; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract.ts deleted file mode 100644 index 00fff92493..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract.ts +++ /dev/null @@ -1,101 +0,0 @@ -/// -/// 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. -/// - -import { isEqual } from '@core/utils'; -import { - GatewayConnector, - MQTTBasicConfig, - MQTTBasicConfig_v3_5_2, - MQTTLegacyBasicConfig, - RequestMappingData, - RequestType, -} from '../gateway-widget.models'; -import { MqttVersionMappingUtil } from '../utils/mqtt-version-mapping.util'; -import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract'; - -export class MqttVersionProcessor extends GatewayConnectorVersionProcessor { - - private readonly mqttRequestTypeKeys = Object.values(RequestType); - - constructor( - protected gatewayVersionIn: string, - protected connector: GatewayConnector - ) { - super(gatewayVersionIn, connector); - } - - getUpgradedVersion(): GatewayConnector { - const { - connectRequests, - disconnectRequests, - attributeRequests, - attributeUpdates, - serverSideRpc - } = this.connector.configurationJson as MQTTLegacyBasicConfig; - let configurationJson = { - ...this.connector.configurationJson, - requestsMapping: MqttVersionMappingUtil.mapRequestsToUpgradedVersion({ - connectRequests, - disconnectRequests, - attributeRequests, - attributeUpdates, - serverSideRpc - }), - mapping: MqttVersionMappingUtil.mapMappingToUpgradedVersion((this.connector.configurationJson as MQTTLegacyBasicConfig).mapping), - }; - - this.mqttRequestTypeKeys.forEach((key: RequestType) => { - const { [key]: removedValue, ...rest } = configurationJson as MQTTLegacyBasicConfig; - configurationJson = { ...rest } as any; - }); - - this.cleanUpConfigJson(configurationJson as MQTTBasicConfig_v3_5_2); - - return { - ...this.connector, - configurationJson, - configVersion: this.gatewayVersionIn - } as GatewayConnector; - } - - getDowngradedVersion(): GatewayConnector { - const { requestsMapping, mapping, ...restConfig } = this.connector.configurationJson as MQTTBasicConfig_v3_5_2; - - const updatedRequestsMapping = requestsMapping - ? MqttVersionMappingUtil.mapRequestsToDowngradedVersion(requestsMapping as Record) : {}; - const updatedMapping = MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping); - - return { - ...this.connector, - configurationJson: { - ...restConfig, - ...updatedRequestsMapping, - mapping: updatedMapping, - }, - configVersion: this.gatewayVersionIn - } as GatewayConnector; - } - - private cleanUpConfigJson(configurationJson: MQTTBasicConfig_v3_5_2): void { - if (isEqual(configurationJson.requestsMapping, {})) { - delete configurationJson.requestsMapping; - } - - if (isEqual(configurationJson.mapping, [])) { - delete configurationJson.mapping; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/opc-version-processor.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/opc-version-processor.abstract.ts deleted file mode 100644 index 541e26001f..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/abstract/opc-version-processor.abstract.ts +++ /dev/null @@ -1,56 +0,0 @@ -/// -/// 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. -/// - -import { - GatewayConnector, LegacyServerConfig, - OPCBasicConfig, - OPCBasicConfig_v3_5_2, - OPCLegacyBasicConfig, -} from '../gateway-widget.models'; -import { GatewayConnectorVersionProcessor } from './gateway-connector-version-processor.abstract'; -import { OpcVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/opc-version-mapping.util'; - -export class OpcVersionProcessor extends GatewayConnectorVersionProcessor { - - constructor( - protected gatewayVersionIn: string, - protected connector: GatewayConnector - ) { - super(gatewayVersionIn, connector); - } - - getUpgradedVersion(): GatewayConnector { - const server = this.connector.configurationJson.server as LegacyServerConfig; - return { - ...this.connector, - configurationJson: { - server: server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(server) : {}, - mapping: server?.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(server.mapping) : [], - }, - configVersion: this.gatewayVersionIn - } as GatewayConnector; - } - - getDowngradedVersion(): GatewayConnector { - return { - ...this.connector, - configurationJson: { - server: OpcVersionMappingUtil.mapServerToDowngradedVersion(this.connector.configurationJson as OPCBasicConfig_v3_5_2) - }, - configVersion: this.gatewayVersionIn - } as GatewayConnector; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.html deleted file mode 100644 index 843e67b0fe..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.html +++ /dev/null @@ -1,25 +0,0 @@ - - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.scss deleted file mode 100644 index 4825ff0a64..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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. - */ - -:host { - .config-container { - height: calc(100% - 60px); - padding: 8px; - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.ts deleted file mode 100644 index 16a0274049..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component.ts +++ /dev/null @@ -1,96 +0,0 @@ -/// -/// 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. -/// - -import { Component, forwardRef, OnDestroy } from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - FormControl, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - ValidationErrors, - Validators -} from '@angular/forms'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { GatewayConfigValue } from '@home/components/widget/lib/gateway/configuration/models/gateway-configuration.models'; - -@Component({ - selector: 'tb-gateway-advanced-configuration', - templateUrl: './gateway-advanced-configuration.component.html', - styleUrls: ['./gateway-advanced-configuration.component.scss'], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => GatewayAdvancedConfigurationComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => GatewayAdvancedConfigurationComponent), - multi: true - } - ], -}) -export class GatewayAdvancedConfigurationComponent implements OnDestroy, ControlValueAccessor, Validators { - - advancedFormControl: FormControl; - - private onChange: (value: unknown) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.advancedFormControl = this.fb.control(''); - this.advancedFormControl.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(value => { - this.onChange(value); - this.onTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: unknown) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(advancedConfig: GatewayConfigValue): void { - this.advancedFormControl.reset(advancedConfig, {emitEvent: false}); - } - - validate(): ValidationErrors | null { - return this.advancedFormControl.valid ? null : { - advancedFormControl: {valid: false} - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.html deleted file mode 100644 index af8dc3119c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.html +++ /dev/null @@ -1,814 +0,0 @@ - - - - -
-
-
- - {{ 'gateway.remote-configuration' | translate }} - -
-
- - {{ 'gateway.remote-shell' | translate }} - -
-
- - gateway.thingsboard-host - - info_outlined - - - {{ 'gateway.thingsboard-host-required' | translate }} - - - - gateway.thingsboard-port - - - {{ 'gateway.thingsboard-port-required' | translate }} - - - {{ 'gateway.thingsboard-port-min' | translate }} - - - {{ 'gateway.thingsboard-port-max' | translate }} - - - {{ 'gateway.thingsboard-port-pattern' | translate }} - - info_outlined - - -
-
-
-
security.security
- - - {{ securityType.value | translate }} - - - - security.access-token - - - {{ 'security.access-token-required' | translate }} - - - - info_outlined - - -
-
- - security.clientId - - - {{ 'security.clientId-required' | translate }} - - - - info_outlined - - - - security.username - - - {{ 'security.username-required' | translate }} - - - - info_outlined - - -
- - gateway.password - - - - info_outlined - - -
- - - -
-
-
-
-
- - -
-
-
- - gateway.logs.date-format - - - {{ 'gateway.logs.date-format-required' | translate }} - - info_outlined - - - - gateway.logs.log-format - - - {{ 'gateway.logs.log-format-required' | translate }} - - info_outlined - - -
-
-
-
gateway.logs.remote
-
- - {{ 'gateway.logs.remote-logs' | translate }} - -
- - gateway.logs.level - - {{ logLevel }} - - -
-
-
gateway.logs.local
- - {{ localLogsConfigTranslateMap.get(logConfig) }} - - -
- - gateway.logs.level - - {{ logLevel }} - - - - gateway.logs.file-path - - - {{ 'gateway.logs.file-path-required' | translate }} - - -
-
-
- - gateway.logs.saving-period - - - {{ 'gateway.logs.saving-period-required' | translate }} - - - {{ 'gateway.logs.saving-period-min' | translate }} - - - - - - {{ period.value | translate }} - - - -
- - gateway.logs.backup-count - - - {{ 'gateway.logs.backup-count-required' | translate }} - - - {{ 'gateway.logs.backup-count-min' | translate }} - - info_outlined - - -
-
-
-
-
-
- - -
-
-
gateway.storage
-
gateway.hints.storage
- - - {{ storageTypesTranslationMap.get(storageType) | translate }} - - -
{{ 'gateway.hints.' + basicFormGroup.get('storage.type').value | translate }}
- -
- - gateway.storage-read-record-count - - - {{ 'gateway.storage-read-record-count-required' | translate }} - - - {{ 'gateway.storage-read-record-count-min' | translate }} - - - {{ 'gateway.storage-read-record-count-pattern' | translate }} - - info_outlined - - - - gateway.storage-max-records - - - {{ 'gateway.storage-max-records-required' | translate }} - - - {{ 'gateway.storage-max-records-min' | translate }} - - - {{ 'gateway.storage-max-records-pattern' | translate }} - - info_outlined - - -
-
-
- - gateway.storage-data-folder-path - - - {{ 'gateway.storage-data-folder-path-required' | translate }} - - info_outlined - - - - gateway.storage-max-files - - - {{ 'gateway.storage-max-files-required' | translate }} - - - {{ 'gateway.storage-max-files-min' | translate }} - - - {{ 'gateway.storage-max-files-pattern' | translate }} - - info_outlined - - -
-
- - gateway.storage-max-read-record-count - - - {{ 'gateway.storage-max-read-record-count-required' | translate }} - - - {{ 'gateway.storage-max-read-record-count-min' | translate }} - - - {{ 'gateway.storage-max-read-record-count-pattern' | translate }} - - info_outlined - - - - gateway.storage-max-file-records - - - {{ 'gateway.storage-max-records-required' | translate }} - - - {{ 'gateway.storage-max-records-min' | translate }} - - - {{ 'gateway.storage-max-records-pattern' | translate }} - - info_outlined - - -
-
-
-
- - gateway.storage-path - - - {{ 'gateway.storage-path-required' | translate }} - - info_outlined - - - - gateway.messages-ttl-check-in-hours - - - {{ 'gateway.messages-ttl-check-in-hours-required' | translate }} - - - {{ 'gateway.messages-ttl-check-in-hours-min' | translate }} - - - {{ 'gateway.messages-ttl-check-in-hours-pattern' | translate }} - - info_outlined - - -
- - gateway.messages-ttl-in-days - - - {{ 'gateway.messages-ttl-in-days-required' | translate }} - - - {{ 'gateway.messages-ttl-in-days-min' | translate }} - - - {{ 'gateway.messages-ttl-in-days-pattern' | translate }} - - info_outlined - - -
-
-
-
-
-
- - -
-
- - {{ 'gateway.grpc' | translate }} - -
- - {{ 'gateway.permit-without-calls' | translate }} - -
-
-
- - gateway.server-port - - info_outlined - - - {{ 'gateway.thingsboard-port-required' | translate }} - - - {{ 'gateway.thingsboard-port-min' | translate }} - - - {{ 'gateway.thingsboard-port-max' | translate }} - - - {{ 'gateway.thingsboard-port-pattern' | translate }} - - - - gateway.grpc-keep-alive-timeout - - info_outlined - - - {{ 'gateway.grpc-keep-alive-timeout-required' | translate }} - - - {{ 'gateway.grpc-keep-alive-timeout-min' | translate }} - - - {{ 'gateway.grpc-keep-alive-timeout-pattern' | translate }} - - -
-
- - gateway.grpc-keep-alive - - info_outlined - - - {{ 'gateway.grpc-keep-alive-required' | translate }} - - - {{ 'gateway.grpc-keep-alive-min' | translate }} - - - {{ 'gateway.grpc-keep-alive-pattern' | translate }} - - - - gateway.grpc-min-time-between-pings - - info_outlined - - - {{ 'gateway.grpc-min-time-between-pings-required' | translate }} - - - {{ 'gateway.grpc-min-time-between-pings-min' | translate }} - - - {{ 'gateway.grpc-min-time-between-pings-pattern' | translate }} - - -
-
- - gateway.grpc-max-pings-without-data - - info_outlined - - - {{ 'gateway.grpc-max-pings-without-data-required' | translate }} - - - {{ 'gateway.grpc-max-pings-without-data-min' | translate }} - - - {{ 'gateway.grpc-max-pings-without-data-pattern' | translate }} - - - - gateway.grpc-min-ping-interval-without-data - - info_outlined - - - {{ 'gateway.grpc-min-ping-interval-without-data-required' | translate }} - - - {{ 'gateway.grpc-min-ping-interval-without-data-min' | translate }} - - - {{ 'gateway.grpc-min-ping-interval-without-data-pattern' | translate }} - - -
-
-
-
-
-
- - -
-
- - {{ 'gateway.statistics.statistics' | translate }} - - - gateway.statistics.send-period - - - {{ 'gateway.statistics.send-period-required' | translate }} - - - {{ 'gateway.statistics.send-period-min' | translate }} - - - {{ 'gateway.statistics.send-period-pattern' | translate }} - - -
-
-
gateway.statistics.commands
-
gateway.hints.commands
- -
-
-
- - gateway.statistics.attribute-name - - - {{ 'gateway.statistics.attribute-name-required' | translate }} - - info_outlined - - - - gateway.statistics.timeout - - - {{ 'gateway.statistics.timeout-required' | translate }} - - - {{ 'gateway.statistics.timeout-min' | translate }} - - - {{ 'gateway.statistics.timeout-pattern' | translate }} - - info_outlined - - -
- - gateway.statistics.command - - - {{ 'gateway.statistics.command-required' | translate }} - - - {{ 'gateway.statistics.command-pattern' | translate }} - - info_outlined - - -
- -
- -
-
-
-
-
- - -
-
-
- - {{ 'gateway.checking-device-activity' | translate }} - -
-
- - gateway.inactivity-timeout-seconds - - - {{ 'gateway.inactivity-timeout-seconds-required' | translate }} - - - {{ 'gateway.inactivity-timeout-seconds-min' | translate }} - - - {{ 'gateway.inactivity-timeout-seconds-pattern' | translate }} - - info_outlined - - - - gateway.inactivity-check-period-seconds - - - {{ 'gateway.inactivity-check-period-seconds-required' | translate }} - - - {{ 'gateway.inactivity-check-period-seconds-min' | translate }} - - - {{ 'gateway.inactivity-check-period-seconds-pattern' | translate }} - - info_outlined - - -
-
-
-
gateway.advanced
-
- - gateway.min-pack-send-delay - - - {{ 'gateway.min-pack-send-delay-required' | translate }} - - - {{ 'gateway.min-pack-send-delay-min' | translate }} - - - {{ 'gateway.min-pack-send-delay-pattern' | translate }} - - info_outlined - - - - gateway.mqtt-qos - - - {{ 'gateway.mqtt-qos-required' | translate }} - - - {{ 'gateway.mqtt-qos-range' | translate }} - - - {{ 'gateway.mqtt-qos-range' | translate }} - - info_outlined - - -
-
- - gateway.statistics.check-connectors-configuration - - - {{ 'gateway.statistics.check-connectors-configuration-required' | translate }} - - - {{ 'gateway.statistics.check-connectors-configuration-min' | translate }} - - - {{ 'gateway.statistics.check-connectors-configuration-pattern' | translate }} - - - - gateway.statistics.max-payload-size-bytes - - - {{ 'gateway.statistics.max-payload-size-bytes-required' | translate }} - - - {{ 'gateway.statistics.max-payload-size-bytes-min' | translate }} - - - {{ 'gateway.statistics.max-payload-size-bytes-pattern' | translate }} - - info_outlined - - -
-
- - gateway.statistics.min-pack-size-to-send - - - {{ 'gateway.statistics.min-pack-size-to-send-required' | translate }} - - - {{ 'gateway.statistics.min-pack-size-to-send-min' | translate }} - - - {{ 'gateway.statistics.min-pack-size-to-send-pattern' | translate }} - - info_outlined - - -
-
-
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.scss deleted file mode 100644 index 9807fb71c4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.scss +++ /dev/null @@ -1,103 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: grid; - grid-template-rows: min-content minmax(auto, 1fr) min-content; - - .configuration-block { - display: flex; - flex-direction: column; - gap: 16px; - max-height: 70vh; - } - - .dialog-mode { - .configuration-block { - max-height: 60vh; - } - } - - .mat-toolbar { - grid-row: 1; - background: transparent; - color: rgba(0, 0, 0, .87) !important; - } - - .tab-group-block { - min-width: 0; - height: 100%; - min-height: 0; - grid-row: 2; - } - - .toggle-group { - margin-right: auto; - } - - .first-capital { - text-transform: capitalize; - } - - textarea { - resize: none; - } - - .saving-period { - flex: 1; - } - - .statistics-container { - width: 100%; - - .command-container { - width: 100%; - } - } - - mat-form-field { - mat-error { - display: none !important; - } - - mat-error:first-child { - display: block !important; - } - } -} - -:host ::ng-deep { - .pointer-event { - pointer-events: all; - } - - .toggle-group span { - padding: 0 25px; - } - - .mat-mdc-form-field-icon-suffix { - color: #E0E0E0; - &:hover { - color: #9E9E9E; - } - } - - .mat-mdc-form-field-icon-suffix { - display: flex; - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.ts deleted file mode 100644 index 1cff165f8a..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component.ts +++ /dev/null @@ -1,568 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectorRef, - Component, - EventEmitter, - forwardRef, - Input, - OnDestroy, - Output -} from '@angular/core'; -import { - ControlValueAccessor, - FormArray, - FormBuilder, - FormControl, - FormGroup, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - ValidationErrors, - ValidatorFn, - Validators -} from '@angular/forms'; -import { EntityId } from '@shared/models/id/entity-id'; -import { MatDialog } from '@angular/material/dialog'; -import { - GatewayRemoteConfigurationDialogComponent, - GatewayRemoteConfigurationDialogData -} from '@home/components/widget/lib/gateway/gateway-remote-configuration-dialog'; -import { DeviceService } from '@core/http/device.service'; -import { Subject } from 'rxjs'; -import { take, takeUntil } from 'rxjs/operators'; -import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; -import { - GatewayLogLevel, - GecurityTypesTranslationsMap, - LocalLogsConfigTranslateMap, - LocalLogsConfigs, - LogSavingPeriod, - LogSavingPeriodTranslations, - SecurityTypes, - StorageTypes, - StorageTypesTranslationMap, -} from '../../gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { coerceBoolean } from '@shared/decorators/coercion'; -import { - GatewayConfigCommand, - GatewayConfigSecurity, - GatewayConfigValue, - LogConfig -} from '@home/components/widget/lib/gateway/configuration/models/gateway-configuration.models'; - -@Component({ - selector: 'tb-gateway-basic-configuration', - templateUrl: './gateway-basic-configuration.component.html', - styleUrls: ['./gateway-basic-configuration.component.scss'], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => GatewayBasicConfigurationComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => GatewayBasicConfigurationComponent), - multi: true - } - ], -}) -export class GatewayBasicConfigurationComponent implements OnDestroy, ControlValueAccessor, Validators { - - @Input() - device: EntityId; - - @coerceBoolean() - @Input() - dialogMode = false; - - @Output() - initialCredentialsUpdated = new EventEmitter(); - - StorageTypes = StorageTypes; - storageTypes = Object.values(StorageTypes); - storageTypesTranslationMap = StorageTypesTranslationMap; - logSavingPeriods = LogSavingPeriodTranslations; - localLogsConfigs = Object.keys(LocalLogsConfigs) as LocalLogsConfigs[]; - localLogsConfigTranslateMap = LocalLogsConfigTranslateMap; - securityTypes = GecurityTypesTranslationsMap; - gatewayLogLevel = Object.values(GatewayLogLevel); - - logSelector: FormControl; - basicFormGroup: FormGroup; - - private onChange: (value: GatewayConfigValue) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, - private deviceService: DeviceService, - private cd: ChangeDetectorRef, - private dialog: MatDialog) { - this.initBasicFormGroup(); - this.observeFormChanges(); - this.basicFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(value => { - this.onChange(value); - this.onTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: GatewayConfigValue) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(basicConfig: GatewayConfigValue): void { - this.basicFormGroup.patchValue(basicConfig, {emitEvent: false}); - this.checkAndFetchCredentials(basicConfig?.thingsboard?.security ?? {} as GatewayConfigSecurity); - if (basicConfig?.grpc) { - this.toggleRpcFields(basicConfig.grpc.enabled); - } - const commands = basicConfig?.thingsboard?.statistics?.commands ?? []; - commands.forEach((command: GatewayConfigCommand) => this.addCommand(command, false)); - } - - validate(): ValidationErrors | null { - return this.basicFormGroup.valid ? null : { - basicFormGroup: {valid: false} - }; - } - - private atLeastOneRequired(validator: ValidatorFn, controls: string[] = null) { - return (group: FormGroup): ValidationErrors | null => { - if (!controls) { - controls = Object.keys(group.controls); - } - const hasAtLeastOne = group?.controls && controls.some(k => !validator(group.controls[k])); - - return hasAtLeastOne ? null : {atLeastOne: true}; - }; - } - - private toggleRpcFields(enable: boolean): void { - const grpcGroup = this.basicFormGroup.get('grpc') as FormGroup; - if (enable) { - grpcGroup.get('serverPort').enable({emitEvent: false}); - grpcGroup.get('keepAliveTimeMs').enable({emitEvent: false}); - grpcGroup.get('keepAliveTimeoutMs').enable({emitEvent: false}); - grpcGroup.get('keepalivePermitWithoutCalls').enable({emitEvent: false}); - grpcGroup.get('maxPingsWithoutData').enable({emitEvent: false}); - grpcGroup.get('minTimeBetweenPingsMs').enable({emitEvent: false}); - grpcGroup.get('minPingIntervalWithoutDataMs').enable({emitEvent: false}); - } else { - grpcGroup.get('serverPort').disable({emitEvent: false}); - grpcGroup.get('keepAliveTimeMs').disable({emitEvent: false}); - grpcGroup.get('keepAliveTimeoutMs').disable({emitEvent: false}); - grpcGroup.get('keepalivePermitWithoutCalls').disable({emitEvent: false}); - grpcGroup.get('maxPingsWithoutData').disable({emitEvent: false}); - grpcGroup.get('minTimeBetweenPingsMs').disable({emitEvent: false}); - grpcGroup.get('minPingIntervalWithoutDataMs').disable({emitEvent: false}); - } - } - - private addLocalLogConfig(name: string, config: LogConfig): void { - const localLogsFormGroup = this.basicFormGroup.get('logs.local') as FormGroup; - const configGroup = this.fb.group({ - logLevel: [config.logLevel || GatewayLogLevel.INFO, [Validators.required]], - filePath: [config.filePath || './logs', [Validators.required]], - backupCount: [config.backupCount || 7, [Validators.required, Validators.min(0)]], - savingTime: [config.savingTime || 3, [Validators.required, Validators.min(0)]], - savingPeriod: [config.savingPeriod || LogSavingPeriod.days, [Validators.required]] - }); - localLogsFormGroup.addControl(name, configGroup); - } - - getLogFormGroup(value: string): FormGroup { - return this.basicFormGroup.get(`logs.local.${value}`) as FormGroup; - } - - commandFormArray(): FormArray { - return this.basicFormGroup.get('thingsboard.statistics.commands') as FormArray; - } - - removeCommandControl(index: number, event: PointerEvent): void { - if (event.pointerType === '') { - return; - } - this.commandFormArray().removeAt(index); - this.basicFormGroup.markAsDirty(); - } - - private removeAllSecurityValidators(): void { - const securityGroup = this.basicFormGroup.get('thingsboard.security') as FormGroup; - securityGroup.clearValidators(); - for (const controlsKey in securityGroup.controls) { - if (controlsKey !== 'type') { - securityGroup.controls[controlsKey].clearValidators(); - securityGroup.controls[controlsKey].setErrors(null); - securityGroup.controls[controlsKey].updateValueAndValidity(); - } - } - } - - private removeAllStorageValidators(): void { - const storageGroup = this.basicFormGroup.get('storage') as FormGroup; - for (const storageKey in storageGroup.controls) { - if (storageKey !== 'type') { - storageGroup.controls[storageKey].clearValidators(); - storageGroup.controls[storageKey].setErrors(null); - storageGroup.controls[storageKey].updateValueAndValidity(); - } - } - } - - - private openConfigurationConfirmDialog(): void { - this.deviceService.getDevice(this.device.id).pipe(takeUntil(this.destroy$)).subscribe(gateway => { - this.dialog.open - (GatewayRemoteConfigurationDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - gatewayName: gateway.name - } - }).afterClosed().pipe(take(1)).subscribe( - (res) => { - if (!res) { - this.basicFormGroup.get('thingsboard.remoteConfiguration').setValue(true, {emitEvent: false}); - } - } - ); - }); - } - - addCommand(command?: GatewayConfigCommand, emitEvent: boolean = true): void { - const { attributeOnGateway = null, command: cmd = null, timeout = null } = command || {}; - - const commandFormGroup = this.fb.group({ - attributeOnGateway: [attributeOnGateway, [Validators.required, Validators.pattern(/^[^.\s]+$/)]], - command: [cmd, [Validators.required, Validators.pattern(/^(?=\S).*\S$/)]], - timeout: [timeout, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/), Validators.pattern(/^[^.\s]+$/)]] - }); - - this.commandFormArray().push(commandFormGroup, { emitEvent }); - } - - private initBasicFormGroup(): void { - this.basicFormGroup = this.fb.group({ - thingsboard: this.initThingsboardFormGroup(), - storage: this.initStorageFormGroup(), - grpc: this.initGrpcFormGroup(), - connectors: this.fb.array([]), - logs: this.initLogsFormGroup(), - }); - } - - private initThingsboardFormGroup(): FormGroup { - return this.fb.group({ - host: [window.location.hostname, [Validators.required, Validators.pattern(/^[^\s]+$/)]], - port: [1883, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]], - remoteShell: [false], - remoteConfiguration: [true], - checkConnectorsConfigurationInSeconds: [60, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - statistics: this.fb.group({ - enable: [true], - statsSendPeriodInSeconds: [3600, [Validators.required, Validators.min(60), Validators.pattern(/^-?[0-9]+$/)]], - commands: this.fb.array([]) - }), - maxPayloadSizeBytes: [8196, [Validators.required, Validators.min(100), Validators.pattern(/^-?[0-9]+$/)]], - minPackSendDelayMS: [50, [Validators.required, Validators.min(10), Validators.pattern(/^-?[0-9]+$/)]], - minPackSizeToSend: [500, [Validators.required, Validators.min(100), Validators.pattern(/^-?[0-9]+$/)]], - handleDeviceRenaming: [true], - checkingDeviceActivity: this.initCheckingDeviceActivityFormGroup(), - security: this.initSecurityFormGroup(), - qos: [1, [Validators.required, Validators.min(0), Validators.max(1), Validators.pattern(/^[^.\s]+$/)]] - }); - } - - private initStorageFormGroup(): FormGroup { - return this.fb.group({ - type: [StorageTypes.MEMORY, [Validators.required]], - read_records_count: [100, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - max_records_count: [100000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - data_folder_path: ['./data/', [Validators.required]], - max_file_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - max_read_records_count: [10, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - max_records_per_file: [10000, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - data_file_path: ['./data/data.db', [Validators.required]], - messages_ttl_check_in_hours: [1, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - messages_ttl_in_days: [7, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]] - }); - } - - private initGrpcFormGroup(): FormGroup { - return this.fb.group({ - enabled: [false], - serverPort: [9595, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]], - keepAliveTimeMs: [10000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - keepAliveTimeoutMs: [5000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - keepalivePermitWithoutCalls: [true], - maxPingsWithoutData: [0, [Validators.required, Validators.min(0), Validators.pattern(/^-?[0-9]+$/)]], - minTimeBetweenPingsMs: [10000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - minPingIntervalWithoutDataMs: [5000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]] - }); - } - - private initLogsFormGroup(): FormGroup { - return this.fb.group({ - dateFormat: ['%Y-%m-%d %H:%M:%S', [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)]], - logFormat: [ - '%(asctime)s - |%(levelname)s| - [%(filename)s] - %(module)s - %(funcName)s - %(lineno)d - %(message)s', - [Validators.required, Validators.pattern(/^[^\s].*[^\s]$/)] - ], - type: ['remote', [Validators.required]], - remote: this.fb.group({ - enabled: [false], - logLevel: [GatewayLogLevel.INFO, [Validators.required]] - }), - local: this.fb.group({}) - }); - } - - private initCheckingDeviceActivityFormGroup(): FormGroup { - return this.fb.group({ - checkDeviceInactivity: [false], - inactivityTimeoutSeconds: [200, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - inactivityCheckPeriodSeconds: [500, [Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]] - }); - } - - private initSecurityFormGroup(): FormGroup { - return this.fb.group({ - type: [SecurityTypes.ACCESS_TOKEN, [Validators.required]], - accessToken: [null, [Validators.required, Validators.pattern(/^[^.\s]+$/)]], - clientId: [null, [Validators.pattern(/^[^.\s]+$/)]], - username: [null, [Validators.pattern(/^[^.\s]+$/)]], - password: [null, [Validators.pattern(/^[^.\s]+$/)]], - caCert: [null], - cert: [null], - privateKey: [null] - }); - } - - private observeFormChanges(): void { - this.observeSecurityPasswordChanges(); - this.observeRemoteConfigurationChanges(); - this.observeDeviceActivityChanges(); - this.observeSecurityTypeChanges(); - this.observeStorageTypeChanges(); - } - - private observeSecurityPasswordChanges(): void { - const securityUsername = this.basicFormGroup.get('thingsboard.security.username'); - this.basicFormGroup.get('thingsboard.security.password').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(password => { - if (password && password !== '') { - securityUsername.setValidators([Validators.required]); - } else { - securityUsername.clearValidators(); - } - securityUsername.updateValueAndValidity({ emitEvent: false }); - }); - } - - private observeRemoteConfigurationChanges(): void { - this.basicFormGroup.get('thingsboard.remoteConfiguration').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(enabled => { - if (!enabled) { - this.openConfigurationConfirmDialog(); - } - }); - - this.logSelector = this.fb.control(LocalLogsConfigs.service); - for (const key of Object.keys(LocalLogsConfigs)) { - this.addLocalLogConfig(key, {} as LogConfig); - } - } - - private observeDeviceActivityChanges(): void { - const checkingDeviceActivityGroup = this.basicFormGroup.get('thingsboard.checkingDeviceActivity') as FormGroup; - checkingDeviceActivityGroup.get('checkDeviceInactivity').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(enabled => { - checkingDeviceActivityGroup.updateValueAndValidity(); - const validators = [Validators.min(1), Validators.required, Validators.pattern(/^-?[0-9]+$/)]; - - if (enabled) { - checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').setValidators(validators); - checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').setValidators(validators); - } else { - checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').clearValidators(); - checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').clearValidators(); - } - checkingDeviceActivityGroup.get('inactivityTimeoutSeconds').updateValueAndValidity({ emitEvent: false }); - checkingDeviceActivityGroup.get('inactivityCheckPeriodSeconds').updateValueAndValidity({ emitEvent: false }); - }); - - this.basicFormGroup.get('grpc.enabled').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => { - this.toggleRpcFields(value); - }); - } - - private observeSecurityTypeChanges(): void { - const securityGroup = this.basicFormGroup.get('thingsboard.security') as FormGroup; - - securityGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(type => { - this.removeAllSecurityValidators(); - - switch (type) { - case SecurityTypes.ACCESS_TOKEN: - this.addAccessTokenValidators(securityGroup); - break; - case SecurityTypes.TLS_PRIVATE_KEY: - this.addTlsPrivateKeyValidators(securityGroup); - break; - case SecurityTypes.TLS_ACCESS_TOKEN: - this.addTlsAccessTokenValidators(securityGroup); - break; - case SecurityTypes.USERNAME_PASSWORD: - securityGroup.addValidators([this.atLeastOneRequired(Validators.required, ['clientId', 'username'])]); - break; - } - - securityGroup.updateValueAndValidity(); - }); - - ['caCert', 'privateKey', 'cert'].forEach(field => { - securityGroup.get(field).valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => this.cd.detectChanges()); - }); - } - - private observeStorageTypeChanges(): void { - const storageGroup = this.basicFormGroup.get('storage') as FormGroup; - - storageGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(type => { - this.removeAllStorageValidators(); - - switch (type) { - case StorageTypes.MEMORY: - this.addMemoryStorageValidators(storageGroup); - break; - case StorageTypes.FILE: - this.addFileStorageValidators(storageGroup); - break; - case StorageTypes.SQLITE: - this.addSqliteStorageValidators(storageGroup); - break; - } - }); - } - - private addAccessTokenValidators(group: FormGroup): void { - group.get('accessToken').addValidators([Validators.required, Validators.pattern(/^[^.\s]+$/)]); - group.get('accessToken').updateValueAndValidity(); - } - - private addTlsPrivateKeyValidators(group: FormGroup): void { - ['caCert', 'privateKey', 'cert'].forEach(field => { - group.get(field).addValidators([Validators.required]); - group.get(field).updateValueAndValidity(); - }); - } - - private addTlsAccessTokenValidators(group: FormGroup): void { - this.addAccessTokenValidators(group); - group.get('caCert').addValidators([Validators.required]); - group.get('caCert').updateValueAndValidity(); - } - - private addMemoryStorageValidators(group: FormGroup): void { - group.get('read_records_count').addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]); - group.get('max_records_count').addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]); - group.get('read_records_count').updateValueAndValidity({ emitEvent: false }); - group.get('max_records_count').updateValueAndValidity({ emitEvent: false }); - } - - private addFileStorageValidators(group: FormGroup): void { - ['max_file_count', 'max_read_records_count', 'max_records_per_file'].forEach(field => { - group.get(field).addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]); - group.get(field).updateValueAndValidity({ emitEvent: false }); - }); - } - - private addSqliteStorageValidators(group: FormGroup): void { - ['messages_ttl_check_in_hours', 'messages_ttl_in_days'].forEach(field => { - group.get(field).addValidators([Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]); - group.get(field).updateValueAndValidity({ emitEvent: false }); - }); - } - - private checkAndFetchCredentials(security: GatewayConfigSecurity): void { - if (security.type === SecurityTypes.TLS_PRIVATE_KEY) { - return; - } - - this.deviceService.getDeviceCredentials(this.device.id).pipe(takeUntil(this.destroy$)).subscribe(credentials => { - this.initialCredentialsUpdated.emit(credentials); - this.updateSecurityType(security, credentials); - this.updateCredentials(credentials, security); - }); - } - - private updateSecurityType(security, credentials: DeviceCredentials): void { - const isAccessToken = credentials.credentialsType === DeviceCredentialsType.ACCESS_TOKEN - || security.type === SecurityTypes.TLS_ACCESS_TOKEN; - const securityType = isAccessToken - ? (security.type === SecurityTypes.TLS_ACCESS_TOKEN ? SecurityTypes.TLS_ACCESS_TOKEN : SecurityTypes.ACCESS_TOKEN) - : (credentials.credentialsType === DeviceCredentialsType.MQTT_BASIC ? SecurityTypes.USERNAME_PASSWORD : null); - - if (securityType) { - this.basicFormGroup.get('thingsboard.security.type').setValue(securityType, { emitEvent: false }); - } - } - - private updateCredentials(credentials: DeviceCredentials, security: GatewayConfigSecurity): void { - switch (credentials.credentialsType) { - case DeviceCredentialsType.ACCESS_TOKEN: - this.updateAccessTokenCredentials(credentials, security); - break; - case DeviceCredentialsType.MQTT_BASIC: - this.updateMqttBasicCredentials(credentials); - break; - case DeviceCredentialsType.X509_CERTIFICATE: - break; - } - } - - private updateAccessTokenCredentials(credentials: DeviceCredentials, security: GatewayConfigSecurity): void { - this.basicFormGroup.get('thingsboard.security.accessToken').setValue(credentials.credentialsId, { emitEvent: false }); - if (security.type === SecurityTypes.TLS_ACCESS_TOKEN) { - this.basicFormGroup.get('thingsboard.security.caCert').setValue(security.caCert, { emitEvent: false }); - } - } - - private updateMqttBasicCredentials(credentials: DeviceCredentials): void { - const parsedValue = JSON.parse(credentials.credentialsValue); - this.basicFormGroup.get('thingsboard.security.clientId').setValue(parsedValue.clientId, { emitEvent: false }); - this.basicFormGroup.get('thingsboard.security.username').setValue(parsedValue.userName, { emitEvent: false }); - this.basicFormGroup.get('thingsboard.security.password').setValue(parsedValue.password, { emitEvent: false }); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.html deleted file mode 100644 index 7d8e0e78b3..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.html +++ /dev/null @@ -1,64 +0,0 @@ - -
-
- -
-

gateway.gateway-configuration

-
- - - {{ 'gateway.basic' | translate }} - - - {{ 'gateway.advanced' | translate }} - - - -
-
-
- - -
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.scss deleted file mode 100644 index 4d1d62ddad..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.scss +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - overflow: hidden; - - .page-header.mat-toolbar { - background: transparent; - color: rgba(0, 0, 0, .87) !important; - } - - .actions { - grid-row: 3; - padding: 8px 16px 8px 8px; - display: flex; - gap: 8px; - justify-content: flex-end; - position: absolute; - bottom: 0; - right: 0; - z-index: 1; - background: white; - width: 100%; - } - - .gateway-config-container { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; - } - - .content-wrapper { - flex: 1; - } - - .toolbar-actions { - display: flex; - align-items: center; - } -} - -.dialog-toggle { - ::ng-deep.mat-button-toggle-button { - color: rgba(255, 255, 255, .75); - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.ts deleted file mode 100644 index 670fd57bd4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/gateway-configuration.component.ts +++ /dev/null @@ -1,396 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectorRef, Component, Input, AfterViewInit, OnDestroy } from '@angular/core'; -import { - FormBuilder, - FormGroup, -} from '@angular/forms'; -import { EntityId } from '@shared/models/id/entity-id'; -import { MatDialogRef } from '@angular/material/dialog'; -import { AttributeService } from '@core/http/attribute.service'; -import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { DeviceService } from '@core/http/device.service'; -import { Observable, of, Subject } from 'rxjs'; -import { mergeMap, switchMap, takeUntil } from 'rxjs/operators'; -import { DeviceCredentials, DeviceCredentialsType } from '@shared/models/device.models'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { - GatewayLogLevel, - SecurityTypes, - ConfigurationModes, - LocalLogsConfigs, - LogSavingPeriod, Attribute -} from '../gateway-widget.models'; -import { deepTrim, isEqual } from '@core/utils'; -import { - GatewayConfigSecurity, - GatewayConfigValue, - GatewayGeneralConfig, - GatewayGRPCConfig, - GatewayLogsConfig, - GatewayStorageConfig, - LocalLogs, - LogAttribute, - LogConfig, -} from './models/gateway-configuration.models'; -import { DeviceId } from '@shared/models/id/device-id'; - -@Component({ - selector: 'tb-gateway-configuration', - templateUrl: './gateway-configuration.component.html', - styleUrls: ['./gateway-configuration.component.scss'] -}) -export class GatewayConfigurationComponent implements AfterViewInit, OnDestroy { - - @Input() device: EntityId; - - @Input() dialogRef: MatDialogRef; - - initialCredentials: DeviceCredentials; - gatewayConfigGroup: FormGroup; - ConfigurationModes = ConfigurationModes; - - private destroy$ = new Subject(); - private readonly gatewayConfigAttributeKeys = - ['general_configuration', 'grpc_configuration', 'logs_configuration', 'storage_configuration', 'RemoteLoggingLevel', 'mode']; - - constructor(private fb: FormBuilder, - private attributeService: AttributeService, - private deviceService: DeviceService, - private cd: ChangeDetectorRef - ) { - - this.gatewayConfigGroup = this.fb.group({ - basicConfig: [], - advancedConfig: [], - mode: [ConfigurationModes.BASIC], - }); - - this.observeAlignConfigs(); - } - - ngAfterViewInit(): void { - this.fetchConfigAttribute(this.device); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - saveConfig(): void { - const { mode, advancedConfig } = deepTrim(this.removeEmpty(this.gatewayConfigGroup.value)); - const value = { mode, ...advancedConfig as GatewayConfigValue }; - value.thingsboard.statistics.commands = Object.values(value.thingsboard.statistics.commands ?? []); - const attributes = this.generateAttributes(value); - - this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, attributes).pipe( - switchMap(_ => this.updateCredentials(value.thingsboard.security)), - takeUntil(this.destroy$), - ).subscribe(() => { - if (this.dialogRef) { - this.dialogRef.close(); - } else { - this.gatewayConfigGroup.markAsPristine(); - this.cd.detectChanges(); - } - }); - } - - private observeAlignConfigs(): void { - this.gatewayConfigGroup.get('basicConfig').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => { - const advancedControl = this.gatewayConfigGroup.get('advancedConfig'); - - if (!isEqual(advancedControl.value, value) && this.gatewayConfigGroup.get('mode').value === ConfigurationModes.BASIC) { - advancedControl.patchValue(value, {emitEvent: false}); - } - }); - - this.gatewayConfigGroup.get('advancedConfig').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => { - const basicControl = this.gatewayConfigGroup.get('basicConfig'); - - if (!isEqual(basicControl.value, value) && this.gatewayConfigGroup.get('mode').value === ConfigurationModes.ADVANCED) { - basicControl.patchValue(value, {emitEvent: false}); - } - }); - } - - private generateAttributes(value: GatewayConfigValue): Attribute[] { - const attributes = []; - - const addAttribute = (key: string, val: unknown) => { - attributes.push({ key, value: val }); - }; - - const addTimestampedAttribute = (key: string, val: unknown) => { - val = {...val as Record, ts: new Date().getTime()}; - addAttribute(key, val); - }; - - addAttribute('RemoteLoggingLevel', value.logs?.remote?.enabled ? value.logs.remote.logLevel : GatewayLogLevel.NONE); - - delete value.connectors; - addAttribute('logs_configuration', this.generateLogsFile(value.logs)); - - addTimestampedAttribute('grpc_configuration', value.grpc); - addTimestampedAttribute('storage_configuration', value.storage); - addTimestampedAttribute('general_configuration', value.thingsboard); - - addAttribute('mode', value.mode); - - return attributes; - } - - private updateCredentials(securityConfig: GatewayConfigSecurity): Observable { - let newCredentials: Partial = {}; - - switch (securityConfig.type) { - case SecurityTypes.USERNAME_PASSWORD: - if (this.shouldUpdateCredentials(securityConfig)) { - newCredentials = this.generateMqttCredentials(securityConfig); - } - break; - - case SecurityTypes.ACCESS_TOKEN: - case SecurityTypes.TLS_ACCESS_TOKEN: - if (this.shouldUpdateAccessToken(securityConfig)) { - newCredentials = { - credentialsType: DeviceCredentialsType.ACCESS_TOKEN, - credentialsId: securityConfig.accessToken - }; - } - break; - } - - return Object.keys(newCredentials).length - ? this.deviceService.saveDeviceCredentials({ ...this.initialCredentials, ...newCredentials }) - : of(null); - } - - private shouldUpdateCredentials(securityConfig: GatewayConfigSecurity): boolean { - if (this.initialCredentials.credentialsType !== DeviceCredentialsType.MQTT_BASIC) { - return true; - } - const parsedCredentials = JSON.parse(this.initialCredentials.credentialsValue); - return !( - parsedCredentials.clientId === securityConfig.clientId && - parsedCredentials.userName === securityConfig.username && - parsedCredentials.password === securityConfig.password - ); - } - - private generateMqttCredentials(securityConfig: GatewayConfigSecurity): Partial { - const { clientId, username, password } = securityConfig; - - const credentialsValue = { - ...(clientId && { clientId }), - ...(username && { userName: username }), - ...(password && { password }), - }; - - return { - credentialsType: DeviceCredentialsType.MQTT_BASIC, - credentialsValue: JSON.stringify(credentialsValue) - }; - } - - private shouldUpdateAccessToken(securityConfig: GatewayConfigSecurity): boolean { - return this.initialCredentials.credentialsType !== DeviceCredentialsType.ACCESS_TOKEN || - this.initialCredentials.credentialsId !== securityConfig.accessToken; - } - - cancel(): void { - if (this.dialogRef) { - this.dialogRef.close(); - } - } - - private removeEmpty(obj: Record): Record { - return Object.fromEntries( - Object.entries(obj) - .filter(([_, v]) => v != null) - .map(([k, v]) => [k, v === Object(v) ? this.removeEmpty(v as Record) : v]) - ); - } - - private generateLogsFile(logsObj: GatewayLogsConfig): LogAttribute { - const logAttrObj = { - version: 1, - disable_existing_loggers: false, - formatters: { - LogFormatter: { - class: 'logging.Formatter', - format: logsObj.logFormat, - datefmt: logsObj.dateFormat, - } - }, - handlers: { - consoleHandler: { - class: 'logging.StreamHandler', - formatter: 'LogFormatter', - level: 0, - stream: 'ext://sys.stdout' - }, - databaseHandler: { - class: 'thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler', - formatter: 'LogFormatter', - filename: './logs/database.log', - backupCount: 1, - encoding: 'utf-8' - } - }, - loggers: { - database: { - handlers: ['databaseHandler', 'consoleHandler'], - level: 'DEBUG', - propagate: false - } - }, - root: { - level: 'ERROR', - handlers: [ - 'consoleHandler' - ] - }, - ts: new Date().getTime() - }; - - this.addLocalLoggers(logAttrObj, logsObj.local); - - return logAttrObj; - } - - private addLocalLoggers(logAttrObj: LogAttribute, localLogs: LocalLogs): void { - for (const key of Object.keys(localLogs)) { - logAttrObj.handlers[key + 'Handler'] = this.createHandlerObj(localLogs[key], key); - logAttrObj.loggers[key] = this.createLoggerObj(localLogs[key], key); - } - } - - private createHandlerObj(logObj: LogConfig, key: string) { - return { - class: 'thingsboard_gateway.tb_utility.tb_handler.TimedRotatingFileHandler', - formatter: 'LogFormatter', - filename: `${logObj.filePath}/${key}.log`, - backupCount: logObj.backupCount, - interval: logObj.savingTime, - when: logObj.savingPeriod, - encoding: 'utf-8' - }; - } - - private createLoggerObj(logObj: LogConfig, key: string) { - return { - handlers: [`${key}Handler`, 'consoleHandler'], - level: logObj.logLevel, - propagate: false - }; - } - - private fetchConfigAttribute(entityId: EntityId): void { - if (entityId.id === NULL_UUID) { - return; - } - - this.attributeService.getEntityAttributes(entityId, AttributeScope.CLIENT_SCOPE, - ) - .pipe( - mergeMap(attributes => attributes.length ? of(attributes) : this.attributeService.getEntityAttributes( - entityId, AttributeScope.SHARED_SCOPE, this.gatewayConfigAttributeKeys) - ), - takeUntil(this.destroy$) - ) - .subscribe(attributes => { - this.updateConfigs(attributes); - this.cd.detectChanges(); - }); - } - - private updateConfigs(attributes: AttributeData[]): void { - const formValue: GatewayConfigValue = { - thingsboard: {} as GatewayGeneralConfig, - grpc: {} as GatewayGRPCConfig, - logs: {} as GatewayLogsConfig, - storage: {} as GatewayStorageConfig, - mode: ConfigurationModes.BASIC - }; - - attributes.forEach(attr => { - switch (attr.key) { - case 'general_configuration': - formValue.thingsboard = attr.value; - this.updateFormControls(attr.value); - break; - case 'grpc_configuration': - formValue.grpc = attr.value; - break; - case 'logs_configuration': - formValue.logs = this.logsToObj(attr.value); - break; - case 'storage_configuration': - formValue.storage = attr.value; - break; - case 'mode': - formValue.mode = attr.value; - break; - case 'RemoteLoggingLevel': - formValue.logs = { - ...formValue.logs, - remote: { - enabled: attr.value !== GatewayLogLevel.NONE, - logLevel: attr.value - } - }; - } - }); - - this.gatewayConfigGroup.get('basicConfig').setValue(formValue, { emitEvent: false }); - this.gatewayConfigGroup.get('advancedConfig').setValue(formValue, { emitEvent: false }); - } - - private updateFormControls(thingsboard: GatewayGeneralConfig): void { - const { type, accessToken, ...securityConfig } = thingsboard.security ?? {}; - - this.initialCredentials = { - deviceId: this.device as DeviceId, - credentialsType: type as unknown as DeviceCredentialsType, - credentialsId: accessToken, - credentialsValue: JSON.stringify(securityConfig) - }; - } - - private logsToObj(logsConfig: LogAttribute): GatewayLogsConfig { - const { format: logFormat, datefmt: dateFormat } = logsConfig.formatters.LogFormatter; - - const localLogs = Object.keys(LocalLogsConfigs).reduce((acc, key) => { - const handler = logsConfig.handlers[`${key}Handler`] || {}; - const logger = logsConfig.loggers[key] || {}; - - acc[key] = { - logLevel: logger.level || GatewayLogLevel.INFO, - filePath: handler.filename?.split(`/${key}`)[0] || './logs', - backupCount: handler.backupCount || 7, - savingTime: handler.interval || 3, - savingPeriod: handler.when || LogSavingPeriod.days - }; - - return acc; - }, {}) as LocalLogs; - - return { local: localLogs, logFormat, dateFormat }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/models/gateway-configuration.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/models/gateway-configuration.models.ts deleted file mode 100644 index 4115c6c024..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/configuration/models/gateway-configuration.models.ts +++ /dev/null @@ -1,170 +0,0 @@ -/// -/// 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. -/// - -import { - ConfigurationModes, - GatewayConnector, - LocalLogsConfigs, - LogSavingPeriod, - SecurityTypes, - StorageTypes -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { GatewayLogLevel } from '@home/components/widget/lib/gateway/gateway-form.models'; - -export interface GatewayConfigValue { - mode: ConfigurationModes; - thingsboard: GatewayGeneralConfig; - storage: GatewayStorageConfig; - grpc: GatewayGRPCConfig; - connectors?: GatewayConnector[]; - logs: GatewayLogsConfig; -} - -export interface GatewayGRPCConfig { - enabled: boolean; - serverPort: number; - keepAliveTimeMs: number; - keepAliveTimeoutMs: number; - keepalivePermitWithoutCalls: boolean; - maxPingsWithoutData: number; - minTimeBetweenPingsMs: number; - minPingIntervalWithoutDataMs: number; -} - -export interface GatewayStorageConfig { - type: StorageTypes; - read_records_count?: number; - max_records_count?: number; - data_folder_path?: string; - max_file_count?: number; - max_read_records_count?: number; - max_records_per_file?: number; - data_file_path?: string; - messages_ttl_check_in_hours?: number; - messages_ttl_in_days?: number; -} - -export interface GatewayGeneralConfig { - host: string; - port: number; - remoteShell: boolean; - remoteConfiguration: boolean; - checkConnectorsConfigurationInSeconds: number; - statistics: { - enable: boolean; - statsSendPeriodInSeconds: number; - commands: GatewayConfigCommand[]; - }; - maxPayloadSizeBytes: number; - minPackSendDelayMS: number; - minPackSizeToSend: number; - handleDeviceRenaming: boolean; - checkingDeviceActivity: { - checkDeviceInactivity: boolean; - inactivityTimeoutSeconds?: number; - inactivityCheckPeriodSeconds?: number; - }; - security: GatewayConfigSecurity; - qos: number; -} - -export interface GatewayLogsConfig { - dateFormat: string; - logFormat: string; - type?: string; - remote?: { - enabled: boolean; - logLevel: GatewayLogLevel; - }; - local: LocalLogs; -} - -export interface GatewayConfigSecurity { - type: SecurityTypes; - accessToken?: string; - clientId?: string; - username?: string; - password?: string; - caCert?: string; - cert?: string; - privateKey?: string; -} - -export interface GatewayConfigCommand { - attributeOnGateway: string; - command: string; - timeout: number; -} - -export interface LogConfig { - logLevel: GatewayLogLevel; - filePath: string; - backupCount: number; - savingTime: number; - savingPeriod: LogSavingPeriod; -} - -export type LocalLogs = Record; - -interface LogFormatterConfig { - class: string; - format: string; - datefmt: string; -} - -interface StreamHandlerConfig { - class: string; - formatter: string; - level: string | number; - stream: string; -} - -interface FileHandlerConfig { - class: string; - formatter: string; - filename: string; - backupCount: number; - encoding: string; -} - -interface LoggerConfig { - handlers: string[]; - level: string; - propagate: boolean; -} - -interface RootConfig { - level: string; - handlers: string[]; -} - -export interface LogAttribute { - version: number; - disable_existing_loggers: boolean; - formatters: { - LogFormatter: LogFormatterConfig; - }; - handlers: { - consoleHandler: StreamHandlerConfig; - databaseHandler: FileHandlerConfig; - }; - loggers: { - database: LoggerConfig; - }; - root: RootConfig; - ts: number; -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.html deleted file mode 100644 index bbaf616471..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.html +++ /dev/null @@ -1,99 +0,0 @@ - -
-
device.device
-
-
-
gateway.device-info.entity-field
-
gateway.device-info.source
-
- gateway.device-info.expression -
-
-
-
-
gateway.device-info.name
-
- - - - {{ SourceTypeTranslationsMap.get(type) | translate }} - - - -
-
- - - - warning - -
-
-
-
-
-
-
gateway.device-info.profile-name
-
- - - - {{ SourceTypeTranslationsMap.get(type) | translate }} - - - -
-
- - - - warning - -
-
-
-
-
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.scss deleted file mode 100644 index 9dbaa52831..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.scss +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: block; - - .tb-form-row { - &.bottom-same-padding { - padding-bottom: 16px; - } - - &.top-same-padding { - padding-top: 16px; - } - - .fixed-title-width { - width: 19%; - } - } - - .table-column { - width: 40%; - } - - .table-name-column { - width: 20%; - } - - .raw-name { - width: 19%; - } - - .raw-value-option { - max-width: 40%; - } - -} - -:host ::ng-deep { - .mat-mdc-form-field-icon-suffix { - display: flex; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.ts deleted file mode 100644 index 91432b7353..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component.ts +++ /dev/null @@ -1,164 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - Component, - forwardRef, - Input, - OnDestroy, - OnInit, -} from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { TranslateService } from '@ngx-translate/core'; -import { MatDialog } from '@angular/material/dialog'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { - DeviceInfoType, - noLeadTrailSpacesRegex, - OPCUaSourceType, - SourceType, - SourceTypeTranslationsMap -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { coerceBoolean } from '@shared/decorators/coercion'; - -@Component({ - selector: 'tb-device-info-table', - templateUrl: './device-info-table.component.html', - styleUrls: ['./device-info-table.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => DeviceInfoTableComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => DeviceInfoTableComponent), - multi: true - } - ] -}) -export class DeviceInfoTableComponent extends PageComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy { - - SourceTypeTranslationsMap = SourceTypeTranslationsMap; - - DeviceInfoType = DeviceInfoType; - - @coerceBoolean() - @Input() - useSource = true; - - @coerceBoolean() - @Input() - required = false; - - @Input() - sourceTypes: Array = Object.values(SourceType); - - deviceInfoTypeValue: any; - - get deviceInfoType(): any { - return this.deviceInfoTypeValue; - } - - @Input() - set deviceInfoType(value: any) { - if (this.deviceInfoTypeValue !== value) { - this.deviceInfoTypeValue = value; - } - } - - mappingFormGroup: UntypedFormGroup; - - private destroy$ = new Subject(); - private propagateChange = (v: any) => {}; - - constructor(protected store: Store, - public translate: TranslateService, - public dialog: MatDialog, - private fb: FormBuilder) { - super(store); - } - - ngOnInit() { - this.mappingFormGroup = this.fb.group({ - deviceNameExpression: ['', this.required ? - [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)] : [Validators.pattern(noLeadTrailSpacesRegex)]] - }); - - if (this.useSource) { - this.mappingFormGroup.addControl('deviceNameExpressionSource', - this.fb.control(this.sourceTypes[0], [])); - } - - if (this.deviceInfoType === DeviceInfoType.FULL) { - if (this.useSource) { - this.mappingFormGroup.addControl('deviceProfileExpressionSource', - this.fb.control(this.sourceTypes[0], [])); - } - this.mappingFormGroup.addControl('deviceProfileExpression', - this.fb.control('', this.required ? - [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)] : [Validators.pattern(noLeadTrailSpacesRegex)])); - } - - this.mappingFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.updateView(value); - }); - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - super.ngOnDestroy(); - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void {} - - writeValue(deviceInfo: any) { - this.mappingFormGroup.patchValue(deviceInfo, {emitEvent: false}); - } - - validate(): ValidationErrors | null { - return this.mappingFormGroup.valid ? null : { - mappingForm: { valid: false } - }; - } - - updateView(value: any) { - this.propagateChange(value); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.html deleted file mode 100644 index 736d9c83c6..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.html +++ /dev/null @@ -1,235 +0,0 @@ - -
-
-
{{ panelTitle | translate }}{{' (' + keysListFormArray.controls.length + ')'}}
-
-
-
- - - - - -
- {{ keyControl.get('key').value }} -
- {{ '-' }} -
-
{{ valueTitle(keyControl) }}
-
-
- -
-
-
gateway.platform-side
-
-
- {{ 'gateway.key' | translate }} -
-
- - - - warning - - -
-
-
-
-
gateway.connector-side
-
-
gateway.type
- - - -
- - - - {{ (valueTypes.get(keyControl.get('type').value)?.name || valueTypes.get(keyControl.get('type').value)) | translate }} - - - {{ 'gateway.raw' | translate }} - -
-
- - - - - - {{ valueTypes.get(valueType).name || valueTypes.get(valueType) | translate }} - - - - - - {{ 'gateway.raw' | translate }} - - -
-
-
-
-
- {{ 'gateway.value' | translate }} -
- - - - warning - -
-
-
-
-
-
-
-
-
gateway.key
-
- - - - warning - - -
-
-
-
gateway.value
- - - - warning - - -
-
-
-
-
- {{ 'gateway.method-name' | translate }} -
-
- - - - warning - - -
-
-
- - - -
- {{ 'gateway.arguments' | translate }}{{' (' + keyControl.get('arguments').value?.length + ')'}} -
-
-
- - - -
-
-
-
-
-
-
- -
-
-
- -
-
- -
- {{ noKeysText }} -
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.scss deleted file mode 100644 index 24498863c9..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.scss +++ /dev/null @@ -1,60 +0,0 @@ -/** - * 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. - */ - -:host { - .tb-mapping-keys-panel { - width: 77vw; - max-width: 700px; - - .title-container { - max-width: 11vw; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap - } - - .key-panel { - height: 500px; - overflow: auto; - } - - tb-value-input { - width: 100%; - } - - .tb-form-panel { - .mat-mdc-icon-button { - width: 56px; - height: 56px; - padding: 16px; - color: rgba(0, 0, 0, 0.54); - } - } - - .see-example { - width: 32px; - height: 32px; - margin: 4px; - } - } -} - -:host ::ng-deep { - .mat-mdc-form-field-icon-suffix { - display: flex; - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.ts deleted file mode 100644 index de289dde2e..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component.ts +++ /dev/null @@ -1,197 +0,0 @@ -/// -/// 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. -/// - -import { - Component, - EventEmitter, - Input, - OnInit, - Output -} from '@angular/core'; -import { - AbstractControl, - FormControl, - FormGroup, - UntypedFormArray, - UntypedFormBuilder, - Validators -} from '@angular/forms'; -import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { coerceBoolean } from '@shared/decorators/coercion'; -import { TbPopoverComponent } from '@shared/components/popover.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { PageComponent } from '@shared/components/page.component'; -import { isDefinedAndNotNull } from '@core/utils'; -import { - MappingDataKey, - MappingKeysType, - MappingValueType, - mappingValueTypesMap, - noLeadTrailSpacesRegex, - OPCUaSourceType, - RpcMethodsMapping, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -@Component({ - selector: 'tb-mapping-data-keys-panel', - templateUrl: './mapping-data-keys-panel.component.html', - styleUrls: ['./mapping-data-keys-panel.component.scss'], - providers: [] -}) -export class MappingDataKeysPanelComponent extends PageComponent implements OnInit { - - @Input() - panelTitle: string; - - @Input() - addKeyTitle: string; - - @Input() - deleteKeyTitle: string; - - @Input() - noKeysText: string; - - @Input() - keys: Array | {[key: string]: any}; - - @Input() - keysType: MappingKeysType; - - @Input() - valueTypeKeys: Array = Object.values(MappingValueType); - - @Input() - valueTypeEnum = MappingValueType; - - @Input() - valueTypes: Map = mappingValueTypesMap; - - @Input() - @coerceBoolean() - rawData = false; - - @Input() - popover: TbPopoverComponent; - - @Output() - keysDataApplied = new EventEmitter | {[key: string]: unknown}>(); - - MappingKeysType = MappingKeysType; - - dataKeyType: DataKeyType; - - keysListFormArray: UntypedFormArray; - - errorText = ''; - - constructor(private fb: UntypedFormBuilder, - protected store: Store) { - super(store); - } - - ngOnInit(): void { - this.keysListFormArray = this.prepareKeysFormArray(this.keys); - } - - trackByKey(index: number, keyControl: AbstractControl): any { - return keyControl; - } - - addKey(): void { - let dataKeyFormGroup: FormGroup; - if (this.keysType === MappingKeysType.RPC_METHODS) { - dataKeyFormGroup = this.fb.group({ - method: ['', [Validators.required]], - arguments: [[], []] - }); - } else { - dataKeyFormGroup = this.fb.group({ - key: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - value: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]] - }); - } - if (this.keysType !== MappingKeysType.CUSTOM && this.keysType !== MappingKeysType.RPC_METHODS) { - const controlValue = this.rawData ? 'raw' : this.valueTypeKeys[0]; - dataKeyFormGroup.addControl('type', this.fb.control(controlValue)); - } - this.keysListFormArray.push(dataKeyFormGroup); - } - - deleteKey($event: Event, index: number): void { - if ($event) { - $event.stopPropagation(); - } - this.keysListFormArray.removeAt(index); - this.keysListFormArray.markAsDirty(); - } - - cancel(): void { - this.popover?.hide(); - } - - applyKeysData(): void { - let keys = this.keysListFormArray.value; - if (this.keysType === MappingKeysType.CUSTOM) { - keys = {}; - for (let key of this.keysListFormArray.value) { - keys[key.key] = key.value; - } - } - this.keysDataApplied.emit(keys); - } - - private prepareKeysFormArray(keys: Array | {[key: string]: any}): UntypedFormArray { - const keysControlGroups: Array = []; - if (keys) { - if (this.keysType === MappingKeysType.CUSTOM) { - keys = Object.keys(keys).map(key => { - return {key, value: keys[key], type: ''}; - }); - } - keys.forEach((keyData) => { - let dataKeyFormGroup: FormGroup; - if (this.keysType === MappingKeysType.RPC_METHODS) { - dataKeyFormGroup = this.fb.group({ - method: [keyData.method, [Validators.required]], - arguments: [[...keyData.arguments], []] - }); - } else { - const { key, value, type } = keyData; - dataKeyFormGroup = this.fb.group({ - key: [key, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - value: [value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - type: [type, []] - }); - } - keysControlGroups.push(dataKeyFormGroup); - }); - } - return this.fb.array(keysControlGroups); - } - - valueTitle(keyControl: FormControl): string { - const value = keyControl.get(this.keysType === MappingKeysType.RPC_METHODS ? 'method' : 'value').value; - if (isDefinedAndNotNull(value)) { - if (typeof value === 'object') { - return JSON.stringify(value); - } - return value; - } - return ''; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.html deleted file mode 100644 index 7e1ab2bb62..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.html +++ /dev/null @@ -1,125 +0,0 @@ - -
-
- -
-
- {{mappingTypeTranslationsMap.get(mappingType) | translate}} -
- - - -
-
- -
- - -   - - - -
-
-
- - - - {{ column.title | translate }} - - - {{ mapping[column.def] }} - - - - - - - - - - -
- -
-
- - - - -
-
-
- - -
-
- -
-
- - widget.no-data-found - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.scss deleted file mode 100644 index 707b5fa056..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.scss +++ /dev/null @@ -1,101 +0,0 @@ -/** - * 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. - */ -@import '../scss/constants'; - -:host { - width: 100%; - height: 100%; - display: block; - .tb-mapping-table { - .tb-mapping-table-content { - width: 100%; - height: 100%; - background: #fff; - overflow: hidden; - - &.tb-outlined-border { - box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%); - border: solid 1px #e0e0e0; - border-radius: 4px; - } - - .mat-toolbar-tools{ - min-height: auto; - } - - .title-container{ - overflow: hidden; - } - - .tb-mapping-table-title { - padding-right: 20px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .table-container { - overflow: auto; - .mat-mdc-table { - table-layout: fixed; - min-width: 450px; - - .table-value-column { - padding: 0 12px; - width: 23%; - - &.request-column { - width: 38%; - } - } - } - } - - .ellipsis { - overflow: hidden; - text-overflow: ellipsis; - } - } - } - - .no-data-found { - height: calc(100% - 120px); - } - - @media #{$mat-xs} { - .mat-toolbar { - height: auto; - min-height: 100px; - - .tb-mapping-table-title{ - padding-bottom: 5px; - width: 100%; - } - } - } -} - -:host ::ng-deep { - mat-cell.tb-value-cell { - cursor: pointer; - .mat-icon { - height: 24px; - width: 24px; - font-size: 24px; - color: #757575 - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.ts deleted file mode 100644 index bdf52beb99..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component.ts +++ /dev/null @@ -1,323 +0,0 @@ -/// -/// 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. -/// - -import { - AfterViewInit, - ChangeDetectionStrategy, - Component, - ElementRef, - forwardRef, - Input, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { MatDialog } from '@angular/material/dialog'; -import { DialogService } from '@core/services/dialog.service'; -import { Subject } from 'rxjs'; -import { debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs/operators'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormArray, - ValidationErrors, - Validator, -} from '@angular/forms'; -import { - AttributeUpdate, - ConnectorMapping, - ConnectRequest, - ConverterConnectorMapping, - ConvertorTypeTranslationsMap, - DeviceConnectorMapping, - DisconnectRequest, - MappingInfo, - MappingType, - MappingTypeTranslationsMap, - MappingValue, - RequestMappingValue, - RequestType, - RequestTypesTranslationsMap, - ServerSideRpc -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { MappingDialogComponent } from '@home/components/widget/lib/gateway/dialog/mapping-dialog.component'; -import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; -import { coerceBoolean } from '@shared/decorators/coercion'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; - -@Component({ - selector: 'tb-mapping-table', - templateUrl: './mapping-table.component.html', - styleUrls: ['./mapping-table.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MappingTableComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => MappingTableComponent), - multi: true - } - ], - standalone: true, - imports: [CommonModule, SharedModule] -}) -export class MappingTableComponent implements ControlValueAccessor, Validator, AfterViewInit, OnInit, OnDestroy { - - @Input() - @coerceBoolean() - required = false; - - @Input() - set mappingType(value: MappingType) { - if (this.mappingTypeValue !== value) { - this.mappingTypeValue = value; - } - } - - get mappingType(): MappingType { - return this.mappingTypeValue; - } - - @ViewChild('searchInput') searchInputField: ElementRef; - - mappingTypeTranslationsMap = MappingTypeTranslationsMap; - mappingTypeEnum = MappingType; - displayedColumns = []; - mappingColumns = []; - textSearchMode = false; - dataSource: MappingDatasource; - hidePageSize = false; - activeValue = false; - dirtyValue = false; - mappingTypeValue: MappingType; - mappingFormGroup: UntypedFormArray; - textSearch = this.fb.control('', {nonNullable: true}); - - private onChange: (value: string) => void = () => {}; - private onTouched: () => void = () => {}; - - private destroy$ = new Subject(); - - constructor(public translate: TranslateService, - public dialog: MatDialog, - private dialogService: DialogService, - private fb: FormBuilder) { - this.mappingFormGroup = this.fb.array([]); - this.dirtyValue = !this.activeValue; - this.dataSource = new MappingDatasource(); - } - - ngOnInit(): void { - this.setMappingColumns(); - this.displayedColumns.push(...this.mappingColumns.map(column => column.def), 'actions'); - this.mappingFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.updateTableData(value); - this.onChange(value); - this.onTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - ngAfterViewInit(): void { - this.textSearch.valueChanges.pipe( - debounceTime(150), - distinctUntilChanged((prev, current) => (prev ?? '') === current.trim()), - takeUntil(this.destroy$) - ).subscribe((text) => { - const searchText = text.trim(); - this.updateTableData(this.mappingFormGroup.value, searchText.trim()); - }); - } - - registerOnChange(fn: (value: string) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(connectorMappings: ConnectorMapping[]): void { - this.mappingFormGroup.clear(); - this.pushDataAsFormArrays(connectorMappings); - } - - validate(): ValidationErrors | null { - return !this.required || this.mappingFormGroup.controls.length ? null : { - mappingFormGroup: {valid: false} - }; - } - - enterFilterMode(): void { - this.textSearchMode = true; - setTimeout(() => { - this.searchInputField.nativeElement.focus(); - this.searchInputField.nativeElement.setSelectionRange(0, 0); - }, 10); - } - - exitFilterMode(): void { - this.updateTableData(this.mappingFormGroup.value); - this.textSearchMode = false; - this.textSearch.reset(); - } - - manageMapping($event: Event, index?: number): void { - if ($event) { - $event.stopPropagation(); - } - const value = isDefinedAndNotNull(index) ? this.mappingFormGroup.at(index).value : {}; - this.dialog.open(MappingDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - mappingType: this.mappingType, - value, - buttonTitle: isUndefinedOrNull(index) ? 'action.add' : 'action.apply' - } - }).afterClosed() - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe(res => { - if (res) { - if (isDefinedAndNotNull(index)) { - this.mappingFormGroup.at(index).patchValue(res); - } else { - this.pushDataAsFormArrays([res]); - } - this.mappingFormGroup.markAsDirty(); - } - }); - } - - private updateTableData(value: ConnectorMapping[], textSearch?: string): void { - let tableValue = value.map(mappingValue => this.getMappingValue(mappingValue)); - if (textSearch) { - tableValue = tableValue.filter(mappingValue => - Object.values(mappingValue).some(val => - val.toString().toLowerCase().includes(textSearch.toLowerCase()) - ) - ); - } - this.dataSource.loadData(tableValue); - } - - deleteMapping($event: Event, index: number): void { - if ($event) { - $event.stopPropagation(); - } - this.dialogService.confirm( - this.translate.instant('gateway.delete-mapping-title'), - '', - this.translate.instant('action.no'), - this.translate.instant('action.yes'), - true - ).subscribe((result) => { - if (result) { - this.mappingFormGroup.removeAt(index); - this.mappingFormGroup.markAsDirty(); - } - }); - } - - private pushDataAsFormArrays(data: ConnectorMapping[]): void { - if (data?.length) { - data.forEach((mapping: ConnectorMapping) => this.mappingFormGroup.push(this.fb.control(mapping))); - } - } - - private getMappingValue(value: ConnectorMapping): MappingValue { - switch (this.mappingType) { - case MappingType.DATA: - const converterType = ConvertorTypeTranslationsMap.get((value as ConverterConnectorMapping).converter?.type); - return { - topicFilter: (value as ConverterConnectorMapping).topicFilter, - QoS: (value as ConverterConnectorMapping).subscriptionQos, - converter: converterType ? this.translate.instant(converterType) : '' - }; - case MappingType.REQUESTS: - let details: string; - const requestValue = value as RequestMappingValue; - if (requestValue.requestType === RequestType.ATTRIBUTE_UPDATE) { - details = (requestValue.requestValue as AttributeUpdate).attributeFilter; - } else if (requestValue.requestType === RequestType.SERVER_SIDE_RPC) { - details = (requestValue.requestValue as ServerSideRpc).methodFilter; - } else { - details = (requestValue.requestValue as ConnectRequest | DisconnectRequest).topicFilter; - } - return { - requestType: (value as RequestMappingValue).requestType, - type: this.translate.instant(RequestTypesTranslationsMap.get((value as RequestMappingValue).requestType)), - details - }; - case MappingType.OPCUA: - const deviceNamePattern = (value as DeviceConnectorMapping).deviceInfo?.deviceNameExpression; - const deviceProfileExpression = (value as DeviceConnectorMapping).deviceInfo?.deviceProfileExpression; - const { deviceNodePattern } = value as DeviceConnectorMapping; - return { - deviceNodePattern, - deviceNamePattern, - deviceProfileExpression - }; - default: - return {} as MappingValue; - } - } - - private setMappingColumns(): void { - switch (this.mappingType) { - case MappingType.DATA: - this.mappingColumns.push( - { def: 'topicFilter', title: 'gateway.topic-filter' }, - { def: 'QoS', title: 'gateway.mqtt-qos' }, - { def: 'converter', title: 'gateway.payload-type' } - ); - break; - case MappingType.REQUESTS: - this.mappingColumns.push( - { def: 'type', title: 'gateway.type' }, - { def: 'details', title: 'gateway.details' } - ); - break; - case MappingType.OPCUA: - this.mappingColumns.push( - { def: 'deviceNodePattern', title: 'gateway.device-node' }, - { def: 'deviceNamePattern', title: 'gateway.device-name' }, - { def: 'deviceProfileExpression', title: 'gateway.device-profile' } - ); - } - } -} - -export class MappingDatasource extends TbTableDatasource { - constructor() { - super(); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract.ts deleted file mode 100644 index 66024c6a8c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract.ts +++ /dev/null @@ -1,76 +0,0 @@ -/// -/// 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. -/// - -import { Directive } from '@angular/core'; -import { FormControl, FormGroup, ValidationErrors } from '@angular/forms'; -import { takeUntil } from 'rxjs/operators'; -import { isEqual } from '@core/utils'; -import { GatewayConnectorBasicConfigDirective } from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract'; -import { - ModbusBasicConfig, - ModbusBasicConfig_v3_5_2, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -@Directive() -export abstract class ModbusBasicConfigDirective - extends GatewayConnectorBasicConfigDirective { - - enableSlaveControl: FormControl = new FormControl(false); - - constructor() { - super(); - - this.enableSlaveControl.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(enable => { - this.updateSlaveEnabling(enable); - this.basicFormGroup.get('slave').updateValueAndValidity({ emitEvent: !!this.onChange }); - }); - } - - override writeValue(basicConfig: OutputBasicConfig & ModbusBasicConfig): void { - super.writeValue(basicConfig); - this.onEnableSlaveControl(basicConfig); - } - - override validate(): ValidationErrors | null { - const { master, slave } = this.basicFormGroup.value; - const isEmpty = !master?.slaves?.length && (isEqual(slave, {}) || !slave); - if (!this.basicFormGroup.valid || isEmpty) { - return { basicFormGroup: { valid: false } }; - } - return null; - } - - protected override initBasicFormGroup(): FormGroup { - return this.fb.group({ - master: [], - slave: [], - }); - } - - private updateSlaveEnabling(isEnabled: boolean): void { - if (isEnabled) { - this.basicFormGroup.get('slave').enable({ emitEvent: false }); - } else { - this.basicFormGroup.get('slave').disable({ emitEvent: false }); - } - } - - private onEnableSlaveControl(basicConfig: ModbusBasicConfig): void { - this.enableSlaveControl.setValue(!!basicConfig.slave && !isEqual(basicConfig.slave, {})); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.html deleted file mode 100644 index 76098288b8..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - -
-
{{ 'gateway.hints.modbus-server' | translate }}
-
- - - {{ 'gateway.enable' | translate }} - - -
-
- -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss deleted file mode 100644 index 3b7e7288c8..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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. - */ -:host { - height: 100%; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts deleted file mode 100644 index dceffd3f7d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core'; -import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { - ModbusBasicConfig_v3_5_2, - ModbusMasterConfig, - ModbusSlave -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { ModbusSlaveConfigComponent } from '../modbus-slave-config/modbus-slave-config.component'; -import { ModbusMasterTableComponent } from '../modbus-master-table/modbus-master-table.component'; -import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; -import { - ModbusBasicConfigDirective -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract'; - -@Component({ - selector: 'tb-modbus-basic-config', - templateUrl: './modbus-basic-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusBasicConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ModbusBasicConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ModbusSlaveConfigComponent, - ModbusMasterTableComponent, - EllipsisChipListDirective, - ], - styleUrls: ['./modbus-basic-config.component.scss'], -}) -export class ModbusBasicConfigComponent extends ModbusBasicConfigDirective { - - isLegacy = false; - - protected override mapConfigToFormValue({ master, slave }: ModbusBasicConfig_v3_5_2): ModbusBasicConfig_v3_5_2 { - return { - master: master?.slaves ? master : { slaves: [] } as ModbusMasterConfig, - slave: slave ?? {} as ModbusSlave, - }; - } - - protected override getMappedValue(value: ModbusBasicConfig_v3_5_2): ModbusBasicConfig_v3_5_2 { - return { - master: value.master, - slave: value.slave, - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component.ts deleted file mode 100644 index c10214d57b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component.ts +++ /dev/null @@ -1,80 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core'; -import { NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { - LegacySlaveConfig, - ModbusBasicConfig, - ModbusLegacyBasicConfig, - ModbusLegacySlave, - ModbusMasterConfig, - ModbusSlave -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { ModbusSlaveConfigComponent } from '../modbus-slave-config/modbus-slave-config.component'; -import { ModbusMasterTableComponent } from '../modbus-master-table/modbus-master-table.component'; -import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; -import { ModbusVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/modbus-version-mapping.util'; -import { - ModbusBasicConfigDirective -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.abstract'; - -@Component({ - selector: 'tb-modbus-legacy-basic-config', - templateUrl: './modbus-basic-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusLegacyBasicConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ModbusLegacyBasicConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ModbusSlaveConfigComponent, - ModbusMasterTableComponent, - EllipsisChipListDirective, - ], - styleUrls: ['./modbus-basic-config.component.scss'], -}) -export class ModbusLegacyBasicConfigComponent extends ModbusBasicConfigDirective { - - isLegacy = true; - - protected override mapConfigToFormValue(config: ModbusLegacyBasicConfig): ModbusBasicConfig { - return { - master: config.master?.slaves ? config.master : { slaves: [] } as ModbusMasterConfig, - slave: config.slave ? ModbusVersionMappingUtil.mapSlaveToUpgradedVersion(config.slave as ModbusLegacySlave) : {}, - } as ModbusBasicConfig; - } - - protected override getMappedValue(value: ModbusBasicConfig): ModbusLegacyBasicConfig { - return { - master: value.master as ModbusMasterConfig, - slave: value.slave ? ModbusVersionMappingUtil.mapSlaveToDowngradedVersion(value.slave as ModbusSlave) : {} as ModbusLegacySlave, - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html deleted file mode 100644 index 890c0428f0..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html +++ /dev/null @@ -1,270 +0,0 @@ - -
-
-
{{ panelTitle | translate }}{{' (' + keysListFormArray.controls.length + ')'}}
-
-
-
- - - - -
- {{ keyControl.get('tag').value }}{{ '-' }}{{ keyControl.get('value').value }} -
- -
-
{{ 'gateway.key' | translate }}: - {{ keyControl.get('tag').value }} -
-
{{ 'gateway.address' | translate }}: - {{ keyControl.get('address').value }} -
-
{{ 'gateway.type' | translate }}: - {{ keyControl.get('type').value }} -
-
-
-
-
- -
- {{ 'gateway.hints.modbus.data-keys' | translate }} -
-
-
-
-
gateway.platform-side
-
-
- gateway.key -
-
- - - - warning - - -
-
-
-
-
gateway.connector-side
-
-
- gateway.type -
-
- - - {{ type }} - - -
-
-
-
gateway.function-code
-
- - - - {{ ModbusFunctionCodeTranslationsMap.get(code) | translate }} - - - -
-
-
-
gateway.objects-count
-
- - - - warning - - -
-
-
-
gateway.address
-
- - - - warning - - -
-
-
- - - - - - {{ 'gateway.modifier' | translate }} - - - - -
-
-
gateway.type
-
- - - -
- - {{ ModifierTypesMap.get(keyControl.get('modifierType').value)?.name | translate}} -
-
- - - - {{ ModifierTypesMap.get(modifierType).name | translate }} - -
-
-
-
-
-
-
gateway.value
- - - - warning - - -
-
-
-
-
gateway.value
-
- - - - warning - - -
-
- -
-
-
-
-
- -
-
-
- -
-
- -
- {{ noKeysText }} -
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss deleted file mode 100644 index d242fd4e71..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss +++ /dev/null @@ -1,45 +0,0 @@ -/** - * 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. - */ - -:host { - .tb-modbus-keys-panel { - width: 77vw; - max-width: 700px; - - .title-container { - width: 180px; - } - - .key-label { - font-weight: 400; - } - - .key-panel { - height: 500px; - overflow: auto; - } - - .tb-form-panel { - .mat-mdc-icon-button { - width: 56px; - height: 56px; - padding: 16px; - color: rgba(0, 0, 0, 0.54); - } - } - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts deleted file mode 100644 index fcf3d49cf0..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts +++ /dev/null @@ -1,306 +0,0 @@ -/// -/// 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. -/// - -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { - AbstractControl, - FormArray, - FormControl, - FormGroup, - UntypedFormArray, - UntypedFormBuilder, - UntypedFormGroup, - Validators -} from '@angular/forms'; -import { TbPopoverComponent } from '@shared/components/popover.component'; -import { - ModbusDataType, - ModbusEditableDataTypes, - ModbusFormValue, - ModbusFunctionCodeTranslationsMap, - ModbusObjectCountByDataType, - ModbusValue, - ModbusValueKey, - ModifierType, - ModifierTypesMap, - noLeadTrailSpacesRegex, - nonZeroFloat, - ReportStrategyDefaultValue, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { GatewayHelpLinkPipe } from '@home/components/widget/lib/gateway/pipes/gateway-help-link.pipe'; -import { generateSecret } from '@core/utils'; -import { coerceBoolean } from '@shared/decorators/coercion'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; -import { - ReportStrategyComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component'; - -@Component({ - selector: 'tb-modbus-data-keys-panel', - templateUrl: './modbus-data-keys-panel.component.html', - styleUrls: ['./modbus-data-keys-panel.component.scss'], - standalone: true, - imports: [ - CommonModule, - SharedModule, - GatewayHelpLinkPipe, - ReportStrategyComponent, - ] -}) -export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { - - @coerceBoolean() - @Input() isMaster = false; - @coerceBoolean() - @Input() hideNewFields = false; - @Input() panelTitle: string; - @Input() addKeyTitle: string; - @Input() deleteKeyTitle: string; - @Input() noKeysText: string; - @Input() keysType: ModbusValueKey; - @Input() values: ModbusValue[]; - @Input() popover: TbPopoverComponent; - - @Output() keysDataApplied = new EventEmitter>(); - - keysListFormArray: FormArray; - modbusDataTypes = Object.values(ModbusDataType); - modifierTypes: ModifierType[] = Object.values(ModifierType); - withFunctionCode = true; - withReportStrategy = true; - - enableModifiersControlMap = new Map>(); - showModifiersMap = new Map(); - functionCodesMap = new Map(); - defaultFunctionCodes = []; - - readonly ModbusEditableDataTypes = ModbusEditableDataTypes; - readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap; - readonly ModifierTypesMap = ModifierTypesMap; - readonly ReportStrategyDefaultValue = ReportStrategyDefaultValue; - - private destroy$ = new Subject(); - - private readonly defaultReadFunctionCodes = [3, 4]; - private readonly bitsReadFunctionCodes = [1, 2]; - private readonly defaultWriteFunctionCodes = [6, 16]; - private readonly bitsWriteFunctionCodes = [5, 15]; - - constructor(private fb: UntypedFormBuilder) {} - - ngOnInit(): void { - this.withFunctionCode = !this.isMaster || (this.keysType !== ModbusValueKey.ATTRIBUTES && this.keysType !== ModbusValueKey.TIMESERIES); - this.withReportStrategy = !this.isMaster - && (this.keysType === ModbusValueKey.ATTRIBUTES || this.keysType === ModbusValueKey.TIMESERIES) - && !this.hideNewFields; - this.keysListFormArray = this.prepareKeysFormArray(this.values); - this.defaultFunctionCodes = this.getDefaultFunctionCodes(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - trackByControlId(_: number, keyControl: AbstractControl): string { - return keyControl.value.id; - } - - addKey(): void { - const id = generateSecret(5); - const dataKeyFormGroup = this.fb.group({ - tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - value: [{value: '', disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - type: [ModbusDataType.BYTES, [Validators.required]], - address: [null, [Validators.required]], - objectsCount: [1, [Validators.required]], - functionCode: [{ value: this.getDefaultFunctionCodes()[0], disabled: !this.withFunctionCode }, [Validators.required]], - reportStrategy: [{ value: null, disabled: !this.withReportStrategy }], - modifierType: [{ value: ModifierType.MULTIPLIER, disabled: true }], - modifierValue: [{ value: 1, disabled: true }, [Validators.pattern(nonZeroFloat)]], - id: [{value: id, disabled: true}], - }); - this.showModifiersMap.set(id, false); - this.enableModifiersControlMap.set(id, this.fb.control(false)); - this.observeKeyDataType(dataKeyFormGroup); - this.observeEnableModifier(dataKeyFormGroup); - - this.keysListFormArray.push(dataKeyFormGroup); - } - - deleteKey($event: Event, index: number): void { - if ($event) { - $event.stopPropagation(); - } - this.keysListFormArray.removeAt(index); - this.keysListFormArray.markAsDirty(); - } - - cancel(): void { - this.popover.hide(); - } - - applyKeysData(): void { - this.keysDataApplied.emit(this.getFormValue()); - } - - private getFormValue(): ModbusValue[] { - return this.mapKeysWithModifier( - this.withReportStrategy - ? this.cleanUpEmptyStrategies(this.keysListFormArray.value) - : this.keysListFormArray.value - ); - } - - private cleanUpEmptyStrategies(values: ModbusValue[]): ModbusValue[] { - return values.map((key) => { - const { reportStrategy, ...updatedKey } = key; - return !reportStrategy ? updatedKey : key; - }); - } - - private mapKeysWithModifier(values: Array): Array { - return values.map((keyData, i) => { - if (this.showModifiersMap.get(this.keysListFormArray.controls[i].get('id').value)) { - const { modifierType, modifierValue, ...value } = keyData; - return modifierType ? { ...value, [modifierType]: modifierValue } : value; - } - return keyData; - }); - } - - private prepareKeysFormArray(values: ModbusValue[]): UntypedFormArray { - const keysControlGroups: Array = []; - - if (values) { - values.forEach(value => { - const dataKeyFormGroup = this.createDataKeyFormGroup(value); - this.observeKeyDataType(dataKeyFormGroup); - this.observeEnableModifier(dataKeyFormGroup); - this.functionCodesMap.set(dataKeyFormGroup.get('id').value, this.getFunctionCodes(value.type)); - - keysControlGroups.push(dataKeyFormGroup); - }); - } - - return this.fb.array(keysControlGroups); - } - - private createDataKeyFormGroup(modbusValue: ModbusValue): FormGroup { - const { tag, value, type, address, objectsCount, functionCode, multiplier, divider, reportStrategy } = modbusValue; - const id = generateSecret(5); - - const showModifier = this.shouldShowModifier(type); - this.showModifiersMap.set(id, showModifier); - this.enableModifiersControlMap.set(id, this.fb.control((multiplier || divider) && showModifier)); - - return this.fb.group({ - tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - value: [{ value, disabled: !this.isMaster }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - type: [type, [Validators.required]], - address: [address, [Validators.required]], - objectsCount: [objectsCount, [Validators.required]], - functionCode: [{ value: functionCode, disabled: !this.withFunctionCode }, [Validators.required]], - modifierType: [{ - value: divider ? ModifierType.DIVIDER : ModifierType.MULTIPLIER, - disabled: !this.enableModifiersControlMap.get(id).value - }], - modifierValue: [ - { value: multiplier ?? divider ?? 1, disabled: !this.enableModifiersControlMap.get(id).value }, - [Validators.pattern(nonZeroFloat)] - ], - id: [{ value: id, disabled: true }], - reportStrategy: [{ value: reportStrategy, disabled: !this.withReportStrategy }], - }); - } - - private shouldShowModifier(type: ModbusDataType): boolean { - return !this.isMaster - && (this.keysType === ModbusValueKey.ATTRIBUTES || this.keysType === ModbusValueKey.TIMESERIES) - && (!this.ModbusEditableDataTypes.includes(type)); - } - - private observeKeyDataType(keyFormGroup: FormGroup): void { - keyFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(dataType => { - if (!this.ModbusEditableDataTypes.includes(dataType)) { - keyFormGroup.get('objectsCount').patchValue(ModbusObjectCountByDataType[dataType], {emitEvent: false}); - } - const withModifier = this.shouldShowModifier(dataType); - this.showModifiersMap.set(keyFormGroup.get('id').value, withModifier); - this.updateFunctionCodes(keyFormGroup, dataType); - }); - } - - private observeEnableModifier(keyFormGroup: FormGroup): void { - this.enableModifiersControlMap.get(keyFormGroup.get('id').value).valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(showModifier => this.toggleModifierControls(keyFormGroup, showModifier)); - } - - private toggleModifierControls(keyFormGroup: FormGroup, enable: boolean): void { - const modifierTypeControl = keyFormGroup.get('modifierType'); - const modifierValueControl = keyFormGroup.get('modifierValue'); - - if (enable) { - modifierTypeControl.enable(); - modifierValueControl.enable(); - } else { - modifierTypeControl.disable(); - modifierValueControl.disable(); - } - } - - private updateFunctionCodes(keyFormGroup: FormGroup, dataType: ModbusDataType): void { - const functionCodes = this.getFunctionCodes(dataType); - this.functionCodesMap.set(keyFormGroup.get('id').value, functionCodes); - if (!functionCodes.includes(keyFormGroup.get('functionCode').value)) { - keyFormGroup.get('functionCode').patchValue(functionCodes[0], {emitEvent: false}); - } - } - - private getFunctionCodes(dataType: ModbusDataType): number[] { - const writeFunctionCodes = [ - ...(dataType === ModbusDataType.BITS ? this.bitsWriteFunctionCodes : []), ...this.defaultWriteFunctionCodes - ]; - - if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) { - return writeFunctionCodes.sort((a, b) => a - b); - } - - const functionCodes = [...this.defaultReadFunctionCodes]; - if (dataType === ModbusDataType.BITS) { - functionCodes.push(...this.bitsReadFunctionCodes); - } - if (this.keysType === ModbusValueKey.RPC_REQUESTS) { - functionCodes.push(...writeFunctionCodes); - } - - return functionCodes.sort((a, b) => a - b); - } - - private getDefaultFunctionCodes(): number[] { - if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) { - return this.defaultWriteFunctionCodes; - } - if (this.keysType === ModbusValueKey.RPC_REQUESTS) { - return [...this.defaultReadFunctionCodes, ...this.defaultWriteFunctionCodes]; - } - return this.defaultReadFunctionCodes; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html deleted file mode 100644 index 0bdfc9ab87..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html +++ /dev/null @@ -1,150 +0,0 @@ - -
-
-
{{ 'gateway.hints.modbus-master' | translate }}
-
-
- -
-
- {{ 'gateway.servers-slaves' | translate}} -
- - - -
-
- -
- - -   - - - -
-
-
- - - -
{{ 'gateway.device-name' | translate }}
-
- -
{{ slave['deviceName'] }}
-
-
- - - {{ 'gateway.info' | translate }} - - -
{{ slave['host'] ?? slave['port'] }}
-
-
- - - {{ 'gateway.unit-id' | translate }} - - -
{{ slave['unitId'] }}
-
-
- - -
{{ 'gateway.type' | translate }}
-
- - {{ ModbusProtocolLabelsMap.get(slave['type']) }} - -
- - - - - - - - -
- -
-
- - - - -
-
-
- - -
-
- -
-
- - widget.no-data-found - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss deleted file mode 100644 index e9a5d3ebcd..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss +++ /dev/null @@ -1,90 +0,0 @@ -/** - * 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. - */ -@import '../scss/constants'; - -:host { - width: 100%; - height: 100%; - display: block; - - .tb-master-table { - - .tb-master-table-content { - width: 100%; - height: 100%; - background: #fff; - overflow: hidden; - - .mat-toolbar-tools{ - min-height: auto; - } - - .title-container{ - overflow: hidden; - } - - .tb-master-table-title { - padding-right: 20px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .table-container { - overflow: auto; - - .mat-mdc-table { - table-layout: fixed; - min-width: 450px; - - .table-value-column { - padding: 0 12px; - width: 38%; - } - } - } - } - } - - .no-data-found { - height: calc(100% - 120px); - } - - @media #{$mat-xs} { - .mat-toolbar { - height: auto; - min-height: 100px; - - .tb-master-table-title{ - padding-bottom: 5px; - width: 100%; - } - } - } -} - -:host ::ng-deep { - mat-cell.tb-value-cell { - cursor: pointer; - - .mat-icon { - height: 24px; - width: 24px; - font-size: 24px; - color: #757575 - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts deleted file mode 100644 index 71bbc76c75..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts +++ /dev/null @@ -1,245 +0,0 @@ -/// -/// 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. -/// - -import { - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ElementRef, - forwardRef, - Input, - OnDestroy, - OnInit, - ViewChild, -} from '@angular/core'; -import { TranslateService } from '@ngx-translate/core'; -import { MatDialog, MatDialogRef } from '@angular/material/dialog'; -import { DialogService } from '@core/services/dialog.service'; -import { Subject } from 'rxjs'; -import { debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs/operators'; -import { - ControlValueAccessor, - FormArray, - FormBuilder, - NG_VALUE_ACCESSOR, - UntypedFormGroup, -} from '@angular/forms'; -import { - LegacySlaveConfig, - ModbusMasterConfig, - ModbusProtocolLabelsMap, - ModbusSlaveInfo, - ModbusValues, - SlaveConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { isDefinedAndNotNull } from '@core/utils'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { ModbusSlaveDialogComponent } from '../modbus-slave-dialog/modbus-slave-dialog.component'; -import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; -import { coerceBoolean } from '@shared/decorators/coercion'; -import { - ModbusLegacySlaveDialogComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-legacy-slave-dialog.component'; - -@Component({ - selector: 'tb-modbus-master-table', - templateUrl: './modbus-master-table.component.html', - styleUrls: ['./modbus-master-table.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusMasterTableComponent), - multi: true - }, - ], - standalone: true, - imports: [CommonModule, SharedModule] -}) -export class ModbusMasterTableComponent implements ControlValueAccessor, AfterViewInit, OnInit, OnDestroy { - - @ViewChild('searchInput') searchInputField: ElementRef; - - @coerceBoolean() - @Input() isLegacy = false; - - textSearchMode = false; - dataSource: SlavesDatasource; - masterFormGroup: UntypedFormGroup; - textSearch = this.fb.control('', {nonNullable: true}); - - readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; - - private onChange: (value: ModbusMasterConfig) => void = () => {}; - private onTouched: () => void = () => {}; - - private destroy$ = new Subject(); - - constructor( - public translate: TranslateService, - public dialog: MatDialog, - private dialogService: DialogService, - private fb: FormBuilder, - private cdr: ChangeDetectorRef, - ) { - this.masterFormGroup = this.fb.group({ slaves: this.fb.array([]) }); - this.dataSource = new SlavesDatasource(); - } - - get slaves(): FormArray { - return this.masterFormGroup.get('slaves') as FormArray; - } - - ngOnInit(): void { - this.masterFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.updateTableData(value.slaves); - this.onChange(value); - this.onTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - ngAfterViewInit(): void { - this.textSearch.valueChanges.pipe( - debounceTime(150), - distinctUntilChanged((prev, current) => (prev ?? '') === current.trim()), - takeUntil(this.destroy$) - ).subscribe(text => this.updateTableData(this.slaves.value, text.trim())); - } - - registerOnChange(fn: (value: ModbusMasterConfig) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(master: ModbusMasterConfig): void { - this.slaves.clear(); - this.pushDataAsFormArrays(master.slaves); - } - - enterFilterMode(): void { - this.textSearchMode = true; - this.cdr.detectChanges(); - const searchInput = this.searchInputField.nativeElement; - searchInput.focus(); - searchInput.setSelectionRange(0, 0); - } - - exitFilterMode(): void { - this.updateTableData(this.slaves.value); - this.textSearchMode = false; - this.textSearch.reset(); - } - - manageSlave($event: Event, index?: number): void { - if ($event) { - $event.stopPropagation(); - } - const withIndex = isDefinedAndNotNull(index); - const value = withIndex ? this.slaves.at(index).value : {}; - this.getSlaveDialog(value, withIndex ? 'action.apply' : 'action.add').afterClosed() - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe(res => { - if (res) { - if (withIndex) { - this.slaves.at(index).patchValue(res); - } else { - this.slaves.push(this.fb.control(res)); - } - this.masterFormGroup.markAsDirty(); - } - }); - } - - private getSlaveDialog( - value: LegacySlaveConfig | SlaveConfig, - buttonTitle: string - ): MatDialogRef { - if (this.isLegacy) { - return this.dialog.open, ModbusValues> - (ModbusLegacySlaveDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - value: value as LegacySlaveConfig, - hideNewFields: true, - buttonTitle - } - }); - } - return this.dialog.open(ModbusSlaveDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - value: value as SlaveConfig, - buttonTitle, - hideNewFields: false, - } - }); - } - - deleteSlave($event: Event, index: number): void { - if ($event) { - $event.stopPropagation(); - } - this.dialogService.confirm( - this.translate.instant('gateway.delete-slave-title'), - '', - this.translate.instant('action.no'), - this.translate.instant('action.yes'), - true - ).pipe(take(1), takeUntil(this.destroy$)).subscribe((result) => { - if (result) { - this.slaves.removeAt(index); - this.masterFormGroup.markAsDirty(); - } - }); - } - - private updateTableData(data: SlaveConfig[], textSearch?: string): void { - if (textSearch) { - data = data.filter(item => - Object.values(item).some(value => - value.toString().toLowerCase().includes(textSearch.toLowerCase()) - ) - ); - } - this.dataSource.loadData(data); - } - - private pushDataAsFormArrays(slaves: SlaveConfig[]): void { - if (slaves?.length) { - slaves.forEach((slave: SlaveConfig) => this.slaves.push(this.fb.control(slave))); - } - } -} - -export class SlavesDatasource extends TbTableDatasource { - constructor() { - super(); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html deleted file mode 100644 index 3fc3e33ad0..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html +++ /dev/null @@ -1,66 +0,0 @@ - -
-
{{ 'gateway.hints.path-in-os' | translate }}
-
-
- gateway.client-cert-path -
-
- - - -
-
-
-
- gateway.private-key-path -
-
- - - -
-
-
-
gateway.password
-
- - -
- -
-
-
-
-
-
gateway.server-hostname
-
- - - -
-
-
- - - {{ 'gateway.request-client-certificate' | translate }} - - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts deleted file mode 100644 index bc40727b55..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts +++ /dev/null @@ -1,163 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - forwardRef, - Input, - OnChanges, - OnDestroy -} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { - ModbusSecurity, - noLeadTrailSpacesRegex, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { coerceBoolean } from '@shared/decorators/coercion'; - -@Component({ - selector: 'tb-modbus-security-config', - templateUrl: './modbus-security-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusSecurityConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ModbusSecurityConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ] -}) -export class ModbusSecurityConfigComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy { - - @coerceBoolean() - @Input() isMaster = false; - - securityConfigFormGroup: UntypedFormGroup; - - private disabled = false; - - private onChange: (value: ModbusSecurity) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) { - this.securityConfigFormGroup = this.fb.group({ - certfile: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - keyfile: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - password: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - server_hostname: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - reqclicert: [{value: false, disabled: true}], - }); - - this.observeValueChanges(); - } - - ngOnChanges(): void { - this.updateMasterEnabling(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: ModbusSecurity) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - if (this.disabled) { - this.securityConfigFormGroup.disable({emitEvent: false}); - } else { - this.securityConfigFormGroup.enable({emitEvent: false}); - } - this.updateMasterEnabling(); - this.cdr.markForCheck(); - } - - validate(): ValidationErrors | null { - return this.securityConfigFormGroup.valid ? null : { - securityConfigFormGroup: { valid: false } - }; - } - - writeValue(securityConfig: ModbusSecurity): void { - const { certfile, password, keyfile, server_hostname } = securityConfig; - const securityState = { - certfile: certfile ?? '', - password: password ?? '', - keyfile: keyfile ?? '', - server_hostname: server_hostname ?? '', - reqclicert: !!securityConfig.reqclicert, - }; - - this.securityConfigFormGroup.reset(securityState, {emitEvent: false}); - } - - private updateMasterEnabling(): void { - if (this.isMaster) { - if (!this.disabled) { - this.securityConfigFormGroup.get('reqclicert').enable({emitEvent: false}); - } - this.securityConfigFormGroup.get('server_hostname').disable({emitEvent: false}); - } else { - if (!this.disabled) { - this.securityConfigFormGroup.get('server_hostname').enable({emitEvent: false}); - } - this.securityConfigFormGroup.get('reqclicert').disable({emitEvent: false}); - } - } - - private observeValueChanges(): void { - this.securityConfigFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value: ModbusSecurity) => { - this.onChange(value); - this.onTouched(); - }); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html deleted file mode 100644 index 7008f3c70a..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html +++ /dev/null @@ -1,274 +0,0 @@ - -
-
-
-
gateway.server-slave-config
- - {{ ModbusProtocolLabelsMap.get(type) }} - -
-
-
-
gateway.host
-
- - - - warning - - -
-
-
-
gateway.port
-
- - - - warning - - -
-
- -
-
gateway.port
-
- - - - warning - - -
-
-
-
-
- gateway.method -
-
- - - {{ ModbusMethodLabelsMap.get(method) }} - - -
-
-
-
-
gateway.unit-id
-
- - - - warning - - -
-
-
-
gateway.device-name
-
- - - - warning - - -
-
-
-
gateway.device-profile
-
- - - - warning - - -
-
-
-
- - gateway.poll-period - -
-
- - - -
-
-
-
gateway.baudrate
-
- - - {{ rate }} - - -
-
-
- - - {{ 'gateway.send-data-to-platform' | translate }} - - -
-
- - - -
gateway.advanced-connection-settings
-
-
-
-
-
gateway.byte-order
-
- - - {{ order }} - - -
-
-
-
gateway.word-order
-
- - - {{ order }} - - -
-
-
- - - - - - {{ 'gateway.tls-connection' | translate }} - - - - - - -
- -
-
gateway.vendor-name
-
- - - -
-
-
-
gateway.product-code
-
- - - -
-
-
-
gateway.vendor-url
-
- - - -
-
-
-
gateway.product-name
-
- - - -
-
-
-
gateway.model-name
-
- - - -
-
-
-
-
-
-
-
gateway.values
- -
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts deleted file mode 100644 index f4dfa57f96..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts +++ /dev/null @@ -1,283 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, forwardRef, OnDestroy } from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - FormControl, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators, -} from '@angular/forms'; -import { - ModbusBaudrates, - ModbusMethodLabelsMap, - ModbusMethodType, - ModbusOrderType, - ModbusProtocolLabelsMap, - ModbusProtocolType, - ModbusRegisterValues, - ModbusSerialMethodType, - ModbusSlave, - noLeadTrailSpacesRegex, - PortLimits, - SlaveConfig, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; -import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; -import { ModbusValuesComponent, } from '../modbus-values/modbus-values.component'; -import { isEqual } from '@core/utils'; - -@Component({ - selector: 'tb-modbus-slave-config', - templateUrl: './modbus-slave-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusSlaveConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ModbusSlaveConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ModbusValuesComponent, - ModbusSecurityConfigComponent, - GatewayPortTooltipPipe, - ], -}) -export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validator, OnDestroy { - - slaveConfigFormGroup: UntypedFormGroup; - showSecurityControl: FormControl; - ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; - ModbusMethodLabelsMap = ModbusMethodLabelsMap; - portLimits = PortLimits; - - readonly modbusProtocolTypes = Object.values(ModbusProtocolType); - readonly modbusMethodTypes = Object.values(ModbusMethodType); - readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType); - readonly modbusOrderType = Object.values(ModbusOrderType); - readonly ModbusProtocolType = ModbusProtocolType; - readonly modbusBaudrates = ModbusBaudrates; - - private isSlaveEnabled = false; - private readonly serialSpecificControlKeys = ['serialPort', 'baudrate']; - private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host']; - - private onChange: (value: SlaveConfig) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.showSecurityControl = this.fb.control(false); - this.slaveConfigFormGroup = this.fb.group({ - type: [ModbusProtocolType.TCP], - host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]], - serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - method: [ModbusMethodType.SOCKET], - unitId: [null, [Validators.required]], - baudrate: [this.modbusBaudrates[0]], - deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - pollPeriod: [5000, [Validators.required]], - sendDataToThingsBoard: [false], - byteOrder:[ModbusOrderType.BIG], - wordOrder: [ModbusOrderType.BIG], - security: [], - identity: this.fb.group({ - vendorName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - productCode: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - vendorUrl: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - productName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - modelName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - }), - values: [], - }); - - this.observeValueChanges(); - this.observeTypeChange(); - this.observeShowSecurity(); - } - - get protocolType(): ModbusProtocolType { - return this.slaveConfigFormGroup.get('type').value; - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: SlaveConfig) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - validate(): ValidationErrors | null { - return this.slaveConfigFormGroup.valid ? null : { - slaveConfigFormGroup: { valid: false } - }; - } - - writeValue(slaveConfig: ModbusSlave): void { - this.showSecurityControl.patchValue(!!slaveConfig.security && !isEqual(slaveConfig.security, {})); - this.updateSlaveConfig(slaveConfig); - } - - setDisabledState(isDisabled: boolean): void { - this.isSlaveEnabled = !isDisabled; - this.updateFormEnableState(); - } - - private observeValueChanges(): void { - this.slaveConfigFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value: SlaveConfig) => { - if (value.type === ModbusProtocolType.Serial) { - value.port = value.serialPort; - delete value.serialPort; - } - this.onChange(value); - this.onTouched(); - }); - } - - private observeTypeChange(): void { - this.slaveConfigFormGroup.get('type').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(type => { - this.updateFormEnableState(); - this.updateMethodType(type); - }); - } - - private updateMethodType(type: ModbusProtocolType): void { - if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) { - this.slaveConfigFormGroup.get('method').patchValue( - type === ModbusProtocolType.Serial - ? ModbusSerialMethodType.ASCII - : ModbusMethodType.SOCKET, - {emitEvent: false} - ); - } - } - - private updateFormEnableState(): void { - if (this.isSlaveEnabled) { - this.slaveConfigFormGroup.enable({emitEvent: false}); - this.showSecurityControl.enable({emitEvent: false}); - } else { - this.slaveConfigFormGroup.disable({emitEvent: false}); - this.showSecurityControl.disable({emitEvent: false}); - } - this.updateEnablingByProtocol(); - this.updateSecurityEnable(this.showSecurityControl.value); - } - - private observeShowSecurity(): void { - this.showSecurityControl.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(value => this.updateSecurityEnable(value)); - } - - private updateSecurityEnable(securityEnabled: boolean): void { - if (securityEnabled && this.isSlaveEnabled && this.protocolType !== ModbusProtocolType.Serial) { - this.slaveConfigFormGroup.get('security').enable({emitEvent: false}); - } else { - this.slaveConfigFormGroup.get('security').disable({emitEvent: false}); - } - } - - private updateEnablingByProtocol(): void { - const isSerial = this.protocolType === ModbusProtocolType.Serial; - const enableKeys = isSerial ? this.serialSpecificControlKeys : this.tcpUdpSpecificControlKeys; - const disableKeys = isSerial ? this.tcpUdpSpecificControlKeys : this.serialSpecificControlKeys; - - if (this.isSlaveEnabled) { - enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false })); - } - - disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false })); - } - - private updateSlaveConfig(slaveConfig: ModbusSlave): void { - const { - type = ModbusProtocolType.TCP, - method = ModbusMethodType.RTU, - unitId = 0, - deviceName = '', - deviceType = '', - pollPeriod = 5000, - sendDataToThingsBoard = false, - byteOrder = ModbusOrderType.BIG, - wordOrder = ModbusOrderType.BIG, - security = {}, - identity = { - vendorName: '', - productCode: '', - vendorUrl: '', - productName: '', - modelName: '', - }, - values = {} as ModbusRegisterValues, - baudrate = this.modbusBaudrates[0], - host = '', - port = null, - } = slaveConfig; - - const slaveState: ModbusSlave = { - type, - method, - unitId, - deviceName, - deviceType, - pollPeriod, - sendDataToThingsBoard: !!sendDataToThingsBoard, - byteOrder, - wordOrder, - security, - identity, - values, - baudrate, - host: type === ModbusProtocolType.Serial ? '' : host, - port: type === ModbusProtocolType.Serial ? null : port, - serialPort: (type === ModbusProtocolType.Serial ? port : '') as string, - }; - - this.slaveConfigFormGroup.setValue(slaveState, { emitEvent: false }); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-legacy-slave-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-legacy-slave-dialog.component.ts deleted file mode 100644 index 137ef385ab..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-legacy-slave-dialog.component.ts +++ /dev/null @@ -1,84 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; -import { - FormBuilder, -} from '@angular/forms'; -import { - LegacySlaveConfig, - ModbusProtocolType, - ModbusSlaveInfo, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { ModbusValuesComponent } from '../modbus-values/modbus-values.component'; -import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; -import { - ReportStrategyComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component'; -import { - ModbusSlaveDialogAbstract -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract'; - -@Component({ - selector: 'tb-modbus-legacy-slave-dialog', - templateUrl: './modbus-slave-dialog.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [ - CommonModule, - SharedModule, - ModbusValuesComponent, - ModbusSecurityConfigComponent, - GatewayPortTooltipPipe, - ReportStrategyComponent, - ], - styleUrls: ['./modbus-slave-dialog.component.scss'], -}) -export class ModbusLegacySlaveDialogComponent extends ModbusSlaveDialogAbstract { - - constructor( - protected fb: FormBuilder, - protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo, - public dialogRef: MatDialogRef, - ) { - super(fb, store, router, data, dialogRef); - } - - protected override getSlaveResultData(): LegacySlaveConfig { - const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value; - const slaveResult = { ...rest, type, ...values }; - - if (type === ModbusProtocolType.Serial) { - slaveResult.port = serialPort; - } - - return slaveResult; - } - - - protected override addFieldsToFormGroup(): void { - this.slaveConfigFormGroup.addControl('sendDataOnlyOnChange', this.fb.control(false)); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract.ts deleted file mode 100644 index f9955b839b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract.ts +++ /dev/null @@ -1,208 +0,0 @@ -/// -/// 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. -/// - -import { Directive, Inject, OnDestroy } from '@angular/core'; -import { - FormBuilder, - FormControl, - UntypedFormGroup, - Validators, -} from '@angular/forms'; -import { - ModbusBaudrates, - ModbusByteSizes, - ModbusMethodLabelsMap, - ModbusMethodType, - ModbusOrderType, - ModbusParity, - ModbusParityLabelsMap, - ModbusProtocolLabelsMap, - ModbusProtocolType, - ModbusSerialMethodType, - ModbusSlaveInfo, - noLeadTrailSpacesRegex, - PortLimits, - ReportStrategyDefaultValue, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { Subject } from 'rxjs'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { takeUntil } from 'rxjs/operators'; -import { isEqual } from '@core/utils'; -import { helpBaseUrl } from '@shared/models/constants'; - -@Directive() -export abstract class ModbusSlaveDialogAbstract extends DialogComponent implements OnDestroy { - - slaveConfigFormGroup: UntypedFormGroup; - showSecurityControl: FormControl; - portLimits = PortLimits; - - readonly modbusProtocolTypes = Object.values(ModbusProtocolType); - readonly modbusMethodTypes = Object.values(ModbusMethodType); - readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType); - readonly modbusParities = Object.values(ModbusParity); - readonly modbusByteSizes = ModbusByteSizes; - readonly modbusBaudrates = ModbusBaudrates; - readonly modbusOrderType = Object.values(ModbusOrderType); - readonly ModbusProtocolType = ModbusProtocolType; - readonly ModbusParityLabelsMap = ModbusParityLabelsMap; - readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; - readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap; - readonly ReportStrategyDefaultValue = ReportStrategyDefaultValue; - readonly modbusHelpLink = - helpBaseUrl + '/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters'; - - private readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict']; - private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host']; - - private destroy$ = new Subject(); - - constructor( - protected fb: FormBuilder, - protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo, - public dialogRef: MatDialogRef, - ) { - super(store, router, dialogRef); - - this.showSecurityControl = this.fb.control(false); - this.initializeSlaveFormGroup(); - this.updateSlaveFormGroup(); - this.updateControlsEnabling(this.data.value.type); - this.observeTypeChange(); - this.observeShowSecurity(); - this.showSecurityControl.patchValue(!!this.data.value.security && !isEqual(this.data.value.security, {})); - } - - get protocolType(): ModbusProtocolType { - return this.slaveConfigFormGroup.get('type').value; - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - cancel(): void { - this.dialogRef.close(null); - } - - add(): void { - if (!this.slaveConfigFormGroup.valid) { - return; - } - - this.dialogRef.close(this.getSlaveResultData()); - } - - private initializeSlaveFormGroup(): void { - this.slaveConfigFormGroup = this.fb.group({ - type: [ModbusProtocolType.TCP], - host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]], - serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - method: [ModbusMethodType.SOCKET, [Validators.required]], - baudrate: [this.modbusBaudrates[0]], - stopbits: [1], - bytesize: [ModbusByteSizes[0]], - parity: [ModbusParity.None], - strict: [true], - unitId: [null, [Validators.required]], - deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - timeout: [35], - byteOrder: [ModbusOrderType.BIG], - wordOrder: [ModbusOrderType.BIG], - retries: [true], - retryOnEmpty: [true], - retryOnInvalid: [true], - pollPeriod: [5000, [Validators.required]], - connectAttemptTimeMs: [5000, [Validators.required]], - connectAttemptCount: [5, [Validators.required]], - waitAfterFailedAttemptsMs: [300000, [Validators.required]], - values: [{}], - security: [{}], - }); - this.addFieldsToFormGroup(); - } - - private updateSlaveFormGroup(): void { - this.slaveConfigFormGroup.patchValue({ - ...this.data.value, - port: this.data.value.type === ModbusProtocolType.Serial ? null : this.data.value.port, - serialPort: this.data.value.type === ModbusProtocolType.Serial ? this.data.value.port : '', - values: { - attributes: this.data.value.attributes ?? [], - timeseries: this.data.value.timeseries ?? [], - attributeUpdates: this.data.value.attributeUpdates ?? [], - rpc: this.data.value.rpc ?? [], - } - }); - } - - private observeTypeChange(): void { - this.slaveConfigFormGroup.get('type').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(type => { - this.updateControlsEnabling(type); - this.updateMethodType(type); - }); - } - - private updateMethodType(type: ModbusProtocolType): void { - if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) { - this.slaveConfigFormGroup.get('method').patchValue( - type === ModbusProtocolType.Serial - ? ModbusSerialMethodType.ASCII - : ModbusMethodType.SOCKET, - {emitEvent: false} - ); - } - } - - private updateControlsEnabling(type: ModbusProtocolType): void { - const [enableKeys, disableKeys] = type === ModbusProtocolType.Serial - ? [this.serialSpecificControlKeys, this.tcpUdpSpecificControlKeys] - : [this.tcpUdpSpecificControlKeys, this.serialSpecificControlKeys]; - - enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false })); - disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false })); - - this.updateSecurityEnabling(this.showSecurityControl.value); - } - - private observeShowSecurity(): void { - this.showSecurityControl.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(value => this.updateSecurityEnabling(value)); - } - - private updateSecurityEnabling(isEnabled: boolean): void { - if (isEnabled && this.protocolType !== ModbusProtocolType.Serial) { - this.slaveConfigFormGroup.get('security').enable({emitEvent: false}); - } else { - this.slaveConfigFormGroup.get('security').disable({emitEvent: false}); - } - } - - protected abstract addFieldsToFormGroup(): void; - protected abstract getSlaveResultData(): Config; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html deleted file mode 100644 index 0a480a88ae..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html +++ /dev/null @@ -1,352 +0,0 @@ - -
- -

{{ 'gateway.server-slave' | translate }}

- -
- -
-
-
-
-
-
gateway.server-connection
- - {{ ModbusProtocolLabelsMap.get(type) }} - -
-
-
-
gateway.host
-
- - - - warning - - -
-
-
-
gateway.port
-
- - - - warning - - -
-
- -
-
gateway.port
-
- - - - warning - - -
-
-
-
-
- gateway.method -
-
- - - {{ ModbusMethodLabelsMap.get(method) }} - - -
-
-
- -
-
gateway.baudrate
-
- - - {{ rate }} - - -
-
-
-
gateway.bytesize
-
- - - {{ size }} - - -
-
-
-
gateway.stopbits
-
- - - -
-
-
-
gateway.parity
-
- - - {{ ModbusParityLabelsMap.get(parity) }} - - -
-
-
- - - {{ 'gateway.strict' | translate }} - - -
-
-
-
gateway.unit-id
-
- - - - warning - - -
-
-
-
gateway.device-name
-
- - - - warning - - -
-
-
-
gateway.device-profile
-
- - - - warning - - -
-
-
- - - {{ 'gateway.send-data-on-change' | translate }} - - -
- - - -
- - - -
gateway.advanced-connection-settings
-
-
-
-
-
gateway.connection-timeout
-
- - - -
-
-
-
gateway.byte-order
-
- - - {{ order }} - - -
-
-
-
gateway.word-order
-
- - - {{ order }} - - -
-
-
- - - - - - {{ 'gateway.tls-connection' | translate }} - - - - - - -
-
- - - {{ 'gateway.retries' | translate }} - - -
-
- - - {{ 'gateway.retries-on-empty' | translate }} - - -
-
- - - {{ 'gateway.retries-on-invalid' | translate }} - - -
-
-
- - gateway.poll-period - -
-
- - - -
-
-
-
gateway.connect-attempt-time
-
- - - -
-
-
-
gateway.connect-attempt-count
-
- - - -
-
-
-
gateway.wait-after-failed-attempts
-
- - - -
-
-
-
-
-
- -
-
-
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss deleted file mode 100644 index 0c68b6af9e..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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. - */ -:host { - .slaves-config-container { - width: 80vw; - max-width: 900px; - } - - .slave-name-label { - margin-right: 16px; - color: rgba(0, 0, 0, 0.87); - } - - .fixed-title-width-260 { - min-width: 260px; - } - - ::ng-deep.security-config { - .fixed-title-width { - min-width: 230px; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts deleted file mode 100644 index d9a9c426c2..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts +++ /dev/null @@ -1,87 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; -import { - FormBuilder, -} from '@angular/forms'; -import { - ModbusProtocolType, - ModbusSlaveInfo, - SlaveConfig, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { ModbusValuesComponent } from '../modbus-values/modbus-values.component'; -import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { Router } from '@angular/router'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; -import { - ReportStrategyComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component'; -import { - ModbusSlaveDialogAbstract -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.abstract'; - -@Component({ - selector: 'tb-modbus-slave-dialog', - templateUrl: './modbus-slave-dialog.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [ - CommonModule, - SharedModule, - ModbusValuesComponent, - ModbusSecurityConfigComponent, - GatewayPortTooltipPipe, - ReportStrategyComponent, - ], - styleUrls: ['./modbus-slave-dialog.component.scss'], -}) -export class ModbusSlaveDialogComponent extends ModbusSlaveDialogAbstract { - - constructor( - protected fb: FormBuilder, - protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo, - public dialogRef: MatDialogRef, - ) { - super(fb, store, router, data, dialogRef); - } - - protected override getSlaveResultData(): SlaveConfig { - const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value; - const slaveResult = { ...rest, type, ...values }; - - if (type === ModbusProtocolType.Serial) { - slaveResult.port = serialPort; - } - - if (!slaveResult.reportStrategy) { - delete slaveResult.reportStrategy; - } - - return slaveResult; - } - - protected override addFieldsToFormGroup(): void { - this.slaveConfigFormGroup.addControl('reportStrategy', this.fb.control(null)); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html deleted file mode 100644 index 8d1048e3cb..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html +++ /dev/null @@ -1,129 +0,0 @@ - - - -
- -
-
- - - - -
- -
-
-
-
- - -
-
gateway.attributes
-
- - - {{ attribute.tag }} - - - - - - -
-
-
-
gateway.timeseries
-
- - - {{ telemetry.tag }} - - - - - - -
-
-
-
gateway.attribute-updates
-
- - - {{ attributeUpdate.tag }} - - - - - - -
-
-
-
gateway.rpc-requests
-
- - - {{ rpcRequest.tag }} - - - - - - -
-
-
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss deleted file mode 100644 index e80998b88a..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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. - */ - -:host ::ng-deep .mat-mdc-tab-body-wrapper { - min-height: 320px; -} - -::ng-deep .mdc-evolution-chip-set__chips { - align-items: center; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts deleted file mode 100644 index c0824869e3..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts +++ /dev/null @@ -1,240 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - forwardRef, - Input, - OnDestroy, - OnInit, - Renderer2, - ViewContainerRef -} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - FormGroup, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - ValidationErrors, - Validator, -} from '@angular/forms'; -import { - ModbusKeysAddKeyTranslationsMap, - ModbusKeysDeleteKeyTranslationsMap, - ModbusKeysNoKeysTextTranslationsMap, - ModbusKeysPanelTitleTranslationsMap, - ModbusRegisterTranslationsMap, - ModbusRegisterType, - ModbusRegisterValues, - ModbusValue, - ModbusValueKey, - ModbusValues, - ModbusValuesState, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; -import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; -import { MatButton } from '@angular/material/button'; -import { TbPopoverService } from '@shared/components/popover.service'; -import { ModbusDataKeysPanelComponent } from '../modbus-data-keys-panel/modbus-data-keys-panel.component'; -import { coerceBoolean } from '@shared/decorators/coercion'; - -@Component({ - selector: 'tb-modbus-values', - templateUrl: './modbus-values.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusValuesComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ModbusValuesComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - EllipsisChipListDirective, - ], - styleUrls: ['./modbus-values.component.scss'] -}) - -export class ModbusValuesComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy { - - @coerceBoolean() - @Input() singleMode = false; - - @coerceBoolean() - @Input() hideNewFields = false; - - disabled = false; - modbusRegisterTypes: ModbusRegisterType[] = Object.values(ModbusRegisterType); - modbusValueKeys = Object.values(ModbusValueKey); - ModbusValuesTranslationsMap = ModbusRegisterTranslationsMap; - ModbusValueKey = ModbusValueKey; - valuesFormGroup: FormGroup; - - private onChange: (value: ModbusValuesState) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, - private popoverService: TbPopoverService, - private renderer: Renderer2, - private viewContainerRef: ViewContainerRef, - private cdr: ChangeDetectorRef, - ) {} - - ngOnInit() { - this.initializeValuesFormGroup(); - this.observeValuesChanges(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: ModbusValuesState) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(values: ModbusValuesState): void { - if (this.singleMode) { - this.valuesFormGroup.setValue(this.getSingleRegisterState(values as ModbusValues), { emitEvent: false }); - } else { - const { holding_registers, coils_initializer, input_registers, discrete_inputs } = values as ModbusRegisterValues; - this.valuesFormGroup.setValue({ - holding_registers: this.getSingleRegisterState(holding_registers), - coils_initializer: this.getSingleRegisterState(coils_initializer), - input_registers: this.getSingleRegisterState(input_registers), - discrete_inputs: this.getSingleRegisterState(discrete_inputs), - }, { emitEvent: false }); - } - this.cdr.markForCheck(); - } - - validate(): ValidationErrors | null { - return this.valuesFormGroup.valid ? null : { - valuesFormGroup: {valid: false} - }; - } - - setDisabledState(isDisabled: boolean): void { - this.disabled = isDisabled; - this.cdr.markForCheck(); - } - - getValueGroup(valueKey: ModbusValueKey, register?: ModbusRegisterType): FormGroup { - return register - ? this.valuesFormGroup.get(register).get(valueKey) as FormGroup - : this.valuesFormGroup.get(valueKey) as FormGroup; - } - - manageKeys($event: Event, matButton: MatButton, keysType: ModbusValueKey, register?: ModbusRegisterType): void { - $event.stopPropagation(); - const trigger = matButton._elementRef.nativeElement; - if (this.popoverService.hasPopover(trigger)) { - this.popoverService.hidePopover(trigger); - return; - } - - const keysControl = this.getValueGroup(keysType, register); - const ctx = { - values: keysControl.value, - isMaster: !this.singleMode, - keysType, - panelTitle: ModbusKeysPanelTitleTranslationsMap.get(keysType), - addKeyTitle: ModbusKeysAddKeyTranslationsMap.get(keysType), - deleteKeyTitle: ModbusKeysDeleteKeyTranslationsMap.get(keysType), - noKeysText: ModbusKeysNoKeysTextTranslationsMap.get(keysType), - hideNewFields: this.hideNewFields, - }; - const dataKeysPanelPopover = this.popoverService.displayPopover( - trigger, - this.renderer, - this.viewContainerRef, - ModbusDataKeysPanelComponent, - 'leftBottom', - false, - null, - ctx, - {}, - {}, - {}, - true - ); - dataKeysPanelPopover.tbComponentRef.instance.popover = dataKeysPanelPopover; - dataKeysPanelPopover.tbComponentRef.instance.keysDataApplied.pipe(takeUntil(this.destroy$)).subscribe((keysData: ModbusValue[]) => { - dataKeysPanelPopover.hide(); - keysControl.patchValue(keysData); - keysControl.markAsDirty(); - this.cdr.markForCheck(); - }); - } - - private initializeValuesFormGroup(): void { - const getValuesFormGroup = () => this.fb.group(this.modbusValueKeys.reduce((acc, key) => { - acc[key] = this.fb.control([[], []]); - return acc; - }, {})); - - if (this.singleMode) { - this.valuesFormGroup = getValuesFormGroup(); - } else { - this.valuesFormGroup = this.fb.group( - this.modbusRegisterTypes.reduce((registersAcc, register) => { - registersAcc[register] = getValuesFormGroup(); - return registersAcc; - }, {}) - ); - } - } - - - private observeValuesChanges(): void { - this.valuesFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(value => { - this.onChange(value); - this.onTouched(); - }); - } - - private getSingleRegisterState(values: ModbusValues): ModbusValues { - return { - attributes: values?.attributes ?? [], - timeseries: values?.timeseries ?? [], - attributeUpdates: values?.attributeUpdates ?? [], - rpc: values?.rpc ?? [], - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract.ts deleted file mode 100644 index 16f5036770..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract.ts +++ /dev/null @@ -1,93 +0,0 @@ -/// -/// 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. -/// - -import { Directive } from '@angular/core'; -import { FormGroup } from '@angular/forms'; -import { - BrokerConfig, - MappingType, - MQTTBasicConfig, - MQTTBasicConfig_v3_5_2, - RequestMappingData, - RequestMappingValue, - RequestType, - WorkersConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { isObject } from '@core/utils'; -import { - GatewayConnectorBasicConfigDirective -} from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract'; - -@Directive() -export abstract class MqttBasicConfigDirective - extends GatewayConnectorBasicConfigDirective { - - MappingType = MappingType; - - protected override initBasicFormGroup(): FormGroup { - return this.fb.group({ - mapping: [], - requestsMapping: [], - broker: [], - workers: [], - }); - } - - protected getRequestDataArray(value: Record): RequestMappingData[] { - const mappingConfigs = []; - - if (isObject(value)) { - Object.keys(value).forEach((configKey: string) => { - for (const mapping of value[configKey]) { - mappingConfigs.push({ - requestType: configKey, - requestValue: mapping - }); - } - }); - } - - return mappingConfigs; - } - - protected getRequestDataObject(array: RequestMappingValue[]): Record { - return array.reduce((result, { requestType, requestValue }) => { - result[requestType].push(requestValue); - return result; - }, { - connectRequests: [], - disconnectRequests: [], - attributeRequests: [], - attributeUpdates: [], - serverSideRpc: [], - }); - } - - protected getBrokerMappedValue(broker: BrokerConfig, workers: WorkersConfig): BrokerConfig { - return { - ...broker, - maxNumberOfWorkers: workers.maxNumberOfWorkers ?? 100, - maxMessageNumberPerWorker: workers.maxMessageNumberPerWorker ?? 10, - }; - } - - writeValue(basicConfig: BasicConfig): void { - this.basicFormGroup.setValue(this.mapConfigToFormValue(basicConfig), { emitEvent: false }); - } - - protected abstract override mapConfigToFormValue(config: BasicConfig): MQTTBasicConfig_v3_5_2; - protected abstract override getMappedValue(config: MQTTBasicConfig): BasicConfig; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.html deleted file mode 100644 index b602ba20d4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - -
- -
-
- -
- -
-
- -
- -
-
-
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.scss deleted file mode 100644 index fd799d8aa4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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. - */ -:host { - height: 100%; -} -:host ::ng-deep { - .mat-mdc-tab-group, .mat-mdc-tab-body-wrapper { - height: 100%; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.ts deleted file mode 100644 index 155b91efb6..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component.ts +++ /dev/null @@ -1,97 +0,0 @@ -/// -/// 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. -/// - -import { Component, forwardRef, ChangeDetectionStrategy } from '@angular/core'; -import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms'; -import { - BrokerConfig, - MQTTBasicConfig_v3_5_2, - RequestMappingData, - RequestMappingValue, - RequestType, WorkersConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { - MqttBasicConfigDirective -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { - SecurityConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; -import { - WorkersConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component'; -import { - BrokerConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component'; -import { - MappingTableComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; - -@Component({ - selector: 'tb-mqtt-basic-config', - templateUrl: './mqtt-basic-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MqttBasicConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => MqttBasicConfigComponent), - multi: true - } - ], - styleUrls: ['./mqtt-basic-config.component.scss'], - standalone: true, - imports: [ - CommonModule, - SharedModule, - SecurityConfigComponent, - WorkersConfigControlComponent, - BrokerConfigControlComponent, - MappingTableComponent, - ], -}) -export class MqttBasicConfigComponent extends MqttBasicConfigDirective { - - protected override mapConfigToFormValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTBasicConfig_v3_5_2 { - const { broker, mapping = [], requestsMapping } = basicConfig; - return{ - workers: broker && (broker.maxNumberOfWorkers || broker.maxMessageNumberPerWorker) ? { - maxNumberOfWorkers: broker.maxNumberOfWorkers, - maxMessageNumberPerWorker: broker.maxMessageNumberPerWorker, - } : {} as WorkersConfig, - mapping: mapping ?? [], - broker: broker ?? {} as BrokerConfig, - requestsMapping: this.getRequestDataArray(requestsMapping as Record), - }; - } - - protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTBasicConfig_v3_5_2 { - const { broker, workers, mapping, requestsMapping } = basicConfig || {}; - - return { - broker: this.getBrokerMappedValue(broker, workers), - mapping, - requestsMapping: (requestsMapping as RequestMappingData[])?.length - ? this.getRequestDataObject(requestsMapping as RequestMappingValue[]) - : {} as Record - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component.ts deleted file mode 100644 index 6209cef677..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component.ts +++ /dev/null @@ -1,117 +0,0 @@ -/// -/// 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. -/// - -import { Component, forwardRef, ChangeDetectionStrategy } from '@angular/core'; -import { NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms'; -import { - BrokerConfig, - MQTTBasicConfig_v3_5_2, - MQTTLegacyBasicConfig, - RequestMappingData, - RequestMappingValue, - RequestType, WorkersConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { MqttVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/mqtt-version-mapping.util'; -import { - MqttBasicConfigDirective -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.abstract'; -import { isDefinedAndNotNull } from '@core/utils'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { - SecurityConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; -import { - WorkersConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component'; -import { - BrokerConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component'; -import { - MappingTableComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; - -@Component({ - selector: 'tb-mqtt-legacy-basic-config', - templateUrl: './mqtt-basic-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MqttLegacyBasicConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => MqttLegacyBasicConfigComponent), - multi: true - } - ], - styleUrls: ['./mqtt-basic-config.component.scss'], - standalone: true, - imports: [ - CommonModule, - SharedModule, - SecurityConfigComponent, - WorkersConfigControlComponent, - BrokerConfigControlComponent, - MappingTableComponent, - ], -}) -export class MqttLegacyBasicConfigComponent extends MqttBasicConfigDirective { - - protected override mapConfigToFormValue(config: MQTTLegacyBasicConfig): MQTTBasicConfig_v3_5_2 { - const { - broker, - mapping = [], - connectRequests = [], - disconnectRequests = [], - attributeRequests = [], - attributeUpdates = [], - serverSideRpc = [] - } = config as MQTTLegacyBasicConfig; - const updatedRequestMapping = MqttVersionMappingUtil.mapRequestsToUpgradedVersion({ - connectRequests, - disconnectRequests, - attributeRequests, - attributeUpdates, - serverSideRpc - }); - return { - workers: broker && (broker.maxNumberOfWorkers || broker.maxMessageNumberPerWorker) ? { - maxNumberOfWorkers: broker.maxNumberOfWorkers, - maxMessageNumberPerWorker: broker.maxMessageNumberPerWorker, - } : {} as WorkersConfig, - mapping: MqttVersionMappingUtil.mapMappingToUpgradedVersion(mapping) || [], - broker: broker || {} as BrokerConfig, - requestsMapping: this.getRequestDataArray(updatedRequestMapping), - }; - } - - protected override getMappedValue(basicConfig: MQTTBasicConfig_v3_5_2): MQTTLegacyBasicConfig { - const { broker, workers, mapping, requestsMapping } = basicConfig || {}; - - const updatedRequestMapping = (requestsMapping as RequestMappingData[])?.length - ? this.getRequestDataObject(requestsMapping as RequestMappingValue[]) - : {} as Record; - - return { - broker: this.getBrokerMappedValue(broker, workers), - mapping: MqttVersionMappingUtil.mapMappingToDowngradedVersion(mapping), - ...(MqttVersionMappingUtil.mapRequestsToDowngradedVersion(updatedRequestMapping as Record)) - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.html deleted file mode 100644 index b5ef346300..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.html +++ /dev/null @@ -1,86 +0,0 @@ - -
-
-
gateway.host
-
- - - - warning - - -
-
-
-
gateway.port
-
- - - - warning - - -
-
-
-
gateway.mqtt-version
-
- - - {{ version.name }} - - -
-
-
-
gateway.client-id
-
- - - - -
-
- - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.ts deleted file mode 100644 index 61c62a49e1..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component.ts +++ /dev/null @@ -1,124 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, forwardRef, OnDestroy } from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { - BrokerConfig, - MqttVersions, - noLeadTrailSpacesRegex, - PortLimits, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { generateSecret } from '@core/utils'; -import { Subject } from 'rxjs'; -import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; -import { SecurityConfigComponent } from '../../security-config/security-config.component'; - -@Component({ - selector: 'tb-broker-config-control', - templateUrl: './broker-config-control.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [ - CommonModule, - SharedModule, - SecurityConfigComponent, - GatewayPortTooltipPipe, - ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => BrokerConfigControlComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => BrokerConfigControlComponent), - multi: true - } - ] -}) -export class BrokerConfigControlComponent implements ControlValueAccessor, Validator, OnDestroy { - brokerConfigFormGroup: UntypedFormGroup; - mqttVersions = MqttVersions; - portLimits = PortLimits; - - private onChange: (value: string) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, - private cdr: ChangeDetectorRef) { - this.brokerConfigFormGroup = this.fb.group({ - host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]], - version: [5, []], - clientId: ['tb_gw_' + generateSecret(5), [Validators.pattern(noLeadTrailSpacesRegex)]], - security: [] - }); - - this.brokerConfigFormGroup.valueChanges.subscribe(value => { - this.onChange(value); - this.onTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - generate(formControlName: string): void { - this.brokerConfigFormGroup.get(formControlName)?.patchValue('tb_gw_' + generateSecret(5)); - } - - registerOnChange(fn: (value: string) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(brokerConfig: BrokerConfig): void { - const { - version = 5, - clientId = `tb_gw_${generateSecret(5)}`, - security = {}, - } = brokerConfig; - - this.brokerConfigFormGroup.reset({ ...brokerConfig, version, clientId, security }, { emitEvent: false }); - this.cdr.markForCheck(); - } - - validate(): ValidationErrors | null { - return this.brokerConfigFormGroup.valid ? null : { - brokerConfigFormGroup: {valid: false} - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.html deleted file mode 100644 index 0a3bd4e6f3..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.html +++ /dev/null @@ -1,63 +0,0 @@ - -
-
-
-
{{ 'gateway.max-number-of-workers' | translate }}
-
-
- - - - warning - - -
-
-
-
-
{{ 'gateway.max-messages-queue-for-worker' | translate }}
-
-
- - - - warning - - -
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.ts deleted file mode 100644 index 8abc3cb416..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component.ts +++ /dev/null @@ -1,108 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - Component, - forwardRef, - OnDestroy, -} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { WorkersConfig } from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; - -@Component({ - selector: 'tb-workers-config-control', - templateUrl: './workers-config-control.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, - imports: [ - CommonModule, - SharedModule, - ], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => WorkersConfigControlComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => WorkersConfigControlComponent), - multi: true - } - ] -}) -export class WorkersConfigControlComponent implements OnDestroy, ControlValueAccessor, Validator { - - workersConfigFormGroup: UntypedFormGroup; - - private onChange: (value: string) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.workersConfigFormGroup = this.fb.group({ - maxNumberOfWorkers: [100, [Validators.required, Validators.min(1)]], - maxMessageNumberPerWorker: [10, [Validators.required, Validators.min(1)]], - }); - - this.workersConfigFormGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(value => { - this.onChange(value); - this.onTouched(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: string) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - writeValue(workersConfig: WorkersConfig): void { - const { maxNumberOfWorkers, maxMessageNumberPerWorker } = workersConfig; - this.workersConfigFormGroup.reset({ - maxNumberOfWorkers: maxNumberOfWorkers || 100, - maxMessageNumberPerWorker: maxMessageNumberPerWorker || 10, - }, {emitEvent: false}); - } - - validate(): ValidationErrors | null { - return this.workersConfigFormGroup.valid ? null : { - workersConfigFormGroup: {valid: false} - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.html deleted file mode 100644 index 4a0e76ee19..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.html +++ /dev/null @@ -1,148 +0,0 @@ - -
-
-
gateway.server-url
-
- - - - warning - - -
-
-
-
-
{{ 'gateway.timeout' | translate }}
-
-
- - - - warning - - -
-
-
-
-
{{ 'gateway.security-policy' | translate }}
-
-
- - - {{ version.name }} - - -
-
-
-
-
{{ 'gateway.scan-period' | translate }}
-
-
- - - - warning - - -
-
-
-
-
{{ 'gateway.poll-period' | translate }}
-
-
- - - - warning - - -
-
-
-
-
{{ 'gateway.sub-check-period' | translate }}
-
-
- - - - warning - - -
-
-
- - -
{{ 'gateway.enable-subscription' | translate }}
-
-
-
-
- - - {{ 'gateway.show-map' | translate }} - - -
- - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.scss deleted file mode 100644 index 416f368279..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: block; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.ts deleted file mode 100644 index c000139ded..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component.ts +++ /dev/null @@ -1,152 +0,0 @@ -/// -/// 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. -/// - -import { AfterViewInit, ChangeDetectionStrategy, Component, forwardRef, Input, OnDestroy } from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { - noLeadTrailSpacesRegex, - SecurityPolicy, - SecurityPolicyTypes, - ServerConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { - SecurityConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; -import { HOUR } from '@shared/models/time/time.models'; -import { coerceBoolean } from '@shared/decorators/coercion'; - -@Component({ - selector: 'tb-opc-server-config', - templateUrl: './opc-server-config.component.html', - styleUrls: ['./opc-server-config.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => OpcServerConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => OpcServerConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - SecurityConfigComponent, - ] -}) -export class OpcServerConfigComponent implements ControlValueAccessor, Validator, AfterViewInit, OnDestroy { - - @Input() - @coerceBoolean() - hideNewFields: boolean = false; - - securityPolicyTypes = SecurityPolicyTypes; - serverConfigFormGroup: UntypedFormGroup; - - onChange!: (value: string) => void; - onTouched!: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.serverConfigFormGroup = this.fb.group({ - url: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - timeoutInMillis: [1000, [Validators.required, Validators.min(1000)]], - scanPeriodInMillis: [HOUR, [Validators.required, Validators.min(1000)]], - pollPeriodInMillis: [5000, [Validators.required, Validators.min(50)]], - enableSubscriptions: [true, []], - subCheckPeriodInMillis: [100, [Validators.required, Validators.min(100)]], - showMap: [false, []], - security: [SecurityPolicy.BASIC128, []], - identity: [] - }); - - this.serverConfigFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.onChange(value); - this.onTouched(); - }); - } - - ngAfterViewInit(): void { - if (this.hideNewFields) { - this.serverConfigFormGroup.get('pollPeriodInMillis').disable({emitEvent: false}); - } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: string) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - validate(): ValidationErrors | null { - return this.serverConfigFormGroup.valid ? null : { - serverConfigFormGroup: { valid: false } - }; - } - - writeValue(serverConfig: ServerConfig): void { - const { - timeoutInMillis = 1000, - scanPeriodInMillis = HOUR, - pollPeriodInMillis = 5000, - enableSubscriptions = true, - subCheckPeriodInMillis = 100, - showMap = false, - security = SecurityPolicy.BASIC128, - identity = {}, - } = serverConfig; - - this.serverConfigFormGroup.reset({ - ...serverConfig, - timeoutInMillis, - scanPeriodInMillis, - pollPeriodInMillis, - enableSubscriptions, - subCheckPeriodInMillis, - showMap, - security, - identity, - }, { emitEvent: false }); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.html deleted file mode 100644 index 2296a472a4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - -
- -
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.scss deleted file mode 100644 index fd799d8aa4..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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. - */ -:host { - height: 100%; -} -:host ::ng-deep { - .mat-mdc-tab-group, .mat-mdc-tab-body-wrapper { - height: 100%; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.ts deleted file mode 100644 index c45eafcc67..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core'; -import { FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { - MappingType, - OPCBasicConfig_v3_5_2, - ServerConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { MappingTableComponent } from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; -import { - SecurityConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; -import { - OpcServerConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component'; -import { - GatewayConnectorBasicConfigDirective -} from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract'; - -@Component({ - selector: 'tb-opc-ua-basic-config', - templateUrl: './opc-ua-basic-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => OpcUaBasicConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => OpcUaBasicConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - SecurityConfigComponent, - MappingTableComponent, - OpcServerConfigComponent, - ], - styleUrls: ['./opc-ua-basic-config.component.scss'] -}) -export class OpcUaBasicConfigComponent extends GatewayConnectorBasicConfigDirective { - - mappingTypes = MappingType; - isLegacy = false; - - protected override initBasicFormGroup(): FormGroup { - return this.fb.group({ - mapping: [], - server: [], - }); - } - - protected override mapConfigToFormValue(config: OPCBasicConfig_v3_5_2): OPCBasicConfig_v3_5_2 { - return { - server: config.server ?? {} as ServerConfig, - mapping: config.mapping ?? [], - }; - } - - protected override getMappedValue(value: OPCBasicConfig_v3_5_2): OPCBasicConfig_v3_5_2 { - return { - server: value.server, - mapping: value.mapping, - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component.ts deleted file mode 100644 index 9458858cbf..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectionStrategy, Component, forwardRef } from '@angular/core'; -import { FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { - MappingType, - OPCBasicConfig_v3_5_2, - OPCLegacyBasicConfig, ServerConfig, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { CommonModule } from '@angular/common'; -import { SharedModule } from '@shared/shared.module'; -import { MappingTableComponent } from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; -import { - SecurityConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; -import { - OpcServerConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component'; -import { - GatewayConnectorBasicConfigDirective -} from '@home/components/widget/lib/gateway/abstract/gateway-connector-basic-config.abstract'; -import { OpcVersionMappingUtil } from '@home/components/widget/lib/gateway/utils/opc-version-mapping.util'; - -@Component({ - selector: 'tb-opc-ua-legacy-basic-config', - templateUrl: './opc-ua-basic-config.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => OpcUaLegacyBasicConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => OpcUaLegacyBasicConfigComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - SecurityConfigComponent, - MappingTableComponent, - OpcServerConfigComponent, - ], - styleUrls: ['./opc-ua-basic-config.component.scss'] -}) -export class OpcUaLegacyBasicConfigComponent extends GatewayConnectorBasicConfigDirective { - - mappingTypes = MappingType; - isLegacy = true; - - protected override initBasicFormGroup(): FormGroup { - return this.fb.group({ - mapping: [], - server: [], - }); - } - - protected override mapConfigToFormValue(config: OPCLegacyBasicConfig): OPCBasicConfig_v3_5_2 { - return { - server: config.server ? OpcVersionMappingUtil.mapServerToUpgradedVersion(config.server) : {} as ServerConfig, - mapping: config.server?.mapping ? OpcVersionMappingUtil.mapMappingToUpgradedVersion(config.server.mapping) : [], - }; - } - - protected override getMappedValue(value: OPCBasicConfig_v3_5_2): OPCLegacyBasicConfig { - return { - server: OpcVersionMappingUtil.mapServerToDowngradedVersion(value), - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.html deleted file mode 100644 index dec8292720..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.html +++ /dev/null @@ -1,57 +0,0 @@ - -
- - - - - - {{ 'gateway.report-strategy.label' | translate }} - - - - - - - -
gateway.report-strategy.label
- -
- -
-
{{ 'gateway.type' | translate }}
- - - {{ ReportTypeTranslateMap.get(type) | translate }} - - -
-
-
- - gateway.report-strategy.report-period - -
-
- - - -
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.ts deleted file mode 100644 index 10eaf98f86..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component.ts +++ /dev/null @@ -1,174 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - Component, - forwardRef, - Input, - OnDestroy, -} from '@angular/core'; -import { Subject } from 'rxjs'; -import { - ControlValueAccessor, - FormBuilder, - FormControl, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validators -} from '@angular/forms'; -import { - ReportStrategyConfig, - ReportStrategyDefaultValue, - ReportStrategyType, - ReportStrategyTypeTranslationsMap -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { filter, takeUntil } from 'rxjs/operators'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { - ModbusSecurityConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component'; -import { coerceBoolean, coerceNumber } from '@shared/decorators/coercion'; - -@Component({ - selector: 'tb-report-strategy', - templateUrl: './report-strategy.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ReportStrategyComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ReportStrategyComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ModbusSecurityConfigComponent, - ] -}) -export class ReportStrategyComponent implements ControlValueAccessor, OnDestroy { - - @coerceBoolean() - @Input() isExpansionMode = false; - - @coerceNumber() - @Input() defaultValue = ReportStrategyDefaultValue.Key; - - reportStrategyFormGroup: UntypedFormGroup; - showStrategyControl: FormControl; - - readonly reportStrategyTypes = Object.values(ReportStrategyType); - readonly ReportTypeTranslateMap = ReportStrategyTypeTranslationsMap; - readonly ReportStrategyType = ReportStrategyType; - - private onChange: (value: ReportStrategyConfig) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.showStrategyControl = this.fb.control(false); - - this.reportStrategyFormGroup = this.fb.group({ - type: [{ value: ReportStrategyType.OnReportPeriod, disabled: true }, []], - reportPeriod: [{ value: this.defaultValue, disabled: true }, [Validators.required]], - }); - - this.observeStrategyFormChange(); - this.observeStrategyToggle(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - writeValue(reportStrategyConfig: ReportStrategyConfig): void { - if (this.isExpansionMode) { - this.showStrategyControl.setValue(!!reportStrategyConfig, {emitEvent: false}); - } - if (reportStrategyConfig) { - this.reportStrategyFormGroup.enable({emitEvent: false}); - } - const { type = ReportStrategyType.OnReportPeriod, reportPeriod = this.defaultValue } = reportStrategyConfig ?? {}; - this.reportStrategyFormGroup.setValue({ type, reportPeriod }, {emitEvent: false}); - this.onTypeChange(type); - } - - validate(): ValidationErrors | null { - return this.reportStrategyFormGroup.valid || this.reportStrategyFormGroup.disabled ? null : { - reportStrategyForm: { valid: false } - }; - } - - registerOnChange(fn: (value: ReportStrategyConfig) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - private observeStrategyFormChange(): void { - this.reportStrategyFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.onChange(value); - this.onTouched(); - }); - - this.reportStrategyFormGroup.get('type').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(type => this.onTypeChange(type)); - } - - private observeStrategyToggle(): void { - this.showStrategyControl.valueChanges - .pipe(takeUntil(this.destroy$), filter(() => this.isExpansionMode)) - .subscribe(enable => { - if (enable) { - this.reportStrategyFormGroup.enable({emitEvent: false}); - this.reportStrategyFormGroup.get('reportPeriod').addValidators(Validators.required); - this.onChange(this.reportStrategyFormGroup.value); - } else { - this.reportStrategyFormGroup.disable({emitEvent: false}); - this.reportStrategyFormGroup.get('reportPeriod').removeValidators(Validators.required); - this.onChange(null); - } - this.reportStrategyFormGroup.updateValueAndValidity({emitEvent: false}); - }); - } - - private onTypeChange(type: ReportStrategyType): void { - const reportPeriodControl = this.reportStrategyFormGroup.get('reportPeriod'); - - if (type === ReportStrategyType.OnChange) { - reportPeriodControl.disable({emitEvent: false}); - } else if (!this.isExpansionMode || this.showStrategyControl.value) { - reportPeriodControl.enable({emitEvent: false}); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html deleted file mode 100644 index afabe53499..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html +++ /dev/null @@ -1,65 +0,0 @@ - -
-
-
gateway.security
- - - {{ SecurityTypeTranslationsMap.get(type) | translate }} - - -
- -
-
gateway.username
-
- - - - warning - - -
-
-
-
gateway.password
-
- - - - warning - -
- -
-
-
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss deleted file mode 100644 index f014f56588..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: block; - margin-bottom: 10px; - - .fields-label { - font-weight: 500; - } - - .hide-toggle { - display: none; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts deleted file mode 100644 index d00813bfd6..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts +++ /dev/null @@ -1,132 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - Component, - forwardRef, - OnDestroy, -} from '@angular/core'; -import { Subject } from 'rxjs'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { takeUntil } from 'rxjs/operators'; -import { - noLeadTrailSpacesRegex, - RestSecurityType, - RestSecurityTypeTranslationsMap -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; - -@Component({ - selector: 'tb-rest-connector-security', - templateUrl: './rest-connector-security.component.html', - styleUrls: ['./rest-connector-security.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => RestConnectorSecurityComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => RestConnectorSecurityComponent), - multi: true - } - ], - standalone: true, - imports: [ - SharedModule, - CommonModule, - ] -}) -export class RestConnectorSecurityComponent implements ControlValueAccessor, Validator, OnDestroy { - BrokerSecurityType = RestSecurityType; - securityTypes: RestSecurityType[] = Object.values(RestSecurityType); - SecurityTypeTranslationsMap = RestSecurityTypeTranslationsMap; - securityFormGroup: UntypedFormGroup; - - private destroy$ = new Subject(); - private propagateChange = (_: any) => {}; - - constructor(private fb: FormBuilder) { - this.securityFormGroup = this.fb.group({ - type: [RestSecurityType.ANONYMOUS, []], - username: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - password: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - }); - this.observeSecurityForm(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void {} - - writeValue(deviceInfo: any): void { - if (!deviceInfo.type) { - deviceInfo.type = RestSecurityType.ANONYMOUS; - } - this.securityFormGroup.reset(deviceInfo); - this.updateView(deviceInfo); - } - - validate(): ValidationErrors | null { - return this.securityFormGroup.valid ? null : { - securityForm: { valid: false } - }; - } - - private updateView(value: any): void { - this.propagateChange(value); - } - - private updateValidators(type: RestSecurityType): void { - if (type === RestSecurityType.BASIC) { - this.securityFormGroup.get('username').enable({emitEvent: false}); - this.securityFormGroup.get('password').enable({emitEvent: false}); - } else { - this.securityFormGroup.get('username').disable({emitEvent: false}); - this.securityFormGroup.get('password').disable({emitEvent: false}); - } - } - - private observeSecurityForm(): void { - this.securityFormGroup.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(value => this.updateView(value)); - - this.securityFormGroup.get('type').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(type => this.updateValidators(type)); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html deleted file mode 100644 index 7122bcd52d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.html +++ /dev/null @@ -1,81 +0,0 @@ - - -
- {{ 'gateway.rpc.hint.modbus-response-reading' | translate }}
- {{ 'gateway.rpc.hint.modbus-writing-functions' | translate }} -
-
- - {{ 'gateway.rpc.type' | translate }} - - {{ type }} - - - - {{ 'gateway.rpc.functionCode' | translate }} - - {{ ModbusFunctionCodeTranslationsMap.get(code) | translate}} - - -
-
- - {{ 'gateway.rpc.address' | translate }} - - - warning - - - - {{ 'gateway.rpc.objectsCount' | translate }} - - -
-
- - {{ 'gateway.rpc.value' | translate }} - - - warning - - -
-
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss deleted file mode 100644 index 62eaca664f..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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. - */ -:host { - .hint-container { - margin-bottom: 12px; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts deleted file mode 100644 index ae110b9418..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component.ts +++ /dev/null @@ -1,166 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - Component, - forwardRef, - OnDestroy, -} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { - ModbusDataType, - ModbusEditableDataTypes, - ModbusFunctionCodeTranslationsMap, - ModbusObjectCountByDataType, - noLeadTrailSpacesRegex, - RPCTemplateConfigModbus, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -@Component({ - selector: 'tb-modbus-rpc-parameters', - templateUrl: './modbus-rpc-parameters.component.html', - styleUrls: ['./modbus-rpc-parameters.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ModbusRpcParametersComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => ModbusRpcParametersComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ], -}) -export class ModbusRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy { - - rpcParametersFormGroup: UntypedFormGroup; - functionCodes: Array; - - readonly ModbusEditableDataTypes = ModbusEditableDataTypes; - readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap; - - readonly modbusDataTypes = Object.values(ModbusDataType) as ModbusDataType[]; - readonly writeFunctionCodes = [5, 6, 15, 16]; - - private readonly defaultFunctionCodes = [3, 4, 6, 16]; - private readonly readFunctionCodes = [1, 2, 3, 4]; - private readonly bitsFunctionCodes = [...this.readFunctionCodes, ...this.writeFunctionCodes]; - - private onChange: (value: RPCTemplateConfigModbus) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.rpcParametersFormGroup = this.fb.group({ - type: [ModbusDataType.BYTES, [Validators.required]], - functionCode: [this.defaultFunctionCodes[0], [Validators.required]], - value: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - address: [null, [Validators.required]], - objectsCount: [1, [Validators.required]], - }); - - this.updateFunctionCodes(this.rpcParametersFormGroup.get('type').value); - this.observeValueChanges(); - this.observeKeyDataType(); - this.observeFunctionCode(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: RPCTemplateConfigModbus) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - validate(): ValidationErrors | null { - return this.rpcParametersFormGroup.valid ? null : { - rpcParametersFormGroup: { valid: false } - }; - } - - writeValue(value: RPCTemplateConfigModbus): void { - this.rpcParametersFormGroup.patchValue(value, {emitEvent: false}); - } - - private observeValueChanges(): void { - this.rpcParametersFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.onChange(value); - this.onTouched(); - }); - } - - private observeKeyDataType(): void { - this.rpcParametersFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(dataType => { - if (!this.ModbusEditableDataTypes.includes(dataType)) { - this.rpcParametersFormGroup.get('objectsCount').patchValue(ModbusObjectCountByDataType[dataType], {emitEvent: false}); - } - this.updateFunctionCodes(dataType); - }); - } - - private observeFunctionCode(): void { - this.rpcParametersFormGroup.get('functionCode').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(code => this.updateValueEnabling(code)); - } - - private updateValueEnabling(code: number): void { - if (this.writeFunctionCodes.includes(code)) { - this.rpcParametersFormGroup.get('value').enable({emitEvent: false}); - } else { - this.rpcParametersFormGroup.get('value').setValue(null); - this.rpcParametersFormGroup.get('value').disable({emitEvent: false}); - } - } - - private updateFunctionCodes(dataType: ModbusDataType): void { - this.functionCodes = dataType === ModbusDataType.BITS ? this.bitsFunctionCodes : this.defaultFunctionCodes; - if (!this.functionCodes.includes(this.rpcParametersFormGroup.get('functionCode').value)) { - this.rpcParametersFormGroup.get('functionCode').patchValue(this.functionCodes[0], {emitEvent: false}); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html deleted file mode 100644 index eb66a6df8c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - {{ 'gateway.rpc.method-name' | translate }} - - - - {{ 'gateway.rpc.requestTopicExpression' | translate }} - - - - {{ 'gateway.rpc.withResponse' | translate }} - - - {{ 'gateway.rpc.responseTopicExpression' | translate }} - - - - {{ 'gateway.rpc.responseTimeout' | translate }} - - - - {{ 'gateway.rpc.valueExpression' | translate }} - - - - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss deleted file mode 100644 index a2dddebc47..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.scss +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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. - */ -:host { - display: flex; - flex-direction: column; - - .mat-mdc-slide-toggle.margin { - margin-bottom: 10px; - margin-left: 10px; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts deleted file mode 100644 index 56d9510e7d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component.ts +++ /dev/null @@ -1,139 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - Component, - forwardRef, - OnDestroy, -} from '@angular/core'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, Validators, -} from '@angular/forms'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil, tap } from 'rxjs/operators'; -import { - integerRegex, - noLeadTrailSpacesRegex, - RPCTemplateConfigMQTT -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -@Component({ - selector: 'tb-mqtt-rpc-parameters', - templateUrl: './mqtt-rpc-parameters.component.html', - styleUrls: ['./mqtt-rpc-parameters.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MqttRpcParametersComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => MqttRpcParametersComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ], -}) -export class MqttRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy { - - rpcParametersFormGroup: UntypedFormGroup; - - private onChange: (value: RPCTemplateConfigMQTT) => void = (_) => {}; - private onTouched: () => void = () => {}; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder) { - this.rpcParametersFormGroup = this.fb.group({ - methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTimeout: [{ value: null, disabled: true }, [Validators.min(10), Validators.pattern(integerRegex)]], - valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - withResponse: [false, []], - }); - - this.observeValueChanges(); - this.observeWithResponse(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: RPCTemplateConfigMQTT) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - validate(): ValidationErrors | null { - return this.rpcParametersFormGroup.valid ? null : { - rpcParametersFormGroup: { valid: false } - }; - } - - writeValue(value: RPCTemplateConfigMQTT): void { - this.rpcParametersFormGroup.patchValue(value, {emitEvent: false}); - this.toggleResponseFields(value.withResponse); - } - - private observeValueChanges(): void { - this.rpcParametersFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.onChange(value); - this.onTouched(); - }); - } - - private observeWithResponse(): void { - this.rpcParametersFormGroup.get('withResponse').valueChanges.pipe( - tap((isActive: boolean) => this.toggleResponseFields(isActive)), - takeUntil(this.destroy$), - ).subscribe(); - } - - private toggleResponseFields(enabled: boolean): void { - const responseTopicControl = this.rpcParametersFormGroup.get('responseTopicExpression'); - const responseTimeoutControl = this.rpcParametersFormGroup.get('responseTimeout'); - if (enabled) { - responseTopicControl.enable(); - responseTimeoutControl.enable(); - } else { - responseTopicControl.disable(); - responseTimeoutControl.disable(); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html deleted file mode 100644 index ec5c20cfb1..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.html +++ /dev/null @@ -1,93 +0,0 @@ - - -
- {{ 'gateway.rpc.hint.opc-method' | translate }} -
- - {{ 'gateway.rpc.method' | translate }} - - -
- - {{ 'gateway.rpc.arguments' | translate }} - -
-
-
gateway.type
-
- - - -
- - - {{ valueTypes.get(argumentFormGroup.get('type').value)?.name | translate }} -
-
- - - - {{ valueTypes.get(valueType).name | translate }} - -
-
-
-
-
-
gateway.value
- - - - - - - true - false - - - - warning - - -
- -
- -
-
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss deleted file mode 100644 index 5108cc70b1..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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. - */ -:host { - .arguments-container { - margin-bottom: 10px; - } - - .type-container { - width: 40%; - } - - .value-container { - width: 50%; - } - - .hint-container { - margin-bottom: 12px; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts deleted file mode 100644 index 0c0dbea3bd..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component.ts +++ /dev/null @@ -1,169 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - forwardRef, - OnDestroy, -} from '@angular/core'; -import { - ControlValueAccessor, - FormArray, - FormBuilder, - FormGroup, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validator, Validators, -} from '@angular/forms'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { - integerRegex, - MappingValueType, - mappingValueTypesMap, - noLeadTrailSpacesRegex, - OPCTypeValue, - RPCTemplateConfigOPC -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { isDefinedAndNotNull, isEqual } from '@core/utils'; - -@Component({ - selector: 'tb-opc-rpc-parameters', - templateUrl: './opc-rpc-parameters.component.html', - styleUrls: ['./opc-rpc-parameters.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => OpcRpcParametersComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => OpcRpcParametersComponent), - multi: true - } - ], - standalone: true, - imports: [ - CommonModule, - SharedModule, - ], -}) -export class OpcRpcParametersComponent implements ControlValueAccessor, Validator, OnDestroy { - - rpcParametersFormGroup: UntypedFormGroup; - - readonly valueTypeKeys: MappingValueType[] = Object.values(MappingValueType); - readonly MappingValueType = MappingValueType; - readonly valueTypes = mappingValueTypesMap; - - private onChange: (value: RPCTemplateConfigOPC) => void = (_) => {} ; - private onTouched: () => void = () => {}; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) { - this.rpcParametersFormGroup = this.fb.group({ - method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - arguments: this.fb.array([]), - }); - - this.observeValueChanges(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - registerOnChange(fn: (value: RPCTemplateConfigOPC) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - validate(): ValidationErrors | null { - return this.rpcParametersFormGroup.valid ? null : { - rpcParametersFormGroup: { valid: false } - }; - } - - writeValue(params: RPCTemplateConfigOPC): void { - this.clearArguments(); - params.arguments?.map(({type, value}) => ({type, [type]: value })) - .forEach(argument => this.addArgument(argument as OPCTypeValue)); - this.cdr.markForCheck(); - this.rpcParametersFormGroup.get('method').patchValue(params.method); - } - - private observeValueChanges(): void { - this.rpcParametersFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe(params => { - const updatedArguments = params.arguments.map(({type, ...config}) => ({type, value: config[type]})); - this.onChange({method: params.method, arguments: updatedArguments}); - this.onTouched(); - }); - } - - removeArgument(index: number): void { - (this.rpcParametersFormGroup.get('arguments') as FormArray).removeAt(index); - } - - addArgument(value: OPCTypeValue = {} as OPCTypeValue): void { - const fromGroup = this.fb.group({ - type: [value.type ?? MappingValueType.STRING], - string: [ - value.string ?? { value: '', disabled: !(isEqual(value, {}) || value.string)}, - [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)] - ], - integer: [ - {value: value.integer ?? 0, disabled: !isDefinedAndNotNull(value.integer)}, - [Validators.required, Validators.pattern(integerRegex)] - ], - double: [{value: value.double ?? 0, disabled: !isDefinedAndNotNull(value.double)}, [Validators.required]], - boolean: [{value: value.boolean ?? false, disabled: !isDefinedAndNotNull(value.boolean)}, [Validators.required]], - }); - this.observeTypeChange(fromGroup); - (this.rpcParametersFormGroup.get('arguments') as FormArray).push(fromGroup, {emitEvent: false}); - } - - clearArguments(): void { - const formArray = this.rpcParametersFormGroup.get('arguments') as FormArray; - while (formArray.length !== 0) { - formArray.removeAt(0); - } - } - - private observeTypeChange(dataKeyFormGroup: FormGroup): void { - dataKeyFormGroup.get('type').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(type => { - dataKeyFormGroup.disable({emitEvent: false}); - dataKeyFormGroup.get('type').enable({emitEvent: false}); - dataKeyFormGroup.get(type).enable({emitEvent: false}); - }); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.html deleted file mode 100644 index 87976abf08..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.html +++ /dev/null @@ -1,128 +0,0 @@ - -
-
-
{{ title | translate }}
- - - {{ SecurityTypeTranslationsMap.get(type) | translate }} - - -
- - -
-
gateway.username
-
- - - - warning - - -
-
-
-
gateway.password
-
- - -
- -
-
-
-
-
- -
{{ 'gateway.path-hint' | translate }}
-
-
gateway.CA-certificate-path
-
- - - -
-
-
-
gateway.private-key-path
-
- - - -
-
-
-
gateway.client-cert-path
-
- - - -
-
- -
-
gateway.mode
-
- - - - {{ type }} - - - -
-
-
-
gateway.username
-
- - - - warning - - -
-
-
-
gateway.password
-
- - -
- -
-
-
-
-
-
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.scss deleted file mode 100644 index 416f368279..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: block; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts deleted file mode 100644 index a6ee3ce93d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts +++ /dev/null @@ -1,177 +0,0 @@ -/// -/// 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. -/// - -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - forwardRef, - Input, - OnDestroy, - OnInit, -} from '@angular/core'; -import { Subject } from 'rxjs'; -import { - ControlValueAccessor, - FormBuilder, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormGroup, - ValidationErrors, - Validators -} from '@angular/forms'; -import { - SecurityType, - SecurityTypeTranslationsMap, - ModeType, - noLeadTrailSpacesRegex, - ConnectorSecurity -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { takeUntil } from 'rxjs/operators'; -import { coerceBoolean } from '@shared/decorators/coercion'; -import { SharedModule } from '@shared/shared.module'; -import { CommonModule } from '@angular/common'; - -@Component({ - selector: 'tb-security-config', - templateUrl: './security-config.component.html', - styleUrls: ['./security-config.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => SecurityConfigComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => SecurityConfigComponent), - multi: true - } - ], - standalone: true, - imports:[ - CommonModule, - SharedModule, - ] -}) -export class SecurityConfigComponent implements ControlValueAccessor, OnInit, OnDestroy { - - @Input() - title = 'gateway.security'; - - @Input() - @coerceBoolean() - extendCertificatesModel = false; - - BrokerSecurityType = SecurityType; - securityTypes = Object.values(SecurityType) as SecurityType[]; - modeTypes = Object.values(ModeType); - SecurityTypeTranslationsMap = SecurityTypeTranslationsMap; - securityFormGroup: UntypedFormGroup; - - private onChange: (value: string) => void; - private onTouched: () => void; - - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) {} - - ngOnInit(): void { - this.securityFormGroup = this.fb.group({ - type: [SecurityType.ANONYMOUS, []], - username: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - password: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - pathToCACert: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - pathToPrivateKey: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], - pathToClientCert: ['', [Validators.pattern(noLeadTrailSpacesRegex)]] - }); - if (this.extendCertificatesModel) { - this.securityFormGroup.addControl('mode', this.fb.control(ModeType.NONE, [])); - } - this.securityFormGroup.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.onChange(value); - this.onTouched(); - }); - this.securityFormGroup.get('type').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((type) => this.updateValidators(type)); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - writeValue(securityInfo: ConnectorSecurity): void { - if (!securityInfo) { - const defaultSecurity = {type: SecurityType.ANONYMOUS}; - this.securityFormGroup.reset(defaultSecurity, {emitEvent: false}); - } else { - if (!securityInfo.type) { - securityInfo.type = SecurityType.ANONYMOUS; - } - this.updateValidators(securityInfo.type); - this.securityFormGroup.reset(securityInfo, {emitEvent: false}); - } - this.cdr.markForCheck(); - } - - validate(): ValidationErrors | null { - return this.securityFormGroup.get('type').value !== SecurityType.BASIC || this.securityFormGroup.valid ? null : { - securityForm: { valid: false } - }; - } - - registerOnChange(fn: (value: string) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - private updateValidators(type: SecurityType): void { - if (type) { - this.securityFormGroup.get('username').disable({emitEvent: false}); - this.securityFormGroup.get('password').disable({emitEvent: false}); - this.securityFormGroup.get('pathToCACert').disable({emitEvent: false}); - this.securityFormGroup.get('pathToPrivateKey').disable({emitEvent: false}); - this.securityFormGroup.get('pathToClientCert').disable({emitEvent: false}); - this.securityFormGroup.get('mode')?.disable({emitEvent: false}); - if (type === SecurityType.BASIC) { - this.securityFormGroup.get('username').enable({emitEvent: false}); - this.securityFormGroup.get('password').enable({emitEvent: false}); - } else if (type === SecurityType.CERTIFICATES) { - this.securityFormGroup.get('pathToCACert').enable({emitEvent: false}); - this.securityFormGroup.get('pathToPrivateKey').enable({emitEvent: false}); - this.securityFormGroup.get('pathToClientCert').enable({emitEvent: false}); - if (this.extendCertificatesModel) { - const modeControl = this.securityFormGroup.get('mode'); - if (modeControl && !modeControl.value) { - modeControl.setValue(ModeType.NONE, {emitEvent: false}); - } - - modeControl?.enable({emitEvent: false}); - this.securityFormGroup.get('username').enable({emitEvent: false}); - this.securityFormGroup.get('password').enable({emitEvent: false}); - } - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html deleted file mode 100644 index f72a2c18af..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.html +++ /dev/null @@ -1,103 +0,0 @@ - -
-
-
-
- - - - -
{{ valueTitle(keyControl.get(keyControl.get('type').value).value) }}
-
-
- -
-
gateway.type
-
- - - -
- - - - {{ valueTypes.get(keyControl.get('type').value)?.name | translate}} - -
-
- - - - {{ valueTypes.get(valueType).name | translate }} - -
-
-
-
-
-
gateway.value
- - - - - - - true - false - - - - warning - - -
-
-
-
-
- -
-
-
- -
-
- -
- {{ 'gateway.no-value' }} -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.scss deleted file mode 100644 index 770f17cac6..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.scss +++ /dev/null @@ -1,49 +0,0 @@ -/** - * 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. - */ - -:host { - - .title-container { - max-width: 11vw; - } - - .key-panel { - height: 250px; - overflow: auto; - } - - .tb-form-panel { - .mat-mdc-icon-button { - width: 56px; - height: 56px; - padding: 16px; - color: rgba(0, 0, 0, 0.54); - } - } - - .see-example { - width: 32px; - height: 32px; - margin: 4px; - } -} - -:host ::ng-deep { - .mat-mdc-form-field-icon-suffix { - display: flex; - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts deleted file mode 100644 index 27096ea25a..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component.ts +++ /dev/null @@ -1,160 +0,0 @@ -/// -/// 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. -/// - -import { Component, forwardRef, OnDestroy, OnInit } from '@angular/core'; -import { - AbstractControl, - ControlValueAccessor, - FormGroup, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, - UntypedFormArray, - UntypedFormBuilder, - ValidationErrors, - Validator, - Validators -} from '@angular/forms'; -import { isDefinedAndNotNull } from '@core/utils'; -import { - integerRegex, - MappingDataKey, - MappingValueType, - mappingValueTypesMap, - noLeadTrailSpacesRegex -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { takeUntil } from 'rxjs/operators'; -import { Subject } from 'rxjs'; - -@Component({ - selector: 'tb-type-value-panel', - templateUrl: './type-value-panel.component.html', - styleUrls: ['./type-value-panel.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => TypeValuePanelComponent), - multi: true - }, - { - provide: NG_VALIDATORS, - useExisting: forwardRef(() => TypeValuePanelComponent), - multi: true - } - ] -}) -export class TypeValuePanelComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy { - - valueTypeKeys: MappingValueType[] = Object.values(MappingValueType); - valueTypes = mappingValueTypesMap; - valueListFormArray: UntypedFormArray; - readonly MappingValueType = MappingValueType; - - private destroy$ = new Subject(); - private propagateChange = (v: any) => {}; - - constructor(private fb: UntypedFormBuilder) {} - - ngOnInit(): void { - this.valueListFormArray = this.fb.array([]); - this.valueListFormArray.valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - this.updateView(value); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - trackByKey(_: number, keyControl: AbstractControl): any { - return keyControl; - } - - addKey(): void { - const dataKeyFormGroup = this.fb.group({ - type: [MappingValueType.STRING], - string: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - integer: [{value: 0, disabled: true}, [Validators.required, Validators.pattern(integerRegex)]], - double: [{value: 0, disabled: true}, [Validators.required]], - boolean: [{value: false, disabled: true}, [Validators.required]], - }); - this.observeTypeChange(dataKeyFormGroup); - this.valueListFormArray.push(dataKeyFormGroup); - } - - private observeTypeChange(dataKeyFormGroup: FormGroup): void { - dataKeyFormGroup.get('type').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(type => { - dataKeyFormGroup.disable({emitEvent: false}); - dataKeyFormGroup.get('type').enable({emitEvent: false}); - dataKeyFormGroup.get(type).enable({emitEvent: false}); - }); - } - - deleteKey($event: Event, index: number): void { - if ($event) { - $event.stopPropagation(); - } - this.valueListFormArray.removeAt(index); - this.valueListFormArray.markAsDirty(); - } - - valueTitle(value: any): string { - if (isDefinedAndNotNull(value)) { - if (typeof value === 'object') { - return JSON.stringify(value); - } - return value; - } - return ''; - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void {} - - writeValue(deviceInfoArray: Array): void { - for (const deviceInfo of deviceInfoArray) { - const config = { - type: [deviceInfo.type], - string: [{value: '', disabled: true}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - integer: [{value: 0, disabled: true}, [Validators.required, Validators.pattern(integerRegex)]], - double: [{value: 0, disabled: true}, [Validators.required]], - boolean: [{value: false, disabled: true}, [Validators.required]], - }; - config[deviceInfo.type][0] = {value: deviceInfo.value, disabled: false}; - - const dataKeyFormGroup = this.fb.group(config); - this.observeTypeChange(dataKeyFormGroup); - this.valueListFormArray.push(dataKeyFormGroup); - } - } - - validate(): ValidationErrors | null { - return this.valueListFormArray.valid ? null : { - valueListForm: { valid: false } - }; - } - - private updateView(value: any): void { - this.propagateChange(value.map(({type, ...config}) => ({type, value: config[type]}))); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html deleted file mode 100644 index 87665b28fa..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.html +++ /dev/null @@ -1,53 +0,0 @@ - -
-
{{ 'gateway.docker-label' | translate }}
-
-
device.connectivity.install-necessary-client-tools
- -
- -
-
gateway.download-configuration-file
-
-
gateway.download-docker-compose
- -
-
- -
-
gateway.launch-gateway
-
gateway.launch-docker-compose
- -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss deleted file mode 100644 index 3f66b67943..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.scss +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 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. - */ -:host { - .tb-commands-hint { - color: inherit; - font-weight: normal; - flex: 1; - } -} - -:host ::ng-deep { - .tb-markdown-view { - .start-code { - .code-wrapper { - padding: 0; - - pre[class*=language-] { - margin: 0; - background: #F3F6FA; - border-color: #305680; - padding-right: 38px; - overflow: scroll; - padding-bottom: 4px; - min-height: 42px; - scrollbar-width: thin; - - &::-webkit-scrollbar { - width: 4px; - height: 4px; - } - } - } - button.clipboard-btn { - right: -2px; - p { - color: #305680; - } - p, div { - background-color: #F3F6FA; - } - div { - img { - display: none; - } - &:after { - content: ""; - position: initial; - display: block; - width: 18px; - height: 18px; - background: #305680; - mask-image: url(/assets/copy-code-icon.svg); - -webkit-mask-image: url(/assets/copy-code-icon.svg); - mask-repeat: no-repeat; - -webkit-mask-repeat: no-repeat; - } - } - } - } - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts deleted file mode 100644 index 8465b5879d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/device-gateway-command.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -/// 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. -/// - -import { Component, Input } from '@angular/core'; -import { DeviceService } from '@core/http/device.service'; - -@Component({ - selector: 'tb-gateway-command', - templateUrl: './device-gateway-command.component.html', - styleUrls: ['./device-gateway-command.component.scss'] -}) - -export class DeviceGatewayCommandComponent { - - @Input() - deviceId: string; - - constructor(private deviceService: DeviceService) { - } - - download($event: Event) { - if ($event) { - $event.stopPropagation(); - } - if (this.deviceId) { - this.deviceService.downloadGatewayDockerComposeFile(this.deviceId).subscribe(() => {}); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html deleted file mode 100644 index d995136b27..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html +++ /dev/null @@ -1,117 +0,0 @@ - -
- -

{{ "gateway.add-connector" | translate}}

- -
- -
-
-
-
-
gateway.type
-
- - - - {{ type.value }} - - - -
-
-
-
gateway.name
-
- - - - warning - - -
-
-
-
gateway.connectors-table-class
-
- - - -
-
-
-
gateway.connectors-table-key
-
- - - -
-
-
-
gateway.remote-logging-level
-
- - - {{ logLevel }} - - -
-
-
- - - {{ 'gateway.fill-connector-defaults' | translate }} - - -
-
- - - {{ 'gateway.send-change-data' | translate }} - - -
-
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.scss deleted file mode 100644 index 56e4bc229c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.scss +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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. - */ - -:host { - .add-connector { - min-width: 400px; - width: 500px; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts deleted file mode 100644 index 1c0f74e8e5..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts +++ /dev/null @@ -1,149 +0,0 @@ -/// -/// 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. -/// - -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { FormBuilder, UntypedFormControl, UntypedFormGroup, ValidatorFn, Validators } from '@angular/forms'; -import { BaseData, HasId } from '@shared/models/base-data'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { Router } from '@angular/router'; -import { - AddConnectorConfigData, - ConnectorType, - CreatedConnectorConfigData, - GatewayConnector, - GatewayConnectorDefaultTypesTranslatesMap, - GatewayLogLevel, - GatewayVersion, - GatewayVersionedDefaultConfig, - noLeadTrailSpacesRegex -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { Observable, Subject } from 'rxjs'; -import { ResourcesService } from '@core/services/resources.service'; -import { takeUntil, tap } from 'rxjs/operators'; -import { helpBaseUrl } from '@shared/models/constants'; -import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pipes/latest-version-config.pipe'; - -@Component({ - selector: 'tb-add-connector-dialog', - templateUrl: './add-connector-dialog.component.html', - styleUrls: ['./add-connector-dialog.component.scss'], - providers: [], -}) -export class AddConnectorDialogComponent - extends DialogComponent> implements OnInit, OnDestroy { - - connectorForm: UntypedFormGroup; - - connectorType = ConnectorType; - - gatewayConnectorDefaultTypesTranslatesMap = GatewayConnectorDefaultTypesTranslatesMap; - gatewayLogLevel = Object.values(GatewayLogLevel); - - submitted = false; - - private destroy$ = new Subject(); - - constructor(protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: AddConnectorConfigData, - public dialogRef: MatDialogRef, - private fb: FormBuilder, - private isLatestVersionConfig: LatestVersionConfigPipe, - private resourcesService: ResourcesService) { - super(store, router, dialogRef); - this.connectorForm = this.fb.group({ - type: [ConnectorType.MQTT, []], - name: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(noLeadTrailSpacesRegex)]], - logLevel: [GatewayLogLevel.INFO, []], - useDefaults: [true, []], - sendDataOnlyOnChange: [false, []], - class: ['', []], - key: ['auto', []], - }); - } - - ngOnInit(): void { - this.observeTypeChange(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - super.ngOnDestroy(); - } - - helpLinkId(): string { - return helpBaseUrl + '/docs/iot-gateway/configuration/'; - } - - cancel(): void { - this.dialogRef.close(null); - } - - add(): void { - this.submitted = true; - const value = this.connectorForm.getRawValue(); - if (value.useDefaults) { - this.getDefaultConfig(value.type).subscribe((defaultConfig: GatewayVersionedDefaultConfig) => { - const gatewayVersion = this.data.gatewayVersion; - if (gatewayVersion) { - value.configVersion = gatewayVersion; - } - value.configurationJson = (this.isLatestVersionConfig.transform(gatewayVersion) - ? defaultConfig[GatewayVersion.Current] - : defaultConfig[GatewayVersion.Legacy]) - ?? defaultConfig; - if (this.connectorForm.valid) { - this.dialogRef.close(value); - } - }); - } else if (this.connectorForm.valid) { - this.dialogRef.close(value); - } - } - - private uniqNameRequired(): ValidatorFn { - return (control: UntypedFormControl) => { - const newName = control.value.trim().toLowerCase(); - const isDuplicate = this.data.dataSourceData.some(({ value: { name } }) => - name.toLowerCase() === newName - ); - - return isDuplicate ? { duplicateName: { valid: false } } : null; - }; - } - - private observeTypeChange(): void { - this.connectorForm.get('type').valueChanges.pipe( - tap((type: ConnectorType) => { - const useDefaultControl = this.connectorForm.get('useDefaults'); - if (type === ConnectorType.GRPC || type === ConnectorType.CUSTOM) { - useDefaultControl.setValue(false); - } else if (!useDefaultControl.value) { - useDefaultControl.setValue(true); - } - }), - takeUntil(this.destroy$), - ).subscribe(); - } - - private getDefaultConfig(type: string): Observable { - return this.resourcesService.loadJsonResource(`/assets/metadata/connector-default-configs/${type}.json`); - }; -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html deleted file mode 100644 index b56449892b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html +++ /dev/null @@ -1,766 +0,0 @@ - -
- -

{{ MappingTypeTranslationsMap.get(this.data?.mappingType) | translate}}

- -
- -
-
-
-
- {{ MappingHintTranslationsMap.get(this.data?.mappingType) | translate }} -
- - -
-
gateway.topic-filter
-
- - - - warning - -
-
-
-
-
-
-
- {{ 'gateway.mqtt-qos' | translate }} -
-
- - - - {{ QualityTranslationsMap.get(type) | translate }} - - - -
-
- -
-
gateway.payload-type
- - - {{ ConvertorTypeTranslationsMap.get(type) | translate }} - - -
-
-
gateway.data-conversion
-
- {{ DataConversionTranslationsMap.get(converterType) | translate }} -
- - - - - - - - - -
-
-
gateway.attributes
-
- - - {{ attribute }} - - - - - - -
-
-
-
gateway.timeseries
-
- - - {{ telemetry }} - - - - - - -
-
-
-
-
-
- {{ 'gateway.extension' | translate }} -
-
- - - - warning - - -
-
-
-
gateway.extension-configuration
-
{{ 'gateway.extension-configuration-hint' | translate }}
-
-
gateway.keys
-
- - - {{ telemetry }} - - - - - - -
-
-
-
-
-
-
-
- -
-
gateway.request-type
-
- - - - {{ RequestTypesTranslationsMap.get(type) | translate }} - - - -
-
- - -
-
gateway.topic-filter
-
- - - - warning - -
-
-
-
-
- - - - - - - - - -
-
gateway.from-device-request-settings
-
- gateway.from-device-request-settings-hint -
-
-
-
gateway.device-info.device-name-expression
-
-
- - - - {{ SourceTypeTranslationsMap.get(type) | translate }} - - - - - - - warning - -
-
-
-
-
-
-
gateway.attribute-name-expression
-
- - - - {{ SourceTypeTranslationsMap.get(type) | translate }} - - - - - - - warning - -
-
-
-
-
-
-
-
gateway.to-device-response-settings
-
- gateway.to-device-response-settings-hint -
-
-
gateway.response-value-expression
-
- - - - warning - -
-
-
-
-
-
-
gateway.response-topic-expression
-
- - - - warning - -
-
-
-
-
-
- - - {{ 'gateway.retain' | translate }} - - -
-
-
- -
-
- {{ 'gateway.device-name-filter' | translate }} -
-
- - - - warning - - -
-
-
-
- {{ 'gateway.attribute-filter' | translate }} -
-
- - - - warning - - -
-
-
-
gateway.response-value-expression
-
- - - - warning - -
-
-
-
-
-
-
gateway.response-topic-expression
-
- - - - warning - -
-
-
-
-
-
- - - {{ 'gateway.retain' | translate }} - - -
-
- -
- - - {{ 'gateway.with-response' | translate }} - - - {{ 'gateway.without-response' | translate }} - - -
-
-
- {{ 'gateway.device-name-filter' | translate }} -
-
- - - - warning - - -
-
-
-
- {{ 'gateway.method-filter' | translate }} -
-
- - - - warning - - -
-
-
-
gateway.request-topic-expression
-
- - - - warning - -
-
-
-
-
-
-
gateway.value-expression
-
- - - - warning - -
-
-
-
-
- -
-
gateway.response-topic-expression
-
- - - - warning - -
-
-
-
-
-
-
- {{ 'gateway.response-topic-Qos' | translate }} -
- - - - {{ QualityTranslationsMap.get(type) | translate }} - - - -
-
-
gateway.response-timeout
-
- - - - warning - - -
-
-
-
-
-
-
- -
-
-
- {{ 'gateway.device-node' | translate }} -
-
-
- - - - {{ SourceTypeTranslationsMap.get(type) | translate }} - - - - - - - warning - -
-
-
-
-
- - -
-
gateway.attributes
-
- - - {{ attribute }} - - - - - - -
-
-
-
gateway.timeseries
-
- - - {{ telemetry }} - - - - - - -
-
-
-
gateway.attribute-updates
-
- - - {{ attribute }} - - - - - - -
-
-
-
gateway.rpc-methods
-
- - - {{ attribute }} - - - - - - -
-
-
-
-
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss deleted file mode 100644 index 4212e8f834..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss +++ /dev/null @@ -1,86 +0,0 @@ -/** - * 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. - */ - -:host { - display: grid; - height: 100%; - - .key-mapping { - max-width: 900px; - display: flex; - flex-direction: column; - - .mat-toolbar { - min-height: 64px; - } - - tb-toggle-select { - padding: 4px 0; - } - } - - .mat-mdc-dialog-content { - height: 670px; - } - - .ellipsis-chips-container { - max-width: 70%; - } -} - -:host ::ng-deep { - .key-mapping { - .mat-mdc-chip-listbox { - .mdc-evolution-chip-set__chips { - justify-content: flex-end; - align-items: center; - flex-wrap: nowrap; - } - } - } - .tb-form-row { - .fixed-title-width { - min-width: 40px; - width: 35%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .mat-mdc-form-field { - width: 0; - } - } - - .see-example { - width: 32px; - height: 32px; - margin: 4px; - } - - .mat-mdc-form-field-icon-suffix { - display: flex; - } - - .device-config { - gap: 12px; - padding-left: 10px; - padding-right: 10px; - } - - .device-node-pattern-field { - flex-basis: 3%; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts deleted file mode 100644 index ef8d7ed7ce..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts +++ /dev/null @@ -1,421 +0,0 @@ -/// -/// 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. -/// - -import { Component, Inject, OnDestroy, Renderer2, ViewContainerRef } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { FormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { Router } from '@angular/router'; -import { - Attribute, - AttributesUpdate, - ConnectorMapping, - ConnectorMappingFormValue, - ConverterMappingFormValue, - ConvertorType, - ConvertorTypeTranslationsMap, - DataConversionTranslationsMap, - DeviceConnectorMapping, - DeviceInfoType, - HelpLinkByMappingTypeMap, - MappingHintTranslationsMap, - MappingInfo, - MappingKeysAddKeyTranslationsMap, - MappingKeysDeleteKeyTranslationsMap, - MappingKeysNoKeysTextTranslationsMap, - MappingKeysPanelTitleTranslationsMap, - MappingKeysType, - MappingType, - MappingTypeTranslationsMap, - noLeadTrailSpacesRegex, - OPCUaSourceType, - QualityTypes, - QualityTypeTranslationsMap, - RequestMappingData, - RequestMappingFormValue, - RequestType, - RequestTypesTranslationsMap, - RpcMethod, - ServerSideRPCType, - SourceType, - SourceTypeTranslationsMap, - Timeseries -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { Subject } from 'rxjs'; -import { startWith, takeUntil } from 'rxjs/operators'; -import { MatButton } from '@angular/material/button'; -import { TbPopoverService } from '@shared/components/popover.service'; -import { TranslateService } from '@ngx-translate/core'; -import { - MappingDataKeysPanelComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component'; - -@Component({ - selector: 'tb-mapping-dialog', - templateUrl: './mapping-dialog.component.html', - styleUrls: ['./mapping-dialog.component.scss'] -}) -export class MappingDialogComponent extends DialogComponent implements OnDestroy { - - mappingForm: UntypedFormGroup; - - MappingType = MappingType; - - qualityTypes = QualityTypes; - QualityTranslationsMap = QualityTypeTranslationsMap; - - convertorTypes: ConvertorType[] = Object.values(ConvertorType); - ConvertorTypeEnum = ConvertorType; - ConvertorTypeTranslationsMap = ConvertorTypeTranslationsMap; - - sourceTypes: SourceType[] = Object.values(SourceType); - OPCUaSourceTypes = Object.values(OPCUaSourceType) as Array; - OPCUaSourceTypesEnum = OPCUaSourceType; - sourceTypesEnum = SourceType; - SourceTypeTranslationsMap = SourceTypeTranslationsMap; - - requestTypes: RequestType[] = Object.values(RequestType); - RequestTypeEnum = RequestType; - RequestTypesTranslationsMap = RequestTypesTranslationsMap; - - DeviceInfoType = DeviceInfoType; - - ServerSideRPCType = ServerSideRPCType; - - MappingKeysType = MappingKeysType; - - MappingHintTranslationsMap = MappingHintTranslationsMap; - - MappingTypeTranslationsMap = MappingTypeTranslationsMap; - - DataConversionTranslationsMap = DataConversionTranslationsMap; - - HelpLinkByMappingTypeMap = HelpLinkByMappingTypeMap; - - keysPopupClosed = true; - - private destroy$ = new Subject(); - - constructor(protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: MappingInfo, - public dialogRef: MatDialogRef, - private fb: FormBuilder, - private popoverService: TbPopoverService, - private renderer: Renderer2, - private viewContainerRef: ViewContainerRef, - private translate: TranslateService) { - super(store, router, dialogRef); - - this.createMappingForm(); - } - - get converterAttributes(): Array { - if (this.converterType) { - return this.mappingForm.get('converter').get(this.converterType).value.attributes.map((value: Attribute) => value.key); - } - } - - get converterTelemetry(): Array { - if (this.converterType) { - return this.mappingForm.get('converter').get(this.converterType).value.timeseries.map((value: Timeseries) => value.key); - } - } - - get opcAttributes(): Array { - return this.mappingForm.get('attributes').value?.map((value: Attribute) => value.key) || []; - } - - get opcTelemetry(): Array { - return this.mappingForm.get('timeseries').value?.map((value: Timeseries) => value.key) || []; - } - - get opcRpcMethods(): Array { - return this.mappingForm.get('rpc_methods').value?.map((value: RpcMethod) => value.method) || []; - } - - get opcAttributesUpdates(): Array { - return this.mappingForm.get('attributes_updates')?.value?.map((value: AttributesUpdate) => value.key) || []; - } - - get converterType(): ConvertorType { - return this.mappingForm.get('converter').get('type').value; - } - - get customKeys(): Array { - return Object.keys(this.mappingForm.get('converter').get('custom').value.extensionConfig); - } - - get requestMappingType(): RequestType { - return this.mappingForm.get('requestType').value; - } - - get responseTimeoutErrorTooltip(): string { - const control = this.mappingForm.get('requestValue.serverSideRpc.responseTimeout'); - if (control.hasError('required')) { - return this.translate.instant('gateway.response-timeout-required'); - } else if (control.hasError('min')) { - return this.translate.instant('gateway.response-timeout-limits-error', {min: 1}); - } - return ''; - } - - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - super.ngOnDestroy(); - } - - private createMappingForm(): void { - switch (this.data.mappingType) { - case MappingType.DATA: - this.mappingForm = this.fb.group({}); - this.createDataMappingForm(); - break; - case MappingType.REQUESTS: - this.mappingForm = this.fb.group({}); - this.createRequestMappingForm(); - break; - case MappingType.OPCUA: - this.createOPCUAMappingForm(); - } - } - - cancel(): void { - if (this.keysPopupClosed) { - this.dialogRef.close(null); - } - } - - add(): void { - if (this.mappingForm.valid) { - this.dialogRef.close(this.prepareMappingData()); - } - } - - manageKeys($event: Event, matButton: MatButton, keysType: MappingKeysType): void { - if ($event) { - $event.stopPropagation(); - } - const trigger = matButton._elementRef.nativeElement; - if (this.popoverService.hasPopover(trigger)) { - this.popoverService.hidePopover(trigger); - } else { - const group = this.data.mappingType !== MappingType.OPCUA ? this.mappingForm.get('converter').get(this.converterType) - : this.mappingForm; - - const keysControl = group.get(keysType); - const ctx: { [key: string]: any } = { - keys: keysControl.value, - keysType, - rawData: this.mappingForm.get('converter.type')?.value === ConvertorType.BYTES, - panelTitle: MappingKeysPanelTitleTranslationsMap.get(keysType), - addKeyTitle: MappingKeysAddKeyTranslationsMap.get(keysType), - deleteKeyTitle: MappingKeysDeleteKeyTranslationsMap.get(keysType), - noKeysText: MappingKeysNoKeysTextTranslationsMap.get(keysType) - }; - if (this.data.mappingType === MappingType.OPCUA) { - ctx.valueTypeKeys = Object.values(OPCUaSourceType); - ctx.valueTypeEnum = OPCUaSourceType; - ctx.valueTypes = SourceTypeTranslationsMap; - } - this.keysPopupClosed = false; - const dataKeysPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, - this.viewContainerRef, MappingDataKeysPanelComponent, 'leftBottom', false, null, - ctx, - {}, - {}, {}, true); - dataKeysPanelPopover.tbComponentRef.instance.popover = dataKeysPanelPopover; - dataKeysPanelPopover.tbComponentRef.instance.keysDataApplied.pipe(takeUntil(this.destroy$)).subscribe((keysData) => { - dataKeysPanelPopover.hide(); - keysControl.patchValue(keysData); - keysControl.markAsDirty(); - }); - dataKeysPanelPopover.tbHideStart.pipe(takeUntil(this.destroy$)).subscribe(() => { - this.keysPopupClosed = true; - }); - } - } - - private prepareMappingData(): ConnectorMapping { - const formValue = this.mappingForm.value; - switch (this.data.mappingType) { - case MappingType.DATA: - const {converter, topicFilter, subscriptionQos} = formValue; - return { - topicFilter, - subscriptionQos, - converter: { - type: converter.type, - ...converter[converter.type] - } - }; - case MappingType.REQUESTS: - return { - requestType: formValue.requestType, - requestValue: formValue.requestValue[formValue.requestType] - }; - default: - return formValue; - } - } - - private getFormValueData(): ConnectorMappingFormValue { - if (this.data.value && Object.keys(this.data.value).length) { - switch (this.data.mappingType) { - case MappingType.DATA: - const {converter, topicFilter, subscriptionQos} = this.data.value; - return { - topicFilter, - subscriptionQos, - converter: { - type: converter.type, - [converter.type]: {...converter} - } - } as ConverterMappingFormValue; - case MappingType.REQUESTS: - return { - requestType: this.data.value.requestType, - requestValue: { - [this.data.value.requestType]: this.data.value.requestValue - } as Record - }; - default: - return this.data.value as DeviceConnectorMapping; - } - } - } - - private createDataMappingForm(): void { - this.mappingForm.addControl('topicFilter', - this.fb.control('', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)])); - this.mappingForm.addControl('subscriptionQos', this.fb.control(0)); - this.mappingForm.addControl('converter', this.fb.group({ - type: [ConvertorType.JSON, []], - json: this.fb.group({ - deviceInfo: [{}, []], - attributes: [[], []], - timeseries: [[], []] - }), - bytes: this.fb.group({ - deviceInfo: [{}, []], - attributes: [[], []], - timeseries: [[], []] - }), - custom: this.fb.group({ - extension: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - extensionConfig: [{}, []] - }), - })); - this.mappingForm.patchValue(this.getFormValueData()); - this.mappingForm.get('converter.type').valueChanges.pipe( - startWith(this.mappingForm.get('converter.type').value), - takeUntil(this.destroy$) - ).subscribe((value) => { - const converterGroup = this.mappingForm.get('converter'); - converterGroup.get('json').disable({emitEvent: false}); - converterGroup.get('bytes').disable({emitEvent: false}); - converterGroup.get('custom').disable({emitEvent: false}); - converterGroup.get(value).enable({emitEvent: false}); - }); - } - - private createRequestMappingForm(): void { - this.mappingForm.addControl('requestType', this.fb.control(RequestType.CONNECT_REQUEST, [])); - this.mappingForm.addControl('requestValue', this.fb.group({ - connectRequests: this.fb.group({ - topicFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - deviceInfo: [{}, []] - }), - disconnectRequests: this.fb.group({ - topicFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - deviceInfo: [{}, []] - }), - attributeRequests: this.fb.group({ - topicFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - deviceInfo: this.fb.group({ - deviceNameExpressionSource: [SourceType.MSG, []], - deviceNameExpression: ['', [Validators.required]], - }), - attributeNameExpressionSource: [SourceType.MSG, []], - attributeNameExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - topicExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - valueExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - retain: [false, []] - }), - attributeUpdates: this.fb.group({ - deviceNameFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - attributeFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - topicExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - valueExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - retain: [true, []] - }), - serverSideRpc: this.fb.group({ - type: [ServerSideRPCType.TWO_WAY, []], - deviceNameFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - methodFilter: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - requestTopicExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTopicExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - valueExpression: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTopicQoS: [0, []], - responseTimeout: [10000, [Validators.required, Validators.min(1)]], - }) - })); - this.mappingForm.get('requestType').valueChanges.pipe( - startWith(this.mappingForm.get('requestType').value), - takeUntil(this.destroy$) - ).subscribe((value) => { - const requestValueGroup = this.mappingForm.get('requestValue'); - requestValueGroup.get('connectRequests').disable({emitEvent: false}); - requestValueGroup.get('disconnectRequests').disable({emitEvent: false}); - requestValueGroup.get('attributeRequests').disable({emitEvent: false}); - requestValueGroup.get('attributeUpdates').disable({emitEvent: false}); - requestValueGroup.get('serverSideRpc').disable({emitEvent: false}); - requestValueGroup.get(value).enable(); - }); - this.mappingForm.get('requestValue.serverSideRpc.type').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((value) => { - const requestValueGroup = this.mappingForm.get('requestValue.serverSideRpc'); - if (value === ServerSideRPCType.ONE_WAY) { - requestValueGroup.get('responseTopicExpression').disable({emitEvent: false}); - requestValueGroup.get('responseTopicQoS').disable({emitEvent: false}); - requestValueGroup.get('responseTimeout').disable({emitEvent: false}); - } else { - requestValueGroup.get('responseTopicExpression').enable({emitEvent: false}); - requestValueGroup.get('responseTopicQoS').enable({emitEvent: false}); - requestValueGroup.get('responseTimeout').enable({emitEvent: false}); - } - }); - this.mappingForm.patchValue(this.getFormValueData()); - } - - private createOPCUAMappingForm(): void { - this.mappingForm = this.fb.group({ - deviceNodeSource: [OPCUaSourceType.PATH, []], - deviceNodePattern: ['', [Validators.required]], - deviceInfo: [{}, []], - attributes: [[], []], - timeseries: [[], []], - rpc_methods: [[], []], - attributes_updates: [[], []] - }); - this.mappingForm.patchValue(this.getFormValueData()); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html deleted file mode 100644 index a700dcc94b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html +++ /dev/null @@ -1,323 +0,0 @@ - -
-
- -

{{ 'gateway.connectors' | translate }}

- - -
-
-
- -
- - - - {{ 'gateway.connectors-table-enabled' | translate }} - - - - - - - - {{ 'gateway.connectors-table-name' | translate }} - - {{ attribute.key }} - - - - - {{ 'gateway.connectors-table-type' | translate }} - - - {{ returnType(attribute) }} - - - - - {{ 'gateway.configuration' | translate }} - - -
- {{ isConnectorSynced(attribute) ? 'sync' : 'out of sync' }} -
-
-
- - - {{ 'gateway.connectors-table-status' | translate }} - - - - - - - - {{ 'gateway.connectors-table-actions' | translate }} - - -
- - - -
-
- - - - - - -
-
-
- - -
-
-
-
-
-
- {{ initialConnector?.type ? GatewayConnectorTypesTranslatesMap.get(initialConnector.type) : '' }} - {{ 'gateway.configuration' | translate }} - v{{connectorForm.get('configVersion').value}} -
- - - {{ 'gateway.basic' | translate }} - - - {{ 'gateway.advanced' | translate }} - - -
- - gateway.select-connector - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
gateway.name
-
- - - - warning - - -
-
-
-
gateway.connectors-table-class
-
- - - -
-
-
-
gateway.connectors-table-key
-
- - - -
-
-
-
gateway.logs-configuration
-
- - - {{ 'gateway.enable-remote-logging' | translate }} - - -
-
-
gateway.remote-logging-level
-
- - - {{ logLevel }} - - -
-
-
-
- - - {{ 'gateway.send-change-data' | translate }} - - -
- -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss deleted file mode 100644 index 5d8c6aad5b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.scss +++ /dev/null @@ -1,156 +0,0 @@ -/** - * 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. - */ -@import '../../../../../../../scss/constants'; - -:host { - width: 100%; - height: 100%; - display: block; - overflow-x: auto; - padding: 0; - - .version-placeholder { - color: gray; - font-size: 12px - } - - .connector-container { - height: 100%; - width: 100%; - flex-direction: row; - @media #{$mat-lt-lg} { - flex-direction: column; - } - - & > section:not(.table-section) { - max-width: unset; - @media #{$mat-gt-md} { - max-width: 50%; - } - } - - .table-section { - min-height: 35vh; - overflow: hidden; - - .table-container { - overflow: auto; - } - } - - .flex { - flex: 1; - } - - .input-container { - height: auto; - } - - .section-container { - background-color: #fff; - } - } - - .mat-toolbar { - background: transparent; - color: rgba(0, 0, 0, .87) !important; - } - - .mat-mdc-slide-toggle { - margin: 0 8px; - } - - .status { - text-align: center; - border-radius: 16px; - font-weight: 500; - width: fit-content; - padding: 5px 15px; - &-sync { - background: rgba(25, 128, 56, .06); - color: rgb(25, 128, 56); - } - &-unsync { - background: rgba(203, 37, 48, .06); - color: rgb(203, 37, 48); - } - } - - mat-row { - cursor: pointer; - } - - .dot { - height: 12px; - width: 12px; - background-color: #bbb; - border-radius: 50%; - display: inline-block; - } - - .hasErrors { - background-color: rgb(203, 37, 48); - } - - .noErrors { - background-color: rgb(25, 128, 56); - } -} - -:host ::ng-deep { - .connector-container { - - .mat-mdc-tab-group, .mat-mdc-tab-body-wrapper { - height: 100%; - } - - .mat-mdc-tab-body.mat-mdc-tab-body-active { - position: absolute; - } - - .tb-form-row { - .fixed-title-width { - min-width: 120px; - width: 30%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - } - - .tb-add-new { - display: flex; - z-index: 999; - pointer-events: none; - background-color: #fff; - - button.connector { - height: auto; - padding-right: 12px; - font-size: 20px; - border-style: dashed; - border-width: 2px; - border-radius: 8px; - display: flex; - flex-wrap: wrap; - justify-content: center; - align-items: center; - color: rgba(0, 0, 0, 0.38); - } - } - } -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts deleted file mode 100644 index 00fd3e19c2..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ /dev/null @@ -1,897 +0,0 @@ -/// -/// 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. -/// - -import { - AfterViewInit, - ChangeDetectorRef, - Component, - ElementRef, - Input, - NgZone, - OnDestroy, - ViewChild -} from '@angular/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { FormBuilder, FormControl, FormGroup, UntypedFormControl, ValidatorFn, Validators } from '@angular/forms'; -import { EntityId } from '@shared/models/id/entity-id'; -import { AttributeService } from '@core/http/attribute.service'; -import { TranslateService } from '@ngx-translate/core'; -import { forkJoin, Observable, of, Subject, Subscription } from 'rxjs'; -import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { PageComponent } from '@shared/components/page.component'; -import { PageLink } from '@shared/models/page/page-link'; -import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; -import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { MatSort } from '@angular/material/sort'; -import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; -import { MatTableDataSource } from '@angular/material/table'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; -import { DialogService } from '@core/services/dialog.service'; -import { WidgetContext } from '@home/models/widget-component.models'; -import { camelCase, deepClone, isEqual, isString } from '@core/utils'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { DatasourceType, widgetType } from '@shared/models/widget.models'; -import { UtilsService } from '@core/services/utils.service'; -import { EntityType } from '@shared/models/entity-type.models'; -import { - AddConnectorConfigData, - ConnectorBaseConfig, - ConnectorBaseInfo, - ConfigurationModes, - ConnectorType, - GatewayAttributeData, - GatewayConnector, - GatewayConnectorDefaultTypesTranslatesMap, - GatewayLogLevel, - noLeadTrailSpacesRegex, - ReportStrategyDefaultValue, - ReportStrategyType, -} from './gateway-widget.models'; -import { MatDialog } from '@angular/material/dialog'; -import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component'; -import { debounceTime, filter, switchMap, take, takeUntil, tap } from 'rxjs/operators'; -import { ErrorStateMatcher } from '@angular/material/core'; -import { PageData } from '@shared/models/page/page-data'; -import { - GatewayConnectorVersionMappingUtil -} from '@home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util'; -import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pipes/latest-version-config.pipe'; - -export class ForceErrorStateMatcher implements ErrorStateMatcher { - isErrorState(control: FormControl | null): boolean { - return (control && control.invalid); - } -} - -@Component({ - selector: 'tb-gateway-connector', - templateUrl: './gateway-connectors.component.html', - providers: [{ provide: ErrorStateMatcher, useClass: ForceErrorStateMatcher }], - styleUrls: ['./gateway-connectors.component.scss'] -}) -export class GatewayConnectorComponent extends PageComponent implements AfterViewInit, OnDestroy { - - @Input() - ctx: WidgetContext; - @Input() - device: EntityId; - - @ViewChild('nameInput') nameInput: ElementRef; - @ViewChild(MatSort, {static: false}) sort: MatSort; - - readonly ConnectorType = ConnectorType; - readonly allowBasicConfig = new Set([ - ConnectorType.MQTT, - ConnectorType.OPCUA, - ConnectorType.MODBUS, - ]); - readonly gatewayLogLevel = Object.values(GatewayLogLevel); - readonly displayedColumns = ['enabled', 'key', 'type', 'syncStatus', 'errors', 'actions']; - readonly GatewayConnectorTypesTranslatesMap = GatewayConnectorDefaultTypesTranslatesMap; - readonly ConnectorConfigurationModes = ConfigurationModes; - readonly ReportStrategyDefaultValue = ReportStrategyDefaultValue; - - pageLink: PageLink; - dataSource: MatTableDataSource; - connectorForm: FormGroup; - activeConnectors: Array; - mode: ConfigurationModes = this.ConnectorConfigurationModes.BASIC; - initialConnector: GatewayConnector; - basicConfigInitSubject = new Subject(); - - private gatewayVersion: string; - private isGatewayActive: boolean; - private inactiveConnectors: Array; - private attributeDataSource: AttributeDatasource; - private inactiveConnectorsDataSource: AttributeDatasource; - private serverDataSource: AttributeDatasource; - private activeData: Array = []; - private inactiveData: Array = []; - private sharedAttributeData: Array = []; - private basicConfigSub: Subscription; - private jsonConfigSub: Subscription; - private subscriptionOptions: WidgetSubscriptionOptions = { - callbacks: { - onDataUpdated: () => this.ctx.ngZone.run(() => { - this.onErrorsUpdated(); - }), - onDataUpdateError: (_, e) => this.ctx.ngZone.run(() => { - this.onDataUpdateError(e); - }) - } - }; - private destroy$ = new Subject(); - private subscription: IWidgetSubscription; - private attributeUpdateSubject = new Subject(); - - constructor(protected store: Store, - private fb: FormBuilder, - private translate: TranslateService, - private attributeService: AttributeService, - private dialogService: DialogService, - private dialog: MatDialog, - private telemetryWsService: TelemetryWebsocketService, - private zone: NgZone, - private utils: UtilsService, - private isLatestVersionConfig: LatestVersionConfigPipe, - private cd: ChangeDetectorRef) { - super(store); - - this.initDataSources(); - this.initConnectorForm(); - this.observeAttributeChange(); - } - - ngAfterViewInit(): void { - this.dataSource.sort = this.sort; - this.dataSource.sortingDataAccessor = this.getSortingDataAccessor(); - this.ctx.$scope.gatewayConnectors = this; - - this.loadConnectors(); - this.loadGatewayState(); - this.observeModeChange(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - super.ngOnDestroy(); - } - - onSaveConnector(): void { - this.saveConnector(this.getUpdatedConnectorData(this.connectorForm.value), false); - } - - private saveConnector(connector: GatewayConnector, isNew = true): void { - const scope = (isNew || this.activeConnectors.includes(this.initialConnector.name)) - ? AttributeScope.SHARED_SCOPE - : AttributeScope.SERVER_SCOPE; - - forkJoin(this.getEntityAttributeTasks(connector, scope)).pipe(take(1)).subscribe(_ => { - this.showToast(isNew - ? this.translate.instant('gateway.connector-created') - : this.translate.instant('gateway.connector-updated') - ); - this.initialConnector = connector; - this.updateData(true); - this.connectorForm.markAsPristine(); - }); - } - - private getEntityAttributeTasks(value: GatewayConnector, scope: AttributeScope): Observable[] { - const tasks = []; - const attributesToSave = [{ key: value.name, value }]; - const attributesToDelete = []; - const shouldAddToConnectorsList = !this.activeConnectors.includes(value.name) && scope === AttributeScope.SHARED_SCOPE - || !this.inactiveConnectors.includes(value.name) && scope === AttributeScope.SERVER_SCOPE; - const isNewConnector = this.initialConnector && this.initialConnector.name !== value.name; - - if (isNewConnector) { - attributesToDelete.push({ key: this.initialConnector.name }); - this.removeConnectorFromList(this.initialConnector.name, true); - this.removeConnectorFromList(this.initialConnector.name, false); - } - - if (shouldAddToConnectorsList) { - if (scope === AttributeScope.SHARED_SCOPE) { - this.activeConnectors.push(value.name); - } else { - this.inactiveConnectors.push(value.name); - } - } - - if (isNewConnector || shouldAddToConnectorsList) { - tasks.push(this.getSaveEntityAttributesTask(scope)); - } - - tasks.push(this.attributeService.saveEntityAttributes(this.device, scope, attributesToSave)); - - if (attributesToDelete.length) { - tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, attributesToDelete)); - } - - return tasks; - } - - private getSaveEntityAttributesTask(scope: AttributeScope): Observable { - const key = scope === AttributeScope.SHARED_SCOPE ? 'active_connectors' : 'inactive_connectors'; - const value = scope === AttributeScope.SHARED_SCOPE ? this.activeConnectors : this.inactiveConnectors; - - return this.attributeService.saveEntityAttributes(this.device, scope, [{ key, value }]); - } - - private removeConnectorFromList(connectorName: string, isActive: boolean): void { - const list = isActive? this.activeConnectors : this.inactiveConnectors; - const index = list.indexOf(connectorName); - if (index !== -1) { - list.splice(index, 1); - } - } - - private getUpdatedConnectorData(connector: GatewayConnector): GatewayConnector { - const value = {...connector }; - value.configuration = `${camelCase(value.name)}.json`; - delete value.basicConfig; - - if (value.type !== ConnectorType.GRPC) { - delete value.key; - } - if (value.type !== ConnectorType.CUSTOM) { - delete value.class; - } - - if (value.type === ConnectorType.MODBUS && this.isLatestVersionConfig.transform(value.configVersion)) { - if (!value.reportStrategy) { - value.reportStrategy = { - type: ReportStrategyType.OnReportPeriod, - reportPeriod: ReportStrategyDefaultValue.Connector - }; - delete value.sendDataOnlyOnChange; - } - } - - if (this.gatewayVersion && !value.configVersion) { - value.configVersion = this.gatewayVersion; - } - - value.ts = Date.now(); - - return value; - } - - private updateData(reload: boolean = false): void { - this.pageLink.sortOrder.property = this.sort.active; - this.pageLink.sortOrder.direction = Direction[this.sort.direction.toUpperCase()]; - this.attributeDataSource.loadAttributes(this.device, AttributeScope.CLIENT_SCOPE, this.pageLink, reload).subscribe(data => { - this.activeData = data.data.filter(value => this.activeConnectors.includes(value.key)); - this.combineData(); - this.generateSubscription(); - this.setClientData(data); - }); - this.inactiveConnectorsDataSource.loadAttributes(this.device, AttributeScope.SHARED_SCOPE, this.pageLink, reload).subscribe(data => { - this.sharedAttributeData = data.data.filter(value => this.activeConnectors.includes(value.key)); - this.combineData(); - }); - this.serverDataSource.loadAttributes(this.device, AttributeScope.SERVER_SCOPE, this.pageLink, reload).subscribe(data => { - this.inactiveData = data.data.filter(value => this.inactiveConnectors.includes(value.key)); - this.combineData(); - }); - } - - isConnectorSynced(attribute: GatewayAttributeData): boolean { - const connectorData = attribute.value; - if (!connectorData.ts || attribute.skipSync || !this.isGatewayActive) { - return false; - } - const clientIndex = this.activeData.findIndex(data => { - const sharedData = typeof data.value === 'string' ? JSON.parse(data.value) : data.value; - return sharedData.name === connectorData.name; - }); - if (clientIndex === -1) { - return false; - } - const sharedIndex = this.sharedAttributeData.findIndex(data => { - const sharedData = data.value; - const hasSameName = sharedData.name === connectorData.name; - const hasEmptyConfig = isEqual(sharedData.configurationJson, {}) && hasSameName; - const hasSameConfig = this.hasSameConfig(sharedData.configurationJson, connectorData.configurationJson); - const isRecentlyCreated = sharedData.ts && sharedData.ts <= connectorData.ts; - return hasSameName && isRecentlyCreated && (hasSameConfig || hasEmptyConfig); - }); - return sharedIndex !== -1; - } - - private hasSameConfig(sharedDataConfigJson: ConnectorBaseInfo, connectorDataConfigJson: ConnectorBaseInfo): boolean { - const { name, id, enableRemoteLogging, logLevel, reportStrategy, configVersion, ...sharedDataConfig } = sharedDataConfigJson; - const { - name: connectorName, - id: connectorId, - enableRemoteLogging: connectorEnableRemoteLogging, - logLevel: connectorLogLevel, - reportStrategy: connectorReportStrategy, - configVersion: connectorConfigVersion, - ...connectorConfig - } = connectorDataConfigJson; - - return isEqual(sharedDataConfig, connectorConfig); - } - - private combineData(): void { - const combinedData = [ - ...this.activeData, - ...this.inactiveData, - ...this.sharedAttributeData - ]; - - const latestData = combinedData.reduce((acc, attribute) => { - const existingItemIndex = acc.findIndex(item => item.key === attribute.key); - - if (existingItemIndex === -1) { - acc.push(attribute); - } else if ( - attribute.lastUpdateTs > acc[existingItemIndex].lastUpdateTs && - !this.isConnectorSynced(acc[existingItemIndex]) - ) { - acc[existingItemIndex] = { ...attribute, skipSync: true }; - } - - return acc; - }, []); - - this.dataSource.data = latestData.map(attribute => ({ - ...attribute, - value: typeof attribute.value === 'string' ? JSON.parse(attribute.value) : attribute.value - })); - } - - private clearOutConnectorForm(): void { - this.initialConnector = null; - this.connectorForm.setValue({ - mode: ConfigurationModes.BASIC, - name: '', - type: ConnectorType.MQTT, - sendDataOnlyOnChange: false, - enableRemoteLogging: false, - logLevel: GatewayLogLevel.INFO, - key: 'auto', - class: '', - configuration: '', - configurationJson: {}, - basicConfig: {}, - configVersion: '', - reportStrategy: [{ value: {}, disabled: true }], - }, {emitEvent: false}); - this.connectorForm.markAsPristine(); - } - - selectConnector($event: Event, attribute: GatewayAttributeData): void { - if ($event) { - $event.stopPropagation(); - } - const connector = attribute.value; - if (connector?.name !== this.initialConnector?.name) { - this.confirmConnectorChange().subscribe((result) => { - if (result) { - this.setFormValue(connector); - } - }); - } - } - - isSameConnector(attribute: GatewayAttributeData): boolean { - if (!this.initialConnector) { - return false; - } - const connector = attribute.value; - return this.initialConnector.name === connector.name; - } - - showToast(message: string): void { - this.store.dispatch(new ActionNotificationShow( - { - message, - type: 'success', - duration: 1000, - verticalPosition: 'top', - horizontalPosition: 'left', - target: 'dashboardRoot', - forceDismiss: true - })); - } - - returnType(attribute: GatewayAttributeData): string { - const value = attribute.value; - return this.GatewayConnectorTypesTranslatesMap.get(value.type); - } - - deleteConnector(attribute: GatewayAttributeData, $event: Event): void { - $event?.stopPropagation(); - - const title = `Delete connector \"${attribute.key}\"?`; - const content = `All connector data will be deleted.`; - - this.dialogService.confirm(title, content, 'Cancel', 'Delete').pipe( - take(1), - switchMap((result) => { - if (!result) { - return; - } - const tasks: Array> = []; - const scope = this.activeConnectors.includes(attribute.value?.name) ? - AttributeScope.SHARED_SCOPE : - AttributeScope.SERVER_SCOPE; - tasks.push(this.attributeService.deleteEntityAttributes(this.device, scope, [attribute])); - this.removeConnectorFromList(attribute.key, true); - this.removeConnectorFromList(attribute.key, false); - tasks.push(this.getSaveEntityAttributesTask(scope)); - - return forkJoin(tasks); - }) - ).subscribe(() => { - if (this.initialConnector ? this.initialConnector.name === attribute.key : true) { - this.clearOutConnectorForm(); - this.cd.detectChanges(); - this.connectorForm.disable(); - } - this.updateData(true); - }); - } - - connectorLogs(attribute: GatewayAttributeData, $event: Event): void { - if ($event) { - $event.stopPropagation(); - } - const params = deepClone(this.ctx.stateController.getStateParams()); - params.connector_logs = attribute; - params.targetEntityParamName = 'connector_logs'; - this.ctx.stateController.openState('connector_logs', params); - } - - connectorRpc(attribute: GatewayAttributeData, $event: Event): void { - if ($event) { - $event.stopPropagation(); - } - const params = deepClone(this.ctx.stateController.getStateParams()); - params.connector_rpc = attribute; - params.targetEntityParamName = 'connector_rpc'; - this.ctx.stateController.openState('connector_rpc', params); - } - - - onEnableConnector(attribute: GatewayAttributeData): void { - attribute.value.ts = new Date().getTime(); - - this.updateActiveConnectorKeys(attribute.key); - - this.attributeUpdateSubject.next(attribute); - } - - getErrorsCount(attribute: GatewayAttributeData): string { - const connectorName = attribute.key; - const connector = this.subscription && this.subscription.data - .find(data => data && data.dataKey.name === `${connectorName}_ERRORS_COUNT`); - return (connector && this.activeConnectors.includes(connectorName)) ? (connector.data[0][1] || 0) : 'Inactive'; - } - - onAddConnector(event?: Event): void { - event?.stopPropagation(); - - this.confirmConnectorChange() - .pipe( - take(1), - filter(Boolean), - switchMap(() => this.openAddConnectorDialog()), - filter(Boolean), - ) - .subscribe(connector => this.addConnector(connector)); - } - - private addConnector(connector: GatewayConnector): void { - if (this.connectorForm.disabled) { - this.connectorForm.enable(); - } - if (!connector.configurationJson) { - connector.configurationJson = {} as ConnectorBaseConfig; - } - if (this.gatewayVersion && !connector.configVersion) { - connector.configVersion = this.gatewayVersion; - } - connector.basicConfig = connector.configurationJson; - this.initialConnector = connector; - - const previousType = this.connectorForm.get('type').value; - - this.setInitialConnectorValues(connector); - - this.saveConnector(this.getUpdatedConnectorData(connector)); - - if (previousType === connector.type || !this.allowBasicConfig.has(connector.type)) { - this.patchBasicConfigConnector(connector); - } else { - this.basicConfigInitSubject.pipe(take(1)).subscribe(() => { - this.patchBasicConfigConnector(connector); - }); - } - } - - private setInitialConnectorValues(connector: GatewayConnector): void { - const {basicConfig, mode, ...initialConnector} = connector; - this.toggleReportStrategy(connector.type); - this.connectorForm.get('mode').setValue(this.allowBasicConfig.has(connector.type) - ? connector.mode ?? ConfigurationModes.BASIC - : null, {emitEvent: false} - ); - this.connectorForm.patchValue(initialConnector, {emitEvent: false}); - } - - private openAddConnectorDialog(): Observable { - return this.ctx.ngZone.run(() => - this.dialog.open(AddConnectorDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - dataSourceData: this.dataSource.data, - gatewayVersion: this.gatewayVersion, - } - }).afterClosed() - ); - } - - uniqNameRequired(): ValidatorFn { - return (control: UntypedFormControl) => { - const newName = control.value?.trim().toLowerCase(); - const isDuplicate = this.dataSource.data.some(connectorAttr => connectorAttr.value.name.toLowerCase() === newName); - const isSameAsInitial = this.initialConnector?.name.toLowerCase() === newName; - - if (isDuplicate && !isSameAsInitial) { - return { duplicateName: { valid: false } }; - } - - return null; - }; - } - - private initDataSources(): void { - const sortOrder: SortOrder = {property: 'key', direction: Direction.ASC}; - this.pageLink = new PageLink(1000, 0, null, sortOrder); - this.attributeDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate); - this.inactiveConnectorsDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate); - this.serverDataSource = new AttributeDatasource(this.attributeService, this.telemetryWsService, this.zone, this.translate); - this.dataSource = new MatTableDataSource([]); - } - - private initConnectorForm(): void { - this.connectorForm = this.fb.group({ - mode: [ConfigurationModes.BASIC], - name: ['', [Validators.required, this.uniqNameRequired(), Validators.pattern(noLeadTrailSpacesRegex)]], - type: ['', [Validators.required]], - enableRemoteLogging: [false], - logLevel: ['', [Validators.required]], - sendDataOnlyOnChange: [false], - key: ['auto'], - class: [''], - configuration: [''], - configurationJson: [{}, [Validators.required]], - basicConfig: [{}], - configVersion: [''], - reportStrategy: [{ value: {}, disabled: true }], - }); - this.connectorForm.disable(); - } - - private getSortingDataAccessor(): (data: GatewayAttributeData, sortHeaderId: string) => string | number { - return (data: GatewayAttributeData, sortHeaderId: string) => { - switch (sortHeaderId) { - case 'syncStatus': - return this.isConnectorSynced(data) ? 1 : 0; - - case 'enabled': - return this.activeConnectors.includes(data.key) ? 1 : 0; - - case 'errors': - const errors = this.getErrorsCount(data); - if (typeof errors === 'string') { - return this.sort.direction.toUpperCase() === Direction.DESC ? -1 : Infinity; - } - return errors; - - default: - return data[sortHeaderId] || data.value[sortHeaderId]; - } - }; - } - - private loadConnectors(): void { - if (!this.device || this.device.id === NULL_UUID) { - return; - } - - forkJoin([ - this.attributeService.getEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, ['active_connectors']), - this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, ['inactive_connectors']), - this.attributeService.getEntityAttributes(this.device, AttributeScope.CLIENT_SCOPE, ['Version']) - ]).pipe(takeUntil(this.destroy$)).subscribe(attributes => { - this.activeConnectors = this.parseConnectors(attributes[0]); - this.inactiveConnectors = this.parseConnectors(attributes[1]); - this.gatewayVersion = attributes[2][0]?.value; - - this.updateData(true); - }); - } - - private loadGatewayState(): void { - this.attributeService.getEntityAttributes(this.device, AttributeScope.SERVER_SCOPE) - .pipe(takeUntil(this.destroy$)) - .subscribe((attributes: AttributeData[]) => { - - const active = attributes.find(data => data.key === 'active').value; - const lastDisconnectedTime = attributes.find(data => data.key === 'lastDisconnectTime')?.value; - const lastConnectedTime = attributes.find(data => data.key === 'lastConnectTime')?.value; - - this.isGatewayActive = this.getGatewayStatus(active, lastConnectedTime, lastDisconnectedTime); - }); - } - - private parseConnectors(attribute: GatewayAttributeData[]): string[] { - const connectors = attribute?.[0]?.value || []; - return isString(connectors) ? JSON.parse(connectors) : connectors; - } - - private observeModeChange(): void { - this.connectorForm.get('mode').valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.connectorForm.get('mode').markAsPristine(); - }); - } - - private observeAttributeChange(): void { - this.attributeUpdateSubject.pipe( - debounceTime(300), - tap((attribute: GatewayAttributeData) => this.executeAttributeUpdates(attribute)), - takeUntil(this.destroy$), - ).subscribe(); - } - - private updateActiveConnectorKeys(key: string): void { - const wasEnabled = this.activeConnectors.includes(key); - if (wasEnabled) { - const index = this.activeConnectors.indexOf(key); - if (index !== -1) { - this.activeConnectors.splice(index, 1); - } - this.inactiveConnectors.push(key); - } else { - const index = this.inactiveConnectors.indexOf(key); - if (index !== -1) { - this.inactiveConnectors.splice(index, 1); - } - this.activeConnectors.push(key); - } - } - - private executeAttributeUpdates(attribute: GatewayAttributeData): void { - forkJoin(this.getAttributeExecutionTasks(attribute)) - .pipe( - take(1), - tap(() => this.updateData(true)), - takeUntil(this.destroy$), - ) - .subscribe(); - } - - private getAttributeExecutionTasks(attribute: GatewayAttributeData): Observable[] { - const isActive = this.activeConnectors.includes(attribute.key); - const scopeOld = isActive ? AttributeScope.SERVER_SCOPE : AttributeScope.SHARED_SCOPE; - const scopeNew = isActive ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; - - return [ - this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, [{ - key: 'active_connectors', - value: this.activeConnectors - }]), - this.attributeService.saveEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, [{ - key: 'inactive_connectors', - value: this.inactiveConnectors - }]), - this.attributeService.deleteEntityAttributes(this.device, scopeOld, [attribute]), - this.attributeService.saveEntityAttributes(this.device, scopeNew, [attribute]) - ]; - } - - private onDataUpdateError(e: any): void { - const exceptionData = this.utils.parseException(e); - let errorText = exceptionData.name; - if (exceptionData.message) { - errorText += ': ' + exceptionData.message; - } - console.error(errorText); - } - - private onErrorsUpdated(): void { - this.cd.detectChanges(); - } - - private onDataUpdated(): void { - const dataSources = this.ctx.defaultSubscription.data; - - const active = dataSources.find(data => data.dataKey.name === 'active').data[0][1]; - const lastDisconnectedTime = dataSources.find(data => data.dataKey.name === 'lastDisconnectTime').data[0][1]; - const lastConnectedTime = dataSources.find(data => data.dataKey.name === 'lastConnectTime').data[0][1]; - - this.isGatewayActive = this.getGatewayStatus(active, lastConnectedTime, lastDisconnectedTime); - - this.cd.detectChanges(); - } - - private getGatewayStatus(active: boolean, lastConnectedTime: number, lastDisconnectedTime: number): boolean { - if (!active) { - return false; - } - return !lastDisconnectedTime || lastConnectedTime > lastDisconnectedTime; - } - - private generateSubscription(): void { - if (this.subscription) { - this.subscription.unsubscribe(); - } - if (this.device) { - const subscriptionInfo = [{ - type: DatasourceType.entity, - entityType: EntityType.DEVICE, - entityId: this.device.id, - entityName: 'Gateway', - timeseries: [] - }]; - this.dataSource.data.forEach(value => { - subscriptionInfo[0].timeseries.push({name: `${value.key}_ERRORS_COUNT`, label: `${value.key}_ERRORS_COUNT`}); - }); - this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.latest, subscriptionInfo, this.subscriptionOptions, - false, true).subscribe(subscription => { - this.subscription = subscription; - }); - } - } - - private createBasicConfigWatcher(): void { - if (this.basicConfigSub) { - this.basicConfigSub.unsubscribe(); - } - this.basicConfigSub = this.connectorForm.get('basicConfig').valueChanges.pipe( - filter(() => !!this.initialConnector), - takeUntil(this.destroy$) - ).subscribe((config) => { - const configJson = this.connectorForm.get('configurationJson'); - const type = this.connectorForm.get('type').value; - const mode = this.connectorForm.get('mode').value; - if (!isEqual(config, configJson?.value) && this.allowBasicConfig.has(type) && mode === ConfigurationModes.BASIC) { - const newConfig = {...configJson.value, ...config}; - this.connectorForm.get('configurationJson').patchValue(newConfig, {emitEvent: false}); - } - }); - } - - private createJsonConfigWatcher(): void { - if (this.jsonConfigSub) { - this.jsonConfigSub.unsubscribe(); - } - this.jsonConfigSub = this.connectorForm.get('configurationJson').valueChanges.pipe( - takeUntil(this.destroy$) - ).subscribe((config) => { - const basicConfig = this.connectorForm.get('basicConfig'); - const type = this.connectorForm.get('type').value; - const mode = this.connectorForm.get('mode').value; - if (!isEqual(config, basicConfig?.value) && this.allowBasicConfig.has(type) && mode === ConfigurationModes.ADVANCED) { - this.connectorForm.get('basicConfig').patchValue(config, {emitEvent: false}); - } - }); - } - - private confirmConnectorChange(): Observable { - if (this.initialConnector && this.connectorForm.dirty) { - return this.dialogService.confirm( - this.translate.instant('gateway.change-connector-title'), - this.translate.instant('gateway.change-connector-text'), - this.translate.instant('action.no'), - this.translate.instant('action.yes'), - true - ); - } - return of(true); - } - - private setFormValue(connector: GatewayConnector): void { - if (this.connectorForm.disabled) { - this.connectorForm.enable(); - } - - const connectorState = GatewayConnectorVersionMappingUtil.getConfig({ - configuration: '', - key: 'auto', - configurationJson: {} as ConnectorBaseConfig, - ...connector, - }, this.gatewayVersion); - - if (this.gatewayVersion && !connectorState.configVersion) { - connectorState.configVersion = this.gatewayVersion; - } - - connectorState.basicConfig = connectorState.configurationJson; - this.initialConnector = connectorState; - this.updateConnector(connectorState); - } - - private updateConnector(connector: GatewayConnector): void { - this.jsonConfigSub?.unsubscribe(); - switch (connector.type) { - case ConnectorType.MQTT: - case ConnectorType.OPCUA: - case ConnectorType.MODBUS: - this.updateBasicConfigConnector(connector); - break; - default: - this.connectorForm.patchValue({...connector, mode: null}); - this.connectorForm.markAsPristine(); - this.createJsonConfigWatcher(); - } - } - - private updateBasicConfigConnector(connector: GatewayConnector): void { - this.basicConfigSub?.unsubscribe(); - const previousType = this.connectorForm.get('type').value; - this.setInitialConnectorValues(connector); - - if (previousType === connector.type || !this.allowBasicConfig.has(connector.type)) { - this.patchBasicConfigConnector(connector); - } else { - this.basicConfigInitSubject.asObservable().pipe(take(1)).subscribe(() => { - this.patchBasicConfigConnector(connector); - }); - } - } - - private patchBasicConfigConnector(connector: GatewayConnector): void { - this.connectorForm.patchValue(connector, {emitEvent: false}); - this.connectorForm.markAsPristine(); - this.createBasicConfigWatcher(); - this.createJsonConfigWatcher(); - } - - private toggleReportStrategy(type: ConnectorType): void { - const reportStrategyControl = this.connectorForm.get('reportStrategy'); - if (type === ConnectorType.MODBUS) { - reportStrategyControl.enable({emitEvent: false}); - } else { - reportStrategyControl.disable({emitEvent: false}); - } - } - - private setClientData(data: PageData): void { - if (this.initialConnector) { - const clientConnectorData = data.data.find(attr => attr.key === this.initialConnector.name); - if (clientConnectorData) { - clientConnectorData.value = typeof clientConnectorData.value === 'string' ? - JSON.parse(clientConnectorData.value) : clientConnectorData.value; - - if (this.isConnectorSynced(clientConnectorData) && clientConnectorData.value.configurationJson) { - this.setFormValue({...clientConnectorData.value, mode: this.connectorForm.get('mode').value ?? clientConnectorData.value.mode}); - } - } - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html deleted file mode 100644 index 7daa490f01..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.html +++ /dev/null @@ -1,266 +0,0 @@ - -
- - - - -
{{ 'gateway.thingsboard' | translate | uppercase }}
-
-
- - -
- - {{'gateway.security-type' | translate }} - - - {{ securityType.value.toString() | translate }} - - - -
-
- - {{ 'gateway.thingsboard-host' | translate }} - - - gateway.thingsboard-host-required - - - - {{ 'gateway.thingsboard-port' | translate }} - - - gateway.thingsboard-port-required - - - gateway.thingsboard-port-min - - - gateway.thingsboard-port-max - - - gateway.thingsboard-port-pattern - - -
- -
- - {{ 'gateway.tls-path-ca-certificate' | translate }} - - - - {{ 'gateway.tls-path-private-key' | translate }} - - - - {{ 'gateway.tls-path-client-certificate' | translate }} - - -
- - {{ 'gateway.remote' | translate }} - -
- - {{'gateway.remote-logging-level' | translate }} - - - {{ logLevel }} - - - - - - {{ 'gateway.path-logs' | translate }} - - - gateway.path-logs-required - - -
- -
- - - - -
{{ 'gateway.storage' | translate | uppercase }}
-
-
- -
- - {{'gateway.storage-type' | translate }} - - - {{ storageType.value.toString() | translate}} - - - - -
- - {{ 'gateway.storage-pack-size' | translate }} - - - gateway.storage-pack-size-required - - - gateway.storage-pack-size-min - - - gateway.storage-pack-size-pattern - - - - - - {{ (gatewayConfigurationGroup.get('storageType').value !== 'file' ? 'gateway.storage-max-records' : 'gateway.storage-max-file-records') | translate}} - - - - gateway.storage-max-records-required - - - gateway.storage-max-records-min - - - gateway.storage-max-records-pattern - - -
- -
- - {{ 'gateway.storage-max-files' | translate }} - - - gateway.storage-max-files-required - - - gateway.storage-max-files-min - - - gateway.storage-max-files-pattern - - - - - {{ 'gateway.storage-path' | translate }} - - - gateway.storage-path-required - - -
-
-
- - - - -
{{ 'gateway.connectors-config' | translate | uppercase }}
-
-
- -
-
-
-
- -
-
- - {{'gateway.connector-type' | translate }} - - - {{ connectorType }} - - - - gateway.connector-type-required - - - - - {{ 'gateway.connector-name' | translate }} - - - gateway.connector-name-required - - -
-
- - -
-
-
- {{'gateway.no-connectors' | translate}} -
- -
-
-
-
-
- - - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.scss deleted file mode 100644 index 3d28120fa3..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.scss +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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. - */ -:host{ - .gateway-form { - height: 100%; - padding: 5px; - background-color: transparent; - overflow-y: auto; - overflow-x: hidden; - - .form-action-buttons{ - padding-top: 8px; - } - - .gateway-config { - .no-data-found { - position: relative; - display: flex; - height: 40px; - } - } - } -} 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 deleted file mode 100644 index 4f8b1ee34b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.component.ts +++ /dev/null @@ -1,426 +0,0 @@ -/// -/// 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. -/// - -import { Component, ElementRef, Inject, Input, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { PageComponent } from '@shared/components/page.component'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, NgForm, Validators } from '@angular/forms'; -import { WidgetContext } from '@home/models/widget-component.models'; -import { UtilsService } from '@core/services/utils.service'; -import { - CONFIGURATION_ATTRIBUTE, - CONFIGURATION_DRAFT_ATTRIBUTE, - ConnectorType, - createFormConfig, - CURRENT_CONFIGURATION_ATTRIBUTE, - DEFAULT_CONNECTOR, - gatewayConfigJSON, - GatewayFormConnectorModel, - GatewayFormModels, - GatewayLogLevel, - generateConnectorConfigFiles, - generateLogConfigFile, - generateYAMLConfigFile, - getDraftConnectorsJSON, - getEntityId, - REMOTE_LOGGING_LEVEL_ATTRIBUTE, - SecurityType, - SecurityTypeTranslationMap, - StorageType, - StorageTypeTranslationMap, - ValidateJSON, - WidgetSetting -} from './gateway-form.models'; -import { WINDOW } from '@core/services/window.service'; -import { MatDialog } from '@angular/material/dialog'; -import { - JsonObjectEditDialogComponent, - JsonObjectEditDialogData -} from '@shared/components/dialog/json-object-edit-dialog.component'; -import { TranslateService } from '@ngx-translate/core'; -import { DeviceService } from '@core/http/device.service'; -import { AttributeService } from '@core/http/attribute.service'; -import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { forkJoin, Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { ImportExportService } from '@shared/import-export/import-export.service'; - -// @dynamic -@Component({ - selector: 'tb-gateway-form', - templateUrl: './gateway-form.component.html', - styleUrls: ['./gateway-form.component.scss'] -}) - -export class GatewayFormComponent extends PageComponent implements OnInit, OnDestroy { - constructor( - protected store: Store, - private elementRef: ElementRef, - private utils: UtilsService, - private ngZone: NgZone, - private fb: UntypedFormBuilder, - @Inject(WINDOW) private window: Window, - private dialog: MatDialog, - private translate: TranslateService, - private deviceService: DeviceService, - private attributeService: AttributeService, - private importExport: ImportExportService - ) { - super(store); - } - - get connectors(): UntypedFormArray { - return this.gatewayConfigurationGroup.get('connectors') as UntypedFormArray; - } - - @ViewChild('formContainer', {static: true}) formContainerRef: ElementRef; - @ViewChild('gatewayConfigurationForm', {static: true}) multipleInputForm: NgForm; - - private successfulSaved: string; - private gatewayNameExists: string; - private archiveFileName: string; - private formResize$: ResizeObserver; - private subscribeStorageType$: any; - private subscribeGateway$: any; - - alignment = 'row'; - layoutGap = '5px'; - gatewayType: string; - gatewayConfigurationGroup: UntypedFormGroup; - securityTypes = SecurityTypeTranslationMap; - gatewayLogLevels = Object.keys(GatewayLogLevel).map(itm => GatewayLogLevel[itm]); - connectorTypes = Object.keys(ConnectorType); - storageTypes = StorageTypeTranslationMap; - - toastTargetId = 'gateway-configuration-widget' + this.utils.guid(); - - @Input() - ctx: WidgetContext; - - @Input() - isStateForm: boolean; - - isReadOnlyForm = false; - deviceNameForm: string; - - ngOnInit(): void { - this.initWidgetSettings(this.ctx.settings); - if (this.ctx.datasources && this.ctx.datasources.length) { - this.deviceNameForm = this.ctx.datasources[0].name; - } - - this.buildForm(); - this.ctx.updateWidgetParams(); - this.formResize$ = new ResizeObserver(() => { - this.resize(); - }); - this.formResize$.observe(this.formContainerRef.nativeElement); - } - - ngOnDestroy(): void { - if (this.formResize$) { - this.formResize$.disconnect(); - } - this.subscribeGateway$.unsubscribe(); - this.subscribeStorageType$.unsubscribe(); - } - - private initWidgetSettings(settings: WidgetSetting): void { - let widgetTitle; - if (settings.gatewayTitle && settings.gatewayTitle.length) { - widgetTitle = this.utils.customTranslation(settings.gatewayTitle, settings.gatewayTitle); - } else { - widgetTitle = this.translate.instant('gateway.gateway'); - } - this.ctx.widgetTitle = widgetTitle; - this.isReadOnlyForm = (settings.readOnly) ? settings.readOnly : false; - - this.archiveFileName = settings.archiveFileName?.length ? settings.archiveFileName : 'gatewayConfiguration'; - this.gatewayType = settings.gatewayType?.length ? settings.gatewayType : 'Gateway'; - this.gatewayNameExists = this.utils.customTranslation(settings.gatewayNameExists, settings.gatewayNameExists) || this.translate.instant('gateway.gateway-exists'); - this.successfulSaved = this.utils.customTranslation(settings.successfulSave, settings.successfulSave) || this.translate.instant('gateway.gateway-saved'); - - this.updateWidgetDisplaying(); - } - - private resize(): void { - this.ngZone.run(() => { - this.updateWidgetDisplaying(); - this.ctx.detectChanges(); - }); - } - - private updateWidgetDisplaying(): void { - if(this.ctx.$container && this.ctx.$container[0].offsetWidth <= 425){ - this.layoutGap = '0'; - this.alignment = 'column'; - } else { - this.layoutGap = '5px'; - this.alignment = 'row'; - } - } - - private saveAttribute(attributeName: string, attributeValue: string, attributeScope: AttributeScope): Observable { - const gatewayId = this.gatewayConfigurationGroup.get('gateway').value; - const attributes: AttributeData = { - key: attributeName, - value: attributeValue - }; - return this.attributeService.saveEntityAttributes(getEntityId(gatewayId), attributeScope, [attributes]); - } - - private createConnector(setting: GatewayFormConnectorModel = DEFAULT_CONNECTOR): void { - this.connectors.push(this.fb.group({ - enabled: [setting.enabled], - configType: [setting.configType, [Validators.required]], - name: [setting.name, [Validators.required]], - config: [setting.config, [Validators.nullValidator, ValidateJSON]] - })); - } - - private getFormField(name: string): AbstractControl { - return this.gatewayConfigurationGroup.get(name); - } - - private buildForm(): void { - this.gatewayConfigurationGroup = this.fb.group({ - gateway: [null, []], - accessToken: [null, [Validators.required]], - securityType: [SecurityType.accessToken], - host: [this.window.location.hostname, [Validators.required]], - port: [1883, [Validators.required, Validators.min(1), Validators.max(65535), Validators.pattern(/^-?[0-9]+$/)]], - remoteConfiguration: [true], - caCertPath: ['/etc/thingsboard-gateway/ca.pem'], - privateKeyPath: ['/etc/thingsboard-gateway/privateKey.pem'], - certPath: ['/etc/thingsboard-gateway/certificate.pem'], - remoteLoggingLevel: [GatewayLogLevel.debug], - remoteLoggingPathToLogs: ['./logs/', [Validators.required]], - storageType: [StorageType.memory], - readRecordsCount: [100, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - maxRecordsCount: [10000, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - maxFilesCount: [5, [Validators.required, Validators.min(1), Validators.pattern(/^-?[0-9]+$/)]], - dataFolderPath: ['./data/', [Validators.required]], - connectors: this.fb.array([]) - }); - - if (this.isReadOnlyForm) { - this.gatewayConfigurationGroup.disable({emitEvent: false}); - } - - this.subscribeStorageType$ = this.getFormField('storageType').valueChanges.subscribe((value: StorageType) => { - if (value === StorageType.memory) { - this.getFormField('maxFilesCount').disable(); - this.getFormField('dataFolderPath').disable(); - } else { - this.getFormField('maxFilesCount').enable(); - this.getFormField('dataFolderPath').enable(); - } - }); - - this.subscribeGateway$ = this.getFormField('gateway').valueChanges.subscribe((gatewayId: string) => { - if (gatewayId !== null) { - forkJoin([ - this.deviceService.getDeviceCredentials(gatewayId).pipe(tap(deviceCredential => { - this.getFormField('accessToken').patchValue(deviceCredential.credentialsId); - })), - ...this.getAttributes(gatewayId)]).subscribe(() => { - this.gatewayConfigurationGroup.markAsPristine(); - this.ctx.detectChanges(); - }); - } else { - this.getFormField('accessToken').patchValue(''); - } - }) - } - - gatewayExist(): void { - this.ctx.showErrorToast(this.gatewayNameExists, 'top', 'left', this.toastTargetId); - } - - exportConfig(): void { - const gatewayConfiguration: GatewayFormModels = this.gatewayConfigurationGroup.value; - const filesZip: any = {}; - filesZip['tb_gateway.yaml'] = generateYAMLConfigFile(gatewayConfiguration); - generateConnectorConfigFiles(filesZip, gatewayConfiguration.connectors); - generateLogConfigFile(filesZip, gatewayConfiguration.remoteLoggingLevel, gatewayConfiguration.remoteLoggingPathToLogs); - this.importExport.exportJSZip(filesZip, this.archiveFileName); - this.saveAttribute( - REMOTE_LOGGING_LEVEL_ATTRIBUTE, - this.gatewayConfigurationGroup.value.remoteLoggingLevel.toUpperCase(), - AttributeScope.SHARED_SCOPE); - } - - addNewConnector(): void { - this.createConnector(); - } - - removeConnector(index: number): void { - if (index > -1) { - this.connectors.removeAt(index); - this.connectors.markAsDirty(); - } - } - - openConfigDialog($event: Event, index: number, config: object, type: string): void { - if ($event) { - $event.stopPropagation(); - $event.preventDefault(); - } - this.dialog.open(JsonObjectEditDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - jsonValue: config, - required: true, - title: this.translate.instant('gateway.title-connectors-json', {typeName: type}) - } - }).afterClosed().subscribe( - (res) => { - if (res) { - this.connectors.at(index).get('config').patchValue(res); - this.ctx.detectChanges(); - } - } - ); - } - - private createConnectorName(connectors: GatewayFormConnectorModel[], name: string, index: number = 0): string { - const newKeyName = index ? name + index : name; - const indexRes = connectors.findIndex((element) => element.name === newKeyName); - return indexRes === -1 ? newKeyName : this.createConnectorName(connectors, name, ++index); - } - - private validateConnectorName(connectors: GatewayFormConnectorModel[], name: string, connectorIndex: number, index = 0): string { - for (let i = 0; i < connectors.length; i++) { - const nameEq = (index === 0) ? name : name + index; - if (i !== connectorIndex && connectors[i].name === nameEq) { - this.validateConnectorName(connectors, name, connectorIndex, ++index); - } - } - return (index === 0) ? name : name + index; - } - - changeConnectorType(connector: AbstractControl): void { - if (!connector.get('name').value) { - const typeConnector = connector.get('configType').value; - const connectors = this.gatewayConfigurationGroup.value.connectors; - connector.get('name').patchValue(this.createConnectorName(connectors, ConnectorType[typeConnector])); - } - } - - changeConnectorName(connector: AbstractControl, index: number): void { - const connectors = this.gatewayConfigurationGroup.value.connectors; - connector.get('name').patchValue(this.validateConnectorName(connectors, connector.get('name').value, index)); - } - - save(): void { - const gatewayConfiguration: GatewayFormModels = this.gatewayConfigurationGroup.value; - forkJoin([ - this.saveAttribute( - CONFIGURATION_ATTRIBUTE, - window.btoa(JSON.stringify(gatewayConfigJSON(gatewayConfiguration))), - AttributeScope.SHARED_SCOPE), - this.saveAttribute( - CONFIGURATION_DRAFT_ATTRIBUTE, - window.btoa(JSON.stringify(getDraftConnectorsJSON(gatewayConfiguration.connectors))), - AttributeScope.SERVER_SCOPE), - this.saveAttribute( - REMOTE_LOGGING_LEVEL_ATTRIBUTE, - this.gatewayConfigurationGroup.value.remoteLoggingLevel.toUpperCase(), - AttributeScope.SHARED_SCOPE) - ]).subscribe(() =>{ - this.ctx.showSuccessToast(this.successfulSaved, - 2000, 'top', 'left', this.toastTargetId); - this.gatewayConfigurationGroup.markAsPristine(); - }) - } - - private getAttributes(gatewayId: string): Array>> { - const tasks = []; - tasks.push(forkJoin([this.getAttribute(CURRENT_CONFIGURATION_ATTRIBUTE, AttributeScope.CLIENT_SCOPE, gatewayId), - this.getAttribute(CONFIGURATION_DRAFT_ATTRIBUTE, AttributeScope.SERVER_SCOPE, gatewayId)]).pipe( - tap(([currentConfig, draftConfig]) => { - this.setFormGatewaySettings(currentConfig); - this.setFormConnectorsDraft(draftConfig); - if (this.isReadOnlyForm) { - this.gatewayConfigurationGroup.disable({emitEvent: false}); - } - }) - ) - ); - tasks.push(this.getAttribute(REMOTE_LOGGING_LEVEL_ATTRIBUTE, AttributeScope.SHARED_SCOPE, gatewayId).pipe( - tap(logsLevel => this.processLoggingLevel(logsLevel)) - )); - return tasks; - } - - private getAttribute(attributeName: string, attributeScope: AttributeScope, gatewayId: string): Observable> { - return this.attributeService.getEntityAttributes(getEntityId(gatewayId), attributeScope, [attributeName]); - } - - private setFormGatewaySettings(response: Array): void { - this.connectors.clear(); - if (response.length > 0) { - const attribute = JSON.parse(window.atob(response[0].value)); - for (const attributeKey of Object.keys(attribute)) { - const keyValue = attribute[attributeKey]; - if (attributeKey === 'thingsboard') { - if (keyValue !== null && Object.keys(keyValue).length > 0) { - this.gatewayConfigurationGroup.patchValue(createFormConfig(keyValue)); - } - } else { - for (const connector of Object.keys(keyValue)) { - let name = 'No name'; - if (Object.prototype.hasOwnProperty.call(keyValue[connector], 'name')) { - name = keyValue[connector].name; - } - const newConnector: GatewayFormConnectorModel = { - enabled: true, - configType: (attributeKey as ConnectorType), - config: keyValue[connector].config, - name - }; - this.createConnector(newConnector); - } - } - } - } - } - - private setFormConnectorsDraft(response: Array): void { - if (response.length > 0) { - const attribute = JSON.parse(window.atob(response[0].value)); - for (const connectorName of Object.keys(attribute)) { - const newConnector: GatewayFormConnectorModel = { - enabled: false, - configType: (attribute[connectorName].connector as ConnectorType), - config: attribute[connectorName].config, - name: connectorName - }; - this.createConnector(newConnector); - } - } - } - - private processLoggingLevel(value: Array): void { - let logsLevel = GatewayLogLevel.debug; - if (value.length > 0 && GatewayLogLevel[value[0].value.toLowerCase()]) { - logsLevel = GatewayLogLevel[value[0].value.toLowerCase()]; - } - this.getFormField('remoteLoggingLevel').patchValue(logsLevel); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.models.ts deleted file mode 100644 index 4bdb4e966b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-form.models.ts +++ /dev/null @@ -1,380 +0,0 @@ -/// -/// 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. -/// - -import { AbstractControl, ValidationErrors } from '@angular/forms'; -import { EntityId } from '@shared/models/id/entity-id'; -import { EntityType } from '@shared/models/entity-type.models'; - -export enum SecurityType { - tls = 'tls', - accessToken = 'accessToken' -} - -export interface WidgetSetting { - widgetTitle: string; - gatewayTitle?: string; - readOnly?: boolean; - archiveFileName: string; - gatewayType: string; - successfulSave: string; - gatewayNameExists: string; -} - -export const CURRENT_CONFIGURATION_ATTRIBUTE = 'current_configuration'; -export const CONFIGURATION_DRAFT_ATTRIBUTE = 'configuration_drafts'; -export const CONFIGURATION_ATTRIBUTE = 'configuration'; -export const REMOTE_LOGGING_LEVEL_ATTRIBUTE = 'RemoteLoggingLevel'; - -export const SecurityTypeTranslationMap = new Map( - [ - [SecurityType.tls, 'gateway.security-types.tls'], - [SecurityType.accessToken, 'gateway.security-types.access-token'] - ] -); - -export enum GatewayLogLevel { - none = 'NONE', - critical = 'CRITICAL', - error = 'ERROR', - warning = 'WARNING', - info = 'INFO', - debug = 'DEBUG' -} - -export enum StorageType { - memory = 'memory', - file = 'file' -} - -export const StorageTypeTranslationMap = new Map( - [ - [StorageType.memory, 'gateway.storage-types.memory-storage'], - [StorageType.file, 'gateway.storage-types.file-storage'] - ] -); - -export enum ConnectorType { - mqtt= 'MQTT', - modbus = 'Modbus', - opcua = 'OPC-UA', - ble = 'BLE', - request = 'Request', - can = 'CAN', - bacnet = 'BACnet', - custom = 'Custom' -} - -export interface GatewayFormModels { - gateway?: string; - accessToken?: string; - securityType?: SecurityType; - host?: string; - port?: number; - remoteConfiguration?: boolean; - caCertPath?: string; - privateKeyPath?: string; - certPath?: string; - remoteLoggingLevel?: GatewayLogLevel; - remoteLoggingPathToLogs?:string; - storageType?: StorageType; - readRecordsCount?: number; - maxRecordsCount?: number; - maxFilesCount?: number; - dataFolderPath?: number; - connectors?: Array; -} - -export interface GatewayFormConnectorModel { - config: object; - name: string; - configType: ConnectorType; - enabled: boolean; -} - -export const DEFAULT_CONNECTOR: GatewayFormConnectorModel = { - config: {}, - name: '', - configType: null, - enabled: false -}; - -type Connector = { - [key in ConnectorType]?: Array; -} - -interface GatewaySetting extends Connector{ - thingsboard: GatewayMainSetting; -} - -interface ConnectorConfig { - name: string; - config: object; -} - -interface GatewayMainSetting { - thingsboard: GatewayMainThingsboardSetting; - connectors: Array, - logs: string, - storage: GatewayStorage -} - -interface GatewayMainThingsboardSetting { - host: string, - remoteConfiguration: boolean, - port: number, - security: GatewaySecurity -} - -type GatewaySecurity = SecurityToken | SecurityCertificate; - -interface SecurityToken { - accessToken: string; -} - -interface SecurityCertificate { - caCert: string; - privateKey: string; - cert: string; -} - -type GatewayStorage = GatewayStorageMemory | GatewayStorageFile; - -interface GatewayStorageMemory { - type: string; - max_records_count: number; - read_records_count: number; -} - -interface GatewayStorageFile { - type: string; - data_folder_path: number; - max_file_count: number; - max_read_records_count: number; - max_records_per_file: number; -} - -interface GatewayMainConnector { - configuration: string; - name: string; - type: ConnectorType; -} - -export function ValidateJSON(control: AbstractControl): ValidationErrors | null { - if (JSON.stringify(control.value) === JSON.stringify({})) { - return { validJSON: true }; - } - return null; -} - -const TEMPLATE_LOGS_CONFIG = '[loggers]}}keys=root, service, connector, converter, tb_connection, storage, extension}}[handlers]}}keys=consoleHandler, serviceHandler, connectorHandler, converterHandler, tb_connectionHandler, storageHandler, extensionHandler}}[formatters]}}keys=LogFormatter}}[logger_root]}}level=ERROR}}handlers=consoleHandler}}[logger_connector]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=connector}}[logger_storage]}}level={ERROR}}}handlers=storageHandler}}formatter=LogFormatter}}qualname=storage}}[logger_tb_connection]}}level={ERROR}}}handlers=tb_connectionHandler}}formatter=LogFormatter}}qualname=tb_connection}}[logger_service]}}level={ERROR}}}handlers=serviceHandler}}formatter=LogFormatter}}qualname=service}}[logger_converter]}}level={ERROR}}}handlers=converterHandler}}formatter=LogFormatter}}qualname=converter}}[logger_extension]}}level={ERROR}}}handlers=connectorHandler}}formatter=LogFormatter}}qualname=extension}}[handler_consoleHandler]}}class=StreamHandler}}level={ERROR}}}formatter=LogFormatter}}args=(sys.stdout,)}}[handler_connectorHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}connector.log", "d", 1, 7,)}}[handler_storageHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}storage.log", "d", 1, 7,)}}[handler_serviceHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}service.log", "d", 1, 7,)}}[handler_converterHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}converter.log", "d", 1, 3,)}}[handler_extensionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}extension.log", "d", 1, 3,)}}[handler_tb_connectionHandler]}}level={ERROR}}}class=logging.handlers.TimedRotatingFileHandler}}formatter=LogFormatter}}args=("{./logs/}tb_connection.log", "d", 1, 3,)}}[formatter_LogFormatter]}}format="%(asctime)s - %(levelname)s - [%(filename)s] - %(module)s - %(lineno)d - %(message)s" }}datefmt="%Y-%m-%d %H:%M:%S"'; - -export function generateYAMLConfigFile(gatewaySetting: GatewayFormModels): string { - let config; - config = 'thingsboard:\n'; - config += ' host: ' + gatewaySetting.host + '\n'; - config += ' remoteConfiguration: ' + gatewaySetting.remoteConfiguration + '\n'; - config += ' port: ' + gatewaySetting.port + '\n'; - config += ' security:\n'; - if (gatewaySetting.securityType === SecurityType.accessToken) { - config += ' access-token: ' + gatewaySetting.accessToken + '\n'; - } else { - config += ' ca_cert: ' + gatewaySetting.caCertPath + '\n'; - config += ' privateKey: ' + gatewaySetting.privateKeyPath + '\n'; - config += ' cert: ' + gatewaySetting.certPath + '\n'; - } - config += 'storage:\n'; - if (gatewaySetting.storageType === StorageType.memory) { - config += ' type: memory\n'; - config += ' read_records_count: ' + gatewaySetting.readRecordsCount + '\n'; - config += ' max_records_count: ' + gatewaySetting.maxRecordsCount + '\n'; - } else { - config += ' type: file\n'; - config += ' data_folder_path: ' + gatewaySetting.dataFolderPath + '\n'; - config += ' max_file_count: ' + gatewaySetting.maxFilesCount + '\n'; - config += ' max_read_records_count: ' + gatewaySetting.readRecordsCount + '\n'; - config += ' max_records_per_file: ' + gatewaySetting.maxRecordsCount + '\n'; - } - config += 'connectors:\n'; - for(const connector of gatewaySetting.connectors){ - if (connector.enabled) { - config += ' -\n'; - config += ' name: ' + connector.name + '\n'; - config += ' type: ' + connector.configType + '\n'; - config += ' configuration: ' + generateFileName(connector.name) + '\n'; - } - } - return config; -} - -export function generateConnectorConfigFiles(fileZipAdd: object, connectors: Array): void { - for(const connector of connectors) { - if (connector.enabled) { - fileZipAdd[generateFileName(connector.name)] = JSON.stringify(connector.config); - } - } -} - -function generateFileName(fileName): string { - return fileName.replace('_', '') - .replace('-', '') - .replace(/^\s+|\s+/g, '') - .toLowerCase() + '.json'; -} - -export function generateLogConfigFile(fileZipAdd: object, logsLevel: string, logsPath: string): void { - fileZipAdd['logs.conf'] = getLogsConfig(logsLevel, logsPath); -} - -function getLogsConfig(logsLevel: string, logsPath: string): string { - return TEMPLATE_LOGS_CONFIG - .replace(/{ERROR}/g, logsLevel) - .replace(/{.\/logs\/}/g, logsPath); -} - -export function getEntityId(gatewayId: string): EntityId { - return { - id: gatewayId, - entityType: EntityType.DEVICE - } -} - -export function createFormConfig(keyValue: GatewayMainSetting): GatewayFormModels { - const formSetting: GatewayFormModels = {}; - if (Object.prototype.hasOwnProperty.call(keyValue, 'thingsboard')) { - formSetting.host = keyValue.thingsboard.host; - formSetting.port = keyValue.thingsboard.port; - formSetting.remoteConfiguration = keyValue.thingsboard.remoteConfiguration; - if (Object.prototype.hasOwnProperty.call(keyValue.thingsboard.security, SecurityType.accessToken)) { - formSetting.securityType = SecurityType.accessToken; - formSetting.accessToken = (keyValue.thingsboard.security as SecurityToken).accessToken; - } else { - formSetting.securityType = SecurityType.tls; - formSetting.caCertPath = (keyValue.thingsboard.security as SecurityCertificate).caCert; - formSetting.privateKeyPath = (keyValue.thingsboard.security as SecurityCertificate).privateKey; - formSetting.certPath = (keyValue.thingsboard.security as SecurityCertificate).cert; - } - } - - if (Object.prototype.hasOwnProperty.call(keyValue, 'storage') && Object.prototype.hasOwnProperty.call(keyValue.storage, 'type')) { - if (keyValue.storage.type === StorageType.memory) { - formSetting.storageType = StorageType.memory; - formSetting.readRecordsCount = (keyValue.storage as GatewayStorageMemory).read_records_count; - formSetting.maxRecordsCount = (keyValue.storage as GatewayStorageMemory).max_records_count; - } else if (keyValue.storage.type === StorageType.file) { - formSetting.storageType = StorageType.file; - formSetting.dataFolderPath = (keyValue.storage as GatewayStorageFile).data_folder_path; - formSetting.maxFilesCount = (keyValue.storage as GatewayStorageFile).max_file_count; - formSetting.readRecordsCount = (keyValue.storage as GatewayStorageMemory).read_records_count; - formSetting.maxRecordsCount = (keyValue.storage as GatewayStorageMemory).max_records_count; - } - } - return formSetting; -} - -export function getDraftConnectorsJSON(currentConnectors: Array) { - const draftConnectors = {}; - for(const connector of currentConnectors){ - if (!connector.enabled) { - draftConnectors[connector.name] = { - connector: connector.configType, - config: connector.config - }; - } - } - return draftConnectors; -} - -export function gatewayConfigJSON(gatewayConfiguration: GatewayFormModels): GatewaySetting { - const gatewayConfig = { - thingsboard: gatewayMainConfigJSON(gatewayConfiguration) - }; - gatewayConnectorJSON(gatewayConfig, gatewayConfiguration.connectors); - return gatewayConfig; -} - -function gatewayMainConfigJSON(gatewayConfiguration: GatewayFormModels): GatewayMainSetting { - let security: GatewaySecurity; - if (gatewayConfiguration.securityType === SecurityType.accessToken) { - security = { - accessToken: gatewayConfiguration.accessToken - } - } else { - security = { - caCert: gatewayConfiguration.caCertPath, - privateKey: gatewayConfiguration.privateKeyPath, - cert: gatewayConfiguration.certPath - } - } - const thingsboard: GatewayMainThingsboardSetting = { - host: gatewayConfiguration.host, - remoteConfiguration: gatewayConfiguration.remoteConfiguration, - port: gatewayConfiguration.port, - security - }; - - let storage: GatewayStorage; - if (gatewayConfiguration.storageType === StorageType.memory) { - storage = { - type: StorageType.memory, - read_records_count: gatewayConfiguration.readRecordsCount, - max_records_count: gatewayConfiguration.maxRecordsCount - }; - } else { - storage = { - type: StorageType.file, - data_folder_path: gatewayConfiguration.dataFolderPath, - max_file_count: gatewayConfiguration.maxFilesCount, - max_read_records_count: gatewayConfiguration.readRecordsCount, - max_records_per_file: gatewayConfiguration.maxRecordsCount - }; - } - - const connectors: Array = []; - for (const connector of gatewayConfiguration.connectors) { - if (connector.enabled) { - const connectorConfig: GatewayMainConnector = { - configuration: generateFileName(connector.name), - name: connector.name, - type: connector.configType - }; - connectors.push(connectorConfig); - } - } - - return { - thingsboard, - connectors, - storage, - logs: window.btoa(getLogsConfig(gatewayConfiguration.remoteLoggingLevel, gatewayConfiguration.remoteLoggingPathToLogs)) - } -} - -function gatewayConnectorJSON(gatewayConfiguration, currentConnectors: Array): void { - for (const connector of currentConnectors) { - if (connector.enabled) { - const typeConnector = connector.configType; - if (!Array.isArray(gatewayConfiguration[typeConnector])) { - gatewayConfiguration[typeConnector] = []; - } - - const connectorConfig: ConnectorConfig = { - name: connector.name, - config: connector.config - }; - gatewayConfiguration[typeConnector].push(connectorConfig); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html deleted file mode 100644 index 098a8ce5e5..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - {{ 'widgets.gateway.created-time' | translate }} - - {{ attribute.ts | date:'yyyy-MM-dd HH:mm:ss' }} - - - - {{ 'widgets.gateway.level' | translate }} - - {{ attribute.status }} - - - - {{ 'widgets.gateway.message' | translate }} - - {{ attribute.message }} - - - - -
-{{ 'attribute.no-telemetry-text' | translate }} - - - - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss deleted file mode 100644 index abddccb51d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.scss +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - overflow-x: auto; - padding: 0; - - - .status { - border-radius: 20px; - font-weight: 500; - padding: 5px 15px; - } - - .status-debug { - color: green; - background: rgba(0, 128, 0, 0.1); - } - - .status-warning { - color: orange; - background: rgba(255, 165, 0, 0.1); - } - - .status-error { - color: red; - background: rgba(255, 0, 0, 0.1); - } - - .status-info { - color: blue; - background: rgba(0, 0, 128, 0.1); - } - - .msg-status-exception { - color: red; - } - -} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts deleted file mode 100644 index 5fba27f86c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts +++ /dev/null @@ -1,200 +0,0 @@ -/// -/// 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. -/// - -import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; -import { MatDialogRef } from '@angular/material/dialog'; -import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { PageLink } from '@shared/models/page/page-link'; -import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { MatSort } from '@angular/material/sort'; -import { MatTableDataSource } from '@angular/material/table'; -import { WidgetContext } from '@home/models/widget-component.models'; -import { MatPaginator } from '@angular/material/paginator'; -import { GatewayLogData, GatewayStatus, LogLink } from './gateway-widget.models'; - -@Component({ - selector: 'tb-gateway-logs', - templateUrl: './gateway-logs.component.html', - styleUrls: ['./gateway-logs.component.scss'] -}) -export class GatewayLogsComponent implements OnInit, AfterViewInit { - - pageLink: PageLink; - - dataSource: MatTableDataSource; - - displayedColumns = ['ts', 'status', 'message']; - - @Input() - ctx: WidgetContext; - - @Input() - dialogRef: MatDialogRef; - - @ViewChild('searchInput') searchInputField: ElementRef; - @ViewChild(MatSort) sort: MatSort; - @ViewChild(MatPaginator) paginator: MatPaginator; - - textSearchMode: boolean; - - logLinks: Array; - - activeLink: LogLink; - - gatewayLogLinks: Array = [ - { - name: 'General', - key: 'LOGS' - }, { - name: 'Service', - key: 'SERVICE_LOGS' - }, - { - name: 'Connection', - key: 'CONNECTION_LOGS' - }, { - name: 'Storage', - key: 'STORAGE_LOGS' - }, - { - key: 'EXTENSIONS_LOGS', - name: 'Extension' - }]; - - - constructor() { - const sortOrder: SortOrder = {property: 'ts', direction: Direction.DESC}; - this.pageLink = new PageLink(10, 0, null, sortOrder); - this.dataSource = new MatTableDataSource([]); - } - - ngOnInit(): void { - this.updateWidgetTitle(); - } - - ngAfterViewInit() { - this.dataSource.sort = this.sort; - this.dataSource.paginator = this.paginator; - this.ctx.defaultSubscription.onTimewindowChangeFunction = timewindow => { - this.ctx.defaultSubscription.options.timeWindowConfig = timewindow; - this.ctx.defaultSubscription.updateDataSubscriptions(); - return timewindow; - }; - if (this.ctx.settings.isConnectorLog && this.ctx.settings.connectorLogState) { - const connector = this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState]; - this.logLinks = [{ - key: `${connector.key}_LOGS`, - name: 'Connector', - filterFn: (attrData) => !attrData.message.includes(`_converter.py`) - }, { - key: `${connector.key}_LOGS`, - name: 'Converter', - filterFn: (attrData) => attrData.message.includes(`_converter.py`) - }]; - } else { - this.logLinks = this.gatewayLogLinks; - } - this.activeLink = this.logLinks[0]; - this.changeSubscription(); - } - - private updateWidgetTitle(): void { - if (this.ctx.settings.isConnectorLog && this.ctx.settings.connectorLogState) { - const widgetTitle = this.ctx.widgetConfig.title; - const titlePlaceholder = '${connectorName}'; - if (widgetTitle.includes(titlePlaceholder)) { - const connector = this.ctx.stateController.getStateParams()[this.ctx.settings.connectorLogState]; - this.ctx.widgetTitle = widgetTitle.replace(titlePlaceholder, connector.key); - } - } - } - - - private updateData() { - if (this.ctx.defaultSubscription.data.length && this.ctx.defaultSubscription.data[0]) { - let attrData = this.ctx.defaultSubscription.data[0].data.map(data => { - const result = { - ts: data[0], - key: this.activeLink.key, - message: data[1], - status: 'INVALID LOG FORMAT' as GatewayStatus - }; - - try { - result.message = /\[(.*)/.exec(data[1])[0]; - } catch (e) { - result.message = data[1]; - } - - try { - result.status = data[1].match(/\|(\w+)\|/)[1]; - } catch (e) { - result.status = 'INVALID LOG FORMAT' as GatewayStatus; - } - - return result; - }); - if (this.activeLink.filterFn) { - attrData = attrData.filter(data => this.activeLink.filterFn(data)); - } - this.dataSource.data = attrData; - } - } - - onTabChanged(link: LogLink) { - this.activeLink = link; - this.changeSubscription(); - } - - statusClass(status: GatewayStatus): string { - switch (status) { - case GatewayStatus.DEBUG: - return 'status status-debug'; - case GatewayStatus.WARNING: - return 'status status-warning'; - case GatewayStatus.ERROR: - case GatewayStatus.EXCEPTION: - return 'status status-error'; - default: - return 'status status-info'; - } - } - - statusClassMsg(status?: GatewayStatus): string { - if (status === GatewayStatus.EXCEPTION) { - return 'msg-status-exception'; - } - } - - trackByLogTs(_: number, log: GatewayLogData): number { - return log.ts; - } - - private changeSubscription() { - if (this.ctx.datasources && this.ctx.datasources[0].entity && this.ctx.defaultSubscription.options.datasources) { - this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ - name: this.activeLink.key, - type: DataKeyType.timeseries, - settings: {} - }]; - this.ctx.defaultSubscription.unsubscribe(); - this.ctx.defaultSubscription.updateDataSubscriptions(); - this.ctx.defaultSubscription.callbacks.onDataUpdated = () => { - this.updateData(); - }; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html deleted file mode 100644 index 0d5d96e45b..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.html +++ /dev/null @@ -1,52 +0,0 @@ - - - warning -

gateway.configuration-delete-dialog-header

- - -
-
- - - gateway.configuration-delete-dialog-input - - - {{ 'gateway.configuration-delete-dialog-input-required' | translate }} - - -
-
- - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts deleted file mode 100644 index b4725016ba..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-remote-configuration-dialog.ts +++ /dev/null @@ -1,57 +0,0 @@ -/// -/// 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. -/// - -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { Router } from '@angular/router'; -import { FormBuilder, FormControl } from '@angular/forms'; - -export interface GatewayRemoteConfigurationDialogData { - gatewayName: string; -} - -@Component({ - selector: 'gateway-remote-configuration-dialog', - templateUrl: './gateway-remote-configuration-dialog.html' -}) - -export class GatewayRemoteConfigurationDialogComponent extends - DialogComponent{ - - gatewayName: string; - gatewayControl: FormControl; - - constructor(protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: GatewayRemoteConfigurationDialogData, - public dialogRef: MatDialogRef, - private fb: FormBuilder) { - super(store, router, dialogRef); - this.gatewayName = this.data.gatewayName; - this.gatewayControl = this.fb.control(''); - } - - close(): void { - this.dialogRef.close(); - } - - turnOff(): void { - this.dialogRef.close(true); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.html deleted file mode 100644 index d8b028361d..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.html +++ /dev/null @@ -1,54 +0,0 @@ - - -

gateway.rpc.save-template

- - -
-
- - gateway.rpc.template-name - - - {{ 'gateway.rpc.template-name-required' | translate }} - - -
- {{ 'gateway.rpc.template-name-duplicate' | translate }} -
-
-
- - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.ts deleted file mode 100644 index 5c70e1f233..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog.ts +++ /dev/null @@ -1,63 +0,0 @@ -/// -/// 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. -/// - -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { DialogComponent } from '@shared/components/dialog.component'; -import { Router } from '@angular/router'; -import { FormBuilder, FormControl, UntypedFormControl, Validators } from '@angular/forms'; -import { RPCTemplate, SaveRPCTemplateData } from '@home/components/widget/lib/gateway/gateway-widget.models'; - -@Component({ - selector: 'gateway-service-rpc-connector-template-dialog', - templateUrl: './gateway-service-rpc-connector-template-dialog.html' -}) - -export class GatewayServiceRPCConnectorTemplateDialogComponent extends DialogComponent { - - config: { - [key: string]: any; - }; - templates: Array; - templateNameCtrl: FormControl; - - constructor(protected store: Store, - protected router: Router, - @Inject(MAT_DIALOG_DATA) public data: SaveRPCTemplateData, - public dialogRef: MatDialogRef, - public fb: FormBuilder) { - super(store, router, dialogRef); - this.config = this.data.config; - this.templates = this.data.templates; - this.templateNameCtrl = this.fb.control('', [Validators.required]); - } - - validateDuplicateName(c: UntypedFormControl) { - const name = c.value.trim(); - return !!this.templates.find((template) => template.name === name); - }; - - close(): void { - this.dialogRef.close(); - } - - save(): void { - this.templateNameCtrl.setValue(this.templateNameCtrl.value.trim()); - if (this.templateNameCtrl.valid) this.dialogRef.close(this.templateNameCtrl.value); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html deleted file mode 100644 index 0d01a1c1d7..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html +++ /dev/null @@ -1,67 +0,0 @@ - -
{{ 'gateway.rpc.templates-title' | translate }}
- - - - {{template.name}} - - - - - - - - - - -
-
- {{!innerValue ? ('gateway.rpc.' + config.key | translate) : config.key}} -
-
- {{ config.value | getRpcTemplateArrayView }} -
- -
- -
- {{ config.value }} - {{ SNMPMethodsTranslations.get(config.value) | translate }} - - - - -
-
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss deleted file mode 100644 index bf037a7249..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss +++ /dev/null @@ -1,113 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - padding: 0; - - .title { - font-weight: 500; - } - - .template-key { - color: rgba(0, 0, 0, 0.38); - height: 32px; - line-height: 32px; - } - - .boolean-true, .boolean-false { - border-radius: 3px; - height: 32px; - line-height: 32px; - padding: 0 12px; - width: fit-content; - font-size: 14px; - text-transform: capitalize; - } - - .boolean-false { - color: #d12730; - background-color: rgba(209, 39, 48, 0.08); - } - - .boolean-true { - color: #198038; - background-color: rgba(25, 128, 56, 0.08); - } - - mat-expansion-panel { - margin-top: 10px; - overflow: visible; - } - - .mat-expansion-panel-header-description { - flex-direction: row-reverse; - align-items: center; - margin-right: 0; - flex: 0; - - & > mat-icon { - margin-left: 15px; - color: rgba(0, 0, 0, 0.38); - } - } - - .mat-expansion-panel-header { - padding: 0 0 0 12px; - - &.mat-expansion-panel-header.mat-expanded { - height: 48px; - } - - .mat-content.mat-content-hide-toggle { - margin-right: 0; - } - } - - .rpc-params-row { - overflow: hidden; - white-space: nowrap; - - & :not(:first-child) { - white-space: pre; - overflow: hidden; - text-overflow: ellipsis; - } - } - - .template-name { - overflow: hidden; - text-overflow: ellipsis; - display: block; - } - - ::ng-deep .mat-content { - align-items: center; - } - - .mat-expansion-panel-header-title { - flex: 1; - margin: 0; - } - - .array-value { - margin-left: 10px; - } -} - - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts deleted file mode 100644 index 9c2e551a22..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.ts +++ /dev/null @@ -1,85 +0,0 @@ -/// -/// 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. -/// - -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { - ConnectorType, - RPCTemplate, - SNMPMethodsTranslations -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { EntityType } from '@shared/models/entity-type.models'; -import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { AttributeService } from '@core/http/attribute.service'; -import { WidgetContext } from '@home/models/widget-component.models'; -import { isLiteralObject } from '@app/core/utils'; - -@Component({ - selector: 'tb-gateway-service-rpc-connector-templates', - templateUrl: './gateway-service-rpc-connector-templates.component.html', - styleUrls: ['./gateway-service-rpc-connector-templates.component.scss'] -}) -export class GatewayServiceRPCConnectorTemplatesComponent implements OnInit { - - @Input() - connectorType: ConnectorType; - - @Input() - ctx: WidgetContext; - - @Output() - saveTemplate: EventEmitter = new EventEmitter(); - - @Output() - useTemplate: EventEmitter = new EventEmitter(); - - @Input() - rpcTemplates: Array; - - public readonly originalOrder = (): number => 0; - public readonly isObject = (value: unknown) => isLiteralObject(value); - public readonly isArray = (value: unknown) => Array.isArray(value); - public readonly SNMPMethodsTranslations = SNMPMethodsTranslations; - - constructor(private attributeService: AttributeService) { - } - - ngOnInit() { - } - - public applyTemplate($event: Event, template: RPCTemplate): void { - $event.stopPropagation(); - this.useTemplate.emit(template); - } - - public deleteTemplate($event: Event, template: RPCTemplate): void { - $event.stopPropagation(); - const index = this.rpcTemplates.findIndex(data => { - return data.name == template.name; - }) - this.rpcTemplates.splice(index, 1); - const key = `${this.connectorType}_template`; - this.attributeService.saveEntityAttributes( - { - id: this.ctx.defaultSubscription.targetDeviceId, - entityType: EntityType.DEVICE - } - , AttributeScope.SERVER_SCOPE, [{ - key, - value: this.rpcTemplates - }]).subscribe(() => { - }) - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html deleted file mode 100644 index fde4ab28ac..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html +++ /dev/null @@ -1,413 +0,0 @@ - -
-
{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}
- - - - - {{ 'gateway.rpc.methodRPC' | translate }} - - - - {{ 'gateway.rpc.requestType' | translate }} - - - {{bACnetRequestTypesTranslates.get(type) | translate}} - - - - - {{ 'gateway.rpc.requestTimeout' | translate }} - - -
- - {{ 'gateway.rpc.objectType' | translate }} - - - {{bACnetObjectTypesTranslates.get(type) | translate}} - - - - - {{ 'gateway.rpc.identifier' | translate }} - - -
- - {{ 'gateway.rpc.propertyId' | translate }} - - -
- - - {{ 'gateway.rpc.methodRPC' | translate }} - - - - {{ 'gateway.rpc.characteristicUUID' | translate }} - - - - {{ 'gateway.rpc.methodProcessing' | translate }} - - - {{bLEMethodsTranslates.get(type) | translate}} - - - - - {{ 'gateway.rpc.withResponse' | translate }} - - - - - {{ 'gateway.rpc.methodRPC' | translate }} - - - - {{ 'gateway.rpc.nodeID' | translate }} - - - - {{ 'gateway.rpc.isExtendedID' | translate }} - - - {{ 'gateway.rpc.isFD' | translate }} - - - {{ 'gateway.rpc.bitrateSwitch' | translate }} - -
- - {{ 'gateway.rpc.dataLength' | translate }} - - - - {{ 'gateway.rpc.dataByteorder' | translate }} - - - {{ order | translate }} - - - -
-
- - {{ 'gateway.rpc.dataBefore' | translate }} - - - - {{ 'gateway.rpc.dataAfter' | translate }} - - -
- - {{ 'gateway.rpc.dataInHEX' | translate }} - - - - {{ 'gateway.rpc.dataExpression' | translate }} - - -
- - - {{ 'gateway.rpc.methodFilter' | translate }} - - - - {{ 'gateway.rpc.valueExpression' | translate }} - - - - - - {{ 'gateway.rpc.methodRPC' | translate }} - - - - {{ 'gateway.rpc.valueExpression' | translate }} - - - - {{ 'gateway.rpc.withResponse' | translate }} - - - - - {{ 'gateway.rpc.methodRPC' | translate }} - - - - {{ 'gateway.rpc.methodProcessing' | translate }} - - - {{ SocketMethodProcessingsTranslates.get(method) | translate }} - - - - - {{ 'gateway.rpc.encoding' | translate }} - - - - {{ 'gateway.rpc.withResponse' | translate }} - - - - - {{ 'gateway.rpc.methodRPC' | translate }} - - - - {{ 'gateway.rpc.valueExpression' | translate }} - - - - {{ 'gateway.rpc.withResponse' | translate }} - - - - - {{ 'gateway.rpc.requestFilter' | translate }} - - - - {{ 'gateway.rpc.method' | translate }} - - - {{ SNMPMethodsTranslations.get(method) | translate }} - - - - - {{ 'gateway.rpc.withResponse' | translate }} - -
- {{ 'gateway.rpc.oids' | translate }}* -
- - - - delete - -
- -
-
- - - {{ 'gateway.rpc.methodFilter' | translate }} - - -
- - {{ 'gateway.rpc.httpMethod' | translate }} - - - {{ method }} - - - - - {{ 'gateway.rpc.requestUrlExpression' | translate }} - - -
-
- - {{ 'gateway.rpc.responseTimeout' | translate }} - - - - {{ 'gateway.rpc.timeout' | translate }} - - - - {{ 'gateway.rpc.tries' | translate }} - - -
- - {{ 'gateway.rpc.valueExpression' | translate }} - - -
- {{ 'gateway.rpc.httpHeaders' | translate }} -
-
- {{ 'gateway.rpc.header-name' | translate }} - {{ 'gateway.rpc.value' | translate }} - -
- -
- - - - - - - - delete - - -
-
- -
- -
- - - {{ 'gateway.rpc.methodFilter' | translate }} - - -
- - {{ 'gateway.rpc.httpMethod' | translate }} - - - {{ method }} - - - - - {{ 'gateway.rpc.requestUrlExpression' | translate }} - - -
-
- - {{ 'gateway.rpc.responseTimeout' | translate }} - - - - {{ 'gateway.rpc.timeout' | translate }} - - - - {{ 'gateway.rpc.tries' | translate }} - - -
- - {{ 'gateway.rpc.requestValueExpression' | translate }} - - - - {{ 'gateway.rpc.responseValueExpression' | translate }} - - -
- {{ 'gateway.rpc.httpHeaders' | translate }} -
-
- {{ 'gateway.rpc.header-name' | translate }} - {{ 'gateway.rpc.value' | translate }} - -
- -
- - - - - - - - delete - - -
-
- -
-
- - - {{ 'gateway.statistics.command' | translate }} - - - - {{ 'widget-config.datasource-parameters' | translate }} - - edit - - - {{ 'gateway.rpc.json-value-invalid' | translate }} - - - -
-
-
- - -
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss deleted file mode 100644 index a0af50aee8..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.scss +++ /dev/null @@ -1,67 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - padding: 0; - - .title { - font-weight: 500; - } - - .command-form { - flex-wrap: nowrap; - - & > button { - margin-top: 10px; - } - } - - .mat-mdc-slide-toggle.margin { - margin-bottom: 10px; - margin-left: 10px; - } - - .fields{ - .fields-label { - font-weight: 500; - } - } - - .border { - padding: 16px; - margin-bottom: 10px; - box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%); - border: solid 1px #e0e0e0; - border-radius: 4px; - - .title { - color: rgba(0, 0, 0, .54); - } - - .mat-icon { - color: rgba(0, 0, 0, .38); - } - - .mat-divider { - margin-left: -16px; - margin-right: -16px; - margin-bottom: 16px; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts deleted file mode 100644 index f77e877aea..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ /dev/null @@ -1,345 +0,0 @@ -/// -/// 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. -/// - -import { Component, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output } from '@angular/core'; -import { - ControlValueAccessor, - FormArray, - FormBuilder, - FormControl, - FormGroup, - NG_VALUE_ACCESSOR, - Validators -} from '@angular/forms'; -import { - BACnetObjectTypes, - BACnetObjectTypesTranslates, - BACnetRequestTypes, - BACnetRequestTypesTranslates, - BLEMethods, - BLEMethodsTranslates, - CANByteOrders, - ConnectorType, - GatewayConnectorDefaultTypesTranslatesMap, - HTTPMethods, - noLeadTrailSpacesRegex, - RPCCommand, - RPCTemplateConfig, - SNMPMethods, - SNMPMethodsTranslations, - SocketEncodings, - SocketMethodProcessings, - SocketMethodProcessingsTranslates -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { MatDialog } from '@angular/material/dialog'; -import { - JsonObjectEditDialogComponent, - JsonObjectEditDialogData -} from '@shared/components/dialog/json-object-edit-dialog.component'; -import { jsonRequired } from '@shared/components/json-object-edit.component'; -import { deepClone } from '@core/utils'; -import { Subject } from "rxjs"; - -@Component({ - selector: 'tb-gateway-service-rpc-connector', - templateUrl: './gateway-service-rpc-connector.component.html', - styleUrls: ['./gateway-service-rpc-connector.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => GatewayServiceRPCConnectorComponent), - multi: true - } - ] -}) -export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, ControlValueAccessor { - - @Input() - connectorType: ConnectorType; - - @Output() - sendCommand: EventEmitter = new EventEmitter(); - - @Output() - saveTemplate: EventEmitter = new EventEmitter(); - - commandForm: FormGroup; - ConnectorType = ConnectorType; - bACnetRequestTypes = Object.values(BACnetRequestTypes) as BACnetRequestTypes[]; - bACnetObjectTypes = Object.values(BACnetObjectTypes) as BACnetObjectTypes[]; - bLEMethods = Object.values(BLEMethods) as BLEMethods[]; - cANByteOrders = Object.values(CANByteOrders) as CANByteOrders[]; - socketMethodProcessings = Object.values(SocketMethodProcessings) as SocketMethodProcessings[]; - socketEncodings = Object.values(SocketEncodings) as SocketEncodings[]; - sNMPMethods = Object.values(SNMPMethods) as SNMPMethods[]; - hTTPMethods = Object.values(HTTPMethods) as HTTPMethods[]; - - bACnetRequestTypesTranslates = BACnetRequestTypesTranslates; - bACnetObjectTypesTranslates = BACnetObjectTypesTranslates; - bLEMethodsTranslates = BLEMethodsTranslates; - SocketMethodProcessingsTranslates = SocketMethodProcessingsTranslates; - SNMPMethodsTranslations = SNMPMethodsTranslations; - gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslatesMap; - - urlPattern = /^[-a-zA-Zd_$:{}?~+=\/.0-9-]*$/; - numbersOnlyPattern = /^[0-9]*$/; - hexOnlyPattern = /^[0-9A-Fa-f ]+$/; - - private propagateChange = (v: any) => { - } - private destroy$ = new Subject(); - - constructor(private fb: FormBuilder, - private dialog: MatDialog,) { - } - - ngOnInit() { - this.commandForm = this.connectorParamsFormGroupByType(this.connectorType); - this.commandForm.valueChanges.subscribe(value => { - const httpHeaders = {}; - switch (this.connectorType) { - case ConnectorType.REST: - value.httpHeaders.forEach(data => { - httpHeaders[data.headerName] = data.value; - }) - value.httpHeaders = httpHeaders; - break; - case ConnectorType.REQUEST: - value.httpHeaders.forEach(data => { - httpHeaders[data.headerName] = data.value; - }) - value.httpHeaders = httpHeaders; - break; - } - if (this.commandForm.valid) { - this.propagateChange({...this.commandForm.value, ...value}); - } - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - connectorParamsFormGroupByType(type: ConnectorType): FormGroup { - let formGroup: FormGroup; - - switch (type) { - case ConnectorType.BACNET: - formGroup = this.fb.group({ - method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - requestType: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - requestTimeout: [null, [Validators.required, Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], - objectType: [null, []], - identifier: [null, [Validators.required, Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]], - propertyId: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]] - }) - break; - case ConnectorType.BLE: - formGroup = this.fb.group({ - methodRPC: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - characteristicUUID: ["00002A00-0000-1000-8000-00805F9B34FB", [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - methodProcessing: [null, [Validators.required]], - withResponse: [false, []] - }) - break; - case ConnectorType.CAN: - formGroup = this.fb.group({ - method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - nodeID: [null, [Validators.required, Validators.min(0), Validators.pattern(this.numbersOnlyPattern)]], - isExtendedID: [false, []], - isFD: [false, []], - bitrateSwitch: [false, []], - dataLength: [null, [Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]], - dataByteorder: [null, []], - dataBefore: [null, [Validators.pattern(noLeadTrailSpacesRegex), Validators.pattern(this.hexOnlyPattern)]], - dataAfter: [null, [Validators.pattern(noLeadTrailSpacesRegex), Validators.pattern(this.hexOnlyPattern)]], - dataInHEX: [null, [Validators.pattern(noLeadTrailSpacesRegex), Validators.pattern(this.hexOnlyPattern)]], - dataExpression: [null, [Validators.pattern(noLeadTrailSpacesRegex)]] - }) - break; - case ConnectorType.FTP: - formGroup = this.fb.group({ - methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]] - }) - break; - case ConnectorType.OCPP: - formGroup = this.fb.group({ - methodRPC: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - withResponse: [false, []] - }) - break; - case ConnectorType.SOCKET: - formGroup = this.fb.group({ - methodRPC: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - methodProcessing: [null, [Validators.required]], - encoding: [SocketEncodings.UTF_8, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - withResponse: [false, []] - }) - break; - case ConnectorType.XMPP: - formGroup = this.fb.group({ - methodRPC: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - withResponse: [false, []] - }) - break; - case ConnectorType.SNMP: - formGroup = this.fb.group({ - requestFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - method: [null, [Validators.required]], - withResponse: [false, []], - oid: this.fb.array([], [Validators.required]) - }) - break; - case ConnectorType.REST: - formGroup = this.fb.group({ - methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - httpMethod: [null, [Validators.required]], - requestUrlExpression: [null, [Validators.required, Validators.pattern(this.urlPattern)]], - responseTimeout: [null, [Validators.required, Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], - timeout: [null, [Validators.required, Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], - tries: [null, [Validators.required, Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]], - valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - httpHeaders: this.fb.array([]), - security: [{}, [Validators.required]] - }) - break; - case ConnectorType.REQUEST: - formGroup = this.fb.group({ - methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - httpMethod: [null, [Validators.required]], - requestUrlExpression: [null, [Validators.required, Validators.pattern(this.urlPattern)]], - responseTimeout: [null, [Validators.required, Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], - timeout: [null, [Validators.required, Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], - tries: [null, [Validators.required, Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]], - requestValueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseValueExpression: [null, [Validators.pattern(noLeadTrailSpacesRegex)]], - httpHeaders: this.fb.array([]), - }) - break; - default: - formGroup = this.fb.group({ - command: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - params: [{}, [jsonRequired]], - }) - } - return formGroup; - } - - addSNMPoid(value: string = null) { - const oidsFA = this.commandForm.get('oid') as FormArray; - if (oidsFA) { - oidsFA.push(this.fb.control(value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]), {emitEvent: false}); - } - } - - removeSNMPoid(index: number) { - const oidsFA = this.commandForm.get('oid') as FormArray; - oidsFA.removeAt(index); - } - - addHTTPHeader(value: { headerName: string, value: string } = {headerName: null, value: null}) { - const headerFA = this.commandForm.get('httpHeaders') as FormArray; - const formGroup = this.fb.group({ - headerName: [value.headerName, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - value: [value.value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]] - }) - if (headerFA) { - headerFA.push(formGroup, {emitEvent: false}); - } - } - - removeHTTPHeader(index: number) { - const oidsFA = this.commandForm.get('httpHeaders') as FormArray; - oidsFA.removeAt(index); - } - - getFormArrayControls(path: string) { - return (this.commandForm.get(path) as FormArray).controls as FormControl[]; - } - - openEditJSONDialog($event: Event) { - if ($event) { - $event.stopPropagation(); - } - this.dialog.open(JsonObjectEditDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: { - jsonValue: this.commandForm.get('params').value, - required: true - } - }).afterClosed().subscribe( - (res) => { - if (res) { - this.commandForm.get('params').setValue(res); - } - } - ); - } - - save() { - this.saveTemplate.emit(); - } - - registerOnChange(fn: any): void { - this.propagateChange = fn; - } - - registerOnTouched(fn: any): void { - } - - clearFromArrayByName(name: string) { - const formArray = this.commandForm.get(name) as FormArray; - while (formArray.length !== 0) { - formArray.removeAt(0) - } - } - - writeValue(value: RPCTemplateConfig): void { - if (typeof value == "object") { - value = deepClone(value); - switch (this.connectorType) { - case ConnectorType.SNMP: - this.clearFromArrayByName("oid"); - value.oid.forEach(value => { - this.addSNMPoid(value) - }) - delete value.oid; - break; - case ConnectorType.REQUEST: - this.clearFromArrayByName("httpHeaders"); - value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => { - this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string}) - }) - delete value.httpHeaders; - break; - case ConnectorType.REST: - this.clearFromArrayByName("httpHeaders"); - value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => { - this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string}) - }) - delete value.httpHeaders; - break; - } - this.commandForm.patchValue(value, {onlySelf: false}); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html deleted file mode 100644 index 41a655d93e..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.html +++ /dev/null @@ -1,88 +0,0 @@ - -
-
- - - {{ 'gateway.statistics.command' | translate }} - - - {{ command }} - - - - - {{ 'gateway.statistics.timeout-ms' | translate }} - - - {{ 'gateway.statistics.timeout-min' | translate }} - - - - - - - -
-
{{ 'gateway.rpc.title' | translate: {type: gatewayConnectorDefaultTypesTranslates.get(connectorType)} }}
- - - - - -
- - -
-
-
-
-
-
- {{ 'gateway.rpc-command-result' | translate }} -
- schedule - {{ resultTime | date: 'yyyy/MM/dd HH:mm:ss' }} -
-
- -
-
- - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss deleted file mode 100644 index b8424dc068..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.scss +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - overflow: auto; - display: flex; - flex-direction: row; - padding: 0 5px; - - & > * { - height: 100%; - overflow: auto; - } - - & > tb-gateway-service-rpc-connector-templates:last-child { - margin-left: 10px; - } - - - .command-form { - flex-wrap: nowrap; - padding: 0 5px 5px; - - & > button { - margin-top: 10px; - } - } - - .rpc-parameters { - width: 100%; - } - - .result-block { - padding: 0 5px; - display: flex; - flex-direction: column; - flex: 1; - - & > span { - font-weight: 600; - position: relative; - font-size: 14px; - margin-bottom: 10px; - - - .result-time { - font-weight: 400; - font-size: 14px; - line-height: 32px; - position: absolute; - left: 0; - top: 25px; - z-index: 5; - color: rgba(0, 0, 0, 0.54); - - span { - padding-left: 10px; - } - } - } - - tb-json-content { - flex: 1; - } - } - - .border { - padding: 16px; - box-shadow: 0 0 0 0 rgb(0 0 0 / 20%), 0 0 0 0 rgb(0 0 0 / 14%), 0 0 0 0 rgb(0 0 0 / 12%); - border: solid 1px #e0e0e0; - border-radius: 4px; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts deleted file mode 100644 index 43050ca5f7..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts +++ /dev/null @@ -1,230 +0,0 @@ -/// -/// 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. -/// - -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { MatDialog, MatDialogRef } from '@angular/material/dialog'; -import { WidgetContext } from '@home/models/widget-component.models'; -import { ContentType } from '@shared/models/constants'; -import { jsonRequired } from '@shared/components/json-object-edit.component'; -import { - ConnectorType, - GatewayConnectorDefaultTypesTranslatesMap, - RPCCommand, - RPCTemplate, - RPCTemplateConfig, - SaveRPCTemplateData -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { - GatewayServiceRPCConnectorTemplateDialogComponent -} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog'; -import { AttributeService } from '@core/http/attribute.service'; -import { AttributeScope } from '@shared/models/telemetry/telemetry.models'; -import { EntityType } from '@shared/models/entity-type.models'; -import { DatasourceType, widgetType } from '@shared/models/widget.models'; -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; -import { UtilsService } from '@core/services/utils.service'; - -@Component({ - selector: 'tb-gateway-service-rpc', - templateUrl: './gateway-service-rpc.component.html', - styleUrls: ['./gateway-service-rpc.component.scss'] -}) -export class GatewayServiceRPCComponent implements OnInit { - - @Input() - ctx: WidgetContext; - - contentTypes = ContentType; - - resultTime: number | null; - - @Input() - dialogRef: MatDialogRef; - - commandForm: FormGroup; - - isConnector: boolean; - - RPCCommands: Array = [ - 'Ping', - 'Stats', - 'Devices', - 'Update', - 'Version', - 'Restart', - 'Reboot' - ]; - - public connectorType: ConnectorType; - public templates: Array = []; - - readonly ConnectorType = ConnectorType; - readonly gatewayConnectorDefaultTypesTranslates = GatewayConnectorDefaultTypesTranslatesMap; - readonly typesWithUpdatedParams = new Set([ - ConnectorType.MQTT, - ConnectorType.OPCUA, - ConnectorType.MODBUS, - ]); - - private subscription: IWidgetSubscription; - private subscriptionOptions: WidgetSubscriptionOptions = { - callbacks: { - onDataUpdated: () => this.ctx.ngZone.run(() => { - this.updateTemplates() - }), - onDataUpdateError: (subscription, e) => this.ctx.ngZone.run(() => { - this.onDataUpdateError(e); - }), - dataLoading: () => { - } - } - }; - - constructor(private fb: FormBuilder, - private dialog: MatDialog, - private utils: UtilsService, - private cd: ChangeDetectorRef, - private attributeService: AttributeService) { - this.commandForm = this.fb.group({ - command: [null, [Validators.required]], - time: [60, [Validators.required, Validators.min(1)]], - params: ['{}', [jsonRequired]], - result: [null] - }); - } - - ngOnInit() { - this.isConnector = this.ctx.settings.isConnector; - if (!this.isConnector) { - this.commandForm.get('command').setValue(this.RPCCommands[0]); - } else { - this.connectorType = this.ctx.stateController.getStateParams().connector_rpc.value.type; - const subscriptionInfo = [{ - type: DatasourceType.entity, - entityType: EntityType.DEVICE, - entityId: this.ctx.defaultSubscription.targetDeviceId, - entityName: 'Connector', - attributes: [{name: `${this.connectorType}_template`}] - }]; - this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.latest, subscriptionInfo, - this.subscriptionOptions, false, true).subscribe(subscription => { - this.subscription = subscription; - }) - } - } - - sendCommand(value?: RPCCommand) { - this.resultTime = null; - const formValues = value || this.commandForm.value; - const commandPrefix = this.isConnector ? `${this.connectorType}_` : 'gateway_'; - const command = !this.isConnector ? formValues.command.toLowerCase() : this.getCommandFromParamsByType(formValues.params); - const params = formValues.params; - this.ctx.controlApi.sendTwoWayCommand(commandPrefix + command, params, formValues.time).subscribe({ - next: resp => { - this.resultTime = new Date().getTime(); - this.commandForm.get('result').setValue(JSON.stringify(resp)) - }, - error: error => { - this.resultTime = new Date().getTime(); - console.error(error); - this.commandForm.get('result').setValue(JSON.stringify(error.error)); - } - }); - } - - getCommandFromParamsByType(params: RPCTemplateConfig) { - switch (this.connectorType) { - case ConnectorType.MQTT: - case ConnectorType.FTP: - case ConnectorType.SNMP: - case ConnectorType.REST: - case ConnectorType.REQUEST: - return params.methodFilter; - case ConnectorType.MODBUS: - return params.tag; - case ConnectorType.BACNET: - case ConnectorType.CAN: - case ConnectorType.OPCUA: - return params.method; - case ConnectorType.BLE: - case ConnectorType.OCPP: - case ConnectorType.SOCKET: - case ConnectorType.XMPP: - return params.methodRPC; - default: - return params.command; - } - } - - saveTemplate() { - this.dialog.open - (GatewayServiceRPCConnectorTemplateDialogComponent, { - disableClose: true, - panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], - data: {config: this.commandForm.value.params, templates: this.templates} - }).afterClosed().subscribe( - (res) => { - if (res) { - const templateAttribute: RPCTemplate = { - name: res, - config: this.commandForm.value.params - } - const templatesArray = this.templates; - const existingIndex = templatesArray.findIndex(template => { - return template.name == templateAttribute.name; - }) - if (existingIndex > -1) { - templatesArray.splice(existingIndex, 1) - } - templatesArray.push(templateAttribute) - const key = `${this.connectorType}_template`; - this.attributeService.saveEntityAttributes( - { - id: this.ctx.defaultSubscription.targetDeviceId, - entityType: EntityType.DEVICE - } - , AttributeScope.SERVER_SCOPE, [{ - key, - value: templatesArray - }]).subscribe(() => { - this.cd.detectChanges(); - }) - } - } - ); - } - - useTemplate($event) { - this.commandForm.get('params').patchValue($event.config); - } - - private updateTemplates() { - this.templates = this.subscription.data[0].data[0][1].length ? - JSON.parse(this.subscription.data[0].data[0][1]) : []; - this.cd.detectChanges(); - } - - private onDataUpdateError(e: any) { - const exceptionData = this.utils.parseException(e); - let errorText = exceptionData.name; - if (exceptionData.message) { - errorText += ': ' + exceptionData.message; - } - console.error(errorText); - } - -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html deleted file mode 100644 index 5dbc0aee00..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.html +++ /dev/null @@ -1,86 +0,0 @@ - -
- - - {{ 'gateway.statistics.statistic' | translate }} - - - {{ key }} - - - {{ command.attributeOnGateway }} - - - - - {{ 'gateway.statistics.statistic-commands-empty' | translate }} - -
- -
- - {{ 'gateway.statistics.command' | translate }} - - -
-
-
- - - {{ 'widgets.gateway.created-time' | translate }} - - - {{ row[0]| date:'yyyy-MM-dd HH:mm:ss' }} - - - - {{ 'widgets.gateway.message' | translate }} - - - {{ row[1] }} - - - - -
- {{ 'attribute.no-telemetry-text' | translate }} -
-
- -
-
-
-
-
-
- diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss deleted file mode 100644 index 9f719807ca..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 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. - */ -:host { - width: 100%; - height: 100%; - padding: 0; - - - .statistics-container { - height: 100%; - overflow-y: auto; - - mat-card { - width: 40%; - height: 100%; - margin-right: 35px; - padding: 15px; - gap: 22px; - } - - @media only screen and (max-width: 750px) { - mat-card { - width: 100%; - } - } - - .chart-box, .chart-container { - height: 100%; - flex-grow: 1; - } - - .chart-box { - overflow: auto; - } - - & > * { - height: 100%; - } - } - - .legend { - flex-wrap: wrap; - width: 100%; - padding-top: 8px; - padding-bottom: 4px; - margin-top: 15px; - - .legend-keys { - .legend-label { - padding: 2px 20px 2px 10px; - white-space: nowrap; - - &.hidden-label { - text-decoration: line-through; - opacity: .6; - } - - &:focus { - outline: none; - } - } - - .legend-line { - display: inline-block; - width: 15px; - height: 3px; - text-align: left; - vertical-align: middle; - outline: none; - } - } - } -} - 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 deleted file mode 100644 index 2537f415da..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts +++ /dev/null @@ -1,291 +0,0 @@ -/// -/// 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. -/// - -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; -import { FormBuilder, FormGroup } from '@angular/forms'; -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 { 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'; -import { EntityType } from '@shared/models/entity-type.models'; -import { EntityId } from '@shared/models/id/entity-id'; -import { BaseData } from '@shared/models/base-data'; -import { PageLink } from '@shared/models/page/page-link'; -import { Direction, SortOrder } from '@shared/models/page/sort-order'; -import { MatTableDataSource } from '@angular/material/table'; -import { MatSort } from '@angular/material/sort'; -import { NULL_UUID } from '@shared/models/id/has-uuid'; -import { deepClone } from '@core/utils'; - -@Component({ - selector: 'tb-gateway-statistics', - templateUrl: './gateway-statistics.component.html', - styleUrls: ['./gateway-statistics.component.scss'] -}) -export class GatewayStatisticsComponent implements AfterViewInit { - - @ViewChild(MatSort) sort: MatSort; - @ViewChild('statisticChart') statisticChart: ElementRef; - - @Input() - ctx: WidgetContext; - - @Input() - public general: boolean; - - public isNumericData = false; - public dataTypeDefined: boolean = false; - public chartInited: boolean; - private flot: TbFlot; - private flotCtx: WidgetContext; - public statisticForm: FormGroup; - public statisticsKeys = []; - public commands = []; - public commandObj: any; - public dataSource: MatTableDataSource; - public pageLink: PageLink; - private resize$: ResizeObserver; - private subscription: IWidgetSubscription; - private subscriptionInfo: SubscriptionInfo []; - public legendData: LegendData; - public displayedColumns: Array; - private subscriptionOptions: WidgetSubscriptionOptions = { - callbacks: { - onDataUpdated: () => this.ctx.ngZone.run(() => { - this.onDataUpdated(); - }), - onDataUpdateError: (subscription, e) => this.ctx.ngZone.run(() => { - this.onDataUpdateError(e); - }) - }, - useDashboardTimewindow: false, - legendConfig: { - position: LegendPosition.bottom - } as LegendConfig - }; - - - constructor(private fb: FormBuilder, - private attributeService: AttributeService, - private utils: UtilsService) { - const sortOrder: SortOrder = {property: '0', direction: Direction.DESC}; - this.pageLink = new PageLink(Number.POSITIVE_INFINITY, 0, null, sortOrder); - this.displayedColumns = ['0', '1']; - this.dataSource = new MatTableDataSource([]); - this.statisticForm = this.fb.group({ - statisticKey: [null, []] - }); - - this.statisticForm.get('statisticKey').valueChanges.subscribe(value => { - this.commandObj = null; - if (this.commands.length) { - this.commandObj = this.commands.find(command => command.attributeOnGateway === value); - } - if (this.subscriptionInfo) { - this.createChartsSubscription(this.ctx.defaultSubscription.datasources[0].entity, value); - } - }); - } - - - ngAfterViewInit() { - this.dataSource.sort = this.sort; - this.sort.sortChange.subscribe(() => this.sortData()); - this.init(); - if (this.ctx.defaultSubscription.datasources.length) { - const gateway = this.ctx.defaultSubscription.datasources[0].entity; - if (gateway.id.id === NULL_UUID) { - return; - } - if (!this.general) { - this.attributeService.getEntityAttributes(gateway.id, AttributeScope.SHARED_SCOPE, ['general_configuration']) - .subscribe((resp: AttributeData[]) => { - if (resp && resp.length) { - this.commands = resp[0].value.statistics.commands; - if (!this.statisticForm.get('statisticKey').value && this.commands && this.commands.length) { - this.statisticForm.get('statisticKey').setValue(this.commands[0].attributeOnGateway); - this.createChartsSubscription(gateway, this.commands[0].attributeOnGateway); - } - } - }); - } else { - this.attributeService.getEntityTimeseriesLatest(gateway.id).subscribe( - data => { - const connectorsTs = Object.keys(data) - .filter(el => el.includes('ConnectorEventsProduced') || el.includes('ConnectorEventsSent')); - this.createGeneralChartsSubscription(gateway, connectorsTs); - }); - } - } - } - - public navigateToStatistics() { - const params = deepClone(this.ctx.stateController.getStateParams()); - this.ctx.stateController.openState('configuration', params); - } - - public sortData() { - this.dataSource.sortData(this.dataSource.data, this.sort); - } - - public onLegendKeyHiddenChange(index: number) { - this.legendData.keys[index].dataKey.hidden = !this.legendData.keys[index].dataKey.hidden; - this.subscription.updateDataVisibility(index); - } - - private createChartsSubscription(gateway: BaseData, attr: string) { - const subscriptionInfo = [{ - type: DatasourceType.entity, - entityType: EntityType.DEVICE, - entityId: gateway.id.id, - entityName: gateway.name, - timeseries: [] - }]; - - subscriptionInfo[0].timeseries = [{name: attr, label: attr}]; - this.subscriptionInfo = subscriptionInfo; - this.changeSubscription(subscriptionInfo); - this.ctx.defaultSubscription.unsubscribe(); - } - - private createGeneralChartsSubscription(gateway: BaseData, attrData: string[]) { - const subscriptionInfo = [{ - type: DatasourceType.entity, - entityType: EntityType.DEVICE, - entityId: gateway.id.id, - entityName: gateway.name, - timeseries: [] - }]; - subscriptionInfo[0].timeseries = []; - if (attrData?.length) { - attrData.forEach(attr => { - subscriptionInfo[0].timeseries.push({name: attr, label: attr}); - }); - } - this.ctx.defaultSubscription.datasources[0].dataKeys.forEach(dataKey => { - subscriptionInfo[0].timeseries.push({name: dataKey.name, label: dataKey.label}); - }); - - this.changeSubscription(subscriptionInfo); - this.ctx.defaultSubscription.unsubscribe(); - } - - private init = () => { - this.flotCtx = { - $scope: this.ctx.$scope, - $injector: this.ctx.$injector, - utils: this.ctx.utils, - isMobile: this.ctx.isMobile, - isEdit: this.ctx.isEdit, - subscriptionApi: this.ctx.subscriptionApi, - detectChanges: this.ctx.detectChanges, - settings: this.ctx.settings - } as WidgetContext; - }; - - private updateChart = () => { - if (this.flot && this.ctx.defaultSubscription.data.length) { - this.flot.update(); - } - }; - - private resize = () => { - if (this.flot) { - this.flot.resize(); - } - }; - - private reset() { - if (this.resize$) { - this.resize$.disconnect(); - } - if (this.subscription) { - this.subscription.unsubscribe(); - } - if (this.flot) { - this.flot.destroy(); - } - } - - private onDataUpdateError(e: any) { - const exceptionData = this.utils.parseException(e); - let errorText = exceptionData.name; - if (exceptionData.message) { - errorText += ': ' + exceptionData.message; - } - console.error(errorText); - } - - private onDataUpdated() { - this.isDataOnlyNumbers(); - if (this.isNumericData) { - if (this.chartInited) { - if (this.flot) { - this.flot.update(); - } - } else { - this.initChart(); - } - } - } - - private initChart() { - this.chartInited = true; - this.flotCtx.$container = $(this.statisticChart.nativeElement); - this.resize$.observe(this.statisticChart.nativeElement); - this.flot = new TbFlot(this.flotCtx as WidgetContext, 'line'); - this.flot.update(); - } - - private isDataOnlyNumbers() { - if (this.general) { - this.isNumericData = true; - return; - } - this.dataSource.data = this.subscription.data.length ? this.subscription.data[0].data : []; - if (this.dataSource.data.length && !this.dataTypeDefined) { - this.dataTypeDefined = true; - this.isNumericData = this.dataSource.data.every(data => !isNaN(+data[1])); - } - } - - - private changeSubscription(subscriptionInfo: SubscriptionInfo[]) { - if (this.subscription) { - this.reset(); - } - if (this.ctx.datasources[0].entity) { - this.ctx.subscriptionApi.createSubscriptionFromInfo(widgetType.timeseries, subscriptionInfo, this.subscriptionOptions, - false, true).subscribe(subscription => { - this.dataTypeDefined = false; - this.subscription = subscription; - this.isDataOnlyNumbers(); - this.legendData = this.subscription.legendData; - this.flotCtx.defaultSubscription = subscription; - this.resize$ = new ResizeObserver(() => { - this.resize(); - }); - this.ctx.detectChanges(); - if (this.isNumericData) { - this.initChart(); - } - }); - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts deleted file mode 100644 index f8f1c99289..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ /dev/null @@ -1,1307 +0,0 @@ -/// -/// 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. -/// - -import { helpBaseUrl, ValueTypeData } from '@shared/models/constants'; -import { AttributeData } from '@shared/models/telemetry/telemetry.models'; - -export const noLeadTrailSpacesRegex = /^\S+(?: \S+)*$/; -export const integerRegex = /^[-+]?\d+$/; -export const nonZeroFloat = /^-?(?!0(\.0+)?$)\d+(\.\d+)?$/; - -export enum StorageTypes { - MEMORY = 'memory', - FILE = 'file', - SQLITE = 'sqlite' -} - -export enum DeviceGatewayStatus { - EXCEPTION = 'EXCEPTION' -} - -export enum GatewayLogLevel { - NONE = 'NONE', - CRITICAL = 'CRITICAL', - ERROR = 'ERROR', - WARNING = 'WARNING', - INFO = 'INFO', - DEBUG = 'DEBUG', - TRACE = 'TRACE' -} - -export enum PortLimits { - MIN = 1, - MAX = 65535 -} - -export const GatewayStatus = { - ...GatewayLogLevel, - ...DeviceGatewayStatus -}; - -export type GatewayStatus = DeviceGatewayStatus | GatewayLogLevel; - -export enum LogSavingPeriod { - days = 'D', - hours = 'H', - minutes = 'M', - seconds = 'S' -} - -export enum LocalLogsConfigs { - service = 'service', - connector = 'connector', - converter = 'converter', - tb_connection = 'tb_connection', - storage = 'storage', - extension = 'extension' -} - -export const LocalLogsConfigTranslateMap = new Map([ - [LocalLogsConfigs.service, 'Service'], - [LocalLogsConfigs.connector, 'Connector'], - [LocalLogsConfigs.converter, 'Converter'], - [LocalLogsConfigs.tb_connection, 'TB Connection'], - [LocalLogsConfigs.storage, 'Storage'], - [LocalLogsConfigs.extension, 'Extension'] -]); - -export const LogSavingPeriodTranslations = new Map( - [ - [LogSavingPeriod.days, 'gateway.logs.days'], - [LogSavingPeriod.hours, 'gateway.logs.hours'], - [LogSavingPeriod.minutes, 'gateway.logs.minutes'], - [LogSavingPeriod.seconds, 'gateway.logs.seconds'] - ] -); - -export const StorageTypesTranslationMap = new Map( - [ - [StorageTypes.MEMORY, 'gateway.storage-types.memory-storage'], - [StorageTypes.FILE, 'gateway.storage-types.file-storage'], - [StorageTypes.SQLITE, 'gateway.storage-types.sqlite'] - ] -); - -export enum SecurityTypes { - ACCESS_TOKEN = 'accessToken', - USERNAME_PASSWORD = 'usernamePassword', - TLS_ACCESS_TOKEN = 'tlsAccessToken', - TLS_PRIVATE_KEY = 'tlsPrivateKey' -} - -export const GecurityTypesTranslationsMap = new Map( - [ - [SecurityTypes.ACCESS_TOKEN, 'gateway.security-types.access-token'], - [SecurityTypes.USERNAME_PASSWORD, 'gateway.security-types.username-password'], - [SecurityTypes.TLS_ACCESS_TOKEN, 'gateway.security-types.tls-access-token'] - ] -); - -export interface GatewayAttributeData extends AttributeData { - skipSync?: boolean; -} - -export interface GatewayConnectorBase { - name: string; - type: ConnectorType; - configuration?: string; - logLevel: string; - key?: string; - class?: string; - mode?: ConfigurationModes; - configVersion?: string; - reportStrategy?: ReportStrategyConfig; - sendDataOnlyOnChange?: boolean; - ts?: number; -} - -export interface GatewayConnector extends GatewayConnectorBase { - configurationJson: BaseConfig; - basicConfig?: BaseConfig; -} - -export interface GatewayVersionedDefaultConfig { - legacy: GatewayConnector; - '3.5.2': GatewayConnector; -} - -export interface DataMapping { - topicFilter: string; - QoS: string | number; - converter: Converter; -} - -export interface RequestsMapping { - requestType: RequestType; - type: string; - details: string; -} - -export interface OpcUaMapping { - deviceNodePattern?: string; - deviceNamePattern?: string; - deviceProfileExpression?: string; -} - -export type MappingValue = DataMapping | RequestsMapping | OpcUaMapping; - -export interface ServerConfig { - name: string; - url: string; - timeoutInMillis: number; - scanPeriodInMillis: number; - pollPeriodInMillis: number; - enableSubscriptions: boolean; - subCheckPeriodInMillis: number; - showMap: boolean; - security: string; - identity: ConnectorSecurity; -} - -export interface BrokerConfig { - name: string; - host: string; - port: number; - version: number; - clientId: string; - maxNumberOfWorkers: number; - maxMessageNumberPerWorker: number; - security: ConnectorSecurity; -} - -export interface ConnectorSecurity { - type: SecurityType; - username?: string; - password?: string; - pathToCACert?: string; - pathToPrivateKey?: string; - pathToClientCert?: string; - mode?: ModeType; -} - -export enum GatewayVersion { - Current = '3.5.2', - Legacy = 'legacy' -} - -export type ConnectorMapping = DeviceConnectorMapping | RequestMappingValue | ConverterConnectorMapping; - -export type ConnectorMappingFormValue = DeviceConnectorMapping | RequestMappingFormValue | ConverterMappingFormValue; - -export type ConnectorBaseConfig = ConnectorBaseConfig_v3_5_2 | ConnectorLegacyConfig; - -export type ConnectorLegacyConfig = ConnectorBaseInfo | MQTTLegacyBasicConfig | OPCLegacyBasicConfig | ModbusBasicConfig; - -export type ConnectorBaseConfig_v3_5_2 = ConnectorBaseInfo | MQTTBasicConfig_v3_5_2 | OPCBasicConfig_v3_5_2; - -export interface ConnectorBaseInfo { - name: string; - id: string; - enableRemoteLogging: boolean; - logLevel: GatewayLogLevel; - configVersion: string | number; - reportStrategy?: ReportStrategyConfig; -} - -export type MQTTBasicConfig = MQTTBasicConfig_v3_5_2 | MQTTLegacyBasicConfig; - -export interface MQTTBasicConfig_v3_5_2 { - mapping: ConverterConnectorMapping[]; - requestsMapping: Record | RequestMappingData[] | RequestMappingValue[]; - broker: BrokerConfig; - workers?: WorkersConfig; -} - -export interface MQTTLegacyBasicConfig { - mapping: LegacyConverterConnectorMapping[]; - broker: BrokerConfig; - workers?: WorkersConfig; - connectRequests: LegacyRequestMappingData[]; - disconnectRequests: LegacyRequestMappingData[]; - attributeRequests: LegacyRequestMappingData[]; - attributeUpdates: LegacyRequestMappingData[]; - serverSideRpc: LegacyRequestMappingData[]; -} - -export type OPCBasicConfig = OPCBasicConfig_v3_5_2 | OPCLegacyBasicConfig; - -export interface OPCBasicConfig_v3_5_2 { - mapping: DeviceConnectorMapping[]; - server: ServerConfig; -} - -export interface OPCLegacyBasicConfig { - server: LegacyServerConfig; -} - -export interface LegacyServerConfig extends Omit { - mapping: LegacyDeviceConnectorMapping[]; - disableSubscriptions: boolean; -} - -export type ModbusBasicConfig = ModbusBasicConfig_v3_5_2 | ModbusLegacyBasicConfig; - -export interface ModbusBasicConfig_v3_5_2 { - master: ModbusMasterConfig; - slave: ModbusSlave; -} - -export interface ModbusLegacyBasicConfig { - master: ModbusMasterConfig; - slave: ModbusLegacySlave; -} - -export interface WorkersConfig { - maxNumberOfWorkers: number; - maxMessageNumberPerWorker: number; -} - -export interface ConnectorDeviceInfo { - deviceNameExpression: string; - deviceNameExpressionSource: SourceType | OPCUaSourceType; - deviceProfileExpression: string; - deviceProfileExpressionSource: SourceType | OPCUaSourceType; -} - -export interface Attribute { - key: string; - type: string; - value: string; -} - -export interface LegacyAttribute { - key: string; - path: string; -} - -export interface Timeseries { - key: string; - type: string; - value: string; -} - -export interface LegacyTimeseries { - key: string; - path: string; -} - -export interface RpcArgument { - type: string; - value: number | string | boolean; -} - -export interface RpcMethod { - method: string; - arguments: RpcArgument[]; -} - -export interface LegacyRpcMethod { - method: string; - arguments: unknown[]; -} - -export interface AttributesUpdate { - key: string; - type: string; - value: string; -} - -export interface LegacyDeviceAttributeUpdate { - attributeOnThingsBoard: string; - attributeOnDevice: string; -} - -export interface Converter { - type: ConvertorType; - deviceInfo?: ConnectorDeviceInfo; - sendDataOnlyOnChange: boolean; - timeout: number; - attributes?: Attribute[]; - timeseries?: Timeseries[]; - extension?: string; - cached?: boolean; - extensionConfig?: Record; -} - -export interface LegacyConverter extends Converter { - deviceNameJsonExpression?: string; - deviceTypeJsonExpression?: string; - deviceNameTopicExpression?: string; - deviceTypeTopicExpression?: string; - deviceNameExpression?: string; - deviceNameExpressionSource?: string; - deviceTypeExpression?: string; - deviceProfileExpression?: string; - deviceProfileExpressionSource?: string; - ['extension-config']?: Record; -} - -export interface ConverterConnectorMapping { - topicFilter: string; - subscriptionQos?: string | number; - converter: Converter; -} - -export interface LegacyConverterConnectorMapping { - topicFilter: string; - subscriptionQos?: string | number; - converter: LegacyConverter; -} - -export type ConverterMappingFormValue = Omit & { - converter: { - type: ConvertorType; - } & Record; -}; - -export interface DeviceConnectorMapping { - deviceNodePattern: string; - deviceNodeSource: OPCUaSourceType; - deviceInfo: ConnectorDeviceInfo; - attributes?: Attribute[]; - timeseries?: Timeseries[]; - rpc_methods?: RpcMethod[]; - attributes_updates?: AttributesUpdate[]; -} - -export interface LegacyDeviceConnectorMapping { - deviceNamePattern: string; - deviceNodePattern: string; - deviceTypePattern: string; - attributes?: LegacyAttribute[]; - timeseries?: LegacyTimeseries[]; - rpc_methods?: LegacyRpcMethod[]; - attributes_updates?: LegacyDeviceAttributeUpdate[]; -} - -export enum ConnectorType { - MQTT = 'mqtt', - MODBUS = 'modbus', - GRPC = 'grpc', - OPCUA = 'opcua', - BLE = 'ble', - REQUEST = 'request', - CAN = 'can', - BACNET = 'bacnet', - ODBC = 'odbc', - REST = 'rest', - SNMP = 'snmp', - FTP = 'ftp', - SOCKET = 'socket', - XMPP = 'xmpp', - OCPP = 'ocpp', - CUSTOM = 'custom' -} - -export const GatewayConnectorDefaultTypesTranslatesMap = new Map([ - [ConnectorType.MQTT, 'MQTT'], - [ConnectorType.MODBUS, 'MODBUS'], - [ConnectorType.GRPC, 'GRPC'], - [ConnectorType.OPCUA, 'OPCUA'], - [ConnectorType.BLE, 'BLE'], - [ConnectorType.REQUEST, 'REQUEST'], - [ConnectorType.CAN, 'CAN'], - [ConnectorType.BACNET, 'BACNET'], - [ConnectorType.ODBC, 'ODBC'], - [ConnectorType.REST, 'REST'], - [ConnectorType.SNMP, 'SNMP'], - [ConnectorType.FTP, 'FTP'], - [ConnectorType.SOCKET, 'SOCKET'], - [ConnectorType.XMPP, 'XMPP'], - [ConnectorType.OCPP, 'OCPP'], - [ConnectorType.CUSTOM, 'CUSTOM'] -]); - -export interface RPCCommand { - command: string; - params: any; - time: number; -} - -export const ModbusFunctionCodeTranslationsMap = new Map([ - [1, 'gateway.function-codes.read-coils'], - [2, 'gateway.function-codes.read-discrete-inputs'], - [3, 'gateway.function-codes.read-multiple-holding-registers'], - [4, 'gateway.function-codes.read-input-registers'], - [5, 'gateway.function-codes.write-single-coil'], - [6, 'gateway.function-codes.write-single-holding-register'], - [15, 'gateway.function-codes.write-multiple-coils'], - [16, 'gateway.function-codes.write-multiple-holding-registers'] -]); - -export enum BACnetRequestTypes { - WriteProperty = 'writeProperty', - ReadProperty = 'readProperty' -} - -export const BACnetRequestTypesTranslates = new Map([ - [BACnetRequestTypes.WriteProperty, 'gateway.rpc.write-property'], - [BACnetRequestTypes.ReadProperty, 'gateway.rpc.read-property'] -]); - -export enum BACnetObjectTypes { - BinaryInput = 'binaryInput', - BinaryOutput = 'binaryOutput', - AnalogInput = 'analogInput', - AnalogOutput = 'analogOutput', - BinaryValue = 'binaryValue', - AnalogValue = 'analogValue' -} - -export const BACnetObjectTypesTranslates = new Map([ - [BACnetObjectTypes.AnalogOutput, 'gateway.rpc.analog-output'], - [BACnetObjectTypes.AnalogInput, 'gateway.rpc.analog-input'], - [BACnetObjectTypes.BinaryOutput, 'gateway.rpc.binary-output'], - [BACnetObjectTypes.BinaryInput, 'gateway.rpc.binary-input'], - [BACnetObjectTypes.BinaryValue, 'gateway.rpc.binary-value'], - [BACnetObjectTypes.AnalogValue, 'gateway.rpc.analog-value'] -]); - -export enum BLEMethods { - WRITE = 'write', - READ = 'read', - SCAN = 'scan' -} - -export const BLEMethodsTranslates = new Map([ - [BLEMethods.WRITE, 'gateway.rpc.write'], - [BLEMethods.READ, 'gateway.rpc.read'], - [BLEMethods.SCAN, 'gateway.rpc.scan'], -]); - -export enum CANByteOrders { - LITTLE = 'LITTLE', - BIG = 'BIG' -} - -export enum SocketMethodProcessings { - WRITE = 'write' -} - -export const SocketMethodProcessingsTranslates = new Map([ - [SocketMethodProcessings.WRITE, 'gateway.rpc.write'] -]); - -export enum SNMPMethods { - SET = 'set', - MULTISET = 'multiset', - GET = 'get', - BULKWALK = 'bulkwalk', - TABLE = 'table', - MULTIGET = 'multiget', - GETNEXT = 'getnext', - BULKGET = 'bulkget', - WALKS = 'walk' -} - -export const SNMPMethodsTranslations = new Map([ - [SNMPMethods.SET, 'gateway.rpc.set'], - [SNMPMethods.MULTISET, 'gateway.rpc.multiset'], - [SNMPMethods.GET, 'gateway.rpc.get'], - [SNMPMethods.BULKWALK, 'gateway.rpc.bulk-walk'], - [SNMPMethods.TABLE, 'gateway.rpc.table'], - [SNMPMethods.MULTIGET, 'gateway.rpc.multi-get'], - [SNMPMethods.GETNEXT, 'gateway.rpc.get-next'], - [SNMPMethods.BULKGET, 'gateway.rpc.bulk-get'], - [SNMPMethods.WALKS, 'gateway.rpc.walk'] -]); - -export enum HTTPMethods { - CONNECT = 'CONNECT', - DELETE = 'DELETE', - GET = 'GET', - HEAD = 'HEAD', - OPTIONS = 'OPTIONS', - PATCH = 'PATCH', - POST = 'POST', - PUT = 'PUT', - TRACE = 'TRACE' - -} - -export enum SocketEncodings { - UTF_8 = 'utf-8' -} - -export interface RPCTemplate { - name?: string; - config: RPCTemplateConfig; -} - -export interface RPCTemplateConfig { - [key: string]: any; -} - -export interface RPCTemplateConfigMQTT { - methodFilter: string; - requestTopicExpression: string; - responseTopicExpression?: string; - responseTimeout?: number; - valueExpression: string; - withResponse: boolean; -} - -export interface RPCTemplateConfigModbus { - tag: string; - type: ModbusDataType; - functionCode?: number; - objectsCount: number; - address: number; - value?: string; -} - -export interface RPCTemplateConfigOPC { - method: string; - arguments: RpcArgument[]; -} - -export interface OPCTypeValue { - type: MappingValueType; - boolean?: boolean; - double?: number; - integer?: number; - string?: string; -} - -export interface SaveRPCTemplateData { - config: RPCTemplateConfig; - templates: Array; -} - -export interface LogLink { - name: string; - key: string; - filterFn?: (arg: any) => boolean; -} - -export interface GatewayLogData { - ts: number; - key: string; - message: string; - status: GatewayStatus; -} - -export interface AddConnectorConfigData { - dataSourceData: Array; - gatewayVersion: string; -} - -export interface CreatedConnectorConfigData { - type: ConnectorType; - name: string; - logLevel: GatewayLogLevel; - useDefaults: boolean; - sendDataOnlyOnChange: boolean; - configurationJson?: {[key: string]: any}; -} - -export interface MappingDataKey { - key: string; - value: any; - type: MappingValueType; -} - -export interface RpcMethodsMapping { - method: string; - arguments: Array; -} - -export interface MappingInfo { - mappingType: MappingType; - value: {[key: string]: any}; - buttonTitle: string; -} - -export interface ModbusSlaveInfo { - value: Slave; - buttonTitle: string; - hideNewFields: boolean; -} - -export enum ConfigurationModes { - BASIC = 'basic', - ADVANCED = 'advanced' -} - -export enum SecurityType { - ANONYMOUS = 'anonymous', - BASIC = 'basic', - CERTIFICATES = 'certificates' -} - -export enum ReportStrategyType { - OnChange = 'ON_CHANGE', - OnReportPeriod = 'ON_REPORT_PERIOD', - OnChangeOrReportPeriod = 'ON_CHANGE_OR_REPORT_PERIOD' -} - -export enum ReportStrategyDefaultValue { - Connector = 60000, - Device = 30000, - Key = 15000 -} - -export const ReportStrategyTypeTranslationsMap = new Map( - [ - [ReportStrategyType.OnChange, 'gateway.report-strategy.on-change'], - [ReportStrategyType.OnReportPeriod, 'gateway.report-strategy.on-report-period'], - [ReportStrategyType.OnChangeOrReportPeriod, 'gateway.report-strategy.on-change-or-report-period'] - ] -); - -export enum ModeType { - NONE = 'None', - SIGN = 'Sign', - SIGNANDENCRYPT = 'SignAndEncrypt' -} - -export const SecurityTypeTranslationsMap = new Map( - [ - [SecurityType.ANONYMOUS, 'gateway.broker.security-types.anonymous'], - [SecurityType.BASIC, 'gateway.broker.security-types.basic'], - [SecurityType.CERTIFICATES, 'gateway.broker.security-types.certificates'] - ] -); - -export enum RestSecurityType { - ANONYMOUS = 'anonymous', - BASIC = 'basic', -} - -export const RestSecurityTypeTranslationsMap = new Map( - [ - [RestSecurityType.ANONYMOUS, 'gateway.broker.security-types.anonymous'], - [RestSecurityType.BASIC, 'gateway.broker.security-types.basic'], - ] -); - -export const MqttVersions = [ - { name: 3.1, value: 3 }, - { name: 3.11, value: 4 }, - { name: 5, value: 5 } -]; - -export enum MappingType { - DATA = 'data', - REQUESTS = 'requests', - OPCUA = 'OPCua' -} - -export const MappingTypeTranslationsMap = new Map( - [ - [MappingType.DATA, 'gateway.data-mapping'], - [MappingType.REQUESTS, 'gateway.requests-mapping'], - [MappingType.OPCUA, 'gateway.data-mapping'] - ] -); - -export const MappingHintTranslationsMap = new Map( - [ - [MappingType.DATA, 'gateway.data-mapping-hint'], - [MappingType.OPCUA, 'gateway.opcua-data-mapping-hint'], - [MappingType.REQUESTS, 'gateway.requests-mapping-hint'] - ] -); - -export const HelpLinkByMappingTypeMap = new Map( - [ - [MappingType.DATA, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#section-mapping'], - [MappingType.OPCUA, helpBaseUrl + '/docs/iot-gateway/config/opc-ua/#section-mapping'], - [MappingType.REQUESTS, helpBaseUrl + '/docs/iot-gateway/config/mqtt/#requests-mapping'] - ] -); - -export const QualityTypes = [0, 1 ,2]; - -export const QualityTypeTranslationsMap = new Map( - [ - [0, 'gateway.qos.at-most-once'], - [1, 'gateway.qos.at-least-once'], - [2, 'gateway.qos.exactly-once'] - ] -); - -export enum ConvertorType { - JSON = 'json', - BYTES = 'bytes', - CUSTOM = 'custom' -} - -export const ConvertorTypeTranslationsMap = new Map( - [ - [ConvertorType.JSON, 'gateway.JSON'], - [ConvertorType.BYTES, 'gateway.bytes'], - [ConvertorType.CUSTOM, 'gateway.custom'] - ] -); - -export enum SourceType { - MSG = 'message', - TOPIC = 'topic', - CONST = 'constant' -} - -export enum OPCUaSourceType { - PATH = 'path', - IDENTIFIER = 'identifier', - CONST = 'constant' -} - -export enum DeviceInfoType { - FULL = 'full', - PARTIAL = 'partial' -} - -export const SourceTypeTranslationsMap = new Map( - [ - [SourceType.MSG, 'gateway.source-type.msg'], - [SourceType.TOPIC, 'gateway.source-type.topic'], - [SourceType.CONST, 'gateway.source-type.const'], - [OPCUaSourceType.PATH, 'gateway.source-type.path'], - [OPCUaSourceType.IDENTIFIER, 'gateway.source-type.identifier'], - [OPCUaSourceType.CONST, 'gateway.source-type.const'] - ] -); - -export interface RequestMappingValue { - requestType: RequestType; - requestValue: RequestMappingData; -} - -export interface RequestMappingFormValue { - requestType: RequestType; - requestValue: Record; -} - -export type RequestMappingData = ConnectRequest | DisconnectRequest | AttributeRequest | AttributeUpdate | ServerSideRpc; - -export type LegacyRequestMappingData = - LegacyConnectRequest - | LegacyDisconnectRequest - | LegacyAttributeRequest - | LegacyAttributeUpdate - | LegacyServerSideRpc; - -export interface ConnectRequest { - topicFilter: string; - deviceInfo: ConnectorDeviceInfo; -} - -export interface DisconnectRequest { - topicFilter: string; - deviceInfo: ConnectorDeviceInfo; -} - -export interface AttributeRequest { - retain: boolean; - topicFilter: string; - deviceInfo: ConnectorDeviceInfo; - attributeNameExpressionSource: SourceType; - attributeNameExpression: string; - topicExpression: string; - valueExpression: string; -} - -export interface AttributeUpdate { - retain: boolean; - deviceNameFilter: string; - attributeFilter: string; - topicExpression: string; - valueExpression: string; -} - -export interface ServerSideRpc { - type: ServerSideRpcType; - deviceNameFilter: string; - methodFilter: string; - requestTopicExpression: string; - responseTopicExpression?: string; - responseTopicQoS?: number; - responseTimeout?: number; - valueExpression: string; -} - -export enum ServerSideRpcType { - WithResponse = 'twoWay', - WithoutResponse = 'oneWay' -} - -export interface LegacyConnectRequest { - topicFilter: string; - deviceNameJsonExpression?: string; - deviceNameTopicExpression?: string; -} - -interface LegacyDisconnectRequest { - topicFilter: string; - deviceNameJsonExpression?: string; - deviceNameTopicExpression?: string; -} - -interface LegacyAttributeRequest { - retain: boolean; - topicFilter: string; - deviceNameJsonExpression: string; - attributeNameJsonExpression: string; - topicExpression: string; - valueExpression: string; -} - -interface LegacyAttributeUpdate { - retain: boolean; - deviceNameFilter: string; - attributeFilter: string; - topicExpression: string; - valueExpression: string; -} - -interface LegacyServerSideRpc { - deviceNameFilter: string; - methodFilter: string; - requestTopicExpression: string; - responseTopicExpression?: string; - responseTimeout?: number; - valueExpression: string; -} - -export enum RequestType { - CONNECT_REQUEST = 'connectRequests', - DISCONNECT_REQUEST = 'disconnectRequests', - ATTRIBUTE_REQUEST = 'attributeRequests', - ATTRIBUTE_UPDATE = 'attributeUpdates', - SERVER_SIDE_RPC = 'serverSideRpc' -} - -export const RequestTypesTranslationsMap = new Map( - [ - [RequestType.CONNECT_REQUEST, 'gateway.request.connect-request'], - [RequestType.DISCONNECT_REQUEST, 'gateway.request.disconnect-request'], - [RequestType.ATTRIBUTE_REQUEST, 'gateway.request.attribute-request'], - [RequestType.ATTRIBUTE_UPDATE, 'gateway.request.attribute-update'], - [RequestType.SERVER_SIDE_RPC, 'gateway.request.rpc-connection'], - ] -); - -export enum MappingKeysType { - ATTRIBUTES = 'attributes', - TIMESERIES = 'timeseries', - CUSTOM = 'extensionConfig', - RPC_METHODS = 'rpc_methods', - ATTRIBUTES_UPDATES = 'attributes_updates' -} - -export const MappingKeysPanelTitleTranslationsMap = new Map( - [ - [MappingKeysType.ATTRIBUTES, 'gateway.attributes'], - [MappingKeysType.TIMESERIES, 'gateway.timeseries'], - [MappingKeysType.CUSTOM, 'gateway.keys'], - [MappingKeysType.ATTRIBUTES_UPDATES, 'gateway.attribute-updates'], - [MappingKeysType.RPC_METHODS, 'gateway.rpc-methods'] - ] -); - -export const MappingKeysAddKeyTranslationsMap = new Map( - [ - [MappingKeysType.ATTRIBUTES, 'gateway.add-attribute'], - [MappingKeysType.TIMESERIES, 'gateway.add-timeseries'], - [MappingKeysType.CUSTOM, 'gateway.add-key'], - [MappingKeysType.ATTRIBUTES_UPDATES, 'gateway.add-attribute-update'], - [MappingKeysType.RPC_METHODS, 'gateway.add-rpc-method'] - ] -); - -export const MappingKeysDeleteKeyTranslationsMap = new Map( - [ - [MappingKeysType.ATTRIBUTES, 'gateway.delete-attribute'], - [MappingKeysType.TIMESERIES, 'gateway.delete-timeseries'], - [MappingKeysType.CUSTOM, 'gateway.delete-key'], - [MappingKeysType.ATTRIBUTES_UPDATES, 'gateway.delete-attribute-update'], - [MappingKeysType.RPC_METHODS, 'gateway.delete-rpc-method'] - ] -); - -export const MappingKeysNoKeysTextTranslationsMap = new Map( - [ - [MappingKeysType.ATTRIBUTES, 'gateway.no-attributes'], - [MappingKeysType.TIMESERIES, 'gateway.no-timeseries'], - [MappingKeysType.CUSTOM, 'gateway.no-keys'], - [MappingKeysType.ATTRIBUTES_UPDATES, 'gateway.no-attribute-updates'], - [MappingKeysType.RPC_METHODS, 'gateway.no-rpc-methods'] - ] -); - -export enum ServerSideRPCType { - ONE_WAY = 'oneWay', - TWO_WAY = 'twoWay' -} - -export enum MappingValueType { - STRING = 'string', - INTEGER = 'integer', - DOUBLE = 'double', - BOOLEAN = 'boolean' -} - -export enum ModifierType { - DIVIDER = 'divider', - MULTIPLIER = 'multiplier', -} - -export const ModifierTypesMap = new Map( - [ - [ - ModifierType.DIVIDER, - { - name: 'gateway.divider', - icon: 'mdi:division' - } - ], - [ - ModifierType.MULTIPLIER, - { - name: 'gateway.multiplier', - icon: 'mdi:multiplication' - } - ], - ] -); - -export const mappingValueTypesMap = new Map( - [ - [ - MappingValueType.STRING, - { - name: 'value.string', - icon: 'mdi:format-text' - } - ], - [ - MappingValueType.INTEGER, - { - name: 'value.integer', - icon: 'mdi:numeric' - } - ], - [ - MappingValueType.DOUBLE, - { - name: 'value.double', - icon: 'mdi:numeric' - } - ], - [ - MappingValueType.BOOLEAN, - { - name: 'value.boolean', - icon: 'mdi:checkbox-marked-outline' - } - ] - ] -); - -export const DataConversionTranslationsMap = new Map( - [ - [ConvertorType.JSON, 'gateway.JSON-hint'], - [ConvertorType.BYTES, 'gateway.bytes-hint'], - [ConvertorType.CUSTOM, 'gateway.custom-hint'] - ] -); - -export enum SecurityPolicy { - BASIC128 = 'Basic128Rsa15', - BASIC256 = 'Basic256', - BASIC256SHA = 'Basic256Sha256' -} - -export const SecurityPolicyTypes = [ - { value: SecurityPolicy.BASIC128, name: 'Basic128RSA15' }, - { value: SecurityPolicy.BASIC256, name: 'Basic256' }, - { value: SecurityPolicy.BASIC256SHA, name: 'Basic256SHA256' } -]; - -export enum ModbusProtocolType { - TCP = 'tcp', - UDP = 'udp', - Serial = 'serial', -} - -export const ModbusProtocolLabelsMap = new Map( - [ - [ModbusProtocolType.TCP, 'TCP'], - [ModbusProtocolType.UDP, 'UDP'], - [ModbusProtocolType.Serial, 'Serial'], - ] -); - -export enum ModbusMethodType { - SOCKET = 'socket', - RTU = 'rtu', -} - -export enum ModbusSerialMethodType { - RTU = 'rtu', - ASCII = 'ascii', -} - -export const ModbusMethodLabelsMap = new Map( - [ - [ModbusMethodType.SOCKET, 'Socket'], - [ModbusMethodType.RTU, 'RTU'], - [ModbusSerialMethodType.ASCII, 'ASCII'], - ] -); - -export const ModbusByteSizes = [5, 6, 7 ,8]; - -export enum ModbusParity { - Even = 'E', - Odd = 'O', - None = 'N' -} - -export const ModbusParityLabelsMap = new Map( - [ - [ModbusParity.Even, 'Even'], - [ModbusParity.Odd, 'Odd'], - [ModbusParity.None, 'None'], - ] -); - -export enum ModbusOrderType { - BIG = 'BIG', - LITTLE = 'LITTLE', -} - -export enum ModbusRegisterType { - HoldingRegisters = 'holding_registers', - CoilsInitializer = 'coils_initializer', - InputRegisters = 'input_registers', - DiscreteInputs = 'discrete_inputs' -} - -export const ModbusRegisterTranslationsMap = new Map( - [ - [ModbusRegisterType.HoldingRegisters, 'gateway.holding_registers'], - [ModbusRegisterType.CoilsInitializer, 'gateway.coils_initializer'], - [ModbusRegisterType.InputRegisters, 'gateway.input_registers'], - [ModbusRegisterType.DiscreteInputs, 'gateway.discrete_inputs'] - ] -); - -export enum ModbusDataType { - STRING = 'string', - BYTES = 'bytes', - BITS = 'bits', - INT8 = '8int', - UINT8 = '8uint', - FLOAT8 = '8float', - INT16 = '16int', - UINT16 = '16uint', - FLOAT16 = '16float', - INT32 = '32int', - UINT32 = '32uint', - FLOAT32 = '32float', - INT64 = '64int', - UINT64 = '64uint', - FLOAT64 = '64float' -} - -export const ModbusEditableDataTypes = [ModbusDataType.BYTES, ModbusDataType.BITS, ModbusDataType.STRING]; - -export enum ModbusObjectCountByDataType { - '8int' = 1, - '8uint' = 1, - '8float' = 1, - '16int' = 1, - '16uint' = 1, - '16float' = 1, - '32int' = 2, - '32uint' = 2, - '32float' = 2, - '64int' = 4, - '64uint' = 4, - '64float' = 4, -} - -export enum ModbusValueKey { - ATTRIBUTES = 'attributes', - TIMESERIES = 'timeseries', - ATTRIBUTES_UPDATES = 'attributeUpdates', - RPC_REQUESTS = 'rpc', -} - -export const ModbusKeysPanelTitleTranslationsMap = new Map( - [ - [ModbusValueKey.ATTRIBUTES, 'gateway.attributes'], - [ModbusValueKey.TIMESERIES, 'gateway.timeseries'], - [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.attribute-updates'], - [ModbusValueKey.RPC_REQUESTS, 'gateway.rpc-requests'] - ] -); - -export const ModbusKeysAddKeyTranslationsMap = new Map( - [ - [ModbusValueKey.ATTRIBUTES, 'gateway.add-attribute'], - [ModbusValueKey.TIMESERIES, 'gateway.add-timeseries'], - [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.add-attribute-update'], - [ModbusValueKey.RPC_REQUESTS, 'gateway.add-rpc-request'] - ] -); - -export const ModbusKeysDeleteKeyTranslationsMap = new Map( - [ - [ModbusValueKey.ATTRIBUTES, 'gateway.delete-attribute'], - [ModbusValueKey.TIMESERIES, 'gateway.delete-timeseries'], - [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.delete-attribute-update'], - [ModbusValueKey.RPC_REQUESTS, 'gateway.delete-rpc-request'] - ] -); - -export const ModbusKeysNoKeysTextTranslationsMap = new Map( - [ - [ModbusValueKey.ATTRIBUTES, 'gateway.no-attributes'], - [ModbusValueKey.TIMESERIES, 'gateway.no-timeseries'], - [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.no-attribute-updates'], - [ModbusValueKey.RPC_REQUESTS, 'gateway.no-rpc-requests'] - ] -); - -export interface ModbusMasterConfig { - slaves: Slave[]; -} - -export interface LegacySlaveConfig extends Omit { - sendDataOnlyOnChange: boolean; -} - -export interface SlaveConfig { - name: string; - host?: string; - port: string | number; - serialPort?: string; - type: ModbusProtocolType; - method: ModbusMethodType; - timeout: number; - byteOrder: ModbusOrderType; - wordOrder: ModbusOrderType; - retries: boolean; - retryOnEmpty: boolean; - retryOnInvalid: boolean; - pollPeriod: number; - unitId: number; - deviceName: string; - deviceType?: string; - reportStrategy: ReportStrategyConfig; - connectAttemptTimeMs: number; - connectAttemptCount: number; - waitAfterFailedAttemptsMs: number; - attributes: ModbusValue[]; - timeseries: ModbusValue[]; - attributeUpdates: ModbusValue[]; - rpc: ModbusValue[]; - security?: ModbusSecurity; - baudrate?: number; - stopbits?: number; - bytesize?: number; - parity?: ModbusParity; - strict?: boolean; -} - -export interface ModbusValue { - tag: string; - type: ModbusDataType; - functionCode?: number; - objectsCount: number; - address: number; - value?: string; - reportStrategy?: ReportStrategyConfig; - multiplier?: number; - divider?: number; -} - -export interface ModbusFormValue extends ModbusValue { - modifierType?: ModifierType; - modifierValue?: string; -} - -export interface ModbusSecurity { - certfile?: string; - keyfile?: string; - password?: string; - server_hostname?: string; - reqclicert?: boolean; -} - -export interface ModbusSlave { - host?: string; - type: ModbusProtocolType; - method: ModbusMethodType; - unitId: number; - serialPort?: string; - baudrate?: number; - deviceName: string; - deviceType: string; - pollPeriod: number; - sendDataToThingsBoard: boolean; - byteOrder: ModbusOrderType; - wordOrder: ModbusOrderType; - identity: ModbusIdentity; - values?: ModbusValuesState; - port: string | number; - security: ModbusSecurity; -} - -export interface ModbusLegacySlave extends Omit { - values?: ModbusLegacyRegisterValues; -} - -export type ModbusValuesState = ModbusRegisterValues | ModbusValues; - -export interface ModbusLegacyRegisterValues { - holding_registers: ModbusValues[]; - coils_initializer: ModbusValues[]; - input_registers: ModbusValues[]; - discrete_inputs: ModbusValues[]; -} - -export interface ModbusRegisterValues { - holding_registers: ModbusValues; - coils_initializer: ModbusValues; - input_registers: ModbusValues; - discrete_inputs: ModbusValues; -} - -export interface ModbusValues { - attributes: ModbusValue[]; - timeseries: ModbusValue[]; - attributeUpdates: ModbusValue[]; - rpc: ModbusValue[]; -} - -export interface ModbusIdentity { - vendorName?: string; - productCode?: string; - vendorUrl?: string; - productName?: string; - modelName?: string; -} - -export interface ReportStrategyConfig { - type: ReportStrategyType; - reportPeriod?: number; -} - -export const ModbusBaudrates = [4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600]; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts deleted file mode 100644 index 27b5285166..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts +++ /dev/null @@ -1,41 +0,0 @@ -/// -/// 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. -/// - -import { Pipe, PipeTransform } from '@angular/core'; -import { - MappingValueType, - OPCUaSourceType, - SourceType -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -@Pipe({ - name: 'getGatewayHelpLink', - standalone: true, -}) -export class GatewayHelpLinkPipe implements PipeTransform { - transform(field: string, sourceType: SourceType | OPCUaSourceType, sourceTypes?: Array ): string { - if (!sourceTypes || sourceTypes?.includes(OPCUaSourceType.PATH)) { - if (sourceType !== OPCUaSourceType.CONST) { - return `widget/lib/gateway/${field}-${sourceType}_fn`; - } else { - return; - } - } else if (field === 'attributes' || field === 'timeseries') { - return 'widget/lib/gateway/attributes_timeseries_expressions_fn'; - } - return 'widget/lib/gateway/expressions_fn'; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts deleted file mode 100644 index fcf5766c05..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts +++ /dev/null @@ -1,42 +0,0 @@ -/// -/// 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. -/// - -import { Pipe, PipeTransform } from '@angular/core'; -import { PortLimits } from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { AbstractControl } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; - -@Pipe({ - name: 'getGatewayPortTooltip', - standalone: true, -}) -export class GatewayPortTooltipPipe implements PipeTransform { - - constructor(private translate: TranslateService) {} - - transform(portControl: AbstractControl): string { - if (portControl.hasError('required')) { - return this.translate.instant('gateway.port-required'); - } - if (portControl.hasError('min') || portControl.hasError('max')) { - return this.translate.instant('gateway.port-limits-error', { - min: PortLimits.MIN, - max: PortLimits.MAX, - }); - } - return ''; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/latest-version-config.pipe.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/latest-version-config.pipe.ts deleted file mode 100644 index 56484dde35..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/latest-version-config.pipe.ts +++ /dev/null @@ -1,32 +0,0 @@ -/// -/// 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. -/// - -import { Pipe, PipeTransform } from '@angular/core'; -import { GatewayVersion } from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { - GatewayConnectorVersionMappingUtil -} from '@home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util'; - -@Pipe({ - name: 'isLatestVersionConfig', - standalone: true, -}) -export class LatestVersionConfigPipe implements PipeTransform { - transform(configVersion: number | string): boolean { - return GatewayConnectorVersionMappingUtil.parseVersion(configVersion) - >= GatewayConnectorVersionMappingUtil.parseVersion(GatewayVersion.Current); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe.ts deleted file mode 100644 index f565195b76..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe.ts +++ /dev/null @@ -1,28 +0,0 @@ -/// -/// 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. -/// - -import { Pipe, PipeTransform } from '@angular/core'; - -@Pipe({ - name: 'getRpcTemplateArrayView', - standalone: true, -}) -export class RpcTemplateArrayViewPipe implements PipeTransform { - - transform(values: {value: string | boolean | number}[]): string { - return values.map(({value}) => value.toString()).join(', '); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util.ts deleted file mode 100644 index fe507c1c46..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/gateway-connector-version-mapping.util.ts +++ /dev/null @@ -1,51 +0,0 @@ -/// -/// 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. -/// - -import { - ConnectorType, - GatewayConnector, - ModbusBasicConfig, - MQTTBasicConfig, - OPCBasicConfig, -} from '@home/components/widget/lib/gateway/gateway-widget.models'; -import { MqttVersionProcessor } from '@home/components/widget/lib/gateway/abstract/mqtt-version-processor.abstract'; -import { OpcVersionProcessor } from '@home/components/widget/lib/gateway/abstract/opc-version-processor.abstract'; -import { ModbusVersionProcessor } from '@home/components/widget/lib/gateway/abstract/modbus-version-processor.abstract'; -import { isNumber, isString } from '@core/utils'; - -export abstract class GatewayConnectorVersionMappingUtil { - - static getConfig(connector: GatewayConnector, gatewayVersion: string): GatewayConnector { - switch(connector.type) { - case ConnectorType.MQTT: - return new MqttVersionProcessor(gatewayVersion, connector as GatewayConnector).getProcessedByVersion(); - case ConnectorType.OPCUA: - return new OpcVersionProcessor(gatewayVersion, connector as GatewayConnector).getProcessedByVersion(); - case ConnectorType.MODBUS: - return new ModbusVersionProcessor(gatewayVersion, connector as GatewayConnector).getProcessedByVersion(); - default: - return connector; - } - } - - static parseVersion(version: string | number): number { - if (isNumber(version)) { - return version as number; - } - - return isString(version) ? parseFloat((version as string).replace(/\./g, '').slice(0, 3)) / 100 : 0; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/modbus-version-mapping.util.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/modbus-version-mapping.util.ts deleted file mode 100644 index eb1ba9033f..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/modbus-version-mapping.util.ts +++ /dev/null @@ -1,106 +0,0 @@ -/// -/// 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. -/// - -import { - LegacySlaveConfig, - ModbusDataType, - ModbusLegacyRegisterValues, - ModbusLegacySlave, - ModbusMasterConfig, - ModbusRegisterValues, - ModbusSlave, - ModbusValue, - ModbusValues, - ReportStrategyType, - SlaveConfig -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -export class ModbusVersionMappingUtil { - - static mapMasterToUpgradedVersion(master: ModbusMasterConfig): ModbusMasterConfig { - return { - slaves: master.slaves.map((slave: LegacySlaveConfig) => { - const { sendDataOnlyOnChange, ...restSlave } = slave; - return { - ...restSlave, - deviceType: slave.deviceType ?? 'default', - reportStrategy: sendDataOnlyOnChange - ? { type: ReportStrategyType.OnChange } - : { type: ReportStrategyType.OnReportPeriod, reportPeriod: slave.pollPeriod } - }; - }) - }; - } - - static mapMasterToDowngradedVersion(master: ModbusMasterConfig): ModbusMasterConfig { - return { - slaves: master.slaves.map((slave: SlaveConfig) => { - const { reportStrategy, ...restSlave } = slave; - return { - ...restSlave, - sendDataOnlyOnChange: reportStrategy?.type !== ReportStrategyType.OnReportPeriod - }; - }) - }; - } - - static mapSlaveToDowngradedVersion(slave: ModbusSlave): ModbusLegacySlave { - if (!slave?.values) { - return slave as Omit; - } - const values = Object.keys(slave.values).reduce((acc, valueKey) => { - acc = { - ...acc, - [valueKey]: [ - slave.values[valueKey] - ] - }; - return acc; - }, {} as ModbusLegacyRegisterValues); - return { - ...slave, - values - }; - } - - static mapSlaveToUpgradedVersion(slave: ModbusLegacySlave): ModbusSlave { - if (!slave?.values) { - return slave as Omit; - } - const values = Object.keys(slave.values).reduce((acc, valueKey) => { - acc = { - ...acc, - [valueKey]: this.mapValuesToUpgradedVersion(slave.values[valueKey][0]) - }; - return acc; - }, {} as ModbusRegisterValues); - return { - ...slave, - values - }; - } - - private static mapValuesToUpgradedVersion(registerValues: ModbusValues): ModbusValues { - return Object.keys(registerValues).reduce((acc, valueKey) => { - acc = { - ...acc, - [valueKey]: registerValues[valueKey].map((value: ModbusValue) => - ({ ...value, type: (value.type as string) === 'int' ? ModbusDataType.INT16 : value.type })) - }; - return acc; - }, {} as ModbusValues); - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/mqtt-version-mapping.util.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/mqtt-version-mapping.util.ts deleted file mode 100644 index 23cc03dc4e..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/mqtt-version-mapping.util.ts +++ /dev/null @@ -1,217 +0,0 @@ -/// -/// 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. -/// - -import { deleteNullProperties } from '@core/utils'; -import { - AttributeRequest, - ConnectorDeviceInfo, - Converter, - ConverterConnectorMapping, - ConvertorType, - LegacyConverter, - LegacyConverterConnectorMapping, - LegacyRequestMappingData, - RequestMappingData, - RequestType, - ServerSideRpc, - ServerSideRpcType, - SourceType -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -export class MqttVersionMappingUtil { - - static readonly mqttRequestTypeKeys = Object.values(RequestType); - static readonly mqttRequestMappingOldFields = - ['attributeNameJsonExpression', 'deviceNameJsonExpression', 'deviceNameTopicExpression', 'extension-config']; - static readonly mqttRequestMappingNewFields = - ['attributeNameExpressionSource', 'responseTopicQoS', 'extensionConfig']; - - static mapMappingToUpgradedVersion( - mapping: LegacyConverterConnectorMapping[] - ): ConverterConnectorMapping[] { - return mapping?.map(({ converter, topicFilter, subscriptionQos = 1 }) => { - const deviceInfo = converter.deviceInfo ?? this.extractConverterDeviceInfo(converter); - - const newConverter = { - ...converter, - deviceInfo, - extensionConfig: converter.extensionConfig || converter['extension-config'] || null - }; - - this.cleanUpOldFields(newConverter); - - return { converter: newConverter, topicFilter, subscriptionQos }; - }) as ConverterConnectorMapping[]; - } - - static mapRequestsToUpgradedVersion( - requestMapping: Record - ): Record { - return this.mqttRequestTypeKeys.reduce((acc, key: RequestType) => { - if (!requestMapping[key]) { - return acc; - } - - acc[key] = requestMapping[key].map(value => { - const newValue = this.mapRequestToUpgradedVersion(value as LegacyRequestMappingData, key); - - this.cleanUpOldFields(newValue as {}); - - return newValue; - }); - - return acc; - }, {}) as Record; - } - - static mapRequestsToDowngradedVersion( - requestsMapping: Record - ): Record { - return this.mqttRequestTypeKeys.reduce((acc, key) => { - if (!requestsMapping[key]) { - return acc; - } - - acc[key] = requestsMapping[key].map((value: RequestMappingData) => { - if (key === RequestType.SERVER_SIDE_RPC) { - delete (value as ServerSideRpc).type; - } - - const { attributeNameExpression, deviceInfo, ...rest } = value as AttributeRequest; - - const newValue = { - ...rest, - attributeNameJsonExpression: attributeNameExpression || null, - deviceNameJsonExpression: deviceInfo?.deviceNameExpressionSource !== SourceType.TOPIC ? deviceInfo?.deviceNameExpression : null, - deviceNameTopicExpression: deviceInfo?.deviceNameExpressionSource === SourceType.TOPIC ? deviceInfo?.deviceNameExpression : null, - }; - - this.cleanUpNewFields(newValue); - - return newValue; - }); - - return acc; - }, {}) as Record; - } - - static mapMappingToDowngradedVersion( - mapping: ConverterConnectorMapping[] - ): LegacyConverterConnectorMapping[] { - return mapping?.map((converterMapping: ConverterConnectorMapping) => { - const converter = this.mapConverterToDowngradedVersion(converterMapping.converter); - - this.cleanUpNewFields(converter as {}); - - return { converter, topicFilter: converterMapping.topicFilter }; - }); - } - - private static mapConverterToDowngradedVersion(converter: Converter): LegacyConverter { - const { deviceInfo, ...restConverter } = converter; - - return converter.type !== ConvertorType.BYTES ? { - ...restConverter, - deviceNameJsonExpression: deviceInfo?.deviceNameExpressionSource === SourceType.MSG ? deviceInfo.deviceNameExpression : null, - deviceTypeJsonExpression: - deviceInfo?.deviceProfileExpressionSource === SourceType.MSG ? deviceInfo.deviceProfileExpression : null, - deviceNameTopicExpression: - deviceInfo?.deviceNameExpressionSource !== SourceType.MSG - ? deviceInfo?.deviceNameExpression - : null, - deviceTypeTopicExpression: deviceInfo?.deviceProfileExpressionSource !== SourceType.MSG - ? deviceInfo?.deviceProfileExpression - : null, - } : { - ...restConverter, - deviceNameExpression: deviceInfo.deviceNameExpression, - deviceTypeExpression: deviceInfo.deviceProfileExpression, - ['extension-config']: converter.extensionConfig, - }; - } - - private static cleanUpOldFields(obj: Record): void { - this.mqttRequestMappingOldFields.forEach(field => delete obj[field]); - deleteNullProperties(obj); - } - - private static cleanUpNewFields(obj: Record): void { - this.mqttRequestMappingNewFields.forEach(field => delete obj[field]); - deleteNullProperties(obj); - } - - private static getTypeSourceByValue(value: string): SourceType { - if (value.includes('${')) { - return SourceType.MSG; - } - if (value.includes(`/`)) { - return SourceType.TOPIC; - } - return SourceType.CONST; - } - - private static extractConverterDeviceInfo(converter: LegacyConverter): ConnectorDeviceInfo { - const deviceNameExpression = converter.deviceNameExpression - || converter.deviceNameJsonExpression - || converter.deviceNameTopicExpression - || null; - const deviceNameExpressionSource = converter.deviceNameExpressionSource - ? converter.deviceNameExpressionSource as SourceType - : deviceNameExpression ? this.getTypeSourceByValue(deviceNameExpression) : null; - const deviceProfileExpression = converter.deviceProfileExpression - || converter.deviceTypeTopicExpression - || converter.deviceTypeJsonExpression - || 'default'; - const deviceProfileExpressionSource = converter.deviceProfileExpressionSource - ? converter.deviceProfileExpressionSource as SourceType - : deviceProfileExpression ? this.getTypeSourceByValue(deviceProfileExpression) : null; - - return deviceNameExpression || deviceProfileExpression ? { - deviceNameExpression, - deviceNameExpressionSource, - deviceProfileExpression, - deviceProfileExpressionSource - } : null; - } - - private static mapRequestToUpgradedVersion(value, key: RequestType): RequestMappingData { - const deviceNameExpression = value.deviceNameJsonExpression || value.deviceNameTopicExpression || null; - const deviceProfileExpression = value.deviceTypeTopicExpression || value.deviceTypeJsonExpression || 'default'; - const deviceProfileExpressionSource = deviceProfileExpression ? this.getTypeSourceByValue(deviceProfileExpression) : null; - const attributeNameExpression = value.attributeNameExpressionSource || value.attributeNameJsonExpression || null; - const responseTopicQoS = key === RequestType.SERVER_SIDE_RPC ? 1 : null; - const type = key === RequestType.SERVER_SIDE_RPC - ? (value as ServerSideRpc).responseTopicExpression - ? ServerSideRpcType.WithResponse - : ServerSideRpcType.WithoutResponse - : null; - - return { - ...value, - attributeNameExpression, - attributeNameExpressionSource: attributeNameExpression ? this.getTypeSourceByValue(attributeNameExpression) : null, - deviceInfo: value.deviceInfo ? value.deviceInfo : deviceNameExpression ? { - deviceNameExpression, - deviceNameExpressionSource: this.getTypeSourceByValue(deviceNameExpression), - deviceProfileExpression, - deviceProfileExpressionSource - } : null, - responseTopicQoS, - type - }; - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/opc-version-mapping.util.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/opc-version-mapping.util.ts deleted file mode 100644 index f589dfea97..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/utils/opc-version-mapping.util.ts +++ /dev/null @@ -1,143 +0,0 @@ -/// -/// 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. -/// - -import { - Attribute, - AttributesUpdate, - DeviceConnectorMapping, - LegacyAttribute, - LegacyDeviceAttributeUpdate, - LegacyDeviceConnectorMapping, - LegacyRpcMethod, - LegacyServerConfig, - LegacyTimeseries, - OPCBasicConfig_v3_5_2, - OPCUaSourceType, - RpcArgument, - RpcMethod, - ServerConfig, - Timeseries -} from '@home/components/widget/lib/gateway/gateway-widget.models'; - -export class OpcVersionMappingUtil { - - static mapServerToUpgradedVersion(server: LegacyServerConfig): ServerConfig { - const { mapping, disableSubscriptions, pollPeriodInMillis, ...restServer } = server; - return { - ...restServer, - pollPeriodInMillis: pollPeriodInMillis ?? 5000, - enableSubscriptions: !disableSubscriptions, - }; - } - - static mapServerToDowngradedVersion(config: OPCBasicConfig_v3_5_2): LegacyServerConfig { - const { mapping, server } = config; - const { enableSubscriptions, ...restServer } = server ?? {} as ServerConfig; - return { - ...restServer, - mapping: mapping ? this.mapMappingToDowngradedVersion(mapping) : [], - disableSubscriptions: !enableSubscriptions, - }; - } - - static mapMappingToUpgradedVersion(mapping: LegacyDeviceConnectorMapping[]): DeviceConnectorMapping[] { - return mapping.map((legacyMapping: LegacyDeviceConnectorMapping) => ({ - ...legacyMapping, - deviceNodeSource: this.getDeviceNodeSourceByValue(legacyMapping.deviceNodePattern), - deviceInfo: { - deviceNameExpression: legacyMapping.deviceNamePattern, - deviceNameExpressionSource: this.getTypeSourceByValue(legacyMapping.deviceNamePattern), - deviceProfileExpression: legacyMapping.deviceTypePattern ?? 'default', - deviceProfileExpressionSource: this.getTypeSourceByValue(legacyMapping.deviceTypePattern ?? 'default'), - }, - attributes: legacyMapping.attributes.map((attribute: LegacyAttribute) => ({ - key: attribute.key, - type: this.getTypeSourceByValue(attribute.path), - value: attribute.path, - })), - attributes_updates: legacyMapping.attributes_updates.map((attributeUpdate: LegacyDeviceAttributeUpdate) => ({ - key: attributeUpdate.attributeOnThingsBoard, - type: this.getTypeSourceByValue(attributeUpdate.attributeOnDevice), - value: attributeUpdate.attributeOnDevice, - })), - timeseries: legacyMapping.timeseries.map((timeseries: LegacyTimeseries) => ({ - key: timeseries.key, - type: this.getTypeSourceByValue(timeseries.path), - value: timeseries.path, - })), - rpc_methods: legacyMapping.rpc_methods.map((rpcMethod: LegacyRpcMethod) => ({ - method: rpcMethod.method, - arguments: rpcMethod.arguments.map(arg => ({ - value: arg, - type: this.getArgumentType(arg), - } as RpcArgument)) - })) - })); - } - - static mapMappingToDowngradedVersion(mapping: DeviceConnectorMapping[]): LegacyDeviceConnectorMapping[] { - return mapping.map((upgradedMapping: DeviceConnectorMapping) => ({ - ...upgradedMapping, - deviceNamePattern: upgradedMapping.deviceInfo.deviceNameExpression, - deviceTypePattern: upgradedMapping.deviceInfo.deviceProfileExpression, - attributes: upgradedMapping.attributes.map((attribute: Attribute) => ({ - key: attribute.key, - path: attribute.value, - })), - attributes_updates: upgradedMapping.attributes_updates.map((attributeUpdate: AttributesUpdate) => ({ - attributeOnThingsBoard: attributeUpdate.key, - attributeOnDevice: attributeUpdate.value, - })), - timeseries: upgradedMapping.timeseries.map((timeseries: Timeseries) => ({ - key: timeseries.key, - path: timeseries.value, - })), - rpc_methods: upgradedMapping.rpc_methods.map((rpcMethod: RpcMethod) => ({ - method: rpcMethod.method, - arguments: rpcMethod.arguments.map((arg: RpcArgument) => arg.value) - })) - })); - } - - private static getTypeSourceByValue(value: string): OPCUaSourceType { - if (value.includes('${')) { - return OPCUaSourceType.IDENTIFIER; - } - if (value.includes(`/`) || value.includes('\\')) { - return OPCUaSourceType.PATH; - } - return OPCUaSourceType.CONST; - } - - private static getDeviceNodeSourceByValue(value: string): OPCUaSourceType { - if (value.includes('${')) { - return OPCUaSourceType.IDENTIFIER; - } else { - return OPCUaSourceType.PATH; - } - } - - private static getArgumentType(arg: unknown): string { - switch (typeof arg) { - case 'boolean': - return 'boolean'; - case 'number': - return Number.isInteger(arg) ? 'integer' : 'float'; - default: - return 'string'; - } - } -} diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 1b044e787d..349d891669 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -33,7 +33,6 @@ import { import { MultipleInputWidgetComponent } from '@home/components/widget/lib/multiple-input-widget.component'; import { TripAnimationComponent } from '@home/components/widget/lib/trip-animation/trip-animation.component'; import { PhotoCameraInputWidgetComponent } from '@home/components/widget/lib/photo-camera-input.component'; -import { GatewayFormComponent } from '@home/components/widget/lib/gateway/gateway-form.component'; import { NavigationCardsWidgetComponent } from '@home/components/widget/lib/navigation-cards-widget.component'; import { NavigationCardWidgetComponent } from '@home/components/widget/lib/navigation-card-widget.component'; import { EdgesOverviewWidgetComponent } from '@home/components/widget/lib/edges-overview-widget.component'; @@ -45,21 +44,6 @@ import { HomePageWidgetsModule } from '@home/components/widget/lib/home-page/hom import { WIDGET_COMPONENTS_MODULE_TOKEN } from '@home/components/tokens'; import { FlotWidgetComponent } from '@home/components/widget/lib/flot-widget.component'; import { LegendComponent } from '@home/components/widget/lib/legend.component'; -import { GatewayConnectorComponent } from '@home/components/widget/lib/gateway/gateway-connectors.component'; -import { GatewayLogsComponent } from '@home/components/widget/lib/gateway/gateway-logs.component'; -import { GatewayStatisticsComponent } from '@home/components/widget/lib/gateway/gateway-statistics.component'; -import { GatewayServiceRPCComponent } from '@home/components/widget/lib/gateway/gateway-service-rpc.component'; -import { - GatewayServiceRPCConnectorComponent -} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector.component'; -import { - GatewayServiceRPCConnectorTemplatesComponent -} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component'; -import { DeviceGatewayCommandComponent } from '@home/components/widget/lib/gateway/device-gateway-command.component'; -import { GatewayConfigurationComponent } from '@home/components/widget/lib/gateway/configuration/gateway-configuration.component'; -import { - GatewayRemoteConfigurationDialogComponent -} from '@home/components/widget/lib/gateway/gateway-remote-configuration-dialog'; import { ValueCardWidgetComponent } from '@home/components/widget/lib/cards/value-card-widget.component'; import { AggregatedValueCardWidgetComponent @@ -78,9 +62,6 @@ import { RangeChartWidgetComponent } from '@home/components/widget/lib/chart/ran import { BarChartWithLabelsWidgetComponent } from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.component'; -import { - GatewayServiceRPCConnectorTemplateDialogComponent -} from '@home/components/widget/lib/gateway/gateway-service-rpc-connector-template-dialog'; import { SingleSwitchWidgetComponent } from '@home/components/widget/lib/rpc/single-switch-widget.component'; import { ActionButtonWidgetComponent } from '@home/components/widget/lib/button/action-button-widget.component'; import { CommandButtonWidgetComponent } from '@home/components/widget/lib/button/command-button-widget.component'; @@ -88,8 +69,6 @@ import { PowerButtonWidgetComponent } from '@home/components/widget/lib/rpc/powe import { SliderWidgetComponent } from '@home/components/widget/lib/rpc/slider-widget.component'; import { ToggleButtonWidgetComponent } from '@home/components/widget/lib/button/toggle-button-widget.component'; import { TimeSeriesChartWidgetComponent } from '@home/components/widget/lib/chart/time-series-chart-widget.component'; -import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component'; -import { MappingDialogComponent } from '@home/components/widget/lib/gateway/dialog/mapping-dialog.component'; import { StatusWidgetComponent } from '@home/components/widget/lib/indicator/status-widget.component'; import { LatestChartComponent } from '@home/components/widget/lib/chart/latest-chart.component'; import { PieChartWidgetComponent } from '@home/components/widget/lib/chart/pie-chart-widget.component'; @@ -100,77 +79,14 @@ import { MobileAppQrcodeWidgetComponent } from '@home/components/widget/lib/mobi import { KeyValueIsNotEmptyPipe } from '@shared/pipe/key-value-not-empty.pipe'; import { LabelCardWidgetComponent } from '@home/components/widget/lib/cards/label-card-widget.component'; import { LabelValueCardWidgetComponent } from '@home/components/widget/lib/cards/label-value-card-widget.component'; -import { - RestConnectorSecurityComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component'; import { UnreadNotificationWidgetComponent } from '@home/components/widget/lib/cards/unread-notification-widget.component'; import { NotificationTypeFilterPanelComponent } from '@home/components/widget/lib/cards/notification-type-filter-panel.component'; -import { GatewayHelpLinkPipe } from '@home/components/widget/lib/gateway/pipes/gateway-help-link.pipe'; import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; -import { - BrokerConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/broker-config-control/broker-config-control.component'; -import { - WorkersConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/workers-config-control/workers-config-control.component'; -import { - OpcServerConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-server-config/opc-server-config.component'; -import { - MqttBasicConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-basic-config.component'; -import { - MappingTableComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; -import { - OpcUaBasicConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-basic-config.component'; -import { - ModbusBasicConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component'; -import { - DeviceInfoTableComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component'; -import { - MappingDataKeysPanelComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component'; -import { - TypeValuePanelComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component'; import { ScadaSymbolWidgetComponent } from '@home/components/widget/lib/scada/scada-symbol-widget.component'; -import { - MqttLegacyBasicConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt/basic-config/mqtt-legacy-basic-config.component'; -import { - GatewayBasicConfigurationComponent -} from '@home/components/widget/lib/gateway/configuration/basic/gateway-basic-configuration.component'; -import { - GatewayAdvancedConfigurationComponent -} from '@home/components/widget/lib/gateway/configuration/advanced/gateway-advanced-configuration.component'; -import { - OpcUaLegacyBasicConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/opc/opc-ua-basic-config/opc-ua-legacy-basic-config.component'; -import { - ModbusLegacyBasicConfigComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-legacy-basic-config.component'; -import { - MqttRpcParametersComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/mqtt-rpc-parameters/mqtt-rpc-parameters.component'; -import { - OpcRpcParametersComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/opc-rpc-parameters/opc-rpc-parameters.component'; -import { - ModbusRpcParametersComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/rpc-parameters/modbus-rpc-parameters/modbus-rpc-parameters.component'; -import { RpcTemplateArrayViewPipe } from '@home/components/widget/lib/gateway/pipes/rpc-template-array-view.pipe'; -import { - ReportStrategyComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/report-strategy/report-strategy.component'; -import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pipes/latest-version-config.pipe'; @NgModule({ declarations: [ @@ -186,7 +102,6 @@ import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pip MultipleInputWidgetComponent, TripAnimationComponent, PhotoCameraInputWidgetComponent, - GatewayFormComponent, NavigationCardsWidgetComponent, NavigationCardWidgetComponent, QrCodeWidgetComponent, @@ -195,21 +110,6 @@ import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pip SelectEntityDialogComponent, LegendComponent, FlotWidgetComponent, - GatewayConnectorComponent, - AddConnectorDialogComponent, - MappingDialogComponent, - DeviceInfoTableComponent, - MappingDataKeysPanelComponent, - TypeValuePanelComponent, - GatewayLogsComponent, - GatewayStatisticsComponent, - GatewayServiceRPCComponent, - GatewayServiceRPCConnectorComponent, - GatewayServiceRPCConnectorTemplatesComponent, - DeviceGatewayCommandComponent, - GatewayConfigurationComponent, - GatewayRemoteConfigurationDialogComponent, - GatewayServiceRPCConnectorTemplateDialogComponent, ValueCardWidgetComponent, AggregatedValueCardWidgetComponent, CountWidgetComponent, @@ -247,29 +147,8 @@ import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pip RpcWidgetsModule, HomePageWidgetsModule, SharedHomeComponentsModule, - RestConnectorSecurityComponent, - GatewayHelpLinkPipe, - BrokerConfigControlComponent, - WorkersConfigControlComponent, - OpcServerConfigComponent, - MqttBasicConfigComponent, - MappingTableComponent, - OpcUaBasicConfigComponent, KeyValueIsNotEmptyPipe, - ModbusBasicConfigComponent, EllipsisChipListDirective, - ModbusRpcParametersComponent, - MqttLegacyBasicConfigComponent, - GatewayBasicConfigurationComponent, - GatewayAdvancedConfigurationComponent, - OpcUaLegacyBasicConfigComponent, - ModbusLegacyBasicConfigComponent, - MqttRpcParametersComponent, - OpcRpcParametersComponent, - ModbusRpcParametersComponent, - RpcTemplateArrayViewPipe, - ReportStrategyComponent, - LatestVersionConfigPipe, ], exports: [ EntitiesTableWidgetComponent, @@ -284,7 +163,6 @@ import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pip MultipleInputWidgetComponent, TripAnimationComponent, PhotoCameraInputWidgetComponent, - GatewayFormComponent, NavigationCardsWidgetComponent, NavigationCardWidgetComponent, QrCodeWidgetComponent, @@ -292,22 +170,7 @@ import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pip MarkdownWidgetComponent, LegendComponent, FlotWidgetComponent, - GatewayConnectorComponent, - AddConnectorDialogComponent, - MappingDialogComponent, - DeviceInfoTableComponent, - MappingDataKeysPanelComponent, - TypeValuePanelComponent, - GatewayLogsComponent, - GatewayServiceRPCConnectorComponent, - GatewayServiceRPCConnectorTemplatesComponent, EllipsisChipListDirective, - GatewayStatisticsComponent, - GatewayServiceRPCComponent, - DeviceGatewayCommandComponent, - GatewayConfigurationComponent, - GatewayRemoteConfigurationDialogComponent, - GatewayServiceRPCConnectorTemplateDialogComponent, ValueCardWidgetComponent, AggregatedValueCardWidgetComponent, CountWidgetComponent, @@ -340,7 +203,6 @@ import { LatestVersionConfigPipe } from '@home/components/widget/lib/gateway/pip ], providers: [ {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule}, - {provide: LatestVersionConfigPipe} ] }) export class WidgetComponentsModule { From e6510a9b1d450d244dd53b06b95d93eab2d07abc Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 7 Oct 2024 12:16:49 +0300 Subject: [PATCH 017/110] Added resources to gateway widgets for extension migration --- .../system/widget_types/gateway_configuration.json | 10 +++++++++- .../gateway_configuration__single_device_.json | 10 +++++++++- .../json/system/widget_types/gateway_connectors.json | 10 +++++++++- .../system/widget_types/gateway_custom_statistics.json | 10 +++++++++- .../widget_types/gateway_general_chart_statistics.json | 10 +++++++++- .../widget_types/gateway_general_configuration.json | 10 +++++++++- .../data/json/system/widget_types/gateway_logs.json | 10 +++++++++- .../data/json/system/widget_types/service_rpc.json | 10 +++++++++- .../src/main/data/json/tenant/dashboards/gateways.json | 10 +++++++++- ui-ngx/src/app/modules/common/modules-map.ts | 10 ++++++++++ ui-ngx/src/app/shared/components/public-api.ts | 1 + 11 files changed, 92 insertions(+), 9 deletions(-) diff --git a/application/src/main/data/json/system/widget_types/gateway_configuration.json b/application/src/main/data/json/system/widget_types/gateway_configuration.json index 985426ef94..a19ffc23c3 100644 --- a/application/src/main/data/json/system/widget_types/gateway_configuration.json +++ b/application/src/main/data/json/system/widget_types/gateway_configuration.json @@ -8,7 +8,15 @@ "type": "static", "sizeX": 8, "sizeY": 6.5, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "\n\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json b/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json index a7b3291666..26c804a1a8 100644 --- a/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json +++ b/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json @@ -8,7 +8,15 @@ "type": "latest", "sizeX": 7.5, "sizeY": 9, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "\n", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", "controllerScript": "self.onInit = function() {\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\t\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_connectors.json b/application/src/main/data/json/system/widget_types/gateway_connectors.json index 43f95194b8..6323c32b3c 100644 --- a/application/src/main/data/json/system/widget_types/gateway_connectors.json +++ b/application/src/main/data/json/system/widget_types/gateway_connectors.json @@ -8,7 +8,15 @@ "type": "latest", "sizeX": 11, "sizeY": 8, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.gatewayConnectors?.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", diff --git a/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json b/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json index 7090490ea6..1eaa061d11 100644 --- a/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json +++ b/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json @@ -8,7 +8,15 @@ "type": "timeseries", "sizeX": 8, "sizeY": 5, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", "controllerScript": "self.onInit = function() { \n};\n\nself.onDataUpdated = function() {\n};\n\nself.onLatestDataUpdated = function() {\n};\n\nself.onResize = function() {\n};\n\nself.onEditModeChanged = function() {\n};\n\nself.onDestroy = function() {\n};\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: false,\n dataKeysOptional: true\n };\n}\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json b/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json index 7e1974d5b7..37a16fa7ba 100644 --- a/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json +++ b/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json @@ -8,7 +8,15 @@ "type": "timeseries", "sizeX": 8, "sizeY": 5, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", "controllerScript": "self.onInit = function() { \n};\n\nself.onDataUpdated = function() {\n};\n\nself.onLatestDataUpdated = function() {\n};\n\nself.onResize = function() {\n};\n\nself.onEditModeChanged = function() {\n};\n\nself.onDestroy = function() {\n};\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: false\n };\n}\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_general_configuration.json b/application/src/main/data/json/system/widget_types/gateway_general_configuration.json index e2129c0271..e260746bec 100644 --- a/application/src/main/data/json/system/widget_types/gateway_general_configuration.json +++ b/application/src/main/data/json/system/widget_types/gateway_general_configuration.json @@ -8,7 +8,15 @@ "type": "latest", "sizeX": 11, "sizeY": 8, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", diff --git a/application/src/main/data/json/system/widget_types/gateway_logs.json b/application/src/main/data/json/system/widget_types/gateway_logs.json index 45b380e544..2d408a5ea6 100644 --- a/application/src/main/data/json/system/widget_types/gateway_logs.json +++ b/application/src/main/data/json/system/widget_types/gateway_logs.json @@ -8,7 +8,15 @@ "type": "timeseries", "sizeX": 7.5, "sizeY": 3, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "\n \n", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", diff --git a/application/src/main/data/json/system/widget_types/service_rpc.json b/application/src/main/data/json/system/widget_types/service_rpc.json index 23cc13adf0..7e1276a486 100644 --- a/application/src/main/data/json/system/widget_types/service_rpc.json +++ b/application/src/main/data/json/system/widget_types/service_rpc.json @@ -8,7 +8,15 @@ "type": "rpc", "sizeX": 8.5, "sizeY": 5.5, - "resources": [], + "resources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "templateHtml": "", "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", "controllerScript": "\nself.onInit = function() {\n};", diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 668b92b7b2..5d910493ab 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -1919,7 +1919,15 @@ "customHtml": "
\n \n

{{ 'gateway.launch-command' | translate }}

\n \n
\n \n
\n \n
\n \n
\n
\n", "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", - "customResources": [], + "customResources": [ + { + "url": { + "entityType": "TB_RESOURCE", + "id": "71347080-80b6-11ef-a567-0114914c70f8" + }, + "isModule": true + } + ], "openInSeparateDialog": false, "openInPopover": false, "id": "337c767b-3217-d3d3-b955-7b0bd0858a1d" diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 512719c513..82401a4d6e 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -196,6 +196,9 @@ import * as HintTooltipIconComponent from '@shared/components/hint-tooltip-icon. import * as ScrollGridComponent from '@shared/components/grid/scroll-grid.component'; import * as GalleryImageInputComponent from '@shared/components/image/gallery-image-input.component'; import * as MultipleGalleryImageInputComponent from '@shared/components/image/multiple-gallery-image-input.component'; +import * as TbPopoverService from '@shared/components/popover.service'; + +import * as AttributeDatasource from '@home/models/datasource/attribute-datasource'; import * as CssUnitSelectComponent from '@home/components/widget/lib/settings/common/css-unit-select.component'; import * as WidgetActionsPanelComponent from '@home/components/widget/config/basic/common/widget-actions-panel.component'; @@ -247,6 +250,7 @@ import * as CustomActionPrettyEditorComponent from '@home/components/widget/lib/ import * as MobileActionEditorComponent from '@home/components/widget/lib/settings/common/action/mobile-action-editor.component'; import * as CustomDialogService from '@home/components/widget/dialog/custom-dialog.service'; import * as CustomDialogContainerComponent from '@home/components/widget/dialog/custom-dialog-container.component'; +import * as ImportExportService from '@shared/import-export/import-export.service'; import * as ImportDialogComponent from '@shared/import-export/import-dialog.component'; import * as AddWidgetToDashboardDialogComponent from '@home/components/attribute/add-widget-to-dashboard-dialog.component'; import * as ImportDialogCsvComponent from '@shared/import-export/import-dialog-csv.component'; @@ -255,6 +259,7 @@ import * as EventContentDialogComponent from '@home/components/event/event-conte import * as SharedHomeComponentsModule from '@home/components/shared-home-components.module'; import * as WidgetConfigComponentsModule from '@home/components/widget/config/widget-config-components.module'; import * as BasicWidgetConfigModule from '@home/components/widget/config/basic/basic-widget-config.module'; +import * as TbFlot from '@home/components/widget/lib/flot-widget'; import * as WidgetSettingsCommonModule from '@home/components/widget/lib/settings/common/widget-settings-common.module'; import * as WidgetComponentsModule from '@home/components/widget/widget-components.module'; import * as SelectTargetLayoutDialogComponent from '@home/components/dashboard/select-target-layout-dialog.component'; @@ -435,6 +440,7 @@ class ModulesMap implements IModulesMap { '@shared/decorators/enumerable': enumerable, '@shared/decorators/tb-inject': TbInject, + '@shared/import-export/import-export.service': ImportExportService, '@shared/import-export/import-dialog.component': ImportDialogComponent, '@shared/import-export/import-dialog-csv.component': ImportDialogCsvComponent, '@shared/import-export/table-columns-assignment.component': TableColumnsAssignmentComponent, @@ -533,6 +539,9 @@ class ModulesMap implements IModulesMap { '@shared/components/grid/scroll-grid.component': ScrollGridComponent, '@shared/components/image/gallery-image-input.component': GalleryImageInputComponent, '@shared/components/image/multiple-gallery-image-input.component': MultipleGalleryImageInputComponent, + '@shared/components/popover.service': TbPopoverService, + + '@home/models/datasource/attribute-datasource': AttributeDatasource, '@home/components/alarm/alarm-filter-config.component': AlarmFilterConfigComponent, '@home/components/alarm/alarm-comment-dialog.component': AlarmCommentDialogComponent, @@ -576,6 +585,7 @@ class ModulesMap implements IModulesMap { '@home/components/widget/config/data-keys.component': DataKeysComponent, '@home/components/widget/config/data-key-config-dialog.component': DataKeyConfigDialogComponent, '@home/components/widget/config/data-key-config.component': DataKeyConfigComponent, + '@home/components/widget/lib/flot-widget': TbFlot, '@home/components/widget/lib/settings/common/legend-config.component': LegendConfigComponent, '@home/components/widget/action/manage-widget-actions.component': ManageWidgetActionsComponent, '@home/components/widget/action/widget-action-dialog.component': WidgetActionDialogComponent, diff --git a/ui-ngx/src/app/shared/components/public-api.ts b/ui-ngx/src/app/shared/components/public-api.ts index 8aef3e83f7..8d7f825b5b 100644 --- a/ui-ngx/src/app/shared/components/public-api.ts +++ b/ui-ngx/src/app/shared/components/public-api.ts @@ -31,3 +31,4 @@ export * from './icon.component'; export * from './hint-tooltip-icon.component'; export * from './grid/scroll-grid-datasource'; export * from './grid/scroll-grid.component'; +export * from './table/table-datasource.abstract'; From 5d66e8b67a9ae4aa1af4a8a3f0cea224fc487b49 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 7 Oct 2024 15:11:32 +0300 Subject: [PATCH 018/110] Updated modules map for gateway extension migration --- .../system/widget_types/gateway_configuration.json | 10 +--------- .../gateway_configuration__single_device_.json | 10 +--------- .../json/system/widget_types/gateway_connectors.json | 10 +--------- .../system/widget_types/gateway_custom_statistics.json | 10 +--------- .../widget_types/gateway_general_chart_statistics.json | 10 +--------- .../widget_types/gateway_general_configuration.json | 10 +--------- .../data/json/system/widget_types/gateway_logs.json | 10 +--------- .../data/json/system/widget_types/service_rpc.json | 10 +--------- .../src/main/data/json/tenant/dashboards/gateways.json | 10 +--------- ui-ngx/src/app/modules/common/modules-map.ts | 2 -- 10 files changed, 9 insertions(+), 83 deletions(-) diff --git a/application/src/main/data/json/system/widget_types/gateway_configuration.json b/application/src/main/data/json/system/widget_types/gateway_configuration.json index a19ffc23c3..985426ef94 100644 --- a/application/src/main/data/json/system/widget_types/gateway_configuration.json +++ b/application/src/main/data/json/system/widget_types/gateway_configuration.json @@ -8,15 +8,7 @@ "type": "static", "sizeX": 8, "sizeY": 6.5, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "\n\n", "templateCss": "", "controllerScript": "self.onInit = function() {\n}\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json b/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json index 26c804a1a8..a7b3291666 100644 --- a/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json +++ b/application/src/main/data/json/system/widget_types/gateway_configuration__single_device_.json @@ -8,15 +8,7 @@ "type": "latest", "sizeX": 7.5, "sizeY": 9, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "\n", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", "controllerScript": "self.onInit = function() {\n}\n\n\nself.onDestroy = function() {\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\t\t\t\n dataKeysOptional: true,\n singleEntity: true\n };\n}\n\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_connectors.json b/application/src/main/data/json/system/widget_types/gateway_connectors.json index 6323c32b3c..43f95194b8 100644 --- a/application/src/main/data/json/system/widget_types/gateway_connectors.json +++ b/application/src/main/data/json/system/widget_types/gateway_connectors.json @@ -8,15 +8,7 @@ "type": "latest", "sizeX": 11, "sizeY": 8, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.gatewayConnectors?.onDataUpdated();\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", diff --git a/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json b/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json index 1eaa061d11..7090490ea6 100644 --- a/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json +++ b/application/src/main/data/json/system/widget_types/gateway_custom_statistics.json @@ -8,15 +8,7 @@ "type": "timeseries", "sizeX": 8, "sizeY": 5, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", "controllerScript": "self.onInit = function() { \n};\n\nself.onDataUpdated = function() {\n};\n\nself.onLatestDataUpdated = function() {\n};\n\nself.onResize = function() {\n};\n\nself.onEditModeChanged = function() {\n};\n\nself.onDestroy = function() {\n};\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: false,\n dataKeysOptional: true\n };\n}\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json b/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json index 37a16fa7ba..7e1974d5b7 100644 --- a/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json +++ b/application/src/main/data/json/system/widget_types/gateway_general_chart_statistics.json @@ -8,15 +8,7 @@ "type": "timeseries", "sizeX": 8, "sizeY": 5, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "", "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", "controllerScript": "self.onInit = function() { \n};\n\nself.onDataUpdated = function() {\n};\n\nself.onLatestDataUpdated = function() {\n};\n\nself.onResize = function() {\n};\n\nself.onEditModeChanged = function() {\n};\n\nself.onDestroy = function() {\n};\n\nself.typeParameters = function() {\n return {\n hasAdditionalLatestDataKeys: false\n };\n}\n", diff --git a/application/src/main/data/json/system/widget_types/gateway_general_configuration.json b/application/src/main/data/json/system/widget_types/gateway_general_configuration.json index e260746bec..e2129c0271 100644 --- a/application/src/main/data/json/system/widget_types/gateway_general_configuration.json +++ b/application/src/main/data/json/system/widget_types/gateway_general_configuration.json @@ -8,15 +8,7 @@ "type": "latest", "sizeX": 11, "sizeY": 8, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n if (self.ctx.datasources && self.ctx.datasources.length) {\n self.ctx.$scope.entityId = self.ctx.datasources[0].entity.id;\n }\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", diff --git a/application/src/main/data/json/system/widget_types/gateway_logs.json b/application/src/main/data/json/system/widget_types/gateway_logs.json index 2d408a5ea6..45b380e544 100644 --- a/application/src/main/data/json/system/widget_types/gateway_logs.json +++ b/application/src/main/data/json/system/widget_types/gateway_logs.json @@ -8,15 +8,7 @@ "type": "timeseries", "sizeX": 7.5, "sizeY": 3, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "\n \n", "templateCss": "#container {\n overflow: auto;\n}\n\n.tbDatasource-container {\n margin: 5px;\n padding: 8px;\n}\n\n.tbDatasource-title {\n font-size: 1.200rem;\n font-weight: 500;\n padding-bottom: 10px;\n}\n\n.tbDatasource-table {\n width: 100%;\n box-shadow: 0 0 10px #ccc;\n border-collapse: collapse;\n white-space: nowrap;\n font-size: 1.000rem;\n color: #757575;\n}\n\n.tbDatasource-table td {\n position: relative;\n border-top: 1px solid rgba(0, 0, 0, 0.12);\n border-bottom: 1px solid rgba(0, 0, 0, 0.12);\n padding: 0px 18px;\n box-sizing: border-box;\n}", "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true,\n singleEntity: true\n };\n}", diff --git a/application/src/main/data/json/system/widget_types/service_rpc.json b/application/src/main/data/json/system/widget_types/service_rpc.json index 7e1276a486..23cc13adf0 100644 --- a/application/src/main/data/json/system/widget_types/service_rpc.json +++ b/application/src/main/data/json/system/widget_types/service_rpc.json @@ -8,15 +8,7 @@ "type": "rpc", "sizeX": 8.5, "sizeY": 5.5, - "resources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "resources": [], "templateHtml": "", "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.gpio-panel section[fxflex] {\n min-width: 0px;\n}\n\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel mat-slide-toggle {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel.col-0 mat-slide-toggle {\n margin-left: 8px;\n margin-right: 4px;\n}\n\n.switch-panel.col-1 mat-slide-toggle {\n margin-left: 4px;\n margin-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", "controllerScript": "\nself.onInit = function() {\n};", diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 5d910493ab..668b92b7b2 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -1919,15 +1919,7 @@ "customHtml": "
\n \n

{{ 'gateway.launch-command' | translate }}

\n \n
\n \n
\n \n
\n \n
\n
\n", "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", - "customResources": [ - { - "url": { - "entityType": "TB_RESOURCE", - "id": "71347080-80b6-11ef-a567-0114914c70f8" - }, - "isModule": true - } - ], + "customResources": [], "openInSeparateDialog": false, "openInPopover": false, "id": "337c767b-3217-d3d3-b955-7b0bd0858a1d" diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 82401a4d6e..4b363fa1df 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -259,7 +259,6 @@ import * as EventContentDialogComponent from '@home/components/event/event-conte import * as SharedHomeComponentsModule from '@home/components/shared-home-components.module'; import * as WidgetConfigComponentsModule from '@home/components/widget/config/widget-config-components.module'; import * as BasicWidgetConfigModule from '@home/components/widget/config/basic/basic-widget-config.module'; -import * as TbFlot from '@home/components/widget/lib/flot-widget'; import * as WidgetSettingsCommonModule from '@home/components/widget/lib/settings/common/widget-settings-common.module'; import * as WidgetComponentsModule from '@home/components/widget/widget-components.module'; import * as SelectTargetLayoutDialogComponent from '@home/components/dashboard/select-target-layout-dialog.component'; @@ -585,7 +584,6 @@ class ModulesMap implements IModulesMap { '@home/components/widget/config/data-keys.component': DataKeysComponent, '@home/components/widget/config/data-key-config-dialog.component': DataKeyConfigDialogComponent, '@home/components/widget/config/data-key-config.component': DataKeyConfigComponent, - '@home/components/widget/lib/flot-widget': TbFlot, '@home/components/widget/lib/settings/common/legend-config.component': LegendConfigComponent, '@home/components/widget/action/manage-widget-actions.component': ManageWidgetActionsComponent, '@home/components/widget/action/widget-action-dialog.component': WidgetActionDialogComponent, From 2ebbbdc9775b127b1c9130728fa26ac2cc431e54 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 7 Oct 2024 19:20:34 +0300 Subject: [PATCH 019/110] Deleted redundant translations --- .../assets/locale/locale.constant-ar_AE.json | 253 +------ .../assets/locale/locale.constant-ca_ES.json | 73 +- .../assets/locale/locale.constant-cs_CZ.json | 66 +- .../assets/locale/locale.constant-da_DK.json | 66 +- .../assets/locale/locale.constant-en_US.json | 682 +----------------- .../assets/locale/locale.constant-es_ES.json | 249 +------ .../assets/locale/locale.constant-ko_KR.json | 65 +- .../assets/locale/locale.constant-lt_LT.json | 248 +------ .../assets/locale/locale.constant-nl_BE.json | 65 +- .../assets/locale/locale.constant-pl_PL.json | 242 +------ .../assets/locale/locale.constant-pt_BR.json | 65 +- .../assets/locale/locale.constant-sl_SI.json | 65 +- .../assets/locale/locale.constant-tr_TR.json | 71 +- .../assets/locale/locale.constant-zh_CN.json | 253 +------ .../assets/locale/locale.constant-zh_TW.json | 71 +- 15 files changed, 45 insertions(+), 2489 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json index 3f148cab2d..0afd671082 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ar_AE.json +++ b/ui-ngx/src/assets/locale/locale.constant-ar_AE.json @@ -3163,261 +3163,16 @@ "function": "الوظيفة" }, "gateway": { - "add-entry": "إضافة تكوين", - "advanced": "متقدم", - "checking-device-activity": "فحص نشاط الجهاز", - "command": "أوامر Docker", - "command-copied-message": "تم نسخ أمر Docker إلى الحافظة", - "configuration": "التكوين", - "connector-add": "إضافة موصل جديد", - "connector-enabled": "تمكين الموصل", - "connector-name": "اسم الموصل", - "connector-name-required": "اسم الموصل مطلوب.", - "connector-type": "نوع الموصل", - "connector-type-required": "نوع الموصل مطلوب.", - "connectors": "الموصلات", - "connectors-config": "تكوينات الموصلات", - "connectors-table-enabled": "ممكّن", - "connectors-table-name": "الاسم", - "connectors-table-type": "النوع", - "connectors-table-status": "الحالة", - "connectors-table-actions": "الإجراءات", - "connectors-table-key": "المفتاح", - "connectors-table-class": "الفئة", - "rpc-command-send": "إرسال", - "rpc-command-result": "الاستجابة", - "rpc-command-edit-params": "تحرير المعلمات", - "gateway-configuration": "تكوين عام", - "docker-label": "استخدم التعليمات التالية لتشغيل IoT Gateway في Docker compose مع بيانات اعتماد للجهاز المحدد", - "install-docker-compose": "استخدم التعليمات لتنزيل وتثبيت وإعداد docker compose", - "download-configuration-file": "تنزيل ملف التكوين", - "download-docker-compose": "تنزيل docker-compose.yml لبوابتك", - "launch-gateway": "تشغيل البوابة", - "launch-docker-compose": "بدء تشغيل البوابة باستخدام الأمر التالي في الطرفية من المجلد الذي يحتوي على ملف docker-compose.yml", - "create-new-gateway": "إنشاء بوابة جديدة", - "create-new-gateway-text": "هل أنت متأكد أنك تريد إنشاء بوابة جديدة باسم: '{{gatewayName}}'؟", - "created-time": "وقت الإنشاء", - "configuration-delete-dialog-header": "سيتم حذف التكوينات", - "configuration-delete-dialog-body": "يمكن تعطيل التكوين عن بُعد فقط إذا كان هناك وصول جسدي إلى البوابة. ستتم حذف جميع التكوينات السابقة.

\n لتعطيل التكوين، أدخل اسم البوابة أدناه", - "configuration-delete-dialog-input": "اسم البوابة", - "configuration-delete-dialog-input-required": "اسم البوابة إلزامي", - "configuration-delete-dialog-confirm": "إيقاف التشغيل", - "delete": "حذف التكوين", - "download-tip": "تنزيل ملف التكوين", - "drop-file": "أفلق الملف هنا أو", "gateway": "البوابة", "gateway-exists": "الجهاز بنفس الاسم موجود بالفعل.", "gateway-name": "اسم البوابة", "gateway-name-required": "اسم البوابة مطلوب.", "gateway-saved": "تم حفظ تكوين البوابة بنجاح.", - "grpc": "GRPC", - "grpc-keep-alive-timeout": "مهلة البقاء على قيد الحياة (بالمللي ثانية)", - "grpc-keep-alive-timeout-required": "مهلة البقاء على قيد الحياة مطلوبة", - "grpc-keep-alive-timeout-min": "مهلة البقاء على قيد الحياة لا يمكن أن تكون أقل من 1", - "grpc-keep-alive-timeout-pattern": "مهلة البقاء على قيد الحياة غير صالحة", - "grpc-keep-alive": "البقاء على قيد الحياة (بالمللي ثانية)", - "grpc-keep-alive-required": "البقاء على قيد الحياة مطلوب", - "grpc-keep-alive-min": "البقاء على قيد الحياة لا يمكن أن يكون أقل من 1", - "grpc-keep-alive-pattern": "البقاء على قيد الحياة غير صالح", - "grpc-min-time-between-pings": "الحد الأدنى للوقت بين البينغات (بالمللي ثانية)", - "grpc-min-time-between-pings-required": "الحد الأدنى للوقت بين البينغات مطلوب", - "grpc-min-time-between-pings-min": "الحد الأدنى للوقت بين البينغات لا يمكن أن يكون أقل من 1", - "grpc-min-time-between-pings-pattern": "الحد الأدنى للوقت بين البينغات غير صالح", - "grpc-min-ping-interval-without-data": "الحد الأدنى لفاصل البينغ بدون بيانات (بالمللي ثانية)", - "grpc-min-ping-interval-without-data-required": "الحد الأدنى لفاصل البينغ بدون بيانات مطلوب", - "grpc-min-ping-interval-without-data-min": "الحد الأدنى لفاصل البينغ بدون بيانات لا يمكن أن يكون أقل من 1", - "grpc-min-ping-interval-without-data-pattern": "الحد الأدنى لفاصل البينغ بدون بيانات غير صالح", - "grpc-max-pings-without-data": "الحد الأقصى لعدد البينغات بدون بيانات", - "grpc-max-pings-without-data-required": "الحد الأقصى لعدد البينغات بدون بيانات مطلوب", - "grpc-max-pings-without-data-min": "الحد الأقصى لعدد البينغات بدون بيانات لا يمكن أن يكون أقل من 1", - "grpc-max-pings-without-data-pattern": "الحد الأقصى لعدد البينغات بدون بيانات غير صالح", - "inactivity-check-period-seconds": "فترة فحص الخمول (بالثواني)", - "inactivity-check-period-seconds-required": "فترة فحص الخمول مطلوبة", - "inactivity-check-period-seconds-min": "فترة فحص الخمول لا يمكن أن تكون أقل من 1", - "inactivity-check-period-seconds-pattern": "فترة فحص الخمول غير صالحة", - "inactivity-timeout-seconds": "فترة الخمول (بالثواني)", - "inactivity-timeout-seconds-required": "فترة الخمول مطلوبة", - "inactivity-timeout-seconds-min": "فترة الخمول لا يمكن أن تكون أقل من 1", - "inactivity-timeout-seconds-pattern": "فترة الخمول غير صالحة", - "json-parse": "JSON غير صالح.", - "json-required": "الحقل لا يمكن أن يكون فارغًا.", - "logs": { - "logs": "السجلات", - "days": "أيام", - "hours": "ساعات", - "minutes": "دقائق", - "seconds": "ثواني", - "date-format": "تنسيق التاريخ", - "date-format-required": "تنسيق التاريخ مطلوب", - "log-format": "تنسيق السجل", - "log-type": "نوع السجل", - "log-format-required": "تنسيق السجل مطلوب", - "remote": "التسجيل عن بُعد", - "remote-logs": "السجلات عن بُعد", - "local": "التسجيل المحلي", - "level": "مستوى السجل", - "file-path": "مسار الملف", - "file-path-required": "مسار الملف مطلوب", - "saving-period": "فترة حفظ السجل", - "saving-period-min": "فترة حفظ السجل لا يمكن أن تكون أقل من 1", - "saving-period-required": "فترة حفظ السجل مطلوبة", - "backup-count": "عدد النسخ الاحتياطية", - "backup-count-min": "عدد النسخ الاحتياطية لا يمكن أن يكون أقل من 1", - "backup-count-required": "عدد النسخ الاحتياطية مطلوب" - }, - "min-pack-send-delay": "الحد الأدنى لتأخير إرسال الحزمة (بالمللي ثانية)", - "min-pack-send-delay-required": "الحد الأدنى لتأخير إرسال الحزمة مطلوب", - "min-pack-send-delay-min": "لا يمكن أن يكون الحد الأدنى لتأخير إرسال الحزمة أقل من 0", - "no-connectors": "لا توجد موصلات", - "no-data": "لا توجد تكوينات", + "launch-gateway": "تشغيل البوابة", + "create-new-gateway": "إنشاء بوابة جديدة", + "create-new-gateway-text": "هل أنت متأكد أنك تريد إنشاء بوابة جديدة باسم: '{{gatewayName}}'؟", "no-gateway-found": "لم يتم العثور على بوابة.", - "no-gateway-matching": "'{{item}}' غير موجود.", - "path-logs": "مسار إلى ملفات السجل", - "path-logs-required": "المسار مطلوب.", - "permit-without-calls": "البقاء على الحياة يسمح بدون مكالمات", - "remote": "التكوين عن بُعد", - "remote-logging-level": "مستوى التسجيل", - "remove-entry": "إزالة التكوين", - "remote-shell": "قشرة عن بُعد", - "remote-configuration": "التكوين عن بُعد", - "other": "آخر", - "save-tip": "حفظ ملف التكوين", - "security-type": "نوع الأمان", - "security-types": { - "access-token": "رمز الوصول", - "username-password": "اسم المستخدم وكلمة المرور", - "tls": "TLS", - "tls-access-token": "TLS + رمز الوصول", - "tls-private-key": "TLS + المفتاح الخاص" - }, - "server-port": "منفذ الخادم", - "statistics": { - "statistic": "إحصائية", - "statistics": "الإحصائيات", - "statistic-commands-empty": "لا تتوفر إحصائيات", - "commands": "الأوامر", - "send-period": "فترة إرسال الإحصائيات (بالثواني)", - "send-period-required": "فترة إرسال الإحصائيات مطلوبة", - "send-period-min": "لا يمكن أن تكون فترة إرسال الإحصائيات أقل من 60", - "send-period-pattern": "فترة إرسال الإحصائيات غير صالحة", - "check-connectors-configuration": "فترة فحص تكوين الموصلات (بالثواني)", - "check-connectors-configuration-required": "فترة فحص تكوين الموصلات مطلوبة", - "check-connectors-configuration-min": "لا يمكن أن تكون فترة فحص تكوين الموصلات أقل من 1", - "check-connectors-configuration-pattern": "فترة فحص تكوين الموصلات غير صالحة", - "add": "إضافة أمر", - "timeout": "المهلة", - "timeout-ms": "المهلة (بالمللي ثانية)", - "timeout-required": "المهلة مطلوبة", - "timeout-min": "لا يمكن أن تكون المهلة أقل من 1", - "timeout-pattern": "المهلة غير صالحة", - "attribute-name": "اسم السمة", - "attribute-name-required": "اسم السمة مطلوب", - "command": "الأمر", - "command-required": "الأمر مطلوب", - "command-pattern": "الأمر غير صالح", - "remove": "إزالة الأمر" - }, - "storage": "التخزين", - "storage-max-file-records": "السجلات القصوى في الملف", - "storage-max-files": "الحد الأقصى لعدد الملفات", - "storage-max-files-min": "الحد الأدنى هو 1.", - "storage-max-files-pattern": "العدد غير صالح.", - "storage-max-files-required": "العدد مطلوب.", - "storage-max-records": "السجلات القصوى في التخزين", - "storage-max-records-min": "الحد الأدنى لعدد السجلات هو 1.", - "storage-max-records-pattern": "العدد غير صالح.", - "storage-max-records-required": "السجلات القصوى مطلوبة.", - "storage-read-record-count": "عدد قراءة السجلات في التخزين", - "storage-read-record-count-min": "الحد الأدنى لعدد السجلات هو 1.", - "storage-read-record-count-pattern": "العدد غير صالح.", - "storage-read-record-count-required": "عدد قراءة السجلات مطلوب.", - "storage-max-read-record-count": "الحد الأقصى لعدد قراءة السجلات في التخزين", - "storage-max-read-record-count-min": "الحد الأدنى لعدد السجلات هو 1.", - "storage-max-read-record-count-pattern": "العدد غير صالح.", - "storage-max-read-record-count-required": "عدد القراءة القصوى مطلوب.", - "storage-data-folder-path": "مسار مجلد البيانات", - "storage-data-folder-path-required": "مسار مجلد البيانات مطلوب.", - "storage-pack-size": "الحد الأقصى لحجم حزمة الحدث", - "storage-pack-size-min": "الحد الأدنى هو 1.", - "storage-pack-size-pattern": "العدد غير صالح.", - "storage-pack-size-required": "الحجم الأقصى لحزمة الحدث مطلوب.", - "storage-path": "مسار التخزين", - "storage-path-required": "مسار التخزين مطلوب.", - "storage-type": "نوع التخزين", - "storage-types": { - "file-storage": "تخزين الملفات", - "memory-storage": "تخزين الذاكرة", - "sqlite": "SQLITE" - }, - "thingsboard": "ثينغزبورد", - "general": "عام", - "thingsboard-host": "مضيف ثينغزبورد", - "thingsboard-host-required": "المضيف مطلوب.", - "thingsboard-port": "منفذ ثينغزبورد", - "thingsboard-port-max": "الحد الأقصى لرقم المنفذ هو 65535.", - "thingsboard-port-min": "الحد الأدنى لرقم المنفذ هو 1.", - "thingsboard-port-pattern": "المنفذ غير صالح.", - "thingsboard-port-required": "المنفذ مطلوب.", - "tidy": "ترتيب", - "tidy-tip": "ترتيب تكوين JSON", - "title-connectors-json": "تكوين موصل {{typeName}}", - "tls-path-ca-certificate": "المسار إلى شهادة CA على البوابة", - "tls-path-client-certificate": "المسار إلى شهادة العميل على البوابة", - "messages-ttl-check-in-hours": "فحص TTL الرسائل بالساعات", - "messages-ttl-check-in-hours-required": "يجب تحديد فحص TTL الرسائل بالساعات.", - "messages-ttl-check-in-hours-min": "الحد الأدنى هو 1.", - "messages-ttl-check-in-hours-pattern": "الرقم غير صالح.", - "messages-ttl-in-days": "TTL الرسائل بالأيام", - "messages-ttl-in-days-required": "يجب تحديد TTL الرسائل بالأيام.", - "messages-ttl-in-days-min": "الحد الأدنى هو 1.", - "messages-ttl-in-days-pattern": "الرقم غير صالح.", - "mqtt-qos": "جودة الخدمة (QoS)", - "mqtt-qos-required": "جودة الخدمة (QoS) مطلوبة", - "mqtt-qos-range": "تتراوح قيم جودة الخدمة (QoS) من 0 إلى 1", - "tls-path-private-key": "المسار إلى المفتاح الخاص على البوابة", - "toggle-fullscreen": "تبديل وضع ملء الشاشة", - "transformer-json-config": "تكوين JSON*", - "update-config": "إضافة/تحديث تكوين JSON", - "hints": { - "remote-configuration": "يمكنك تمكين التكوين وإدارة البوابة عن بُعد", - "remote-shell": "يمكنك تمكين التحكم البعيد في نظام التشغيل مع البوابة من عنصر واجهة المستخدم قشرة عن بُعد", - "host": "اسم المضيف أو عنوان IP لخادم ثينغزبورد", - "port": "منفذ خدمة MQTT على خادم ثينغزبورد", - "token": "رمز الوصول للبوابة من خادم ثينغزبورد", - "client-id": "معرف عميل MQTT للبوابة من خادم ثينغزبورد", - "username": "اسم المستخدم MQTT للبوابة من خادم ثينغزبورد", - "password": "كلمة المرور MQTT للبوابة من خادم ثينغزبورد", - "ca-cert": "المسار إلى ملف شهادة CA", - "date-form": "تنسيق التاريخ في رسالة السجل", - "data-folder": "المسار إلى المجلد الذي سيحتوي على البيانات (نسبي أو مطلق)", - "log-format": "تنسيق رسالة السجل", - "remote-log": "يمكنك تمكين التسجيل البعيد وقراءة السجلات من البوابة", - "backup-count": "إذا كان عدد النسخ الاحتياطية > 0، عند عملية تدوير، لا يتم الاحتفاظ بأكثر من عدد النسخ الاحتياطية المحددة - يتم حذف الأقدم", - "storage": "يوفر تكوينًا لحفظ البيانات الواردة قبل إرسالها إلى المنصة", - "max-file-count": "العدد الأقصى لعدد الملفات التي سيتم إنشاؤها", - "max-read-count": "عدد الرسائل للحصول عليها من التخزين وإرسالها إلى ثينغزبورد", - "max-records": "العدد الأقصى للسجلات التي ستخزن في ملف واحد", - "read-record-count": "عدد الرسائل للحصول عليها من التخزين وإرسالها إلى ثينغزبورد", - "max-records-count": "العدد الأقصى للبيانات في التخزين قبل إرسالها إلى ثينغزبورد", - "ttl-check-hour": "كم مرة سيتحقق البوابة من البيانات القديمة", - "ttl-messages-day": "الحد الأقصى لعدد الأيام التي ستحتفظ فيها التخزين بالبيانات", - "commands": "الأوامر لجمع الإحصائيات الإضافية", - "attribute": "مفتاح تلقي الإحصائيات", - "timeout": "مهلة زمنية لتنفيذ الأمر", - "command": "سيتم استخدام نتيجة تنفيذ الأمر كقيمة لتلقي الإحصائيات", - "check-device-activity": "يمكنك تمكين مراقبة نشاط كل جهاز متصل", - "inactivity-timeout": "الوقت بعد الذي ستفصل البوابة الجهاز", - "inactivity-period": "تكرار فحص نشاط الجهاز", - "minimal-pack-delay": "التأخير بين إرسال حزم الرسائل (يؤدي تقليل هذا الإعداد إلى زيادة استخدام وحدة المعالجة المركزية)", - "qos": "جودة الخدمة في رسائل MQTT (0 - على الأكثر مرة واحدة، 1 - على الأقل مرة واحدة)", - "server-port": "منفذ الشبكة الذي سيستمع فيه خادم GRPC للاستفسارات الواردة.", - "grpc-keep-alive-timeout": "الحد الأقصى للوقت الذي يجب أن ينتظره الخادم لاستجابة رسالة الحفاظ على الاتصال قبل اعتبار الاتصال ميتًا.", - "grpc-keep-alive": "المدة بين رسائل حفظ الاتصال المتعاقبة عند عدم وجود استدعاء RPC نشط.", - "grpc-min-time-between-pings": "الحد الأدنى للوقت الذي يجب فيه أن ينتظر الخادم بين إرسال رسائل حفظ الاتصال", - "grpc-max-pings-without-data": "الحد الأقصى لعدد رسائل حفظ الاتصال التي يمكن للخادم إرسالها دون تلقي أي بيانات قبل اعتبار الاتصال ميتًا.", - "grpc-min-ping-interval-without-data": "الحد الأدنى للوقت الذي يجب فيه أن ينتظر الخادم بين إرسال رسائل حفظ الاتصال عند عدم إرسال أو استلام بيانات.", - "permit-without-calls": "السماح للخادم بإبقاء اتصال GRPC حيًا حتى عندما لا تكون هناك استدعاءات RPC نشطة." - } + "no-gateway-matching": "'{{item}}' غير موجود." }, "grid": { "delete-item-title": "هل أنت متأكد أنك تريد حذف هذا العنصر؟", diff --git a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json index f66f248303..9d92babd82 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ca_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-ca_ES.json @@ -2690,78 +2690,17 @@ "function": "Funció" }, "gateway": { - "add-entry": "Afegir configuració", - "connector-add": "Afegir conector", - "connector-enabled": "Activar conector", - "connector-name": "Nom conector", - "connector-name-required": "Cal nom conector.", - "connector-type": "Tipus conector", - "connector-type-required": "Cal tipus conector.", - "connectors": "Configuració de conectors", - "create-new-gateway": "Crear un gateway nou", - "create-new-gateway-text": "Crear un nou gateway amb el nom: '{{gatewayName}}'?", - "delete": "Esborrar configuració", - "download-tip": "Descarregar fitxer de configuració", - "gateway": "Gateway", "gateway-exists": "Ja existeix un dispositiu amb el mateix nom.", "gateway-name": "Nom de Gateway", "gateway-name-required": "Cal un nom de gateway.", "gateway-saved": "Configuració de gateway gravada satisfactòriament.", - "json-parse": "JSON no vàlid.", - "json-required": "El camp no pot ser buit.", - "no-connectors": "No hi ha conectors", - "no-data": "No hi ha configuracions", + "gateway": "Gateway", + "launch-gateway": "Llançar gateway", + "launch-command": "Comanda de llançament", + "create-new-gateway": "Crear un gateway nou", + "create-new-gateway-text": "Crear un nou gateway amb el nom: '{{gatewayName}}'?", "no-gateway-found": "No s'ha trobat cap gateway.", - "no-gateway-matching": " '{{item}}' no trobat.", - "path-logs": "Ruta als fitxers de log", - "path-logs-required": "Cal ruta.", - "remote": "Configuració remota", - "remote-logging-level": "Nivel de logging", - "remove-entry": "Esborrar configuració", - "save-tip": "Gravar fitxer de configuració", - "security-type": "Tipus de seguretat", - "security-types": { - "access-token": "Token d'accés", - "tls": "TLS" - }, - "storage": "Grabació", - "storage-max-file-records": "Número màxim de registres en fitxer", - "storage-max-files": "Número màxim de fitxers", - "storage-max-files-min": "El número mínim és 1.", - "storage-max-files-pattern": "Número no vàlid.", - "storage-max-files-required": "Cal número.", - "storage-max-records": "Màxim de registres en el magatzem", - "storage-max-records-min": "El número mínim és 1.", - "storage-max-records-pattern": "Número no vàlid.", - "storage-max-records-required": "Cal número.", - "storage-pack-size": "Mida màxim de esdeveniments", - "storage-pack-size-min": "El número mínim és 1.", - "storage-pack-size-pattern": "Número no vàlid.", - "storage-pack-size-required": "Cal número.", - "storage-path": "Ruta de magatzem", - "storage-path-required": "Cal ruta de magatzem.", - "storage-type": "Tipus de magatzem", - "storage-types": { - "file-storage": "Magatzem fitxer", - "memory-storage": "Magatzem en memoria" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "Host ThingsBoard", - "thingsboard-host-required": "Cal Host.", - "thingsboard-port": "Port ThingsBoard", - "thingsboard-port-max": "El port màxim és 65535.", - "thingsboard-port-min": "El port mínim és 1.", - "thingsboard-port-pattern": "Port no vàlid.", - "thingsboard-port-required": "Cal port.", - "tidy": "Endreçat", - "tidy-tip": "Endreçat JSON", - "title-connectors-json": "Configuració conector {{typeName}}", - "tls-path-ca-certificate": "Ruta al certificat CA al gateway", - "tls-path-client-certificate": "Ruta al certificat client al gateway", - "tls-path-private-key": "Ruta a la clau privada al gateway", - "toggle-fullscreen": "Pantalla completa fullscreen", - "transformer-json-config": "Configuració JSON*", - "update-config": "Afegir/actualizar configuració JSON" + "no-gateway-matching": " '{{item}}' no trobat." }, "grid": { "delete-item-title": "Vols eliminar aquest item?", diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 3be2bfdd24..169dfc4096 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -2024,78 +2024,14 @@ "function": "Funkce" }, "gateway": { - "add-entry": "Přidat konfiguraci", - "connector-add": "Přidat nový konektor", - "connector-enabled": "Povolit konektor", - "connector-name": "Název konektoru", - "connector-name-required": "Název konektoru je povinný.", - "connector-type": "Typ konektoru", - "connector-type-required": "Typ konektoru je povinný.", - "connectors": "Konfigurace konektoru", "create-new-gateway": "Vytvořit novou bránu", "create-new-gateway-text": "Jste si jisti, že chcete vytvořit novou bránu s názvem: '{{gatewayName}}'?", - "delete": "Smazat konfiguraci", - "download-tip": "Stáhnout soubor konfigurace", - "gateway": "Brána", "gateway-exists": "Zařízení se shodným názvem již existuje.", "gateway-name": "Název brány", "gateway-name-required": "Název brány je povinný.", "gateway-saved": "Konfigurace brány byla úspěšně uložena.", - "json-parse": "Neplatný JSON.", - "json-required": "Pole nemůže být prázdné.", - "no-connectors": "Žádné konektory", - "no-data": "Žádné konfigurace", "no-gateway-found": "Žádné brány nebyly nalezeny.", - "no-gateway-matching": " '{{item}}' nenalezena.", - "path-logs": "Cesta k souborům logu", - "path-logs-required": "Cesta je povinná.", - "remote": "Vzdálená konfigurace", - "remote-logging-level": "Úroveň logování", - "remove-entry": "Odstranit konfiguraci", - "save-tip": "Uložit soubor konfigurace", - "security-type": "Typ zabezpečení", - "security-types": { - "access-token": "Přístupový token", - "tls": "TLS" - }, - "storage": "Úložiště", - "storage-max-file-records": "Maximální počet záznamů v souboru", - "storage-max-files": "Maximální počet souborů", - "storage-max-files-min": "Minimální počet je 1.", - "storage-max-files-pattern": "Počet není platný.", - "storage-max-files-required": "Počet je povinný.", - "storage-max-records": "Maximální počet záznamů v úložišti", - "storage-max-records-min": "Minimální počet záznamů je 1.", - "storage-max-records-pattern": "Počet není platný.", - "storage-max-records-required": "Maximální počet záznamů je povinný.", - "storage-pack-size": "Maximální velikost souboru událostí", - "storage-pack-size-min": "Minimální počet je 1.", - "storage-pack-size-pattern": "Počet není platný.", - "storage-pack-size-required": "Maximální velikost souboru událostí je povinná.", - "storage-path": "Cesta k úložišti", - "storage-path-required": "Cesta k úložišti je povinná.", - "storage-type": "Typ úložiště", - "storage-types": { - "file-storage": "Soubor", - "memory-storage": "Paměť" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "Host ThingsBoard", - "thingsboard-host-required": "Host je povinný.", - "thingsboard-port": "Port ThingsBoard", - "thingsboard-port-max": "Maximální číslo portu je 65535.", - "thingsboard-port-min": "Minimální číslo portu je 1.", - "thingsboard-port-pattern": "Port není platný.", - "thingsboard-port-required": "Port je povinný.", - "tidy": "Uspořádat", - "tidy-tip": "Uspořádat JSON konfiguraci", - "title-connectors-json": "Konfigurace {{typeName}} konektoru", - "tls-path-ca-certificate": "Cesta k certifikátu CA brány", - "tls-path-client-certificate": "Cesta k certifikátu klienta brány", - "tls-path-private-key": "Cesta k privátnímu klíči brány", - "toggle-fullscreen": "Přepnout do režimu celé obrazovky", - "transformer-json-config": "JSON* konfigurace", - "update-config": "Přidat/editovat JSON konfiguraci" + "no-gateway-matching": " '{{item}}' nenalezena." }, "grid": { "delete-item-title": "Jste si jisti, že chcete smazat tuto položku?", diff --git a/ui-ngx/src/assets/locale/locale.constant-da_DK.json b/ui-ngx/src/assets/locale/locale.constant-da_DK.json index 7a1cea02a3..53695ddfa1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-da_DK.json +++ b/ui-ngx/src/assets/locale/locale.constant-da_DK.json @@ -1964,78 +1964,14 @@ "function": "Funktion" }, "gateway": { - "add-entry": "Tilføj konfiguration", - "connector-add": "Tilføj ny stikforbindelse", - "connector-enabled": "Aktivér stikforbindelse", - "connector-name": "Navn på stikforbindelse", - "connector-name-required": "Navn på stikforbindelse er påkrævet.", - "connector-type": "Stikforbindelsestype", - "connector-type-required": "Stikforbindelsestype er påkrævet.", - "connectors": "Konfiguration af stikforbindelser", "create-new-gateway": "Opret en ny gateway", "create-new-gateway-text": "", - "delete": "Slet konfiguration", - "download-tip": "Download konfigurationsfil", - "gateway": "Gateway", "gateway-exists": "Enhed med samme navn findes allerede.", "gateway-name": "Gateway-navn", "gateway-name-required": "Gateway-navn er påkrævet.", "gateway-saved": "Gateway-konfigurationen blev gemt.", - "json-parse": "Ikke gyldig JSON.", - "json-required": "Feltet må ikke være tomt.", - "no-connectors": "Ingen stikforbindelser", - "no-data": "Ingen konfigurationer", "no-gateway-found": "Ingen gateway fundet.", - "no-gateway-matching": "", - "path-logs": "Sti til logfiler", - "path-logs-required": "Sti er påkrævet.", - "remote": "Fjernkonfiguration", - "remote-logging-level": "Logføringsniveau", - "remove-entry": "Fjern konfiguration", - "save-tip": "Gem konfigurationsfil", - "security-type": "Sikkerhedstype", - "security-types": { - "access-token": "Adgangstoken", - "tls": "TLS" - }, - "storage": "Lagring", - "storage-max-file-records": "Maks. antal poster i fil", - "storage-max-files": "Maks. antal filer", - "storage-max-files-min": "Min. antal er 1.", - "storage-max-files-pattern": "Antal er ikke gyldigt.", - "storage-max-files-required": "Antal er påkrævet.", - "storage-max-records": "Maks. antal poster i lagring", - "storage-max-records-min": "Min. antal poster er 1.", - "storage-max-records-pattern": "Antal er ikke gyldigt.", - "storage-max-records-required": "Maks. antal poster er påkrævet.", - "storage-pack-size": "Maks. antal pakkestørrelse for begivenhed", - "storage-pack-size-min": "Min. antal er 1.", - "storage-pack-size-pattern": "Antal er ikke gyldigt.", - "storage-pack-size-required": "Maks. antal pakkestørrelse for begivenhed er påkrævet.", - "storage-path": "Lagringssti", - "storage-path-required": "Lagringssti er påkrævet.", - "storage-type": "Lagringstype", - "storage-types": { - "file-storage": "Lagring af filter", - "memory-storage": "Lagring af hukommelse" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "ThingsBoard-vært", - "thingsboard-host-required": "Vært er påkrævet.", - "thingsboard-port": "ThingsBoard-port", - "thingsboard-port-max": "Maks. portnummer er 65535.", - "thingsboard-port-min": "Min. portnummer er 1.", - "thingsboard-port-pattern": "Port er ikke gyldig.", - "thingsboard-port-required": "Port er påkrævet.", - "tidy": "Tidy", - "tidy-tip": "Tidy konfig. JSON", - "title-connectors-json": "", - "tls-path-ca-certificate": "Sti til CA-certifikat på gateway", - "tls-path-client-certificate": "Sti til klientcertifikat på gateway", - "tls-path-private-key": "Sti til privat nøgle på gateway", - "toggle-fullscreen": "Skift til fuld skærm", - "transformer-json-config": "Konfiguration JSON*", - "update-config": "Tilføj/opdater konfiguration JSON" + "no-gateway-matching": "" }, "grid": { "delete-item-title": "Er du sikker på, du ønsker at slette dette element?", diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index d4a8cffa53..4f48013f20 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2837,687 +2837,17 @@ "function": "Function" }, "gateway": { - "address": "Address", - "address-required": "Address required", - "add-entry": "Add configuration", - "add-attribute": "Add attribute", - "add-attribute-update": "Add attribute update", - "add-key": "Add key", - "add-timeseries": "Add time series", - "add-mapping": "Add mapping", - "add-slave": "Add Slave", - "arguments": "Arguments", - "add-rpc-method": "Add method", - "add-rpc-request": "Add request", - "add-value": "Add argument", - "baudrate": "Baudrate", - "bytesize": "Bytesize", - "delete-value": "Delete value", - "delete-rpc-method": "Delete method", - "delete-rpc-request": "Delete request", - "delete-attribute-update": "Delete attribute update", - "advanced": "Advanced", - "advanced-connection-settings": "Advanced connection settings", - "attributes": "Attributes", - "attribute-updates": "Attribute updates", - "attribute-filter": "Attribute filter", - "attribute-filter-hint": "Filter for incoming attribute name from platform, supports regular expression.", - "attribute-filter-required": "Attribute filter required.", - "attribute-name-expression": "Attribute name expression", - "attribute-name-expression-required": "Attribute name expression required.", - "attribute-name-expression-hint": "Hint for Attribute name expression", - "basic": "Basic", - "byte-order": "Byte order", - "word-order": "Word order", - "broker": { - "connection": "Connection to broker", - "name": "Broker name", - "name-required": "Broker name required.", - "security-types": { - "anonymous": "Anonymous", - "basic": "Basic", - "certificates": "Certificates" - } - }, - "CA-certificate-path": "Path to CA certificate file", - "path-to-CA-cert-required": "Path to CA certificate file is required.", - "change-connector-title": "Confirm connector change", - "change-connector-text": "Switching connectors will discard any unsaved changes. Continue?", - "checking-device-activity": "Checking device activity", - "command": "Docker commands", - "command-copied-message": "Docker command has been copied to clipboard", - "configuration": "Configuration", - "add-connector": "Add connector", - "connector-add": "Add new connector", - "connector-enabled": "Enable connector", - "connector-name": "Connector name", - "connector-name-required": "Connector name is required.", - "connector-type": "Connector type", - "connector-type-required": "Connector type is required.", - "connectors": "Connectors", - "connectors-config": "Connectors configuration", - "connectors-table-enabled": "Enabled", - "connectors-table-name": "Name", - "connectors-table-type": "Type", - "connectors-table-status": "Status", - "connectors-table-actions": "Actions", - "connectors-table-key": "Key", - "connectors-table-class": "Class", - "connection-timeout": "Connection timeout (s)", - "connect-attempt-time": "Connect attempt time (ms)", - "connect-attempt-count": "Connect attempt count", - "copy-username": "Copy username", - "copy-password": "Copy password", - "copy-client-id": "Copy client ID", - "connector-created": "Connector created", - "connector-updated": "Connector updated", - "rpc-command-save-template": "Save Template", - "rpc-command-send": "Send", - "rpc-command-result": "Response", - "rpc-command-edit-params": "Edit parameters", - "gateway-configuration": "General Configuration", - "docker-label": "Use the following instruction to run IoT Gateway in Docker compose with credentials for selected device", - "install-docker-compose": "Use the instructions to download, install and setup docker compose", - "device-info-settings": "Device info settings", - "device-info": { - "entity-field": "Entity field", - "source": "Source", - "expression": "Value / Expression", - "expression-hint": "Show help", - "name": "Name", - "profile-name": "Profile name", - "device-name-expression": "Device name expression", - "device-name-expression-required": "Device name expression is required.", - "device-profile-expression-required": "Device profile expression is required." - }, - "device-name-filter": "Device name filter", - "device-name-filter-hint": "This field supports Regular expressions to filter incoming data by device name.", - "device-name-filter-required": "Device name filter is required.", - "details": "Details", - "delete-mapping-title": "Delete mapping?", - "delete-slave-title": "Delete slave?", - "divider": "Divider", - "download-configuration-file": "Download configuration file", - "download-docker-compose": "Download docker-compose.yml for your gateway", - "enable-remote-logging": "Enable remote logging", - "ellipsis-chips-text": "+ {{count}} more", - "launch-gateway": "Launch gateway", - "launch-command": "Launch command", - "launch-docker-compose": "Start the gateway using the following command in the terminal from folder with docker-compose.yml file", - "logs-configuration": "Logs configuration", - "create-new-gateway": "Create a new gateway", - "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", - "created-time": "Created time", - "configuration-delete-dialog-header": "Configurations will be deleted", - "configuration-delete-dialog-body": "Turning off Remote Configuration is possible only if there is physical access to the Gateway. All previous configurations will be deleted.

\nTo turn off configuration, enter gateway name below", - "configuration-delete-dialog-input": "Gateway name", - "configuration-delete-dialog-input-required": "Gateway name is mandatory", - "configuration-delete-dialog-confirm": "Turn Off", - "connector-duplicate-name": "Connector with such name already exists.", - "connector-side": "Connector side", - "payload-type": "Payload type", - "platform-side": "Platform side", - "JSON": "JSON", - "JSON-hint": "Converter for this payload type processes MQTT messages in JSON format. It uses JSON Path expressions to extract vital details such as device names, device profile names, attributes, and time series from the message. And regular expressions to get device details from topics.", - "bytes": "Bytes", - "bytes-hint": "Converter for this payload type designed for binary MQTT payloads, this converter directly interprets binary data to retrieve device names and device profile names, along with attributes and time series, using specific byte positions for data extraction.", - "custom": "Custom", - "custom-hint": "This option allows you to use a custom converter for specific data tasks. You need to add your custom converter to the extension folder and enter its class name in the UI settings. Any keys you provide will be sent as configuration to your custom converter.", - "client-cert-path": "Path to client certificate file", - "path-to-client-cert-required": "Path to client certificate file is required.", - "client-id": "Client ID", - "data-conversion": "Data conversion", - "data-mapping": "Data mapping", - "data-mapping-hint": "Data mapping provides the capability to parse and convert the data received from a MQTT client in incoming messages into specific attributes and time series data keys.", - "opcua-data-mapping-hint": "Data mapping provides the capability to parse and convert the data received from a OPCUA server into specific data keys.", - "delete": "Delete configuration", - "delete-attribute": "Delete attribute", - "delete-key": "Delete key", - "delete-timeseries": "Delete time series", - "default": "Default", - "device-node": "Device node", - "device-node-required": "Device node required.", - "device-node-hint": "Path or identifier for device node on OPC UA server. Relative paths from it for attributes and time series can be used.", - "device-name": "Device name", - "device-profile": "Device profile", - "device-name-required": "Device name required", - "device-profile-required": "Device profile required", - "download-tip": "Download configuration file", - "drop-file": "Drop file here or", - "enable": "Enable", - "enable-subscription": "Enable subscription", - "extension": "Extension", - "extension-hint": "Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.", - "extension-required": "Extension is required.", - "extension-configuration": "Extension configuration", - "extension-configuration-hint": "Configuration for convertor", - "fill-connector-defaults": "Fill configuration with default values", - "fill-connector-defaults-hint": "This property allows to fill connector configuration with default values on it's creation.", - "from-device-request-settings": "Input request parsing", - "from-device-request-settings-hint": "These fields support JSONPath expressions to extract a name from incoming message.", - "function-code": "Function code", - "function-codes": { - "read-coils": "01 - Read Coils", - "read-discrete-inputs": "02 - Read Discrete Inputs", - "read-multiple-holding-registers": "03 - Read Multiple Holding Registers", - "read-input-registers": "04 - Read Input Registers", - "write-single-coil": "05 - Write Single Coil", - "write-single-holding-register": "06 - Write Single Holding Register", - "write-multiple-coils": "15 - Write Multiple Coils", - "write-multiple-holding-registers": "16 - Write Multiple Holding Registers" - }, - "to-device-response-settings": "Output request processing", - "to-device-response-settings-hint": "For these fields you can use the following variables and they will be replaced with actual values: ${deviceName}, ${attributeKey}, ${attributeValue}", - "gateway": "Gateway", "gateway-exists": "Device with same name is already exists.", "gateway-name": "Gateway name", "gateway-name-required": "Gateway name is required.", "gateway-saved": "Gateway configuration successfully saved.", - "generate-client-id": "Generate Client ID", - "grpc": "GRPC", - "grpc-keep-alive-timeout": "Keep alive timeout (in ms)", - "grpc-keep-alive-timeout-required": "Keep alive timeout is required", - "grpc-keep-alive-timeout-min": "Keep alive timeout can not be less then 1", - "grpc-keep-alive-timeout-pattern": "Keep alive timeout is not valid", - "grpc-keep-alive": "Keep alive (in ms)", - "grpc-keep-alive-required": "Keep alive is required", - "grpc-keep-alive-min": "Keep alive can not be less then 1", - "grpc-keep-alive-pattern": "Keep alive is not valid", - "grpc-min-time-between-pings": "Min time between pings (in ms)", - "grpc-min-time-between-pings-required": "Min time between pings is required", - "grpc-min-time-between-pings-min": "Min time between pings can not be less then 1", - "grpc-min-time-between-pings-pattern": "Min time between pings is not valid", - "grpc-min-ping-interval-without-data": "Min ping interval without data (in ms)", - "grpc-min-ping-interval-without-data-required": "Min ping interval without data is required", - "grpc-min-ping-interval-without-data-min": "Min ping interval without data can not be less then 1", - "grpc-min-ping-interval-without-data-pattern": "Min ping interval without data is not valid", - "grpc-max-pings-without-data": "Max pings without data", - "grpc-max-pings-without-data-required": "Max pings without data is required", - "grpc-max-pings-without-data-min": "Max pings without data can not be less then 1", - "grpc-max-pings-without-data-pattern": "Max pings without data is not valid", - "info": "Info", - "identity": "Identity", - "inactivity-check-period-seconds": "Inactivity check period (in sec)", - "inactivity-check-period-seconds-required": "Inactivity check period is required", - "inactivity-check-period-seconds-min": "Inactivity check period can not be less then 1", - "inactivity-check-period-seconds-pattern": "Inactivity check period is not valid", - "inactivity-timeout-seconds": "Inactivity timeout (in sec)", - "inactivity-timeout-seconds-required": "Inactivity timeout is required", - "inactivity-timeout-seconds-min": "Inactivity timeout can not be less then 1", - "inactivity-timeout-seconds-pattern": "Inactivity timeout is not valid", - "unit-id": "Unit ID", - "host": "Host", - "host-required": "Host is required.", - "holding_registers": "Holding registers", - "coils_initializer": "Coils initializer", - "input_registers": "Input registers", - "discrete_inputs": "Discrete inputs", - "json-parse": "Not valid JSON.", - "json-required": "Field cannot be empty.", - "JSONPath-hint": "This field supports constants and JSONPath expressions.", - "logs": { - "logs": "Logs", - "days": "days", - "hours": "hours", - "minutes": "minutes", - "seconds": "seconds", - "date-format": "Date format", - "date-format-required": "Date format required", - "log-format": "Log format", - "log-type": "Log type", - "log-format-required": "Log format required", - "remote": "Remote logging", - "remote-logs": "Remote logs", - "local": "Local logging", - "level": "Log level", - "file-path": "File path", - "file-path-required": "File path required", - "saving-period": "Log saving period", - "saving-period-min": "Log saving period can not be less then 1", - "saving-period-required": "Log saving period required", - "backup-count": "Backup count", - "backup-count-min": "Backup count can not be less then 1", - "backup-count-required": "Backup count required" - }, - "max-number-of-workers": "Max number of workers", - "max-number-of-workers-hint": "Maximal number of workers threads for converters \n(The amount of workers changes dynamically, depending on load) \nRecommended amount 50-150.", - "max-number-of-workers-required": "Max number of workers is required.", - "max-messages-queue-for-worker": "Max messages queue per worker", - "max-messages-queue-for-worker-hint": "Maximal messages count that will be in the queue \nfor each converter worker.", - "max-messages-queue-for-worker-required": "Max messages queue per worker is required.", - "method": "Method", - "method-name": "Method name", - "method-required": "Method name is required.", - "min-pack-send-delay": "Min pack send delay (in ms)", - "min-pack-send-delay-required": "Min pack send delay is required", - "min-pack-send-delay-min": "Min pack send delay can not be less then 10", - "min-pack-send-delay-pattern": "Min pack send delay is not valid", - "multiplier": "Multiplier", - "mode": "Mode", - "model-name": "Model name", - "modifier": "Modifier", - "modifier-invalid": "Modifier is not valid", - "mqtt-version": "MQTT version", - "name": "Name", - "name-required": "Name is required.", - "no-attributes": "No attributes", - "no-attribute-updates": "No attribute updates", - "no-connectors": "No connectors", - "no-data": "No configurations", + "gateway": "Gateway", + "launch-gateway": "Launch gateway", + "launch-command": "Launch command", + "create-new-gateway": "Create a new gateway", + "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", "no-gateway-found": "No gateway found.", - "no-gateway-matching": " '{{item}}' not found.", - "no-timeseries": "No time series", - "no-keys": "No keys", - "no-value": "No arguments", - "no-rpc-methods": "No RPC methods", - "no-rpc-requests": "No RPC requests", - "path-hint": "The path is local to the gateway file system", - "path-logs": "Path to log files", - "path-logs-required": "Path is required.", - "password": "Password", - "password-required": "Password is required.", - "permit-without-calls": "Keep alive permit without calls", - "poll-period": "Poll period (ms)", - "poll-period-error": "Poll period should be at least {{min}} (ms).", - "port": "Port", - "port-required": "Port is required.", - "port-limits-error": "Port should be number from {{min}} to {{max}}.", - "private-key-path": "Path to private key file", - "path-to-private-key-required": "Path to private key file is required.", - "parity": "Parity", - "product-code": "Product code", - "product-name": "Product name", - "raw": "Raw", - "retain": "Retain", - "retain-hint": "This flag tells the broker to store the message for a topic\nand ensures any new client subscribing to that topic\nwill receive the stored message.", - "remote": "Remote configuration", - "remote-logging-level": "Logging level", - "remove-entry": "Remove configuration", - "remote-shell": "Remote shell", - "remote-configuration": "Remote Configuration", - "retries": "Retries", - "retries-on-empty": "Retries on empty", - "retries-on-invalid": "Retries on invalid", - "rpc": { - "title": "{{type}} Connector RPC parameters", - "templates-title": "Connector RPC Templates", - "methodFilter": "Method filter", - "method-name": "Method name", - "requestTopicExpression": "Request topic expression", - "responseTopicExpression": "Response topic expression", - "responseTimeout": "Response timeout", - "valueExpression": "Value expression", - "tag": "Tag", - "type": "Type", - "functionCode": "Function Code", - "objectsCount": "Objects Count", - "address": "Address", - "method": "Method", - "requestType": "Request Type", - "requestTimeout": "Request Timeout", - "objectType": "Object type", - "identifier": "Identifier", - "propertyId": "Property ID", - "methodRPC": "Method RPC name", - "withResponse": "With Response", - "characteristicUUID": "Characteristic UUID", - "methodProcessing": "Method Processing", - "nodeID": "Node ID", - "isExtendedID": "Is Extended ID", - "isFD": "Is FD", - "bitrateSwitch": "Bitrate Switch", - "dataInHEX": "Data In HEX", - "dataLength": "Data Length", - "dataByteorder": "Data Byte Order", - "dataBefore": "Data Before", - "dataAfter": "Data After", - "dataExpression": "Data Expression", - "encoding": "Encoding", - "oid": "OID", - "add-oid": "Add OID", - "add-header": "Add header", - "add-security": "Add security", - "remove": "Remove", - "requestFilter": "Request Filter", - "requestUrlExpression": "Request URL Expression", - "httpMethod": "HTTP Method", - "timeout": "Timeout", - "tries": "Tries", - "httpHeaders": "HTTP Headers", - "header-name": "Header name", - "hint": { - "modbus-response-reading": "RPC response will return all subtracted values from all connected devices when the reading functions are selected.", - "modbus-writing-functions": "RPC will write a filled value to all connected devices when the writing functions are selected.", - "opc-method": "A filled method name is the OPC-UA method that will processed on the server side (make sure your node has the requested method)." - }, - "security-name": "Security name", - "value": "Value", - "security": "Security", - "responseValueExpression": "Response Value Expression", - "requestValueExpression": "Request Value Expression", - "arguments": "Arguments", - "add-argument": "Add argument", - "write-property": "Write property", - "read-property": "Read property", - "analog-output": "Analog output", - "analog-input": "Analog input", - "binary-output": "Binary output", - "binary-input": "Binary input", - "binary-value": "Binary value", - "analog-value": "Analog value", - "write": "Write", - "read": "Read", - "scan": "Scan", - "oids": "OIDS", - "set": "Set", - "multiset": "Multiset", - "get": "Get", - "bulk-walk": "Bulk walk", - "table": "Table", - "multi-get": "Multiget", - "get-next": "Get next", - "bulk-get": "Bulk get", - "walk": "Walk", - "save-template": "Save template", - "template-name": "Template name", - "template-name-required": "Template name is required.", - "template-name-duplicate": "Template with such name already exists, it will be updated.", - "command": "Command", - "params": "Params", - "json-value-invalid": "JSON value has an invalid format" - }, - "rpc-methods": "RPC methods", - "rpc-requests": "RPC requests", - "request" : { - "connect-request": "Connect request", - "disconnect-request": "Disconnect request", - "attribute-request": "Attribute request", - "attribute-update": "Attribute update", - "rpc-connection": "RPC command" - }, - "request-type": "Request type", - "requests-mapping": "Requests mapping", - "requests-mapping-hint": "MQTT Connector requests allows you to connect, disconnect, process attribute requests from the device, handle attribute updates on the server and RPC processing configuration.", - "request-topic-expression": "Request topic expression", - "request-client-certificate": "Request client certificate", - "request-topic-expression-required": "Request topic expression is required.", - "response-timeout": "Response timeout (ms)", - "response-timeout-required": "Response timeout is required.", - "response-timeout-limits-error": "Timeout must be more then {{min}} ms.", - "response-topic-Qos": "Response topic QoS", - "response-topic-Qos-hint": "MQTT Quality of Service (QoS) is an agreement between the message sender and receiver that defines the level of delivery guarantee for a specific message.", - "response-topic-expression": "Response topic expression", - "response-topic-expression-required": "Response topic expression is required.", - "response-value-expression": "Response value expression", - "response-value-expression-required": "Response value expression is required.", - "vendor-name": "Vendor name", - "vendor-url": "Vendor URL", - "value": "Value", - "values": "Values", - "value-required": "Value is required.", - "value-expression": "Value expression", - "value-expression-required": "Value expression is required.", - "with-response": "With response", - "without-response": "Without response", - "other": "Other", - "save-tip": "Save configuration file", - "scan-period": "Scan period (ms)", - "scan-period-error": "Scan period should be at least {{min}} (ms).", - "sub-check-period": "Subscription check period (ms)", - "sub-check-period-error": "Subscription check period should be at least {{min}} (ms).", - "security": "Security", - "security-policy": "Security policy", - "security-type": "Security type", - "security-types": { - "access-token": "Access Token", - "username-password": "Username and Password", - "tls": "TLS", - "tls-access-token": "TLS + Access Token", - "tls-private-key": "TLS + Private Key" - }, - "select-connector": "Select connector to display config", - "send-change-data": "Send data only on change", - "send-data-to-platform": "Send data to platform", - "send-data-on-change": "Send data only on change", - "send-change-data-hint": "The values will be saved to the database only if they are different from the corresponding values in the previous converted message. This functionality applies to both attributes and time series in the converter output.", - "server": "Server", - "server-hostname": "Server hostname", - "server-slave": "Server (Slave)", - "servers-slaves": "Servers (Slaves)", - "server-port": "Server port", - "server-url": "Server endpoint url", - "server-connection": "Server Connection", - "server-config": "Server configuration", - "server-slave-config": "Server (Slave) configuration", - "server-url-required": "Server endpoint url is required.", - "stopbits": "Stopbits", - "strict": "Strict", - "set": "Set", - "show-map": "Show map", - "statistics": { - "statistic": "Statistic", - "statistics": "Statistics", - "statistic-commands-empty": "No configured statistic keys found. You can configure them in \"Statistics\" tab in general configuration.", - "statistics-button": "Go to configuration", - "commands": "Commands", - "send-period": "Statistic send period (in sec)", - "send-period-required": "Statistic send period is required", - "send-period-min": "Statistic send period can not be less then 60", - "send-period-pattern": "Statistic send period is not valid", - "check-connectors-configuration": "Check connectors configuration (in sec)", - "max-payload-size-bytes": "Max payload size in bytes", - "max-payload-size-bytes-required": "Max payload size in bytes is required", - "max-payload-size-bytes-min": "Max payload size in bytes can not be less then 100", - "max-payload-size-bytes-pattern": "Max payload size in bytes is not valid", - "min-pack-size-to-send": "Min packet size to send", - "min-pack-size-to-send-required": "Min packet size to send is required", - "min-pack-size-to-send-min": "Min packet size to send can not be less then 100", - "min-pack-size-to-send-pattern": "Min packet size to send is not valid", - "check-connectors-configuration-required": "Check connectors configuration is required", - "check-connectors-configuration-min": "Check connectors configuration can not be less then 1", - "check-connectors-configuration-pattern": "Check connectors configuration is not valid", - "add": "Add command", - "timeout": "Timeout (in sec)", - "timeout-ms": "Timeout (in ms)", - "timeout-required": "Timeout is required", - "timeout-min": "Timeout can not be less then 1", - "timeout-pattern": "Timeout is not valid", - "attribute-name": "Attribute name", - "attribute-name-required": "Attribute name is required", - "command": "Command", - "command-required": "Command is required", - "command-pattern": "Command is not valid", - "remove": "Remove command" - }, - "storage": "Storage", - "storage-max-file-records": "Maximum records in file", - "storage-max-files": "Maximum number of files", - "storage-max-files-min": "Minimum number is 1.", - "storage-max-files-pattern": "Number is not valid.", - "storage-max-files-required": "Number is required.", - "storage-max-records": "Maximum records in storage", - "storage-max-records-min": "Minimum number of records is 1.", - "storage-max-records-pattern": "Number is not valid.", - "storage-max-records-required": "Maximum records is required.", - "storage-read-record-count": "Read record count in storage", - "storage-read-record-count-min": "Minimum number of records is 1.", - "storage-read-record-count-pattern": "Number is not valid.", - "storage-read-record-count-required": "Read record count is required.", - "storage-max-read-record-count": "Max read record count in storage", - "storage-max-read-record-count-min": "Minimum number of records is 1.", - "storage-max-read-record-count-pattern": "Number is not valid.", - "storage-max-read-record-count-required": "Max Read record count is required.", - "storage-data-folder-path": "Data folder path", - "storage-data-folder-path-required": "Data folder path is required.", - "storage-pack-size": "Maximum event pack size", - "storage-pack-size-min": "Minimum number is 1.", - "storage-pack-size-pattern": "Number is not valid.", - "storage-pack-size-required": "Maximum event pack size is required.", - "storage-path": "Storage path", - "storage-path-required": "Storage path is required.", - "storage-type": "Storage type", - "storage-types": { - "file-storage": "File storage", - "memory-storage": "Memory storage", - "sqlite": "SQLITE" - }, - "report-strategy": { - "label": "Report strategy", - "on-change": "On value change", - "on-report-period": "On report period", - "on-change-or-report-period": "On value change or report period", - "report-period": "Report period" - }, - "source-type": { - "msg": "Extract from message", - "topic": "Extract from topic", - "const": "Constant", - "identifier": "Identifier", - "path": "Path" - }, - "workers-settings": "Workers settings", - "thingsboard": "ThingsBoard", - "general": "General", - "timeseries": "Time series", - "key": "Key", - "keys": "Keys", - "key-required": "Key is required.", - "thingsboard-host": "Platform host", - "thingsboard-host-required": "Host is required.", - "thingsboard-port": "Platform port", - "thingsboard-port-max": "Maximum port number is 65535.", - "thingsboard-port-min": "Minimum port number is 1.", - "thingsboard-port-pattern": "Port is not valid.", - "thingsboard-port-required": "Port is required.", - "tidy": "Tidy", - "tidy-tip": "Tidy config JSON", - "timeout": "Timeout (ms)", - "timeout-error": "Timeout should be at least {{min}} (ms).", - "title-connectors-json": "Connector {{typeName}} configuration", - "type": "Type", - "topic-filter": "Topic filter", - "topic-required": "Topic filter is required.", - "tls-path-ca-certificate": "Path to CA certificate on gateway", - "tls-path-client-certificate": "Path to client certificate on gateway", - "tls-connection": "TLS Connection", - "master-connections": "Master Connections", - "method-filter": "Method filter", - "method-filter-hint": "Regular expression to filter incoming RPC method from platform.", - "method-filter-required": "Method filter is required.", - "messages-ttl-check-in-hours": "Messages TTL check in hours", - "messages-ttl-check-in-hours-required": "Messages TTL check in hours is required.", - "messages-ttl-check-in-hours-min": "Min number is 1.", - "messages-ttl-check-in-hours-pattern": "Number is not valid.", - "messages-ttl-in-days": "Messages TTL in days", - "messages-ttl-in-days-required": "Messages TTL in days is required.", - "messages-ttl-in-days-min": "Min number is 1.", - "messages-ttl-in-days-pattern": "Number is not valid.", - "mqtt-qos": "QoS", - "mqtt-qos-required": "QoS is required", - "mqtt-qos-range": "QoS values range is from 0 to 1", - "qos": { - "at-most-once": "0 - At most once", - "at-least-once": "1 - At least once", - "exactly-once": "2 - Exactly once" - }, - "objects-count": "Objects count", - "objects-count-required": "Objects count is required", - "wait-after-failed-attempts": "Wait after failed attempts (ms)", - "tls-path-private-key": "Path to private key on gateway", - "toggle-fullscreen": "Toggle fullscreen", - "transformer-json-config": "Configuration JSON*", - "update-config": "Add/update configuration JSON", - "username": "Username", - "username-required": "Username is required.", - "unit-id-required": "Unit ID is required.", - "write-coil": "Write Coil", - "write-coils": "Write Coils", - "write-register": "Write Register", - "write-registers": "Write Registers", - "hints": { - "modbus-master": "Configuration sections for connecting to Modbus servers and reading data from them.", - "modbus-server": "Configuration section for the Modbus server, storing data and sending updates to the platform when changes occur or at fixed intervals.", - "remote-configuration": "Enables remote configuration and management of the gateway", - "remote-shell": "Enables remote control of the operating system with the gateway from the Remote Shell widget", - "host": "Hostname or IP address of platform server", - "port": "Port of MQTT service on platform server", - "token": "Access token for the gateway from platform server", - "client-id": "MQTT client id for the gateway form platform server", - "username": "MQTT username for the gateway form platform server", - "password": "MQTT password for the gateway form platform server", - "ca-cert": "Path to CA certificate file", - "date-form": "Date format in log message", - "data-folder": "Path to the folder that will contain data (Relative or Absolute)", - "log-format": "Log message format", - "remote-log": "Enables remote logging and logs reading from the gateway", - "backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted", - "storage": "Provides configuration for saving incoming data before it is sent to the platform", - "max-file-count": "Maximum number of files that will be created", - "max-read-count": "Number of messages to retrieve from the storage and send to platform", - "max-records": "Maximum count of records that will be stored in one file", - "read-record-count": "Number of messages to retrieve from the storage and send to platform", - "max-records-count": "Maximum number of data entries in storage before sending to platform", - "ttl-check-hour": "How often will the Gateway check data for obsolescence", - "ttl-messages-day": "Maximum number of days that the storage will retain data", - "commands": "Commands for collecting additional statistic", - "attribute": "Statistic telemetry key", - "timeout": "Timeout for command executing", - "command": "The result of the command execution, will be used as the value for telemetry", - "check-device-activity": "Enables monitor the activity of each connected device", - "inactivity-timeout": "Time after whose the gateway will disconnect device", - "inactivity-period": "Periodicity of device activity check", - "minimal-pack-delay": "Delay between sending packs of messages (Decreasing this setting results in increased CPU usage)", - "qos": "Quality of Service in MQTT messaging (0 - at most once, 1 - at least once)", - "server-port": "Network port on which GRPC server will listen for incoming connections.", - "grpc-keep-alive-timeout": "Maximum time the server should wait for a keepalive ping response before considering the connection dead.", - "grpc-keep-alive": "Duration between two successive keepalive ping messages when there is no active RPC call.", - "grpc-min-time-between-pings": "Minimum amount of time the server should wait between sending keepalive ping messages", - "grpc-max-pings-without-data": "Maximum number of keepalive ping messages that the server can send without receiving any data before it considers the connection dead.", - "grpc-min-ping-interval-without-data": "Minimum amount of time the server should wait between sending keepalive ping messages when there is no data being sent or received.", - "permit-without-calls": "Allow server to keep the GRPC connection alive even when there are no active RPC calls.", - "path-in-os": "Path in gateway os.", - "memory": "Your data will be stored in the in-memory queue, it is a fastest but no persistence guarantee.", - "file": "Your data will be stored in separated files and will be saved even after the gateway restart.", - "sqlite": "Your data will be stored in file based database. And will be saved even after the gateway restart.", - "opc-timeout": "Timeout in milliseconds for connecting to OPC-UA server.", - "security-policy": "Security Policy defines the security mechanisms to be applied.", - "scan-period": "Period in milliseconds to rescan the server.", - "sub-check-period": "Period to check the subscriptions in the OPC-UA server.", - "enable-subscription": "If true - the gateway will subscribe to interesting nodes and wait for data update and if false - the gateway will rescan OPC-UA server every scanPeriodInMillis.", - "show-map": "Show nodes on scanning.", - "method-name": "Name of method on OPC-UA server.", - "arguments": "Arguments for the method (will be overwritten by arguments from the RPC request).", - "min-pack-size-to-send": "Minimum package size for sending.", - "max-payload-size-bytes": "Maximum package size in bytes", - "poll-period": "Period in milliseconds to read data from nodes.", - "modbus": { - "framer-type": "Type of a framer (Socket, RTU, or ASCII), if needed.", - "host": "Hostname or IP address of Modbus server.", - "port": "Modbus server port for connection.", - "unit-id": "Modbus slave ID.", - "connection-timeout": "Connection timeout (in seconds) for the Modbus server.", - "byte-order": "Byte order for reading data.", - "word-order": "Word order when reading multiple registers.", - "retries": "Retrying data transmission to the master. Acceptable values: true or false.", - "retries-on-empty": "Retry sending data to the master if the data is empty.", - "retries-on-invalid": "Retry sending data to the master if it fails.", - "poll-period": "Period in milliseconds to check attributes and telemetry on the slave.", - "connect-attempt-time": "A waiting period in milliseconds before establishing a connection to the master.", - "connect-attempt-count": "The number of connection attempts made through the gateway.", - "wait-after-failed-attempts": "A waiting period in milliseconds before attempting to send data to the master.", - "serial-port": "Serial port for connection.", - "baudrate": "Baud rate for the serial device.", - "stopbits": "The number of stop bits sent after each character in a message to indicate the end of the byte.", - "bytesize": "The number of bits in a byte of serial data. This can be one of 5, 6, 7, or 8.", - "parity": "The type of checksum used to verify data integrity. Options: (E)ven, (O)dd, (N)one.", - "strict": "Use inter-character timeout for baudrates ≤ 19200.", - "objects-count": "Depends on the selected type.", - "address": "Register address to verify.", - "key": "Key to be used as the attribute key for the platform instance.", - "data-keys": "For more information about function codes and data types click on help icon", - "modifier": "The retrieved value will be adjusted (by multiplying or dividing it) based on the specified modifier value." - } - } + "no-gateway-matching": " '{{item}}' not found." }, "grid": { "delete-item-title": "Are you sure you want to delete this item?", diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 86539fef58..f653e7179a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -2628,254 +2628,17 @@ "function": "Función" }, "gateway": { - "add-entry": "Añadir configuración", - "advanced": "Avanzado", - "checking-device-activity": "Probando actividad de dispositivo", - "command": "Comandos Docker", - "command-copied-message": "Se han copiado los comandos al portapapeles", - "configuration": "Configuración", - "connector-add": "Añadir conector", - "connector-enabled": "Activar conector", - "connector-name": "Nombre conector", - "connector-name-required": "Se requiere nombre conector.", - "connector-type": "Tipo conector", - "connector-type-required": "Se requiere tipo conector.", - "connectors": "Conectores", - "connectors-config": "Configuración de conectores", - "connectors-table-enabled": "Enabled", - "connectors-table-name": "Nombre", - "connectors-table-type": "Tipo", - "connectors-table-status": "Estado", - "connectors-table-actions": "Acciones", - "connectors-table-key": "Clave", - "connectors-table-class": "Clase", - "rpc-command-send": "Enviar", - "rpc-command-result": "Resultado", - "rpc-command-edit-params": "Editar parametros", - "gateway-configuration": "Configuración General", - "create-new-gateway": "Crear un gateway nuevo", - "create-new-gateway-text": "Crear un nuevo gateway con el nombre: '{{gatewayName}}'?", - "created-time": "Hora de creación", - "configuration-delete-dialog-header": "Las configuraciones se borrarán", - "configuration-delete-dialog-body": "Sólo es posible desactivar la configuración remota, si hay acceso físico al gateway. Se borrarán todas las configuraciones previas.

\nPara desactivar la configuración, introduce el nombre del gateway aquí", - "configuration-delete-dialog-input": "Nombre Gateway", - "configuration-delete-dialog-input-required": "Se requiere nombre de gateway", - "configuration-delete-dialog-confirm": "Desactivar", - "delete": "Borrar configuración", - "download-tip": "Descargar fichero de configuración", - "drop-file": "Arrastra un fichero o", - "gateway": "Gateway", "gateway-exists": "Ya existe un dispositivo con el mismo nombre.", "gateway-name": "Nombre de Gateway", "gateway-name-required": "Se requiere un nombre de gateway.", "gateway-saved": "Configuración de gateway grabada satisfactoriamente.", - "grpc": "GRPC", - "grpc-keep-alive-timeout": "Timeout Keep alive (en ms)", - "grpc-keep-alive-timeout-required": "Se requiere Timeout Keep alive", - "grpc-keep-alive-timeout-min": "El valor no puede ser menor de 1", - "grpc-keep-alive-timeout-pattern": "El valor no es válido", - "grpc-keep-alive": "Keep alive (en ms)", - "grpc-keep-alive-required": "Se requiere keep alive", - "grpc-keep-alive-min": "El valor no puede ser menor de 1", - "grpc-keep-alive-pattern": "El valor keep alive no es válido", - "grpc-min-time-between-pings": "Tiempo mínimo entre pings (en ms)", - "grpc-min-time-between-pings-required": "Se requiere tiempo mínimo entre pings", - "grpc-min-time-between-pings-min": "El valor no puede ser menor de 1", - "grpc-min-time-between-pings-pattern": "El valor de tiempo mínimo entre pings no es válido", - "grpc-min-ping-interval-without-data": "Intervalo mínimo sin datos (en ms)", - "grpc-min-ping-interval-without-data-required": "Se requiere intervalo", - "grpc-min-ping-interval-without-data-min": "El valor no puede ser menor de 1", - "grpc-min-ping-interval-without-data-pattern": "El valor de intervalo no es válido", - "grpc-max-pings-without-data": "Intervalo máximo sin datos", - "grpc-max-pings-without-data-required": "Se requiere intervalo", - "grpc-max-pings-without-data-min": "El valor no puede ser menor de 1", - "grpc-max-pings-without-data-pattern": "El valor de intervalo no es válido", - "inactivity-check-period-seconds": "Periodo de control de inactividad (en segundos)", - "inactivity-check-period-seconds-required": "Se requiere periodo", - "inactivity-check-period-seconds-min": "El valor no puede ser menor de 1", - "inactivity-check-period-seconds-pattern": "El valor del periodo no es válido", - "inactivity-timeout-seconds": "Timeout de inactividad (en segundos)", - "inactivity-timeout-seconds-required": "Se requiere timeout de inactividad", - "inactivity-timeout-seconds-min": "El valor no puede ser menor de 1", - "inactivity-timeout-seconds-pattern": "El valor de inactividad no es válido", - "json-parse": "JSON no válido.", - "json-required": "El campo no puede estar vacío.", - "logs": { - "logs": "Registros", - "days": "días", - "hours": "horas", - "minutes": "minutos", - "seconds": "segundos", - "date-format": "Formato de fecha", - "date-format-required": "Se requiere formato de fecha", - "log-format": "Formato de registro", - "log-type": "Tipo de registro", - "log-format-required": "Se requiere tipo de registro", - "remote": "Registro remoto", - "remote-logs": "Registro remoto", - "local": "Registro local", - "level": "Nivel de registro", - "file-path": "Ruta de fichero", - "file-path-required": "Se requiere ruta de fichero", - "saving-period": "Periodo de guardado de registros", - "saving-period-min": "El periodo no puede ser menor que 1", - "saving-period-required": "Se requiere periodo de guardado", - "backup-count": "Número de backups", - "backup-count-min": "El número de backups no puede ser menor que 1", - "backup-count-required": "Se requiere número de backups" - }, - "min-pack-send-delay": "Tiempo de espera, envío de paquetes (en ms)", - "min-pack-send-delay-required": "Se requiere tiempo de espera", - "min-pack-send-delay-min": "El tiempo de espera no puede ser menor que 0", - "no-connectors": "No hay conectores", - "no-data": "No hay configuraciones", + "gateway": "Gateway", + "launch-gateway": "Iniciar gateway", + "launch-command": "Comando de lanzamiento", + "create-new-gateway": "Crear un gateway nuevo", + "create-new-gateway-text": "¿Está seguro de que desea crear un nuevo gateway con el nombre: '{{gatewayName}}'?", "no-gateway-found": "No se ha encontrado ningún gateway.", - "no-gateway-matching": " '{{item}}' no encontrado.", - "path-logs": "Ruta a los archivos de log", - "path-logs-required": "Ruta requerida.", - "permit-without-calls": "Permitir Keep alive si llamadas", - "remote": "Configuración remota", - "remote-logging-level": "Nivel de logging", - "remove-entry": "Borrar configuración", - "remote-shell": "Consola remota", - "remote-configuration": "Configuración remota", - "other": "otros", - "save-tip": "Grabar fichero de configuración", - "security-type": "Tipo de seguridad", - "security-types": { - "access-token": "Tóken de acceso", - "username-password": "Usuario y contraseña", - "tls": "TLS", - "tls-access-token": "TLS + Tóken de acceso", - "tls-private-key": "TLS + Clave privada" - }, - "server-port": "Puerto del servidor", - "statistics": { - "statistic": "Estadística", - "statistics": "Estadísticas", - "statistic-commands-empty": "No hay estadísticas", - "commands": "Comandos", - "send-period": "Periodo de envío de estadísticas (en segundos)", - "send-period-required": "Se requiere periodo de envío", - "send-period-min": "El periodo de envío no puede ser menor de 60", - "send-period-pattern": "El periodo de envío no es válido", - "check-connectors-configuration": "Revisar configuración de conectores (en segundos)", - "check-connectors-configuration-required": "Se requiere un valor", - "check-connectors-configuration-min": "El valor no puede ser menor de 1", - "check-connectors-configuration-pattern": "La configuración no es válida", - "add": "Añadir comando", - "timeout": "Timeout", - "timeout-ms": "Timeout (en ms)", - "timeout-required": "Se requiere timeout", - "timeout-min": "El timeout no puede ser menor de 1", - "timeout-pattern": "El timeout no es válido", - "attribute-name": "Nombre de atributo", - "attribute-name-required": "Se requiere nombre de atributo", - "command": "Comando", - "command-required": "Se requiere comando", - "remove": "Borrar comando" - }, - "storage": "Grabación", - "storage-max-file-records": "Número máximo de registros en fichero", - "storage-max-files": "Número máximo de ficheros", - "storage-max-files-min": "El número mínimo es 1.", - "storage-max-files-pattern": "Número no válido.", - "storage-max-files-required": "Se requiere número.", - "storage-max-records": "Máximo de registros en el almacén", - "storage-max-records-min": "El número mínimo es 1.", - "storage-max-records-pattern": "Número no válido.", - "storage-max-records-required": "Se requiere número.", - "storage-read-record-count": "Leer número de entradas en almacén", - "storage-read-record-count-min": "El número mínimo de entradas es 1.", - "storage-read-record-count-pattern": "El número no es válido.", - "storage-read-record-count-required": "Se requiere número de entradas.", - "storage-max-read-record-count": "Número máximo de entradas en el almacén", - "storage-max-read-record-count-min": "El número mínimo es 1.", - "storage-max-read-record-count-pattern": "El número no es válido", - "storage-max-read-record-count-required": "Se requiere número máximo de entradas.", - "storage-data-folder-path": "Ruta de carpeta de datos", - "storage-data-folder-path-required": "Se requiere ruta.", - "storage-pack-size": "Tamaño máximo de eventos", - "storage-pack-size-min": "El número mínimo es 1.", - "storage-pack-size-pattern": "Número no válido.", - "storage-pack-size-required": "Se requiere número.", - "storage-path": "Ruta de almacén", - "storage-path-required": "Se requiere ruta de almacén.", - "storage-type": "Tipo de almacén", - "storage-types": { - "file-storage": "Almacén en fichero", - "memory-storage": "Almacén en memoria", - "sqlite": "SQLITE" - }, - "thingsboard": "ThingsBoard", - "general": "General", - "thingsboard-host": "Host ThingsBoard", - "thingsboard-host-required": "Se requiere Host.", - "thingsboard-port": "Puerto ThingsBoard", - "thingsboard-port-max": "El puerto máximo es 65535.", - "thingsboard-port-min": "El puerto mínimo es 1.", - "thingsboard-port-pattern": "Puerto no válido.", - "thingsboard-port-required": "Se requiere puerto.", - "tidy": "Tidy", - "tidy-tip": "Tidy JSON", - "title-connectors-json": "Configuración conector {{typeName}}", - "tls-path-ca-certificate": "Ruta al certificado CA en el gateway", - "tls-path-client-certificate": "Ruta al certificado cliente en el gateway", - "messages-ttl-check-in-hours": "Comprobación de TTL de mensajes en horas", - "messages-ttl-check-in-hours-required": "Campo requerido.", - "messages-ttl-check-in-hours-min": "El mínimo es 1.", - "messages-ttl-check-in-hours-pattern": "El número no es válido.", - "messages-ttl-in-days": "TTL (Time to live) de mensages en días", - "messages-ttl-in-days-required": "Se requiere TTL de mensajes.", - "messages-ttl-in-days-min": "El número mínimo es 1.", - "messages-ttl-in-days-pattern": "El número no es válido.", - "mqtt-qos": "QoS", - "mqtt-qos-required": "Se requiere QoS", - "mqtt-qos-range": "El rango de valores es desde 0 a 1", - "tls-path-private-key": "Ruta a la clave privada en el gateway", - "toggle-fullscreen": "Pantalla completa fullscreen", - "transformer-json-config": "Configuración JSON*", - "update-config": "Añadir/actualizar configuración JSON", - "hints": { - "remote-configuration": "Habilita la administración y configuración remota del gateway", - "remote-shell": "Habilita el control remoto del sistema operativo del gateway desde el widget terminal remoto", - "host": "Hostname o dirección IP del servidor Thingsboard", - "port": "Puerto del servicio MQTT en el servidor Thingsboard", - "token": "Access token para el gateway", - "client-id": "ID de cliente MQTT para el gateway", - "username": "Usuario MQTT para el gateway", - "password": "Contraseña MQTT para el gateway", - "ca-cert": "Ruta al fichero del certificado CA", - "date-form": "Formato de fecha en los mensajes de registro", - "data-folder": "Ruta a la carpeta que contendrá los datos (Relativa o absoluta)", - "log-format": "Formato de mensajes en registro", - "remote-log": "Habilita el registro remoto y la posterior lectura desde el gateway", - "backup-count": "Si el contaje de copias de seguridad es mayor que 0, cuando se realice una renovación, no se conservan más que los archivos de recuento de copias de seguridad, los más antíguos se eliminarán", - "storage": "Provee la configuración para el grabado de datos entrantes antes de que se envíen a la plataforma", - "max-file-count": "Número máximo de ficheros que se crearán", - "max-read-count": "Númeo máximo de mensajes a obtener desde el disco y enviados a la plataforma", - "max-records": "Número máximo de registros que se guardarán en un solo fichero", - "read-record-count": "Número de mensages a obtener desde el almacenamiento y enviados a la plataforma", - "max-records-count": "Número máximo de datos en almacenamiento antes de enviar a la plataforma", - "ttl-check-hour": "Con qué frecuencia el gateway comprobará si los datos están obsoletos", - "ttl-messages-day": "Número máximo de días para la retención de datos en el almacén", - "commands": "Comandos para recoger estadísticas adicionales", - "attribute": "Clave de telemetría para estadísticas", - "timeout": "Timeout para la ejecución de comandos", - "command": "El resultado de la ejecución del comando, se usará como valor para la telemetría", - "check-device-activity": "Habilita la monitorización de cada uno de los dispositivos conectados", - "inactivity-timeout": "Tiempo tras que el gateway desconectará el dispositivo", - "inactivity-period": "Periodo de monitorización de actividad en el dispositivo", - "minimal-pack-delay": "Tiempo de espera entre envío de paquetes de mensajes (Un valor muy bajo, resultará en un aumento de uso de la CPU en el gateway)", - "qos": "Quality of Service en los mensajes MQTT (0 - at most once, 1 - at least once)", - "server-port": "Puerto de red en el cual el servidor GRPC escuchará conexiones entrantes.", - "grpc-keep-alive-timeout": "Tiempo máximo, el cual el servidor esperara un ping keepalive antes de considerar la conexión terminada.", - "grpc-keep-alive": "Duración entre dos pings keepalive cuando no haya llamada RPC activa.", - "grpc-min-time-between-pings": "Mínimo tiempo que el servidor debe esperar entre envíos de mensajes de ping", - "grpc-max-pings-without-data": "Número máximo de pings keepalive que el servidor puede enviar sin recibir ningún dato antes de considerar la conexión terminada.", - "grpc-min-ping-interval-without-data": "Mínimo tiempo que el servidor debe esperar entre envíos de ping keepalive cuando no haya ningún dato en envío o recepción.", - "permit-without-calls": "Permitir al servidor mantener la conexión GRPC abierta, cuando no haya llamadas RPC activas." - } + "no-gateway-matching": "No se encontró '{{item}}'." }, "grid": { "delete-item-title": "¿Quieres eliminar este item?", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index 6b8dc130d4..8d2a1647a4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -1584,78 +1584,15 @@ "function": "기능" }, "gateway": { - "add-entry": "설정 추가", - "connector-add": "새로운 연결자 추가", - "connector-enabled": "Enable connector", - "connector-name": "Connector name", - "connector-name-required": "Connector name is required.", - "connector-type": "Connector type", - "connector-type-required": "Connector type is required.", - "connectors": "Connectors configuration", "create-new-gateway": "Create a new gateway", "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", - "delete": "Delete configuration", - "download-tip": "Download configuration file", "gateway": "Gateway", "gateway-exists": "Device with same name is already exists.", "gateway-name": "Gateway name", "gateway-name-required": "Gateway name is required.", "gateway-saved": "Gateway configuration successfully saved.", - "json-parse": "Not valid JSON.", - "json-required": "Field cannot be empty.", - "no-connectors": "No connectors", - "no-data": "No configurations", "no-gateway-found": "No gateway found.", - "no-gateway-matching": " '{{item}}' not found.", - "path-logs": "Path to log files", - "path-logs-required": "Path is required.", - "remote": "Remote configuration", - "remote-logging-level": "Logging level", - "remove-entry": "Remove configuration", - "save-tip": "Save configuration file", - "security-type": "Security type", - "security-types": { - "access-token": "Access Token", - "tls": "TLS" - }, - "storage": "Storage", - "storage-max-file-records": "Maximum records in file", - "storage-max-files": "Maximum number of files", - "storage-max-files-min": "Minimum number is 1.", - "storage-max-files-pattern": "Number is not valid.", - "storage-max-files-required": "Number is required.", - "storage-max-records": "Maximum records in storage", - "storage-max-records-min": "Minimum number of records is 1.", - "storage-max-records-pattern": "Number is not valid.", - "storage-max-records-required": "Maximum records is required.", - "storage-pack-size": "Maximum event pack size", - "storage-pack-size-min": "Minimum number is 1.", - "storage-pack-size-pattern": "Number is not valid.", - "storage-pack-size-required": "Maximum event pack size is required.", - "storage-path": "Storage path", - "storage-path-required": "Storage path is required.", - "storage-type": "Storage type", - "storage-types": { - "file-storage": "File storage", - "memory-storage": "Memory storage" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "ThingsBoard host", - "thingsboard-host-required": "Host is required.", - "thingsboard-port": "ThingsBoard port", - "thingsboard-port-max": "Maximum port number is 65535.", - "thingsboard-port-min": "Minimum port number is 1.", - "thingsboard-port-pattern": "Port is not valid.", - "thingsboard-port-required": "Port is required.", - "tidy": "Tidy", - "tidy-tip": "Tidy config JSON", - "title-connectors-json": "Connector {{typeName}} configuration", - "tls-path-ca-certificate": "Path to CA certificate on gateway", - "tls-path-client-certificate": "Path to client certificate on gateway", - "tls-path-private-key": "Path to private key on gateway", - "toggle-fullscreen": "Toggle fullscreen", - "transformer-json-config": "Configuration JSON*", - "update-config": "Add/update configuration JSON" + "no-gateway-matching": " '{{item}}' not found." }, "grid": { "delete-item-title": "이 항목을 삭제 하시겠습니까?", diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index df1659e112..cb3a96f989 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -3128,255 +3128,15 @@ "function": "Funkcija" }, "gateway": { - "add-entry": "Add configuration", - "advanced": "Advanced", - "checking-device-activity": "Checking device activity", - "command": "Docker commands", - "command-copied-message": "Docker command has been copied to clipboard", - "configuration": "Configuration", - "connector-add": "Add new connector", - "connector-enabled": "Enable connector", - "connector-name": "Connector name", - "connector-name-required": "Connector name is required.", - "connector-type": "Connector type", - "connector-type-required": "Connector type is required.", - "connectors": "Connectors", - "connectors-config": "Connectors configuration", - "connectors-table-enabled": "Enabled", - "connectors-table-name": "Name", - "connectors-table-type": "Type", - "connectors-table-status": "Status", - "connectors-table-actions": "Actions", - "connectors-table-key": "Key", - "connectors-table-class": "Class", - "rpc-command-send": "Send", - "rpc-command-result": "Result", - "rpc-command-edit-params": "Edit parameters", - "gateway-configuration": "General Configuration", - "docker-label": "In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.", - "create-new-gateway": "Create a new gateway", - "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", - "created-time": "Created time", - "configuration-delete-dialog-header": "Configurations will be deleted", - "configuration-delete-dialog-body": "Turning off Remote Configuration is possible only if there is physical access to the Gateway. All previous configurations will be deleted.

\nTo turn off configuration, enter gateway name below", - "configuration-delete-dialog-input": "Gateway name", - "configuration-delete-dialog-input-required": "Gateway name is mandatory", - "configuration-delete-dialog-confirm": "Turn Off", - "delete": "Delete configuration", - "download-tip": "Download configuration file", - "drop-file": "Drop file here or", - "gateway": "Gateway", "gateway-exists": "Device with same name is already exists.", "gateway-name": "Gateway name", "gateway-name-required": "Gateway name is required.", "gateway-saved": "Gateway configuration successfully saved.", - "grpc": "GRPC", - "grpc-keep-alive-timeout": "Keep alive timeout (in ms)", - "grpc-keep-alive-timeout-required": "Keep alive timeout is required", - "grpc-keep-alive-timeout-min": "Keep alive timeout can not be less then 1", - "grpc-keep-alive-timeout-pattern": "Keep alive timeout is not valid", - "grpc-keep-alive": "Keep alive (in ms)", - "grpc-keep-alive-required": "Keep alive is required", - "grpc-keep-alive-min": "Keep alive can not be less then 1", - "grpc-keep-alive-pattern": "Keep alive is not valid", - "grpc-min-time-between-pings": "Min time between pings (in ms)", - "grpc-min-time-between-pings-required": "Min time between pings is required", - "grpc-min-time-between-pings-min": "Min time between pings can not be less then 1", - "grpc-min-time-between-pings-pattern": "Min time between pings is not valid", - "grpc-min-ping-interval-without-data": "Min ping interval without data (in ms)", - "grpc-min-ping-interval-without-data-required": "Min ping interval without data is required", - "grpc-min-ping-interval-without-data-min": "Min ping interval without data can not be less then 1", - "grpc-min-ping-interval-without-data-pattern": "Min ping interval without data is not valid", - "grpc-max-pings-without-data": "Max pings without data", - "grpc-max-pings-without-data-required": "Max pings without data is required", - "grpc-max-pings-without-data-min": "Max pings without data can not be less then 1", - "grpc-max-pings-without-data-pattern": "Max pings without data is not valid", - "inactivity-check-period-seconds": "Inactivity check period (in sec)", - "inactivity-check-period-seconds-required": "Inactivity check period is required", - "inactivity-check-period-seconds-min": "Inactivity check period can not be less then 1", - "inactivity-check-period-seconds-pattern": "Inactivity check period is not valid", - "inactivity-timeout-seconds": "Inactivity timeout (in sec)", - "inactivity-timeout-seconds-required": "Inactivity timeout is required", - "inactivity-timeout-seconds-min": "Inactivity timeout can not be less then 1", - "inactivity-timeout-seconds-pattern": "Inactivity timeout is not valid", - "json-parse": "Not valid JSON.", - "json-required": "Field cannot be empty.", - "logs": { - "logs": "Logs", - "days": "days", - "hours": "hours", - "minutes": "minutes", - "seconds": "seconds", - "date-format": "Date format", - "date-format-required": "Date format required", - "log-format": "Log format", - "log-type": "Log type", - "log-format-required": "Log format required", - "remote": "Remote logging", - "remote-logs": "Remote logs", - "local": "Local logging", - "level": "Log level", - "file-path": "File path", - "file-path-required": "File path required", - "saving-period": "Log saving period", - "saving-period-min": "Log saving period can not be less then 1", - "saving-period-required": "Log saving period required", - "backup-count": "Backup count", - "backup-count-min": "Backup count can not be less then 1", - "backup-count-required": "Backup count required" - }, - "min-pack-send-delay": "Min pack send delay (in ms)", - "min-pack-send-delay-required": "Min pack send delay is required", - "min-pack-send-delay-min": "Min pack send delay can not be less then 0", - "no-connectors": "No connectors", - "no-data": "No configurations", + "gateway": "Gateway", + "create-new-gateway": "Create a new gateway", + "create-new-gateway-text": "Are you sure you want create a new gateway with name: '{{gatewayName}}'?", "no-gateway-found": "No gateway found.", - "no-gateway-matching": " '{{item}}' not found.", - "path-logs": "Path to log files", - "path-logs-required": "Path is required.", - "permit-without-calls": "Keep alive permit without calls", - "remote": "Remote configuration", - "remote-logging-level": "Logging level", - "remove-entry": "Remove configuration", - "remote-shell": "Remote shell", - "remote-configuration": "Remote Configuration", - "other": "Other", - "save-tip": "Save configuration file", - "security-type": "Security type", - "security-types": { - "access-token": "Access Token", - "username-password": "Username and Password", - "tls": "TLS", - "tls-access-token": "TLS + Access Token", - "tls-private-key": "TLS + Private Key" - }, - "server-port": "Server port", - "statistics": { - "statistic": "Statistic", - "statistics": "Statistics", - "statistic-commands-empty": "No statistics available", - "commands": "Commands", - "send-period": "Statistic send period (in sec)", - "send-period-required": "Statistic send period is required", - "send-period-min": "Statistic send period can not be less then 60", - "send-period-pattern": "Statistic send period is not valid", - "check-connectors-configuration": "Check connectors configuration (in sec)", - "check-connectors-configuration-required": "Check connectors configuration is required", - "check-connectors-configuration-min": "Check connectors configuration can not be less then 1", - "check-connectors-configuration-pattern": "Check connectors configuration is not valid", - "add": "Add command", - "timeout": "Timeout", - "timeout-ms": "Timeout (in ms)", - "timeout-required": "Timeout is required", - "timeout-min": "Timeout can not be less then 1", - "timeout-pattern": "Timeout is not valid", - "attribute-name": "Attribute name", - "attribute-name-required": "Attribute name is required", - "command": "Command", - "command-required": "Command is required", - "remove": "Remove command" - }, - "storage": "Storage", - "storage-max-file-records": "Maximum records in file", - "storage-max-files": "Maximum number of files", - "storage-max-files-min": "Minimum number is 1.", - "storage-max-files-pattern": "Number is not valid.", - "storage-max-files-required": "Number is required.", - "storage-max-records": "Maximum records in storage", - "storage-max-records-min": "Minimum number of records is 1.", - "storage-max-records-pattern": "Number is not valid.", - "storage-max-records-required": "Maximum records is required.", - "storage-read-record-count": "Read record count in storage", - "storage-read-record-count-min": "Minimum number of records is 1.", - "storage-read-record-count-pattern": "Number is not valid.", - "storage-read-record-count-required": "Read record count is required.", - "storage-max-read-record-count": "Max read record count in storage", - "storage-max-read-record-count-min": "Minimum number of records is 1.", - "storage-max-read-record-count-pattern": "Number is not valid.", - "storage-max-read-record-count-required": "Max Read record count is required.", - "storage-data-folder-path": "Data folder path", - "storage-data-folder-path-required": "Data folder path is required.", - "storage-pack-size": "Maximum event pack size", - "storage-pack-size-min": "Minimum number is 1.", - "storage-pack-size-pattern": "Number is not valid.", - "storage-pack-size-required": "Maximum event pack size is required.", - "storage-path": "Storage path", - "storage-path-required": "Storage path is required.", - "storage-type": "Storage type", - "storage-types": { - "file-storage": "File storage", - "memory-storage": "Memory storage", - "sqlite": "SQLITE" - }, - "thingsboard": "ThingsBoard", - "general": "General", - "thingsboard-host": "ThingsBoard host", - "thingsboard-host-required": "Host is required.", - "thingsboard-port": "ThingsBoard port", - "thingsboard-port-max": "Maximum port number is 65535.", - "thingsboard-port-min": "Minimum port number is 1.", - "thingsboard-port-pattern": "Port is not valid.", - "thingsboard-port-required": "Port is required.", - "tidy": "Tidy", - "tidy-tip": "Tidy config JSON", - "title-connectors-json": "Connector {{typeName}} configuration", - "tls-path-ca-certificate": "Path to CA certificate on gateway", - "tls-path-client-certificate": "Path to client certificate on gateway", - "messages-ttl-check-in-hours": "Messages TTL check in hours", - "messages-ttl-check-in-hours-required": "Messages TTL check in hours is required.", - "messages-ttl-check-in-hours-min": "Min number is 1.", - "messages-ttl-check-in-hours-pattern": "Number is not valid.", - "messages-ttl-in-days": "Messages TTL in days", - "messages-ttl-in-days-required": "Messages TTL in days is required.", - "messages-ttl-in-days-min": "Min number is 1.", - "messages-ttl-in-days-pattern": "Number is not valid.", - "mqtt-qos": "QoS", - "mqtt-qos-required": "QoS is required", - "mqtt-qos-range": "QoS values range is from 0 to 1", - "tls-path-private-key": "Path to private key on gateway", - "toggle-fullscreen": "Toggle fullscreen", - "transformer-json-config": "Configuration JSON*", - "update-config": "Add/update configuration JSON", - "hints": { - "remote-configuration": "Enables remote configuration and management of the gateway", - "remote-shell": "Enables remote control of the operating system with the gateway from the Remote Shell widget", - "host": "Hostname or IP address of ThingsBoard server", - "port": "Port of MQTT service on ThingsBoard server", - "token": "Access token for the gateway from ThingsBoard server", - "client-id": "MQTT client id for the gateway form ThingsBoard server", - "username": "MQTT username for the gateway form ThingsBoard server", - "password": "MQTT password for the gateway form ThingsBoard server", - "ca-cert": "Path to CA certificate file", - "date-form": "Date format in log message", - "data-folder": "Path to folder, that will contains data (Relative or Absolute)", - "log-format": "Log message format", - "remote-log": "Enables remote logging and logs reading from the gateway", - "backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted", - "storage": "Provides configuration for saving incoming data before it is sent to the platform", - "max-file-count": "Maximum count of file that will be created", - "max-read-count": "Count of messages to get from storage and send to ThingsBoard", - "max-records": "Maximum count of records that will be stored in one file", - "read-record-count": "Count of messages to get from storage and send to ThingsBoard", - "max-records-count": "Maximum count of data in storage before send to ThingsBoard", - "ttl-check-hour": "How often will Gateway check data for obsolescence", - "ttl-messages-day": "Maximum days that storage will save data", - "commands": "Commands for collecting additional statistic", - "attribute": "Statistic telemetry key", - "timeout": "Timeout for command executing", - "command": "The result of the command execution, will be used as the value for telemetry", - "check-device-activity": "Enables monitor the activity of each connected device", - "inactivity-timeout": "Time after whose the gateway will disconnect device", - "inactivity-period": "Periodicity of device activity check", - "minimal-pack-delay": "Delay between sending packs of messages (Decreasing this setting results in increased CPU usage)", - "qos": "Quality of Service in MQTT messaging (0 - at most once, 1 - at least once)", - "server-port": "Network port on which GRPC server will listen for incoming connections.", - "grpc-keep-alive-timeout": "Maximum time the server should wait for a keepalive ping response before considering the connection dead.", - "grpc-keep-alive": "Duration between two successive keepalive ping messages when there is no active RPC call.", - "grpc-min-time-between-pings": "Minimum amount of time the server should wait between sending keepalive ping messages", - "grpc-max-pings-without-data": "Maximum number of keepalive ping messages that the server can send without receiving any data before it considers the connection dead.", - "grpc-min-ping-interval-without-data": "Minimum amount of time the server should wait between sending keepalive ping messages when there is no data being sent or received.", - "permit-without-calls": "Allow server to keep the GRPC connection alive even when there are no active RPC calls." - } + "no-gateway-matching": " '{{item}}' not found." }, "grid": { "delete-item-title": "Ar tikrai norite pašalinti šį elementą?", diff --git a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json index afe23e039a..c16c9c5e40 100644 --- a/ui-ngx/src/assets/locale/locale.constant-nl_BE.json +++ b/ui-ngx/src/assets/locale/locale.constant-nl_BE.json @@ -2993,78 +2993,15 @@ "function": "Functie" }, "gateway": { - "add-entry": "Configuratie toevoegen", - "connector-add": "Nieuwe connector toevoegen", - "connector-enabled": "Connector inschakelen", - "connector-name": "Naam van de connector", - "connector-name-required": "De naam van de connector is vereist.", - "connector-type": "Type aansluiting", - "connector-type-required": "Het type connector is vereist.", - "connectors": "Configuratie van connectoren", "create-new-gateway": "Een nieuwe gateway maken", "create-new-gateway-text": "Weet u zeker dat u een nieuwe gateway wilt maken met de naam: '{{gatewayName}}'?", - "delete": "Configuratie verwijderen", - "download-tip": "Configuratiebestand downloaden", "gateway": "Gateway", "gateway-exists": "Device met dezelfde naam bestaat al.", "gateway-name": "Naam van de gateway", "gateway-name-required": "De naam van de gateway is vereist.", "gateway-saved": "Gatewayconfiguratie succesvol opgeslagen.", - "json-parse": "Ongeldige JSON.", - "json-required": "Het veld mag niet leeg zijn.", - "no-connectors": "Geen connectoren", - "no-data": "Geen configuraties", "no-gateway-found": "Geen gateway gevonden.", - "no-gateway-matching": "'{{item}}' niet gevonden.", - "path-logs": "Pad naar logbestanden", - "path-logs-required": "Pad is vereist.", - "remote": "Configuratie op afstand", - "remote-logging-level": "Registratie niveau", - "remove-entry": "Configuratie verwijderen", - "save-tip": "Configuratiebestand opslaan", - "security-type": "Soort beveiliging", - "security-types": { - "access-token": "Toegang tot token", - "tls": "TLS (TLS)" - }, - "storage": "Opslag", - "storage-max-file-records": "Maximum aantal records in bestand", - "storage-max-files": "Maximaal aantal bestanden", - "storage-max-files-min": "Minimum aantal is 1.", - "storage-max-files-pattern": "Nummer is niet geldig.", - "storage-max-files-required": "Nummer is vereist.", - "storage-max-records": "Maximum aantal records in opslag", - "storage-max-records-min": "Minimum aantal records is 1.", - "storage-max-records-pattern": "Nummer is niet geldig.", - "storage-max-records-required": "Maximale records zijn vereist.", - "storage-pack-size": "Maximale pakketgrootte voor events", - "storage-pack-size-min": "Minimum aantal is 1.", - "storage-pack-size-pattern": "Nummer is niet geldig.", - "storage-pack-size-required": "De maximale pakketgrootte van het event is vereist.", - "storage-path": "Opslag pad", - "storage-path-required": "Opslagpad is vereist.", - "storage-type": "Type opslag", - "storage-types": { - "file-storage": "Opslag van bestanden", - "memory-storage": "Geheugen opslag" - }, - "thingsboard": "Dingen Bord", - "thingsboard-host": "ThingsBoard-gastheer", - "thingsboard-host-required": "Server host is vereist.", - "thingsboard-port": "ThingsBoard-poort", - "thingsboard-port-max": "Het maximale poortnummer is 65535.", - "thingsboard-port-min": "Het minimale poortnummer is 1.", - "thingsboard-port-pattern": "Poort is niet geldig.", - "thingsboard-port-required": "Poort is vereist.", - "tidy": "Ordelijk", - "tidy-tip": "Opgeruimde configuratie JSON", - "title-connectors-json": "Configuratie van connector {{typeName}}", - "tls-path-ca-certificate": "Pad naar CA-certificaat op gateway", - "tls-path-client-certificate": "Pad naar clientcertificaat op gateway", - "tls-path-private-key": "Pad naar privésleutel op gateway", - "toggle-fullscreen": "Volledig scherm in- en uitschakelen", - "transformer-json-config": "Configuratie JSON*", - "update-config": "Configuratie JSON toevoegen/bijwerken" + "no-gateway-matching": "'{{item}}' niet gevonden." }, "grid": { "delete-item-title": "Weet u zeker dat u dit item wilt verwijderen?", diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index 8f35a7daa2..ac27313248 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -3137,255 +3137,15 @@ "function": "Funkcjonować" }, "gateway": { - "add-entry": "Dodaj konfigurację", - "advanced": "Advanced", - "checking-device-activity": "Checking device activity", - "command": "Docker commands", - "command-copied-message": "Docker command has been copied to clipboard", - "configuration": "Configuration", - "connector-add": "Dodaj nowe złącze", - "connector-enabled": "Włącz złącze", - "connector-name": "Nazwa złącza", - "connector-name-required": "Nazwa złącza jest wymagana.", - "connector-type": "Typ złącza", - "connector-type-required": "Typ złącza jest wymagany.", - "connectors": "Konfiguracja złączy", - "connectors-config": "Connectors configuration", - "connectors-table-enabled": "Enabled", - "connectors-table-name": "Name", - "connectors-table-type": "Type", - "connectors-table-status": "Status", - "connectors-table-actions": "Actions", - "connectors-table-key": "Key", - "connectors-table-class": "Class", - "rpc-command-send": "Send", - "rpc-command-result": "Result", - "rpc-command-edit-params": "Edit parameters", - "gateway-configuration": "General Configuration", - "docker-label": "In order to run ThingsBoard IoT gateway in docker with credentials for this device you can use the following commands.", "create-new-gateway": "Utwórz nowy gateway", "create-new-gateway-text": "Czy na pewno chcesz utworzyć nowy gateway o nazwie: '{{gatewayName}}'?", - "created-time": "Created time", - "configuration-delete-dialog-header": "Configurations will be deleted", - "configuration-delete-dialog-body": "Turning off Remote Configuration is possible only if there is physical access to the Gateway. All previous configurations will be deleted.

\nTo turn off configuration, enter gateway name below", - "configuration-delete-dialog-input": "Gateway name", - "configuration-delete-dialog-input-required": "Gateway name is mandatory", - "configuration-delete-dialog-confirm": "Turn Off", - "delete": "Usuń konfigurację", - "download-tip": "Pobierz plik konfiguracyjny", - "drop-file": "Drop file here or", "gateway": "Wejście", "gateway-exists": "Urządzenie o tej samej nazwie już istnieje.", "gateway-name": "Nazwa Gateway", "gateway-name-required": "Nazwa Gateway'a jest wymagana.", "gateway-saved": "Konfiguracja Gatewey'a została pomyślnie zapisana.", - "grpc": "GRPC", - "grpc-keep-alive-timeout": "Keep alive timeout (in ms)", - "grpc-keep-alive-timeout-required": "Keep alive timeout is required", - "grpc-keep-alive-timeout-min": "Keep alive timeout can not be less then 1", - "grpc-keep-alive-timeout-pattern": "Keep alive timeout is not valid", - "grpc-keep-alive": "Keep alive (in ms)", - "grpc-keep-alive-required": "Keep alive is required", - "grpc-keep-alive-min": "Keep alive can not be less then 1", - "grpc-keep-alive-pattern": "Keep alive is not valid", - "grpc-min-time-between-pings": "Min time between pings (in ms)", - "grpc-min-time-between-pings-required": "Min time between pings is required", - "grpc-min-time-between-pings-min": "Min time between pings can not be less then 1", - "grpc-min-time-between-pings-pattern": "Min time between pings is not valid", - "grpc-min-ping-interval-without-data": "Min ping interval without data (in ms)", - "grpc-min-ping-interval-without-data-required": "Min ping interval without data is required", - "grpc-min-ping-interval-without-data-min": "Min ping interval without data can not be less then 1", - "grpc-min-ping-interval-without-data-pattern": "Min ping interval without data is not valid", - "grpc-max-pings-without-data": "Max pings without data", - "grpc-max-pings-without-data-required": "Max pings without data is required", - "grpc-max-pings-without-data-min": "Max pings without data can not be less then 1", - "grpc-max-pings-without-data-pattern": "Max pings without data is not valid", - "inactivity-check-period-seconds": "Inactivity check period (in sec)", - "inactivity-check-period-seconds-required": "Inactivity check period is required", - "inactivity-check-period-seconds-min": "Inactivity check period can not be less then 1", - "inactivity-check-period-seconds-pattern": "Inactivity check period is not valid", - "inactivity-timeout-seconds": "Inactivity timeout (in sec)", - "inactivity-timeout-seconds-required": "Inactivity timeout is required", - "inactivity-timeout-seconds-min": "Inactivity timeout can not be less then 1", - "inactivity-timeout-seconds-pattern": "Inactivity timeout is not valid", - "json-parse": "Nieprawidłowy JSON.", - "json-required": "Pole nie może być puste.", - "logs": { - "logs": "Logs", - "days": "days", - "hours": "hours", - "minutes": "minutes", - "seconds": "seconds", - "date-format": "Date format", - "date-format-required": "Date format required", - "log-format": "Log format", - "log-type": "Log type", - "log-format-required": "Log format required", - "remote": "Remote logging", - "remote-logs": "Remote logs", - "local": "Local logging", - "level": "Log level", - "file-path": "File path", - "file-path-required": "File path required", - "saving-period": "Log saving period", - "saving-period-min": "Log saving period can not be less then 1", - "saving-period-required": "Log saving period required", - "backup-count": "Backup count", - "backup-count-min": "Backup count can not be less then 1", - "backup-count-required": "Backup count required" - }, - "min-pack-send-delay": "Min pack send delay (in ms)", - "min-pack-send-delay-required": "Min pack send delay is required", - "min-pack-send-delay-min": "Min pack send delay can not be less then 0", - "no-connectors": "Brak złączy", - "no-data": "Brak konfiguracji", "no-gateway-found": "Nie znaleziono gateway'a.", - "no-gateway-matching": " '{{item}}' nie znaleziono.", - "path-logs": "Ścieżka do plików dziennika", - "path-logs-required": "Ścieżka jest wymagana.", - "permit-without-calls": "Keep alive permit without calls", - "remote": "Zdalna konfiguracja", - "remote-logging-level": "Poziom logowania", - "remove-entry": "Usuń konfigurację", - "remote-shell": "Remote shell", - "remote-configuration": "Remote Configuration", - "other": "Other", - "save-tip": "Zapisz plik konfiguracyjny", - "security-type": "Rodzaj zabezpieczenia", - "security-types": { - "access-token": "Token dostępu", - "username-password": "Username and Password", - "tls": "TLS", - "tls-access-token": "TLS + Access Token", - "tls-private-key": "TLS + Private Key" - }, - "server-port": "Server port", - "statistics": { - "statistic": "Statistic", - "statistics": "Statistics", - "statistic-commands-empty": "No statistics available", - "commands": "Commands", - "send-period": "Statistic send period (in sec)", - "send-period-required": "Statistic send period is required", - "send-period-min": "Statistic send period can not be less then 60", - "send-period-pattern": "Statistic send period is not valid", - "check-connectors-configuration": "Check connectors configuration (in sec)", - "check-connectors-configuration-required": "Check connectors configuration is required", - "check-connectors-configuration-min": "Check connectors configuration can not be less then 1", - "check-connectors-configuration-pattern": "Check connectors configuration is not valid", - "add": "Add command", - "timeout": "Timeout", - "timeout-ms": "Timeout (in ms)", - "timeout-required": "Timeout is required", - "timeout-min": "Timeout can not be less then 1", - "timeout-pattern": "Timeout is not valid", - "attribute-name": "Attribute name", - "attribute-name-required": "Attribute name is required", - "command": "Command", - "command-required": "Command is required", - "remove": "Remove command" - }, - "storage": "Składowanie", - "storage-max-file-records": "Maksymalna liczba rekordów w pliku", - "storage-max-files": "Maksymalna liczba plików", - "storage-max-files-min": "Minimalna liczba to 1.", - "storage-max-files-pattern": "Numer jest nieprawidłowy.", - "storage-max-files-required": "Numer jest wymagany.", - "storage-max-records": "Maksymalna liczba rekordów w pamięci", - "storage-max-records-min": "Minimalna liczba rekordów to 1.", - "storage-max-records-pattern": "Numer jest nieprawidłowy.", - "storage-max-records-required": "Maksymalna liczba rekordów jest wymagana.", - "storage-read-record-count": "Read record count in storage", - "storage-read-record-count-min": "Minimum number of records is 1.", - "storage-read-record-count-pattern": "Number is not valid.", - "storage-read-record-count-required": "Read record count is required.", - "storage-max-read-record-count": "Max read record count in storage", - "storage-max-read-record-count-min": "Minimum number of records is 1.", - "storage-max-read-record-count-pattern": "Number is not valid.", - "storage-max-read-record-count-required": "Max Read record count is required.", - "storage-data-folder-path": "Data folder path", - "storage-data-folder-path-required": "Data folder path is required.", - "storage-pack-size": "Maksymalny rozmiar pakietu wydarzeń", - "storage-pack-size-min": "Minimalna liczba to 1.", - "storage-pack-size-pattern": "Numer jest nieprawidłowy.", - "storage-pack-size-required": "Maksymalny rozmiar pakietu wydarzeń jest wymagany.", - "storage-path": "Ścieżka przechowywania", - "storage-path-required": "Ścieżka do przechowywania jest wymagana.", - "storage-type": "Typ składowania", - "storage-types": { - "file-storage": "Nośnik danych", - "memory-storage": "Przechowywanie pamięci", - "sqlite": "SQLITE" - }, - "thingsboard": "ThingsBoard", - "general": "General", - "thingsboard-host": "Gospodarz ThingsBoard", - "thingsboard-host-required": "Host jest wymagany.", - "thingsboard-port": "Port ThingsBoard", - "thingsboard-port-max": "Maksymalny numer portu to 65535.", - "thingsboard-port-min": "Minimalny numer portu to 1.", - "thingsboard-port-pattern": "Port jest nieprawidłowy.", - "thingsboard-port-required": "Port jest wymagany.", - "tidy": "Uporządkuj", - "tidy-tip": "Uporządkowana konfiguracja JSON", - "title-connectors-json": "Złącze {{typeName}} konfiguracja", - "tls-path-ca-certificate": "Ścieżka do certyfikatu CA na gateway", - "tls-path-client-certificate": "Ścieżka do certyfikatu klienta na gateway", - "messages-ttl-check-in-hours": "Messages TTL check in hours", - "messages-ttl-check-in-hours-required": "Messages TTL check in hours is required.", - "messages-ttl-check-in-hours-min": "Min number is 1.", - "messages-ttl-check-in-hours-pattern": "Number is not valid.", - "messages-ttl-in-days": "Messages TTL in days", - "messages-ttl-in-days-required": "Messages TTL in days is required.", - "messages-ttl-in-days-min": "Min number is 1.", - "messages-ttl-in-days-pattern": "Number is not valid.", - "mqtt-qos": "QoS", - "mqtt-qos-required": "QoS is required", - "mqtt-qos-range": "QoS values range is from 0 to 1", - "tls-path-private-key": "Ścieżka do klucza prywatnego na bramce", - "toggle-fullscreen": "Przełącz tryb pełnoekranowy", - "transformer-json-config": "Konfiguracja JSON*", - "update-config": "Dodaj/zaktualizuj konfigurację JSON", - "hints": { - "remote-configuration": "Enables remote configuration and management of the gateway", - "remote-shell": "Enables remote control of the operating system with the gateway from the Remote Shell widget", - "host": "Hostname or IP address of ThingsBoard server", - "port": "Port of MQTT service on ThingsBoard server", - "token": "Access token for the gateway from ThingsBoard server", - "client-id": "MQTT client id for the gateway form ThingsBoard server", - "username": "MQTT username for the gateway form ThingsBoard server", - "password": "MQTT password for the gateway form ThingsBoard server", - "ca-cert": "Path to CA certificate file", - "date-form": "Date format in log message", - "data-folder": "Path to folder, that will contains data (Relative or Absolute)", - "log-format": "Log message format", - "remote-log": "Enables remote logging and logs reading from the gateway", - "backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted", - "storage": "Provides configuration for saving incoming data before it is sent to the platform", - "max-file-count": "Maximum count of file that will be created", - "max-read-count": "Count of messages to get from storage and send to ThingsBoard", - "max-records": "Maximum count of records that will be stored in one file", - "read-record-count": "Count of messages to get from storage and send to ThingsBoard", - "max-records-count": "Maximum count of data in storage before send to ThingsBoard", - "ttl-check-hour": "How often will Gateway check data for obsolescence", - "ttl-messages-day": "Maximum days that storage will save data", - "commands": "Commands for collecting additional statistic", - "attribute": "Statistic telemetry key", - "timeout": "Timeout for command executing", - "command": "The result of the command execution, will be used as the value for telemetry", - "check-device-activity": "Enables monitor the activity of each connected device", - "inactivity-timeout": "Time after whose the gateway will disconnect device", - "inactivity-period": "Periodicity of device activity check", - "minimal-pack-delay": "Delay between sending packs of messages (Decreasing this setting results in increased CPU usage)", - "qos": "Quality of Service in MQTT messaging (0 - at most once, 1 - at least once)", - "server-port": "Network port on which GRPC server will listen for incoming connections.", - "grpc-keep-alive-timeout": "Maximum time the server should wait for a keepalive ping response before considering the connection dead.", - "grpc-keep-alive": "Duration between two successive keepalive ping messages when there is no active RPC call.", - "grpc-min-time-between-pings": "Minimum amount of time the server should wait between sending keepalive ping messages", - "grpc-max-pings-without-data": "Maximum number of keepalive ping messages that the server can send without receiving any data before it considers the connection dead.", - "grpc-min-ping-interval-without-data": "Minimum amount of time the server should wait between sending keepalive ping messages when there is no data being sent or received.", - "permit-without-calls": "Allow server to keep the GRPC connection alive even when there are no active RPC calls." - } + "no-gateway-matching": " '{{item}}' nie znaleziono." }, "grid": { "delete-item-title": "Czy na pewno chcesz usunąć ten element?", diff --git a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json index de9baa3998..8b77be4573 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pt_BR.json +++ b/ui-ngx/src/assets/locale/locale.constant-pt_BR.json @@ -1260,78 +1260,15 @@ "function": "Função" }, "gateway": { - "add-entry": "Adicionar configuração", - "connector-add": "Adicionar novo conector", - "connector-enabled": "Habilitar conector", - "connector-name": "Nome do conector", - "connector-name-required": "O nome do conector é obrigatório.", - "connector-type": "Tipo de conector", - "connector-type-required": "O tipo de conector é obrigatório.", - "connectors": "Configuração de conectores", "create-new-gateway": "Criar um novo gateway", "create-new-gateway-text": "Tem certeza de que deseja criar um novo gateway com o nome: '{{gatewayName}}'?", - "delete": "Excluir configuração", - "download-tip": "Download de arquivo de configuração", "gateway": "Gateway", "gateway-exists": "Já existe um dispositivo com o mesmo nome.", "gateway-name": "Nome do gateway", "gateway-name-required": "O nome do gateway é obrigatório.", "gateway-saved": "A configuração do gateway foi salva corretamente.", - "json-parse": "JSON inválido.", - "json-required": "O campo não pode estar em branco.", - "no-connectors": "Sem conectores", - "no-data": "Sem configurações", "no-gateway-found": "Nenhum gateway encontrado.", - "no-gateway-matching": " '{{item}}' não encontrado.", - "path-logs": "Caminho para arquivos de log", - "path-logs-required": "O caminho é obrigatório", - "remote": "Configuração remota", - "remote-logging-level": "Nível de registro em log", - "remove-entry": "Remover configuração", - "save-tip": "Salvar arquivo de configuração", - "security-type": "Tipo de segurança", - "security-types": { - "access-token": "Token de Acesso", - "tls": "TLS" - }, - "storage": "Armazenamento", - "storage-max-file-records": "Número máximo de registros em arquivo", - "storage-max-files": "Número máximo de arquivos", - "storage-max-files-min": "O número mínimo é 1.", - "storage-max-files-pattern": "O número não é válido.", - "storage-max-files-required": "O número é obrigatório.", - "storage-max-records": "Número máximo de registros em armazenamento", - "storage-max-records-min": "O número mínimo de registros é 1.", - "storage-max-records-pattern": "O número não é válido.", - "storage-max-records-required": "O número máximo de registros é obrigatório.", - "storage-pack-size": "Tamanho máximo de pacote de eventos", - "storage-pack-size-min": "O número mínimo é 1.", - "storage-pack-size-pattern": "O número não é válido.", - "storage-pack-size-required": "O tamanho máximo de pacote de eventos é obrigatório.", - "storage-path": "Caminho de armazenamento", - "storage-path-required": "O caminho de armazenamento é obrigatório.", - "storage-type": "Tipo de armazenamento", - "storage-types": { - "file-storage": "Armazenamento de arquivo", - "memory-storage": "Armazenamento de memória" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "Host ThingsBoard", - "thingsboard-host-required": "O host é obrigatório.", - "thingsboard-port": "Porta ThingsBoard", - "thingsboard-port-max": "O número máximo de portas é 65535.", - "thingsboard-port-min": "O número mínimo de portas é 1.", - "thingsboard-port-pattern": "A porta não é válida.", - "thingsboard-port-required": "A porta é obrigatória.", - "tidy": "Tidy", - "tidy-tip": "Config Tidy JSON", - "title-connectors-json": "Configuração do conector {{typeName}}", - "tls-path-ca-certificate": "Caminho para certificado de Autoridade de Certificação no gateway", - "tls-path-client-certificate": "Caminho para certificado de cliente no gateway", - "tls-path-private-key": "Caminho para chave privada no gateway", - "toggle-fullscreen": "Alternar tela inteira", - "transformer-json-config": "Configuração JSON*", - "update-config": "Adicionar/atualizar configuração de JSON" + "no-gateway-matching": " '{{item}}' não encontrado." }, "grid": { "delete-item-title": "Tem certeza de que deseja excluir este item?", diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index 0fab425e80..b705358124 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -1584,78 +1584,15 @@ "function": "Funkcija" }, "gateway": { - "add-entry": "Dodaj konfiguracijo", - "connector-add": "Dodaj nov priključek", - "connector-enabled": "Omogoči priključek", - "connector-name": "Ime priključka", - "connector-name-required": "Ime priključka je obvezno.", - "connector-type": "Vrsta priključka", - "connector-type-required": "Zahteva se vrsta priključka.", - "connectors": "Konfiguracija priključkov", "create-new-gateway": "Ustvari nov prehod", "create-new-gateway-text": "Ali ste prepričani, da želite ustvariti nov prehod z imenom: '{{gatewayName}}'?", - "delete": "Izbriši konfiguracijo", - "download-tip": "Prenos konfiguracijske datoteke", "gateway": "Prehod", "gateway-exists": "Naprava z istim imenom že obstaja.", "gateway-name": "Ime prehoda", "gateway-name-required": "Ime prehoda je obvezno.", "gateway-saved": "Konfiguracija prehoda je uspešno shranjena.", - "json-parse": "Neveljaven JSON.", - "json-required": "Polje ne sme biti prazno.", - "no-connectors": "Ni priključkov", - "no-data": "Brez konfiguracij", "no-gateway-found": "Prehod ni najden.", - "no-gateway-matching": " '{{item}}' ni mogoče najti.", - "path-logs": "Pot do dnevniških datotek", - "path-logs-required": "Pot je obvezna.", - "remote": "Oddaljena konfiguracija", - "remote-logging-level": "Raven beleženja", - "remove-entry": "Odstrani konfiguracijo", - "save-tip": "Shrani konfiguracijsko datoteko", - "security-type": "Vrsta zaščite", - "security-types": { - "access-token": "Dostopni žeton", - "tls": "TLS" - }, - "storage": "Shramba", - "storage-max-file-records": "Največ zapisov v datoteki", - "storage-max-files": "Največje število datotek", - "storage-max-files-min": "Najmanjše število je 1.", - "storage-max-files-pattern": "Številka ni veljavna.", - "storage-max-files-required": "Številka je obvezna.", - "storage-max-records": "Največ zapisov v pomnilniku", - "storage-max-records-min": "Najmanjše število zapisov je 1.", - "storage-max-records-pattern": "Številka ni veljavna.", - "storage-max-records-required": "Zahtevan je največ zapisov.", - "storage-pack-size": "Največja velikost paketa dogodkov", - "storage-pack-size-min": "Najmanjše število je 1.", - "storage-pack-size-pattern": "Številka ni veljavna.", - "storage-pack-size-required": "Zahtevana je največja velikost paketa dogodkov.", - "storage-path": "Pot pomnilnika", - "storage-path-required": "Zahtevana je pot do pomnilnika.", - "storage-type": "Vrsta pomnilnika", - "storage-types": { - "file-storage": "Shramba datotek", - "memory-storage": "Spomin pomnilnika" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "Gostitelj ThingsBoard", - "thingsboard-host-required": "Potreben je gostitelj.", - "thingsboard-port": "Vrata ThingsBoard", - "thingsboard-port-max": "Največja številka vrat je 65535.", - "thingsboard-port-min": "Najmanjša številka vrat je 1.", - "thingsboard-port-pattern": "Vrata niso veljavna.", - "thingsboard-port-required": "Potrebna so vrata.", - "tidy": "Urejeno", - "tidy-tip": "Urejena konfiguracija JSON", - "title-connectors-json": "Konfiguracija konektorja {{typeName}}", - "tls-path-ca-certificate": "Pot do potrdila CA na prehodu", - "tls-path-client-certificate": "Pot do potrdila stranke na prehodu", - "tls-path-private-key": "Pot do zasebnega ključa na prehodu", - "toggle-fullscreen": "Preklop na celozaslonski način", - "transformer-json-config": "Konfiguracija JSON *", - "update-config": "Dodaj / posodobi konfiguracijo JSON" + "no-gateway-matching": " '{{item}}' ni mogoče najti." }, "grid": { "delete-item-title": "Ali ste prepričani, da želite izbrisati ta element?", diff --git a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json index b01c3548ab..175d4f1b02 100644 --- a/ui-ngx/src/assets/locale/locale.constant-tr_TR.json +++ b/ui-ngx/src/assets/locale/locale.constant-tr_TR.json @@ -2039,78 +2039,15 @@ "function": "Fonksiyon" }, "gateway": { - "add-entry": "Yapılandırma ekle", - "connector-add": "Yeni bağlayıcı ekle", - "connector-enabled": "Bağlayıcıyı etkinleştir", - "connector-name": "Bağlayıcı adı", - "connector-name-required": "Bağlayıcı adı gerekli.", - "connector-type": "Bağlayıcı tipi", - "connector-type-required": "Bağlayıcı türü gerekli.", - "connectors": "Bağlayıcıların yapılandırması", - "create-new-gateway": "Yeni bir ağ geçidi oluştur", - "create-new-gateway-text": "'{{gatewayName}}' adında yeni bir ağ geçidi oluşturmak istediğinizden emin misiniz?", - "delete": "Yapılandırmayı sil", - "download-tip": "Yapılandırma dosyasını indirin", - "gateway": "Ağ geçidi", "gateway-exists": "Aynı ada sahip cihaz zaten var.", "gateway-name": "Ağ geçidi adı", "gateway-name-required": "Ağ geçidi adı gerekli.", "gateway-saved": "Ağ geçidi yapılandırması başarıyla kaydedildi.", - "json-parse": "Geçerli bir JSON değil.", - "json-required": "Alan boş olamaz.", - "no-connectors": "Bağlayıcı yok", - "no-data": "Yapılandırma yok", + "gateway": "Ağ geçidi", + "create-new-gateway": "Yeni bir ağ geçidi oluştur", + "create-new-gateway-text": "'{{gatewayName}}' adında yeni bir ağ geçidi oluşturmak istediğinizden emin misiniz?", "no-gateway-found": "Ağ geçidi bulunamadı.", - "no-gateway-matching": " '{{item}}' bulunamadı.", - "path-logs": "Log dosyaları yolu", - "path-logs-required": "Log dosyaları dizini gerekli.", - "remote": "Uzaktan yapılandırma", - "remote-logging-level": "Loglama seviyesi", - "remove-entry": "Yapılandırmayı kaldır", - "save-tip": "Yapılandırma dosyasını kaydet", - "security-type": "Güvenlik türü", - "security-types": { - "access-token": "Access Token", - "tls": "TLS" - }, - "storage": "Depolama", - "storage-max-file-records": "Dosyadaki maksimum kayıt", - "storage-max-files": "Maksimum dosya sayısı", - "storage-max-files-min": "Minimum sayı 1'dir.", - "storage-max-files-pattern": "Sayı geçerli değil.", - "storage-max-files-required": "Sayı gerekli.", - "storage-max-records": "Depodaki maksimum kayıt", - "storage-max-records-min": "Minimum kayıt sayısı 1'dir.", - "storage-max-records-pattern": "Sayı geçerli değil.", - "storage-max-records-required": "Maksimum kayıt gerekli.", - "storage-pack-size": "Maksimum etkinlik paketi boyutu", - "storage-pack-size-min": "Minimum sayı 1'dir.", - "storage-pack-size-pattern": "Sayı geçerli değil.", - "storage-pack-size-required": "Maksimum etkinlik paketi boyutu gerekli.", - "storage-path": "Depolama yolu", - "storage-path-required": "Depolama yolu gerekli.", - "storage-type": "Depolama türü", - "storage-types": { - "file-storage": "Dosya depolama", - "memory-storage": "Bellek depolama" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "ThingsBoard host", - "thingsboard-host-required": "Host gerekli.", - "thingsboard-port": "ThingsBoard port", - "thingsboard-port-max": "Maksimum port numarası 65535.", - "thingsboard-port-min": "Minimum port numarası 1'dir.", - "thingsboard-port-pattern": "Port geçerli değil.", - "thingsboard-port-required": "Port gerekli.", - "tidy": "Tidy", - "tidy-tip": "Tidy config JSON", - "title-connectors-json": "Connector {{typeName}} configuration", - "tls-path-ca-certificate": "Path to CA certificate on gateway", - "tls-path-client-certificate": "Path to client certificate on gateway", - "tls-path-private-key": "Path to private key on gateway", - "toggle-fullscreen": "Toggle fullscreen", - "transformer-json-config": "Configuration JSON*", - "update-config": "Add/update configuration JSON" + "no-gateway-matching": " '{{item}}' bulunamadı." }, "grid": { "delete-item-title": "Bu öğeyi silmek istediğinizden emin misiniz?", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index 2e3f3e0355..c601e1d6a1 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -2671,261 +2671,16 @@ "function": "函数" }, "gateway": { - "add-entry": "添加配置", - "advanced": "高级", - "checking-device-activity": "检查设备活动", - "command": "Docker命令", - "command-copied-message": "Docker命令已复制到剪贴板", - "configuration": "配置", - "connector-add": "添加连接器", - "connector-enabled": "启用连接器", - "connector-name": "连接器名称", - "connector-name-required": "连接器名称必填。", - "connector-type": "连接器类型", - "connector-type-required": "连接器类型必填。", - "connectors": "连接器配置", - "connectors-config": "连接器配置", - "connectors-table-enabled": "已启用", - "connectors-table-name": "名称", - "connectors-table-type": "类型", - "connectors-table-status": "状态", - "connectors-table-actions": "操作", - "connectors-table-key": "键", - "connectors-table-class": "类", - "rpc-command-send": "发送", - "rpc-command-result": "结果", - "rpc-command-edit-params": "编辑参数", - "gateway-configuration": "通用配置", - "create-new-gateway": "创建网关", - "create-new-gateway-text": "确定要创建名为 '{{gatewayName}}' 的新网关?", - "created-time": "创建时间", - "configuration-delete-dialog-header": "配置将被删除", - "configuration-delete-dialog-body": "只有对网关进行物理访问时,才有可能关闭远程配置。所有先前的配置都将被删除。

\n要关闭配置,请在下面输入网关名称", - "configuration-delete-dialog-input": "网关名称", - "configuration-delete-dialog-input-required": "网关名称是必需的", - "configuration-delete-dialog-confirm": "关闭", - "delete": "删除配置", - "download-tip": "下载配置", - "drop-file": "将文件拖放到此处或", - "gateway": "网关", "gateway-exists": "同名设备已存在。", "gateway-name": "网关名称", "gateway-name-required": "网关名称必填。", "gateway-saved": "已成功保存网关配置。", - "grpc": "GRPC", - "grpc-keep-alive-timeout": "保持连接超时(毫秒)", - "grpc-keep-alive-timeout-required": "需要保持连接超时", - "grpc-keep-alive-timeout-min": "保持连接超时不能小于1", - "grpc-keep-alive-timeout-pattern": "保持连接超时无效", - "grpc-keep-alive": "保持连接(毫秒)", - "grpc-keep-alive-required": "需要保持连接", - "grpc-keep-alive-min": "保持连接不能小于1", - "grpc-keep-alive-pattern": "保持连接无效", - "grpc-min-time-between-pings": "最小Ping间隔(毫秒)", - "grpc-min-time-between-pings-required": "需要最小Ping间隔", - "grpc-min-time-between-pings-min": "最小Ping间隔不能小于1", - "grpc-min-time-between-pings-pattern": "最小Ping间隔无效", - "grpc-min-ping-interval-without-data": "无数据时的最小Ping间隔(毫秒)", - "grpc-min-ping-interval-without-data-required": "需要无数据时的最小Ping间隔", - "grpc-min-ping-interval-without-data-min": "无数据时的最小Ping间隔不能小于1", - "grpc-min-ping-interval-without-data-pattern": "无数据时的最小Ping间隔无效", - "grpc-max-pings-without-data": "无数据时的最大Ping数", - "grpc-max-pings-without-data-required": "需要无数据时的最大Ping数", - "grpc-max-pings-without-data-min": "无数据时的最大Ping数不能小于1", - "grpc-max-pings-without-data-pattern": "无数据时的最大Ping数无效", - "inactivity-check-period-seconds": "不活跃检查期(秒)", - "inactivity-check-period-seconds-required": "需要不活跃检查期", - "inactivity-check-period-seconds-min": "不活跃检查期不能小于1", - "inactivity-check-period-seconds-pattern": "不活跃检查期无效", - "inactivity-timeout-seconds": "不活跃超时(秒)", - "inactivity-timeout-seconds-required": "需要不活跃超时", - "inactivity-timeout-seconds-min": "不活跃超时不能小于1", - "inactivity-timeout-seconds-pattern": "不活跃超时无效", - "json-parse": "无效的JSON。", - "json-required": "字段不能为空。", - "logs": { - "logs": "日志", - "days": "天", - "hours": "小时", - "minutes": "分钟", - "seconds": "秒", - "date-format": "日期格式", - "date-format-required": "需要日期格式", - "log-format": "日志格式", - "log-type": "日志类型", - "log-format-required": "需要日志格式", - "remote": "远程日志记录", - "remote-logs": "远程日志", - "local": "本地日志记录", - "level": "日志级别", - "file-path": "文件路径", - "file-path-required": "需要文件路径", - "saving-period": "日志保存期限", - "saving-period-min": "日志保存期限不能小于1", - "saving-period-required": "需要日志保存期限", - "backup-count": "备份数量", - "backup-count-min": "备份数量不能小于1", - "backup-count-required": "需要备份数量" - }, - "min-pack-send-delay": "最小包发送延迟(毫秒)", - "min-pack-send-delay-required": "最小包发送延迟是必需的", - "min-pack-send-delay-min": "最小包发送延迟不能小于0", - "no-connectors": "无连接器", - "no-data": "没有配置", + "gateway": "网关", + "create-new-gateway": "创建网关", + "create-new-gateway-text": "确定要创建名为 '{{gatewayName}}' 的新网关?", "no-gateway-found": "未找到网关。", "no-gateway-matching": "未找到 '{{item}}' 。", - "path-logs": "日志文件的路径", - "path-logs-required": "路径是必需的。", - "permit-without-calls": "保持连接许可,无需响应", - "remote": "远程配置", - "remote-logging-level": "日志记录级别", - "remove-entry": "删除配置", - "remote-shell": "远程Shell", - "remote-configuration": "远程配置", - "other": "其他", - "save-tip": "保存配置", - "security-type": "安全类型", - "security-types": { - "access-token": "访问令牌", - "username-password": "用户名和密码", - "tls": "TLS", - "tls-access-token": "TLS + 访问令牌", - "tls-private-key": "TLS + 私钥" - }, - "server-port": "服务器端口", - "statistics": { - "statistic": "统计信息", - "statistics": "统计信息", - "statistic-commands-empty": "无可用统计信息", - "commands": "命令", - "send-period": "统计信息发送周期(秒)", - "send-period-required": "统计信息发送周期是必需的", - "send-period-min": "统计信息发送周期不能小于60", - "send-period-pattern": "统计信息发送周期无效", - "check-connectors-configuration": "检查连接器配置(秒)", - "check-connectors-configuration-required": "检查连接器配置是必需的", - "check-connectors-configuration-min": "检查连接器配置不能小于1", - "check-connectors-configuration-pattern": "检查连接器配置无效", - "add": "添加命令", - "timeout": "超时时间", - "timeout-ms": "超时时间(毫秒)", - "timeout-required": "超时时间是必需的", - "timeout-min": "超时时间不能小于1", - "timeout-pattern": "超时时间无效", - "attribute-name": "属性名称", - "attribute-name-required": "属性名称是必需的", - "command": "命令", - "command-required": "命令是必需的", - "command-pattern": "命令无效", - "remove": "删除命令" - }, - "storage": "存储", - "storage-max-file-records": "文件中的最大记录数", - "storage-max-files": "最大文件数", - "storage-max-files-min": "最小值为1。", - "storage-max-files-pattern": "数字无效。", - "storage-max-files-required": "数字是必需的。", - "storage-max-records": "存储中的最大记录数", - "storage-max-records-min": "最小记录数为1。", - "storage-max-records-pattern": "数字无效。", - "storage-max-records-required": "最大记录项必填。", - "storage-read-record-count": "存储中的读取记录数", - "storage-read-record-count-min": "最小记录数为1。", - "storage-read-record-count-pattern": "数字不合法。", - "storage-read-record-count-required": "需要读取记录数。", - "storage-max-read-record-count": "存储中的最大读取记录数", - "storage-max-read-record-count-min": "最小记录数为1。", - "storage-max-read-record-count-pattern": "数字不合法。", - "storage-max-read-record-count-required": "最大读取记录数必需。", - "storage-data-folder-path": "数据文件夹路径", - "storage-data-folder-path-required": "需要数据文件夹路径。", - "storage-pack-size": "最大事件包大小", - "storage-pack-size-min": "最小值为1。", - "storage-pack-size-pattern": "数字无效。", - "storage-pack-size-required": "最大事件包大小必填。", - "storage-path": "存储路径", - "storage-path-required": "存储路径必填。", - "storage-type": "存储类型", - "storage-types": { - "file-storage": "文件存储", - "memory-storage": "内存存储", - "sqlite": "SQLITE" - }, - "thingsboard": "ThingsBoard", - "general": "常规", - "thingsboard-host": "ThingsBoard主机", - "thingsboard-host-required": "主机必填。", - "thingsboard-port": "ThingsBoard端口", - "thingsboard-port-max": "最大端口号为65535。", - "thingsboard-port-min": "最小端口号为1。", - "thingsboard-port-pattern": "端口无效。", - "thingsboard-port-required": "端口必填。", - "tidy": "整理", - "tidy-tip": "整理配置JSON", - "title-connectors-json": "连接器 {{typeName}} 配置", - "tls-path-ca-certificate": "网关上CA证书的路径", - "tls-path-client-certificate": "网关上客户端证书的路径", - "messages-ttl-check-in-hours": "消息TTL检查小时数", - "messages-ttl-check-in-hours-required": "需要提供消息TTL检查小时数。", - "messages-ttl-check-in-hours-min": "最小值为1。", - "messages-ttl-check-in-hours-pattern": "数字无效。", - "messages-ttl-in-days": "消息TTL天数", - "messages-ttl-in-days-required": "需要提供消息TTL天数。", - "messages-ttl-in-days-min": "最小值为1。", - "messages-ttl-in-days-pattern": "数字无效。", - "mqtt-qos": "QoS", - "mqtt-qos-required": "需要提供QoS", - "mqtt-qos-range": "QoS值的范围是从0到1", - "tls-path-private-key": "网关上私钥的路径", - "toggle-fullscreen": "切换全屏", - "transformer-json-config": "配置JSON*", - "update-config": "添加/更新配置JSON", - "hints": { - "remote-configuration": "启用对网关的远程配置和管理", - "remote-shell": "通过远程Shell小部件启用对网关操作系统的远程控制", - "host": "ThingsBoard 主机名或IP地址", - "port": "ThingsBoard MQTT服务端口", - "token": "ThingsBoard 网关访问令牌", - "client-id": "ThingsBoard 网关MQTT客户端ID", - "username": "ThingsBoard 网关MQTT用户名", - "password": "ThingsBoard 网关MQTT密码", - "ca-cert": "CA证书文件的路径", - "date-form": "日志消息中的日期格式", - "data-folder": "包含数据的文件夹的路径(相对或绝对路径)", - "log-format": "日志消息格式", - "remote-log": "启用对网关的远程日志记录和日志读取", - "backup-count": "如果备份计数大于0,则在执行轮换时,最多保留备份计数个文件-最旧的文件将被删除", - "storage": "提供将数据发送到平台之前保存传入数据的配置", - "max-file-count": "将创建的文件的最大数量", - "max-read-count": "从存储中获取的消息计数并发送到ThingsBoard", - "max-records": "一个文件中存储的最大记录数", - "read-record-count": "从存储中获取的消息计数并发送到ThingsBoard", - "max-records-count": "在将数据发送到ThingsBoard之前,存储中的最大数据计数", - "ttl-check-hour": "网关多久检查一次数据是否过时", - "ttl-messages-day": "存储将保存数据的最大天数", - "commands": "用于收集附加统计信息的命令", - "attribute": "统计遥测键", - "timeout": "命令执行的超时时间", - "command": "命令执行的结果,将用作遥测的值", - "check-device-activity": "启用监视每个连接设备的活动", - "inactivity-timeout": "在此时间后,网关将断开设备的连接", - "inactivity-period": "设备活动检查的周期", - "minimal-pack-delay": "发送消息包之间的延迟(减小此设置会导致增加CPU使用率)", - "qos": "MQTT消息传递的服务质量(0-至多一次,1-至少一次)", - "server-port": "GRPC服务器侦听传入连接的网络端口", - "grpc-keep-alive-timeout": "在考虑连接死亡之前,服务器等待keepalive ping响应的最长时间", - "grpc-keep-alive": "没有活动RPC调用时两个连续keepalive ping消息之间的持续时间", - "grpc-min-time-between-pings": "服务器在发送keepalive ping消息之间应等待的最小时间量", - "grpc-max-pings-without-data": "在没有接收到任何数据之前,服务器可以发送的keepalive ping消息的最大数量,然后将连接视为死亡", - "grpc-min-ping-interval-without-data": "在没有发送或接收数据时,服务器在发送keepalive ping消息之间应等待的最小时间量", - "permit-without-calls": "允许服务器在没有活动RPC调用时保持GRPC连接活动" - }, - "docker-label": "使用以下指令在 Docker compose 中运行 IoT 网关,并为选定的设备提供凭据", - "install-docker-compose": "使用以下说明下载、安装和设置 Docker Compose", - "download-configuration-file": "下载配置文件", - "download-docker-compose": "下载您的网关的 docker-compose.yml 文件", - "launch-gateway": "启动网关", - "launch-docker-compose": "在包含 docker-compose.yml 文件的文件夹中,使用以下命令在终端中启动网关" + "launch-gateway": "启动网关" }, "grid": { "delete-item-title": "确定要删除此项吗?", diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json index 959d1d2048..9782faf351 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_TW.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_TW.json @@ -2251,78 +2251,15 @@ "function": "函數" }, "gateway": { - "add-entry": "增加配置", - "connector-add": "增加新連接器", - "connector-enabled": "啟用連接器", - "connector-name": "連接器名稱", - "connector-name-required": "需要連接器名稱。", - "connector-type": "連接器類型", - "connector-type-required": "需要連接器類型。", - "connectors": "連接器配置", - "create-new-gateway": "建立新閘道", - "create-new-gateway-text": "您確定要建立一個名稱為:'{{gatewayName}}'的新閘道嗎?", - "delete": "刪除配置", - "download-tip": "下載配置文件", + "no-gateway-found": "未找到閘道。", + "no-gateway-matching": " 未找到'{{item}}'。", "gateway": "閘道", "gateway-exists": "同名設備已存在。", "gateway-name": "閘道名稱", "gateway-name-required": "需要閘道名稱。", "gateway-saved": "閘道配置已成功保存。", - "json-parse": "無效的JSON", - "json-required": "欄位不能為空。", - "no-connectors": "無連接器", - "no-data": "無配置", - "no-gateway-found": "未找到閘道。", - "no-gateway-matching": " 未找到'{{item}}'。", - "path-logs": "日誌文件的路徑", - "path-logs-required": "需要路徑。", - "remote": "移除配置", - "remote-logging-level": "日誌記錄級別", - "remove-entry": "移除配置", - "save-tip": "保存配置文件", - "security-type": "安全類型", - "security-types": { - "access-token": "訪問Token", - "tls": "TLS" - }, - "storage": "貯存", - "storage-max-file-records": "文件中的最大紀錄", - "storage-max-files": "最大文件數", - "storage-max-files-min": "最小數量為1。", - "storage-max-files-pattern": "號碼無效。", - "storage-max-files-required": "需要號碼。", - "storage-max-records": "存儲中的最大紀錄", - "storage-max-records-min": "最小紀錄數為1。", - "storage-max-records-pattern": "號碼無效。", - "storage-max-records-required": "需要最大紀錄數", - "storage-pack-size": "最大事件包大小", - "storage-pack-size-min": "最小數量為1。", - "storage-pack-size-pattern": "號碼無效.", - "storage-pack-size-required": "需要最大事件包大小", - "storage-path": "存儲路徑", - "storage-path-required": "需要存儲路徑。", - "storage-type": "存儲類型", - "storage-types": { - "file-storage": "文件存儲", - "memory-storage": "記憶體存儲" - }, - "thingsboard": "ThingsBoard", - "thingsboard-host": "ThingsBoard主機", - "thingsboard-host-required": "需要主機。", - "thingsboard-port": "ThingsBoard連接埠", - "thingsboard-port-max": "最大埠號為 65535。", - "thingsboard-port-min": "最小埠號為1。", - "thingsboard-port-pattern": "連接埠無效。", - "thingsboard-port-required": "需要連接埠。", - "tidy": "整理", - "tidy-tip": "整理配置JSON", - "title-connectors-json": "連接器{{typeName}}配置", - "tls-path-ca-certificate": "閘道上CA證書的路徑", - "tls-path-client-certificate": "閘道上用戶端憑據的路徑", - "tls-path-private-key": "閘道上的私鑰路徑", - "toggle-fullscreen": "切換全螢幕", - "transformer-json-config": "配置JSON*", - "update-config": "增加/更新配置JSON" + "create-new-gateway": "建立新閘道", + "create-new-gateway-text": "您確定要建立一個名稱為:'{{gatewayName}}'的新閘道嗎?" }, "grid": { "delete-item-title": "您確定要刪除此項嗎?", From 9e74c1b29cd09d3cf4e1a2df4b253e5e47cc31bd Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 7 Oct 2024 19:59:36 +0300 Subject: [PATCH 020/110] deleted AttributeDatasource --- ui-ngx/src/app/modules/common/modules-map.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui-ngx/src/app/modules/common/modules-map.ts b/ui-ngx/src/app/modules/common/modules-map.ts index 445fe9df42..5743fbc425 100644 --- a/ui-ngx/src/app/modules/common/modules-map.ts +++ b/ui-ngx/src/app/modules/common/modules-map.ts @@ -198,7 +198,6 @@ import * as GalleryImageInputComponent from '@shared/components/image/gallery-im import * as MultipleGalleryImageInputComponent from '@shared/components/image/multiple-gallery-image-input.component'; import * as TbPopoverService from '@shared/components/popover.service'; -import * as AttributeDatasource from '@home/models/datasource/attribute-datasource'; import * as CssUnitSelectComponent from '@home/components/widget/lib/settings/common/css-unit-select.component'; import * as WidgetActionsPanelComponent from '@home/components/widget/config/basic/common/widget-actions-panel.component'; @@ -538,7 +537,6 @@ class ModulesMap implements IModulesMap { '@shared/components/image/multiple-gallery-image-input.component': MultipleGalleryImageInputComponent, '@shared/components/popover.service': TbPopoverService, - '@home/models/datasource/attribute-datasource': AttributeDatasource, '@home/components/alarm/alarm-filter-config.component': AlarmFilterConfigComponent, '@home/components/alarm/alarm-comment-dialog.component': AlarmCommentDialogComponent, From 7248279a881532a6807ec3e6b1d0aaddb7eef972 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 9 Oct 2024 14:43:31 +0300 Subject: [PATCH 021/110] Introduce Git sync service --- .../service/sync/DefaultGitSyncService.java | 164 ++++++++++++++++++ .../server/service/sync/GitSyncService.java | 33 ++++ .../sync/vc/DefaultGitRepositoryService.java | 17 +- .../server/service/sync/vc/GitRepository.java | 45 ++++- 4 files changed, 237 insertions(+), 22 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java new file mode 100644 index 0000000000..3480a5c9c8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java @@ -0,0 +1,164 @@ +/** + * 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.service.sync; + +import jakarta.annotation.PreDestroy; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.sync.vc.RepositorySettings; +import org.thingsboard.server.service.sync.vc.GitRepository; +import org.thingsboard.server.service.sync.vc.GitRepository.FileType; +import org.thingsboard.server.service.sync.vc.GitRepository.RepoFile; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Service +@RequiredArgsConstructor +@Slf4j +public class DefaultGitSyncService implements GitSyncService { + + @Value("${vc.git.repositories-folder:${java.io.tmpdir}/repositories}") + private String repositoriesFolder; + + private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("git-sync")); + private final Map repositories = new ConcurrentHashMap<>(); + private final Map updateListeners = new ConcurrentHashMap<>(); + + @Override + public void registerSync(String key, String repoUri, String branch, long fetchFrequencyMs, Runnable onUpdate) { + RepositorySettings settings = new RepositorySettings(); + settings.setRepositoryUri(repoUri); + settings.setDefaultBranch(branch); + if (onUpdate != null) { + updateListeners.put(key, onUpdate); + } + + executor.execute(() -> { + initRepository(key, settings); + }); + + executor.scheduleWithFixedDelay(() -> { + GitRepository repository = repositories.get(key); + if (repository == null || Files.notExists(Path.of(repository.getDirectory()))) { + initRepository(key, settings); + return; + } + + try { + log.debug("Fetching {} repository", key); + repository.fetch(); + onUpdate(key); + } catch (Throwable e) { + log.error("Failed to fetch {} repository", key, e); + } + }, fetchFrequencyMs, fetchFrequencyMs, TimeUnit.MILLISECONDS); + } + + @Override + public List listFiles(String key, String path, int depth, FileType type) { + GitRepository repository = getRepository(key); + return repository.listFilesAtCommit(getBranchRef(repository), path, depth).stream() + .filter(file -> type == null || file.type() == type) + .toList(); + } + + + @Override + public String getFileContent(String key, String path) { + GitRepository repository = getRepository(key); + try { + return repository.getFileContentAtCommit(path, getBranchRef(repository)); + } catch (Exception e) { + log.warn("Failed to get file content for path {} for {} repository: {}", path, key, e.getMessage()); + return "{}"; + } + } + + @Override + public String getGithubRawContentUrl(String key, String path) { + if (path == null) { + return ""; + } + RepositorySettings settings = getRepository(key).getSettings(); + return StringUtils.removeEnd(settings.getRepositoryUri(), ".git") + "/blob/" + settings.getDefaultBranch() + "/" + path + "?raw=true"; + } + + private GitRepository getRepository(String key) { + GitRepository repository = repositories.get(key); + if (repository != null) { + if (Files.notExists(Path.of(repository.getDirectory()))) { + // reinitializing the repository because folder was deleted + initRepository(key, repository.getSettings()); + } + } + + repository = repositories.get(key); + if (repository == null) { + throw new IllegalStateException("{} repository is not initialized"); + } + return repository; + } + + private void initRepository(String key, RepositorySettings settings) { + try { + repositories.remove(key); + Path directory = getRepoDirectory(settings); + + GitRepository repository = GitRepository.openOrClone(directory, settings, true); + repositories.put(key, repository); + log.info("Initialized {} repository", key); + + onUpdate(key); + } catch (Throwable e) { + log.error("Failed to init {} repository for settings {}", key, settings, e); + } + } + + private void onUpdate(String key) { + Runnable listener = updateListeners.get(key); + if (listener != null) { + listener.run(); + } + } + + private Path getRepoDirectory(RepositorySettings settings) { + // using uri to define folder name in case repo url is changed + String name = URI.create(settings.getRepositoryUri()).getPath().replaceAll("[^a-zA-Z]", ""); + return Path.of(repositoriesFolder, name); + } + + private String getBranchRef(GitRepository repository) { + return "refs/remotes/origin/" + repository.getSettings().getDefaultBranch(); + } + + @PreDestroy + private void preDestroy() { + executor.shutdownNow(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java b/application/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java new file mode 100644 index 0000000000..d1a09757f7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/GitSyncService.java @@ -0,0 +1,33 @@ +/** + * 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.service.sync; + +import org.thingsboard.server.service.sync.vc.GitRepository.FileType; +import org.thingsboard.server.service.sync.vc.GitRepository.RepoFile; + +import java.util.List; + +public interface GitSyncService { + + void registerSync(String key, String repoUri, String branch, long fetchFrequencyMs, Runnable onUpdate); + + List listFiles(String key, String path, int depth, FileType type); + + String getFileContent(String key, String path); + + String getGithubRawContentUrl(String key, String path); + +} diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java index 0788143030..07edfedf21 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java @@ -283,22 +283,7 @@ public class DefaultGitRepositoryService implements GitRepositoryService { private GitRepository openOrCloneRepository(TenantId tenantId, RepositorySettings settings, boolean fetch) throws Exception { log.debug("[{}] Init tenant repository started.", tenantId); Path repositoryDirectory = Path.of(repositoriesFolder, settings.isLocalOnly() ? "local_" + settings.getRepositoryUri() : tenantId.getId().toString()); - - GitRepository repository; - if (Files.exists(repositoryDirectory)) { - repository = GitRepository.open(repositoryDirectory.toFile(), settings); - if (fetch) { - repository.fetch(); - } - } else { - Files.createDirectories(repositoryDirectory); - if (settings.isLocalOnly()) { - repository = GitRepository.create(settings, repositoryDirectory.toFile()); - } else { - repository = GitRepository.clone(settings, repositoryDirectory.toFile()); - } - } - + GitRepository repository = GitRepository.openOrClone(repositoryDirectory, settings, fetch); repositories.put(tenantId, repository); log.debug("[{}] Init tenant repository completed.", tenantId); return repository; diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index aaa4c22289..015eab1492 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -21,6 +21,7 @@ import com.google.common.collect.Streams; import lombok.Data; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; @@ -75,6 +76,7 @@ import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.security.KeyPair; import java.security.PublicKey; import java.util.ArrayList; @@ -136,6 +138,24 @@ public class GitRepository { return new GitRepository(git, settings, authHandler, directory.getAbsolutePath()); } + public static GitRepository openOrClone(Path directory, RepositorySettings settings, boolean fetch) throws IOException, GitAPIException { + GitRepository repository; + if (Files.exists(directory)) { + repository = GitRepository.open(directory.toFile(), settings); + if (fetch) { + repository.fetch(); + } + } else { + Files.createDirectories(directory); + if (settings.isLocalOnly()) { + repository = GitRepository.create(settings, directory.toFile()); + } else { + repository = GitRepository.clone(settings, directory.toFile()); + } + } + return repository; + } + public static void test(RepositorySettings settings, File directory) throws Exception { if (settings.isLocalOnly()) { return; @@ -233,22 +253,29 @@ public class GitRepository { return iterableToPageData(commits, this::toCommit, pageLink, revCommitComparatorFunction); } - public List listFilesAtCommit(String commitId) throws IOException { - return listFilesAtCommit(commitId, null); + public List listFilesAtCommit(String commitId, String path) { + return listFilesAtCommit(commitId, path, -1).stream().map(RepoFile::path).toList(); } - public List listFilesAtCommit(String commitId, String path) throws IOException { + @SneakyThrows + public List listFilesAtCommit(String commitId, String path, int depth) { log.debug("Executing listFilesAtCommit [{}][{}][{}]", settings.getRepositoryUri(), commitId, path); - List files = new ArrayList<>(); + List files = new ArrayList<>(); RevCommit revCommit = resolveCommit(commitId); try (TreeWalk treeWalk = new TreeWalk(git.getRepository())) { treeWalk.reset(revCommit.getTree().getId()); if (StringUtils.isNotEmpty(path)) { treeWalk.setFilter(PathFilter.create(path)); } - treeWalk.setRecursive(true); + boolean fixedDepth = depth != -1; + treeWalk.setRecursive(!fixedDepth); while (treeWalk.next()) { - files.add(treeWalk.getPathString()); + if (!fixedDepth || treeWalk.getDepth() == depth) { + files.add(new RepoFile(treeWalk.getPathString(), treeWalk.getNameString(), treeWalk.isSubtree() ? FileType.DIRECTORY : FileType.FILE)); + } + if (fixedDepth && treeWalk.getDepth() < depth) { + treeWalk.enterSubtree(); + } } } return files; @@ -584,4 +611,10 @@ public class GitRepository { private String diffStringValue; } + public record RepoFile(String path, String name, FileType type) {} + + public enum FileType { + FILE, DIRECTORY + } + } From 37f2ec134ddba3b9bf6fad74179d7e52d41803de Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 9 Oct 2024 16:28:04 +0300 Subject: [PATCH 022/110] Git: minor refactoring --- .../server/service/sync/DefaultGitSyncService.java | 5 ++--- .../service/sync/vc/DefaultGitRepositoryService.java | 3 +-- .../thingsboard/server/service/sync/vc/GitRepository.java | 7 ++++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java index 3480a5c9c8..3b4e8d6985 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultGitSyncService.java @@ -28,7 +28,6 @@ import org.thingsboard.server.service.sync.vc.GitRepository.FileType; import org.thingsboard.server.service.sync.vc.GitRepository.RepoFile; import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Map; @@ -64,7 +63,7 @@ public class DefaultGitSyncService implements GitSyncService { executor.scheduleWithFixedDelay(() -> { GitRepository repository = repositories.get(key); - if (repository == null || Files.notExists(Path.of(repository.getDirectory()))) { + if (repository == null || !GitRepository.exists(repository.getDirectory())) { initRepository(key, settings); return; } @@ -111,7 +110,7 @@ public class DefaultGitSyncService implements GitSyncService { private GitRepository getRepository(String key) { GitRepository repository = repositories.get(key); if (repository != null) { - if (Files.notExists(Path.of(repository.getDirectory()))) { + if (!GitRepository.exists(repository.getDirectory())) { // reinitializing the repository because folder was deleted initRepository(key, repository.getSettings()); } diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java index 07edfedf21..239eb18748 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java @@ -40,7 +40,6 @@ import org.thingsboard.server.service.sync.vc.GitRepository.Diff; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.List; @@ -203,7 +202,7 @@ public class DefaultGitRepositoryService implements GitRepositoryService { GitRepository gitRepository = Optional.ofNullable(repositories.get(tenantId)) .orElseThrow(() -> new IllegalStateException("Repository is not initialized")); - if (!Files.exists(Path.of(gitRepository.getDirectory()))) { + if (!GitRepository.exists(gitRepository.getDirectory())) { try { return openOrCloneRepository(tenantId, gitRepository.getSettings(), false); } catch (Exception e) { diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index 015eab1492..251821b74e 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -140,12 +140,13 @@ public class GitRepository { public static GitRepository openOrClone(Path directory, RepositorySettings settings, boolean fetch) throws IOException, GitAPIException { GitRepository repository; - if (Files.exists(directory)) { + if (GitRepository.exists(directory.toString())) { repository = GitRepository.open(directory.toFile(), settings); if (fetch) { repository.fetch(); } } else { + FileUtils.deleteDirectory(directory.toFile()); Files.createDirectories(directory); if (settings.isLocalOnly()) { repository = GitRepository.create(settings, directory.toFile()); @@ -436,6 +437,10 @@ public class GitRepository { return result; } + public static boolean exists(String directory) { + return Files.exists(Path.of(directory, ".git")); + } + private , T> T execute(C command) throws GitAPIException { if (command instanceof TransportCommand transportCommand && authHandler != null) { authHandler.configureCommand(transportCommand); From a289913476ec739c1758496f76610a4792913298 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 9 Oct 2024 19:36:42 +0300 Subject: [PATCH 023/110] Fx flex replacements. Switch to tailwind.css. --- ui-ngx/package.json | 3 + .../src/app/modules/home/home.component.html | 44 ++-- .../home/menu/side-menu.component.html | 2 +- .../scada-symbol-editor.component.html | 2 +- .../scada-symbol-editor.component.ts | 5 + .../login/create-password.component.html | 9 +- .../pages/login/link-expired.component.html | 4 +- .../login/pages/login/login.component.html | 14 +- .../reset-password-request.component.html | 9 +- .../pages/login/reset-password.component.html | 9 +- .../two-factor-auth-login.component.html | 24 +-- .../components/breadcrumb.component.html | 8 +- .../color-picker-panel.component.html | 4 +- .../app/shared/components/css.component.html | 14 +- .../dashboard-autocomplete.component.html | 2 +- .../dashboard-select-panel.component.html | 2 +- .../dashboard-select.component.html | 4 +- .../dialog/alert-dialog.component.html | 2 +- .../dialog/confirm-dialog.component.html | 2 +- .../entity-conflict-dialog.component.html | 4 +- .../dialog/error-alert-dialog.component.html | 2 +- .../json-object-edit-dialog.component.html | 8 +- .../node-script-test-dialog.component.html | 20 +- .../dialog/todo-dialog.component.html | 2 +- .../entity/entity-autocomplete.component.html | 2 +- .../entity/entity-list-select.component.html | 6 +- .../entity-subtype-select.component.html | 2 +- .../footer-fab-buttons.component.html | 2 +- .../grid/scroll-grid.component.html | 5 +- .../app/shared/components/html.component.html | 14 +- .../image/embed-image-dialog.component.html | 7 +- .../image/image-dialog.component.html | 2 +- .../image/image-gallery.component.html | 94 +++++---- .../image/image-gallery.component.scss | 14 +- .../image/image-gallery.component.ts | 5 - .../image/images-in-use-dialog.component.html | 8 +- ...ultiple-gallery-image-input.component.html | 14 +- .../image/upload-image-dialog.component.html | 4 +- .../shared/components/js-func.component.html | 18 +- .../components/json-content.component.html | 14 +- .../json-object-edit.component.html | 18 +- .../json-object-view.component.html | 6 +- .../shared/components/kv-map.component.html | 20 +- .../app/shared/components/logo.component.html | 4 +- .../components/markdown-editor.component.html | 18 +- .../shared/components/markdown.component.html | 2 +- .../material-icon-select.component.html | 6 +- .../components/material-icons.component.html | 10 +- .../multiple-image-input.component.html | 12 +- .../notification/notification.component.html | 16 +- .../ota-package-autocomplete.component.html | 2 +- .../protobuf-content.component.html | 12 +- .../components/script-lang.component.html | 6 +- .../components/snack-bar-component.html | 27 ++- .../socialshare-panel.component.html | 3 +- .../string-autocomplete.component.html | 2 +- .../string-items-list.component.html | 2 +- .../shared/components/svg-xml.component.html | 14 +- .../time/datapoints-limit.component.html | 4 +- .../components/time/datetime.component.html | 2 +- .../time/quick-time-interval.component.html | 4 +- .../time/timeinterval.component.html | 4 +- .../timewindow-config-dialog.component.html | 4 +- .../time/timewindow-panel.component.html | 10 +- .../components/time/timewindow.component.html | 4 +- .../time/timezone-select.component.html | 2 +- .../components/time/timezone.component.html | 2 +- .../components/unit-input.component.html | 4 +- .../components/user-menu.component.html | 6 +- .../components/value-input.component.html | 14 +- .../widgets-bundle-search.component.html | 2 +- ui-ngx/src/styles.scss | 78 ++++---- ui-ngx/tailwind.config.js | 67 +++++++ ui-ngx/yarn.lock | 189 +++++++++++++++++- 74 files changed, 625 insertions(+), 361 deletions(-) create mode 100644 ui-ngx/tailwind.config.js diff --git a/ui-ngx/package.json b/ui-ngx/package.json index 5f67ac948f..a753381720 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -147,6 +147,7 @@ "@typescript-eslint/eslint-plugin": "^8.7.0", "@typescript-eslint/parser": "^8.7.0", "@typescript-eslint/utils": "^8.7.0", + "autoprefixer": "^10.4.20", "compression-webpack-plugin": "^11.1.0", "directory-tree": "^3.5.2", "eslint": "~8.57.1", @@ -155,8 +156,10 @@ "eslint-plugin-prefer-arrow": "latest", "ngrx-store-freeze": "^0.2.4", "patch-package": "^8.0.0", + "postcss": "^8.4.47", "postinstall-prepare": "^2.0.0", "raw-loader": "^4.0.2", + "tailwindcss": "^3.4.13", "ts-node": "^10.9.2", "typescript": "~5.5.4", "webpack": "5.95.0" diff --git a/ui-ngx/src/app/modules/home/home.component.html b/ui-ngx/src/app/modules/home/home.component.html index 4c52919d34..bab9c76824 100644 --- a/ui-ngx/src/app/modules/home/home.component.html +++ b/ui-ngx/src/app/modules/home/home.component.html @@ -23,50 +23,58 @@ [opened]="sidenavOpened && !forceFullscreen">
-
+
- + -
- - - - + -
- +
+
- - @@ -75,7 +83,7 @@ -
+
diff --git a/ui-ngx/src/app/modules/home/menu/side-menu.component.html b/ui-ngx/src/app/modules/home/menu/side-menu.component.html index bbbc323502..6e5a666e84 100644 --- a/ui-ngx/src/app/modules/home/menu/side-menu.component.html +++ b/ui-ngx/src/app/modules/home/menu/side-menu.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
    +
    • diff --git a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.html b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.html index 96bd1d7a8a..3ece699a83 100644 --- a/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.html +++ b/ui-ngx/src/app/modules/home/pages/scada-symbol/scada-symbol-editor.component.html @@ -70,7 +70,7 @@
{ if (this.svgContent !== svgContent) { this.svgContent = svgContent; diff --git a/ui-ngx/src/app/modules/login/pages/login/create-password.component.html b/ui-ngx/src/app/modules/login/pages/login/create-password.component.html index 37781ac83a..752d6cf319 100644 --- a/ui-ngx/src/app/modules/login/pages/login/create-password.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/create-password.component.html @@ -15,8 +15,8 @@ limitations under the License. --> -
- +
+ login.create-password @@ -28,7 +28,7 @@
-
+
common.password @@ -42,8 +42,7 @@ lock -
+
diff --git a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html index a1d52f0735..2d9fa4d58b 100644 --- a/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/link-expired.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -