Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into edge/refactoring

pull/4418/head
Volodymyr Babak 6 years ago
parent
commit
cc1b42f93d
  1. 37
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  2. 5
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  3. 4
      application/src/main/resources/thingsboard.yml
  4. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java
  5. 45
      common/data/src/main/java/org/thingsboard/server/common/data/event/DebugEvent.java
  6. 23
      common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleChainEventFilter.java
  7. 23
      common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleNodeEventFilter.java
  8. 36
      common/data/src/main/java/org/thingsboard/server/common/data/event/ErrorEventFilter.java
  9. 40
      common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java
  10. 20
      common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java
  11. 37
      common/data/src/main/java/org/thingsboard/server/common/data/event/LifeCycleEventFilter.java
  12. 36
      common/data/src/main/java/org/thingsboard/server/common/data/event/StatisticsEventFilter.java
  13. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapServers.java
  14. 11
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java
  15. 123
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java
  16. 47
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
  17. 4
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java
  18. 236
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
  19. 3
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java
  20. 23
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java
  21. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java
  22. 7
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  23. 3
      dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java
  24. 171
      dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java
  25. 106
      dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java
  26. 8
      ui-ngx/src/app/core/http/event.service.ts
  27. 7
      ui-ngx/src/app/core/http/resource.service.ts
  28. 18
      ui-ngx/src/app/core/services/time.service.ts
  29. 2
      ui-ngx/src/app/core/utils.ts
  30. 64
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html
  31. 35
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.scss
  32. 101
      ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts
  33. 122
      ui-ngx/src/app/modules/home/components/event/event-table-config.ts
  34. 13
      ui-ngx/src/app/modules/home/components/event/event-table.component.ts
  35. 2
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  36. 25
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  37. 3
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html
  38. 8
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss
  39. 6
      ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts
  40. 3
      ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts
  41. 2
      ui-ngx/src/app/modules/home/pages/asset/assets-table-config.resolver.ts
  42. 7
      ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts
  43. 54
      ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts
  44. 12
      ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts
  45. 5
      ui-ngx/src/app/modules/home/pages/entity-view/entity-views-table-config.resolver.ts
  46. 6
      ui-ngx/src/app/modules/home/pages/resource/resources-library.component.ts
  47. 72
      ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts
  48. 4
      ui-ngx/src/app/shared/components/file-input.component.ts
  49. 2
      ui-ngx/src/app/shared/components/json-object-edit.component.ts
  50. 34
      ui-ngx/src/app/shared/models/event.models.ts
  51. 2
      ui-ngx/src/app/shared/models/resource.models.ts
  52. 5
      ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
  53. 5
      ui-ngx/src/assets/locale/locale.constant-de_DE.json
  54. 5
      ui-ngx/src/assets/locale/locale.constant-el_GR.json
  55. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  56. 5
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  57. 5
      ui-ngx/src/assets/locale/locale.constant-fa_IR.json
  58. 5
      ui-ngx/src/assets/locale/locale.constant-fr_FR.json
  59. 5
      ui-ngx/src/assets/locale/locale.constant-it_IT.json
  60. 5
      ui-ngx/src/assets/locale/locale.constant-ja_JA.json
  61. 5
      ui-ngx/src/assets/locale/locale.constant-ka_GE.json
  62. 5
      ui-ngx/src/assets/locale/locale.constant-ko_KR.json
  63. 5
      ui-ngx/src/assets/locale/locale.constant-lv_LV.json
  64. 5
      ui-ngx/src/assets/locale/locale.constant-pt_BR.json
  65. 9
      ui-ngx/src/assets/locale/locale.constant-ro_RO.json
  66. 5
      ui-ngx/src/assets/locale/locale.constant-ru_RU.json
  67. 5
      ui-ngx/src/assets/locale/locale.constant-sl_SI.json
  68. 5
      ui-ngx/src/assets/locale/locale.constant-tr_TR.json
  69. 5
      ui-ngx/src/assets/locale/locale.constant-uk_UA.json
  70. 5
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  71. 5
      ui-ngx/src/assets/locale/locale.constant-zh_TW.json

37
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -18,12 +18,14 @@ package org.thingsboard.server.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
@ -101,4 +104,38 @@ public class EventController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
public PageData<Event> getEvents(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam("tenantId") String strTenantId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestBody EventFilter eventFilter,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
if(sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) {
sortProperty = ModelConstants.CREATED_TIME_PROPERTY;
}
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
} }

5
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -17,10 +17,8 @@ package org.thingsboard.server.service.install;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile; import org.springframework.context.annotation.Profile;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@ -60,11 +58,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.BooleanFilterPredicate; import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
import org.thingsboard.server.common.data.query.DynamicValue; import org.thingsboard.server.common.data.query.DynamicValue;
import org.thingsboard.server.common.data.query.DynamicValueSourceType; import org.thingsboard.server.common.data.query.DynamicValueSourceType;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.EntityKeyValueType;
import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;

4
application/src/main/resources/thingsboard.yml

@ -322,7 +322,7 @@ actors:
# Enable/disable actor statistics # Enable/disable actor statistics
enabled: "${ACTORS_STATISTICS_ENABLED:true}" enabled: "${ACTORS_STATISTICS_ENABLED:true}"
js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}" js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}"
persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:10000}"
cache: cache:
# caffeine or redis # caffeine or redis
@ -516,7 +516,7 @@ js:
# Built-in JVM JavaScript environment properties # Built-in JVM JavaScript environment properties
local: local:
# Use Sandboxed (secured) JVM JavaScript environment # Use Sandboxed (secured) JVM JavaScript environment
use_js_sandbox: "${USE_LOCAL_JS_SANDBOX:false}" use_js_sandbox: "${USE_LOCAL_JS_SANDBOX:true}"
# Specify thread pool size for JavaScript sandbox resource monitor # Specify thread pool size for JavaScript sandbox resource monitor
monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
# Maximum CPU time in milliseconds allowed for script execution # Maximum CPU time in milliseconds allowed for script execution

3
common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.event;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -41,6 +42,8 @@ public interface EventService {
List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit); List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
PageData<Event> findEventsByFilter(TenantId tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink);
void removeEvents(TenantId tenantId, EntityId entityId); void removeEvents(TenantId tenantId, EntityId entityId);
} }

45
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugEvent.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public abstract class DebugEvent implements EventFilter {
private String msgDirectionType;
private String server;
private String dataSearch;
private String metadataSearch;
private String entityName;
private String relationType;
private String entityId;
private String msgType;
private boolean isError;
private String error;
public void setIsError(boolean isError) {
this.isError = isError;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(msgDirectionType) || !StringUtils.isEmpty(server) || !StringUtils.isEmpty(dataSearch) || !StringUtils.isEmpty(metadataSearch)
|| !StringUtils.isEmpty(entityName) || !StringUtils.isEmpty(relationType) || !StringUtils.isEmpty(entityId) || !StringUtils.isEmpty(msgType) || !StringUtils.isEmpty(error) || isError;
}
}

23
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleChainEventFilter.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
public class DebugRuleChainEventFilter extends DebugEvent {
@Override
public EventType getEventType() {
return EventType.DEBUG_RULE_CHAIN;
}
}

23
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleNodeEventFilter.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
public class DebugRuleNodeEventFilter extends DebugEvent {
@Override
public EventType getEventType() {
return EventType.DEBUG_RULE_NODE;
}
}

36
common/data/src/main/java/org/thingsboard/server/common/data/event/ErrorEventFilter.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public class ErrorEventFilter implements EventFilter {
private String server;
private String method;
private String error;
@Override
public EventType getEventType() {
return EventType.ERROR;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(method) || !StringUtils.isEmpty(error);
}
}

40
common/data/src/main/java/org/thingsboard/server/common/data/event/EventFilter.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "eventType")
@JsonSubTypes({
@JsonSubTypes.Type(value = DebugRuleNodeEventFilter.class, name = "DEBUG_RULE_NODE"),
@JsonSubTypes.Type(value = DebugRuleChainEventFilter.class, name = "DEBUG_RULE_CHAIN"),
@JsonSubTypes.Type(value = ErrorEventFilter.class, name = "ERROR"),
@JsonSubTypes.Type(value = LifeCycleEventFilter.class, name = "LC_EVENT"),
@JsonSubTypes.Type(value = StatisticsEventFilter.class, name = "STATS")
})
public interface EventFilter {
@JsonIgnore
EventType getEventType();
boolean hasFilterForJsonBody();
}

20
common/data/src/main/java/org/thingsboard/server/common/data/event/EventType.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
public enum EventType {
ERROR, LC_EVENT, STATS, DEBUG_RULE_NODE, DEBUG_RULE_CHAIN
}

37
common/data/src/main/java/org/thingsboard/server/common/data/event/LifeCycleEventFilter.java

@ -0,0 +1,37 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public class LifeCycleEventFilter implements EventFilter {
private String server;
private String event;
private String status;
private String error;
@Override
public EventType getEventType() {
return EventType.LC_EVENT;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(event) || !StringUtils.isEmpty(status) || !StringUtils.isEmpty(error);
}
}

36
common/data/src/main/java/org/thingsboard/server/common/data/event/StatisticsEventFilter.java

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.event;
import lombok.Data;
import org.eclipse.leshan.core.util.StringUtils;
@Data
public class StatisticsEventFilter implements EventFilter {
private String server;
private Integer messagesProcessed;
private Integer errorsOccurred;
@Override
public EventType getEventType() {
return EventType.STATS;
}
@Override
public boolean hasFilterForJsonBody() {
return !StringUtils.isEmpty(server) || (messagesProcessed != null && messagesProcessed > 0) || (errorsOccurred != null && errorsOccurred > 0);
}
}

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/LwM2MBootstrapServers.java

@ -23,5 +23,5 @@ public class LwM2MBootstrapServers {
private Integer lifetime = 300; private Integer lifetime = 300;
private Integer defaultMinPeriod = 1; private Integer defaultMinPeriod = 1;
private boolean notifIfDisabled = true; private boolean notifIfDisabled = true;
private String binding = "U"; private String binding = "UQ";
} }

11
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mSessionMsgListener.java

@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
@ -74,12 +75,12 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
@Override @Override
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
log.info("[{}] toDeviceRpcRequest", toDeviceRequest); this.service.onToDeviceRpcRequest(toDeviceRequest);
} }
@Override @Override
public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
log.info("[{}] toServerRpcResponse", toServerResponse); this.service.onToServerRpcResponse(toServerResponse);
} }
@Override @Override
@ -87,13 +88,15 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s
log.info("[{}] operationComplete", future); log.info("[{}] operationComplete", future);
} }
public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { @Override
public void onResourceUpdate(@NotNull Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) {
if (ResourceType.LWM2M_MODEL.name().equals(resourceUpdateMsgOpt.get().getResourceType())) { if (ResourceType.LWM2M_MODEL.name().equals(resourceUpdateMsgOpt.get().getResourceType())) {
this.service.onResourceUpdate(resourceUpdateMsgOpt); this.service.onResourceUpdate(resourceUpdateMsgOpt);
} }
} }
public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) { @Override
public void onResourceDelete(@NotNull Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) {
if (ResourceType.LWM2M_MODEL.name().equals(resourceDeleteMsgOpt.get().getResourceType())) { if (ResourceType.LWM2M_MODEL.name().equals(resourceDeleteMsgOpt.get().getResourceType())) {
this.service.onResourceDelete(resourceDeleteMsgOpt); this.service.onResourceDelete(resourceDeleteMsgOpt);
} }

123
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportHandler.java

@ -22,15 +22,14 @@ import com.google.gson.JsonSyntaxException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.eclipse.californium.core.network.config.NetworkConfig; import org.eclipse.californium.core.network.config.NetworkConfig;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
import org.eclipse.leshan.core.node.LwM2mNode;
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.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.codec.CodecException; import org.eclipse.leshan.core.node.codec.CodecException;
import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.WriteAttributesRequest;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.server.californium.LeshanServerBuilder; import org.eclipse.leshan.server.californium.LeshanServerBuilder;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
@ -44,11 +43,19 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION;
import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
@ -56,8 +63,19 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA
public class LwM2mTransportHandler { public class LwM2mTransportHandler {
public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me";
public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
public static final String BOOTSTRAP = "bootstrap";
public static final String SERVERS = "servers";
public static final String LWM2M_SERVER = "lwm2mServer";
public static final String BOOTSTRAP_SERVER = "bootstrapServer";
public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";
public static final String ATTRIBUTE = "attribute"; public static final String ATTRIBUTE = "attribute";
public static final String TELEMETRY = "telemetry"; public static final String TELEMETRY = "telemetry";
public static final String KEY_NAME = "keyName";
public static final String OBSERVE = "observe";
public static final String ATTRIBUTE_LWM2M = "attributeLwm2m";
private static final String REQUEST = "/request"; private static final String REQUEST = "/request";
private static final String RESPONSE = "/response"; private static final String RESPONSE = "/response";
private static final String ATTRIBUTES = "/" + ATTRIBUTE; private static final String ATTRIBUTES = "/" + ATTRIBUTE;
@ -70,14 +88,6 @@ public class LwM2mTransportHandler {
public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES; public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES;
public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr";
public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings";
public static final String KEY_NAME = "keyName";
public static final String OBSERVE = "observe";
public static final String BOOTSTRAP = "bootstrap";
public static final String SERVERS = "servers";
public static final String LWM2M_SERVER = "lwm2mServer";
public static final String BOOTSTRAP_SERVER = "bootstrapServer";
public static final String LOG_LW2M_TELEMETRY = "logLwm2m"; public static final String LOG_LW2M_TELEMETRY = "logLwm2m";
public static final String LOG_LW2M_INFO = "info"; public static final String LOG_LW2M_INFO = "info";
@ -144,19 +154,19 @@ public class LwM2mTransportHandler {
throw new CodecException("Invalid value type for resource %s, type %s", resourcePath, type); throw new CodecException("Invalid value type for resource %s, type %s", resourcePath, type);
} }
} }
//
public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) { // public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) {
if (content instanceof LwM2mObject) { // if (content instanceof LwM2mObject) {
return (LwM2mObject) content; // return (LwM2mObject) content;
} else if (content instanceof LwM2mObjectInstance) { // } else if (content instanceof LwM2mObjectInstance) {
return (LwM2mObjectInstance) content; // return (LwM2mObjectInstance) content;
} else if (content instanceof LwM2mSingleResource) { // } else if (content instanceof LwM2mSingleResource) {
return (LwM2mSingleResource) content; // return (LwM2mSingleResource) content;
} else if (content instanceof LwM2mMultipleResource) { // } else if (content instanceof LwM2mMultipleResource) {
return (LwM2mMultipleResource) content; // return (LwM2mMultipleResource) content;
} // }
return null; // return null;
} // }
public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) {
LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile();
@ -166,6 +176,7 @@ public class LwM2mTransportHandler {
lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray());
lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray());
lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).getAsJsonArray()); lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).getAsJsonArray());
lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject());
return lwM2MClientProfile; return lwM2MClientProfile;
} }
@ -184,6 +195,9 @@ public class LwM2mTransportHandler {
* "attribute":["/2/0/1","/3/0/9"], * "attribute":["/2/0/1","/3/0/9"],
* "telemetry":["/1/0/1","/2/0/1","/6/0/1"], * "telemetry":["/1/0/1","/2/0/1","/6/0/1"],
* "observe":["/2/0","/2/0/0","/4/0/2"]} * "observe":["/2/0","/2/0/0","/4/0/2"]}
* "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"},
* "/3_1.0/0": {"gt": 17},
* "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}}
*/ */
public static LwM2mClientProfile getLwM2MClientProfileFromThingsboard(DeviceProfile deviceProfile) { public static LwM2mClientProfile getLwM2MClientProfileFromThingsboard(DeviceProfile deviceProfile) {
if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) {
@ -192,7 +206,7 @@ public class LwM2mTransportHandler {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String profileStr = mapper.writeValueAsString(profile); String profileStr = mapper.writeValueAsString(profile);
JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null;
return (getValidateCredentialsBodyFromThingsboard(profileJson)) ? LwM2mTransportHandler.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportHandler.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null;
} catch (IOException e) { } catch (IOException e) {
log.error("", e); log.error("", e);
} }
@ -240,7 +254,10 @@ public class LwM2mTransportHandler {
objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonArray() && objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonArray() &&
objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(OBSERVE) && objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(OBSERVE) &&
!objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonNull() && !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonNull() &&
objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonArray()); objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonArray() &&
objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE_LWM2M) &&
!objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonNull() &&
objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonObject());
} }
private static boolean getValidateBootstrapProfileFromThingsboard(JsonObject objectMsg) { private static boolean getValidateBootstrapProfileFromThingsboard(JsonObject objectMsg) {
@ -360,4 +377,54 @@ public class LwM2mTransportHandler {
return ObjectModel.DEFAULT_VERSION; return ObjectModel.DEFAULT_VERSION;
} }
} }
/**
* As example:
* a)Write-Attributes/3/0/9?pmin=1 means the Battery Level value will be notified
* to the Server with a minimum interval of 1sec;
* this value is set at theResource level.
* b)Write-Attributes/3/0/9?pmin means the Battery Level will be notified
* to the Server with a minimum value (pmin) given by the default one
* (resource 2 of Object Server ID=1),
* or with another value if this Attribute has been set at another level
* (Object or Object Instance: see section5.1.1).
* c)Write-Attributes/3/0?pmin=10 means that all Resources of Instance 0 of the Object ‘Device (ID:3)’
* will be notified to the Server with a minimum interval of 10 sec;
* this value is set at the Object Instance level.
* d)Write-Attributes /3/0/9?gt=45&st=10 means the Battery Level will be notified to the Server
* when:
* a.old value is 20 and new value is 35 due to step condition
* b.old value is 45 and new value is 50 due to gt condition
* c.old value is 50 and new value is 40 due to both gt and step conditions
* d.old value is 35 and new value is 20 due to step conditione)
* Write-Attributes /3/0/9?lt=20&gt=85&st=10 means the Battery Level will be notified to the Server
* when:
* a.old value is 17 and new value is 24 due to lt condition
* b.old value is 75 and new value is 90 due to both gt and step conditions
* String uriQueries = "pmin=10&pmax=60";
* AttributeSet attributes = AttributeSet.parse(uriQueries);
* WriteAttributesRequest request = new WriteAttributesRequest(target, attributes);
* Attribute gt = new Attribute(GREATER_THAN, Double.valueOf("45"));
* Attribute st = new Attribute(LESSER_THAN, Double.valueOf("10"));
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st};
*/
public static DownlinkRequest createWriteAttributeRequest(String target, Object params) {
AttributeSet attrSet = new AttributeSet(createWriteAttributes(params));
return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null;
}
private static Attribute[] createWriteAttributes(Object params) {
List attributeLists = new ArrayList<Attribute>();
ObjectMapper oMapper = new ObjectMapper();
Map<String, Object> map = oMapper.convertValue(params, ConcurrentHashMap.class);
map.forEach((k, v) -> {
if (!v.toString().isEmpty() || (v.toString().isEmpty() && OBJECT_VERSION.equals(k))) {
attributeLists.add(new Attribute(k,
(DIMENSION.equals(k) || MINIMUM_PERIOD.equals(k) || MAXIMUM_PERIOD.equals(k)) ?
((Double) v).longValue() : v));
}
});
return (Attribute[]) attributeLists.toArray(Attribute[]::new);
}
} }

47
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java

@ -18,8 +18,6 @@ package org.thingsboard.server.transport.lwm2m.server;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.Response; import org.eclipse.californium.core.coap.Response;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.ResourceModel; import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mNode; import org.eclipse.leshan.core.node.LwM2mNode;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
@ -33,7 +31,6 @@ import org.eclipse.leshan.core.request.DownlinkRequest;
import org.eclipse.leshan.core.request.ExecuteRequest; import org.eclipse.leshan.core.request.ExecuteRequest;
import org.eclipse.leshan.core.request.ObserveRequest; import org.eclipse.leshan.core.request.ObserveRequest;
import org.eclipse.leshan.core.request.ReadRequest; 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;
import org.eclipse.leshan.core.request.exception.ClientSleepingException; import org.eclipse.leshan.core.request.exception.ClientSleepingException;
import org.eclipse.leshan.core.response.CancelObservationResponse; import org.eclipse.leshan.core.response.CancelObservationResponse;
@ -60,7 +57,6 @@ import java.util.Date;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE;
@ -75,6 +71,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.createWriteAttributeRequest;
@Slf4j @Slf4j
@Service @Service
@ -174,47 +171,7 @@ public class LwM2mTransportRequest {
} }
break; break;
case PUT_TYPE_OPER_WRITE_ATTRIBUTES: case PUT_TYPE_OPER_WRITE_ATTRIBUTES:
/** request = createWriteAttributeRequest (target, params);
* As example:
* a)Write-Attributes/3/0/9?pmin=1 means the Battery Level value will be notified
* to the Server with a minimum interval of 1sec;
* this value is set at theResource level.
* b)Write-Attributes/3/0/9?pmin means the Battery Level will be notified
* to the Server with a minimum value (pmin) given by the default one
* (resource 2 of Object Server ID=1),
* or with another value if this Attribute has been set at another level
* (Object or Object Instance: see section5.1.1).
* c)Write-Attributes/3/0?pmin=10 means that all Resources of Instance 0 of the Object ‘Device (ID:3)’
* will be notified to the Server with a minimum interval of 10 sec;
* this value is set at the Object Instance level.
* d)Write-Attributes /3/0/9?gt=45&st=10 means the Battery Level will be notified to the Server
* when:
* a.old value is 20 and new value is 35 due to step condition
* b.old value is 45 and new value is 50 due to gt condition
* c.old value is 50 and new value is 40 due to both gt and step conditions
* d.old value is 35 and new value is 20 due to step conditione)
* Write-Attributes /3/0/9?lt=20&gt=85&st=10 means the Battery Level will be notified to the Server
* when:
* a.old value is 17 and new value is 24 due to lt condition
* b.old value is 75 and new value is 90 due to both gt and step conditions
* String uriQueries = "pmin=10&pmax=60";
* AttributeSet attributes = AttributeSet.parse(uriQueries);
* WriteAttributesRequest request = new WriteAttributesRequest(target, attributes);
* Attribute gt = new Attribute(GREATER_THAN, Double.valueOf("45"));
* Attribute st = new Attribute(LESSER_THAN, Double.valueOf("10"));
* Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60");
* Attribute [] attrs = {gt, st};
*/
Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.parseInt("1")));
Attribute[] attrs = {pmin};
AttributeSet attrSet = new AttributeSet(attrs);
if (resultIds.isResource()) {
request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), attrSet);
} else if (resultIds.isObjectInstance()) {
request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), attrSet);
} else if (resultIds.getObjectId() >= 0) {
request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet);
}
break; break;
} }

4
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportService.java

@ -51,6 +51,10 @@ public interface LwM2mTransportService {
void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt); void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt);
void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest);
void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse);
void doTrigger(Registration registration, String path); void doTrigger(Registration registration, String path);
void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); void doDisconnect(TransportProtos.SessionInfoProto sessionInfo);

236
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java

@ -74,11 +74,14 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION;
import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_TOPIC; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_TOPIC;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_TELEMETRY_TOPIC; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_TELEMETRY_TOPIC;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_READ;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR;
@ -87,6 +90,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandle
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_EXECUTE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_WRITE_REPLACE;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer;
@ -213,7 +217,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect
*/ */
public void unReg(Registration registration, Collection<Observation> observations) { public void unReg(Registration registration, Collection<Observation> observations) {
executorUnRegistered.submit(() -> { executorUnRegistered.submit(() -> {
try { try {
this.setCancelObservations(registration); this.setCancelObservations(registration);
this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration);
@ -365,17 +369,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
} }
/** /**
*
* @param resourceUpdateMsgOpt - * @param resourceUpdateMsgOpt -
*/ */
@Override @Override
public void onResourceUpdate (Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) {
String idVer = resourceUpdateMsgOpt.get().getResourceKey(); String idVer = resourceUpdateMsgOpt.get().getResourceKey();
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())); lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider()));
} }
/** /**
*
* @param resourceDeleteMsgOpt - * @param resourceDeleteMsgOpt -
*/ */
@Override @Override
@ -384,6 +386,14 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())); lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider()));
} }
public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) {
log.info("[{}] toDeviceRpcRequest", toDeviceRequest);
}
public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) {
log.info("[{}] toServerRpcResponse", toServerResponse);
}
/** /**
* Trigger Server path = "/1/0/8" * Trigger Server path = "/1/0/8"
* <p> * <p>
@ -496,15 +506,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
*/ */
private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) { private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
Set<String> clientObjects = this.getAllOjectsInClient(registration); Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration);
if (clientObjects != null && LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { if (clientObjects != null && clientObjects.size() > 0) {
// #2 if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) {
lwM2MClient.getPendingRequests().addAll(clientObjects); // #2
clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), lwM2MClient.getPendingRequests().addAll(clientObjects);
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout())); clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()));
}
// #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_OBSERVE, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, PUT_TYPE_OPER_WRITE_ATTRIBUTES, clientObjects);
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_DISCOVER, clientObjects);
} }
// #1
this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_OBSERVE);
} }
/** /**
@ -611,41 +625,61 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/** /**
* Start observe/read: Attr/Telemetry * Start observe/read: Attr/Telemetry
* #1 - Analyze: * #1 - Analyze: path in resource profile == client resource
* #1.1 path in resource profile == client resource
* *
* @param registration - * @param registration -
*/ */
private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient, String typeOper) { private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient,
String typeOper, Set<String> clientObjects) {
LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration);
Set<String> clientInstances = this.getAllInstancesInClient(registration); Set<String> result = null;
Set<String> result; ConcurrentHashMap<String, Object> params = null;
if (GET_TYPE_OPER_READ.equals(typeOper)) { if (GET_TYPE_OPER_READ.equals(typeOper)) {
result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(), new TypeReference<>() { result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(),
}); new TypeReference<>() {
result.addAll(JacksonUtil.convertValue(lwM2MClientProfile.getPostTelemetryProfile().toString(), new TypeReference<>() { });
})); result.addAll(JacksonUtil.fromString(lwM2MClientProfile.getPostTelemetryProfile().toString(),
} else { new TypeReference<>() {
result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(), new TypeReference<>() { }));
}); } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(),
new TypeReference<>() {
});
} else if (GET_TYPE_OPER_DISCOVER.equals(typeOper)) {
result = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()).keySet();
;
} else if (PUT_TYPE_OPER_WRITE_ATTRIBUTES.equals(typeOper)) {
params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile());
result = params.keySet();
} }
Set<String> pathSend = ConcurrentHashMap.newKeySet(); if (!result.isEmpty()) {
result.forEach(target -> { // #1
// #1.1 Set<String> pathSend = result.stream().filter(target -> {
String[] resPath = target.split("/"); return target.split(LWM2M_SEPARATOR_PATH).length < 3 ?
String instance = "/" + resPath[1] + "/" + resPath[2]; clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) :
if (clientInstances != null && clientInstances.size() > 0 && clientInstances.contains(instance)) { clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]);
pathSend.add(target); }
)
.collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
lwM2MClient.getPendingRequests().addAll(pathSend);
ConcurrentHashMap<String, Object> finalParams = params;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
null, finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()));
if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
lwM2MClient.initValue(this, null);
}
} }
});
lwM2MClient.getPendingRequests().addAll(pathSend);
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(),
null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()));
if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) {
lwM2MClient.initValue(this, null);
} }
} }
private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) {
ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType());
return pathAttributes;
}
/** /**
* Update parameters device in LwM2MClient * Update parameters device in LwM2MClient
* If new deviceProfile != old deviceProfile => update deviceProfile * If new deviceProfile != old deviceProfile => update deviceProfile
@ -665,21 +699,6 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
lwM2MClient.setProfileId(device.getDeviceProfileId().getId()); lwM2MClient.setProfileId(device.getDeviceProfileId().getId());
} }
/**
* @param registration -
* @return - all object in client
*/
private Set<String> getAllOjectsInClient(Registration registration) {
Set<String> clientObjects = ConcurrentHashMap.newKeySet();
Arrays.stream(registration.getObjectLinks()).forEach(url -> {
LwM2mPath pathIds = new LwM2mPath(url.getUrl());
if (pathIds.isObjectInstance()) {
clientObjects.add("/" + pathIds.getObjectId());
}
});
return (clientObjects.size() > 0) ? clientObjects : null;
}
/** /**
* @param registration - * @param registration -
* @return all instances in client * @return all instances in client
@ -724,16 +743,20 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
private void addParameters(String path, JsonObject parameters, Registration registration) { private void addParameters(String path, JsonObject parameters, Registration registration) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
JsonObject names = lwM2mClientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile(); JsonObject names = lwM2mClientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile();
String resName = names.get(path).getAsString(); if (names != null && names.has(path)) {
if (resName != null && !resName.isEmpty()) { String resName = names.get(path).getAsString();
try { if (resName != null && !resName.isEmpty()) {
String resValue = this.getResourceValueToString(lwM2MClient, path); try {
if (resValue != null) { String resValue = this.getResourceValueToString(lwM2MClient, path);
parameters.addProperty(resName, resValue); if (resValue != null) {
parameters.addProperty(resName, resValue);
}
} catch (Exception e) {
log.error("Failed to add parameters.", e);
} }
} catch (Exception e) {
log.error("Failed to add parameters.", e);
} }
} else {
log.error("Failed to add parameters. path: [{}], names: [{}]", path, names);
} }
} }
@ -750,7 +773,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
/** /**
* @param lwM2MClient - * @param lwM2MClient -
* @param path - * @param path -
* @return - return value of Resource by idPath * @return - return value of Resource by idPath
*/ */
private LwM2mResource returnResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { private LwM2mResource returnResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) {
@ -779,6 +802,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* #3.1 Attribute isChange (add&del) * #3.1 Attribute isChange (add&del)
* #3.2 Telemetry isChange (add&del) * #3.2 Telemetry isChange (add&del)
* #3.3 KeyName isChange (add) * #3.3 KeyName isChange (add)
* #3.4 attributeLwm2m isChange (update WrightAttribute: add/update/del)
* #4 update * #4 update
* #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and send Value to thingsboard * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and send Value to thingsboard
* #4.2 del * #4.2 del
@ -789,6 +813,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
* -- path Attr/Telemetry includes newObserve and does not include oldObserve: send Request observe to Client * -- path Attr/Telemetry includes newObserve and does not include oldObserve: send Request observe to Client
* #5.3 Observe.del * #5.3 Observe.del
* -- different between newObserve and oldObserve: send Request cancel observe to client * -- different between newObserve and oldObserve: send Request cancel observe to client
* #6
* #6.1 - update WriteAttribute
* #6.2 - del WriteAttribute
* *
* @param registrationIds - * @param registrationIds -
* @param deviceProfile - * @param deviceProfile -
@ -803,6 +830,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
Set<String> telemetrySetOld = this.convertJsonArrayToSet(telemetryOld); Set<String> telemetrySetOld = this.convertJsonArrayToSet(telemetryOld);
JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile(); JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile();
JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile(); JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile();
JsonObject attributeLwm2mOld = lwM2MClientProfileOld.getPostAttributeLwm2mProfile();
LwM2mClientProfile lwM2MClientProfileNew = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId()); LwM2mClientProfile lwM2MClientProfileNew = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId());
JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile(); JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile();
@ -811,32 +839,41 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
Set<String> telemetrySetNew = this.convertJsonArrayToSet(telemetryNew); Set<String> telemetrySetNew = this.convertJsonArrayToSet(telemetryNew);
JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile(); JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile();
JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile(); JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile();
JsonObject attributeLwm2mNew = lwM2MClientProfileNew.getPostAttributeLwm2mProfile();
// #3 // #3
ResultsAnalyzerParameters sendAttrToThingsboard = new ResultsAnalyzerParameters(); ResultsAnalyzerParameters sendAttrToThingsboard = new ResultsAnalyzerParameters();
// #3.1 // #3.1
if (!attributeOld.equals(attributeNew)) { if (!attributeOld.equals(attributeNew)) {
ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, new TypeToken<Set<String>>() { ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld,
}.getType()), attributeSetNew); new TypeToken<Set<String>>() {
}.getType()), attributeSetNew);
sendAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); sendAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd());
sendAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); sendAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel());
} }
// #3.2 // #3.2
if (!telemetryOld.equals(telemetryNew)) { if (!telemetryOld.equals(telemetryNew)) {
ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, new TypeToken<Set<String>>() { ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld,
}.getType()), telemetrySetNew); new TypeToken<Set<String>>() {
}.getType()), telemetrySetNew);
sendAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); sendAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd());
sendAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); sendAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel());
} }
// #3.3 // #3.3
if (!keyNameOld.equals(keyNameNew)) { if (!keyNameOld.equals(keyNameNew)) {
ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), new TypeToken<ConcurrentHashMap<String, String>>() { ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(),
new TypeToken<ConcurrentHashMap<String, String>>() {
}.getType()), }.getType()),
new Gson().fromJson(keyNameNew.toString(), new TypeToken<ConcurrentHashMap<String, String>>() { new Gson().fromJson(keyNameNew.toString(), new TypeToken<ConcurrentHashMap<String, String>>() {
}.getType())); }.getType()));
sendAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); sendAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd());
} }
// #3.4, #6
if (!attributeLwm2mOld.equals(attributeLwm2mNew)) {
this.getAnalyzerAttributeLwm2m(registrationIds, attributeLwm2mOld, attributeLwm2mNew);
}
// #4.1 add // #4.1 add
if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) { if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) {
// update value in Resources // update value in Resources
@ -933,7 +970,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
}); });
} }
private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) { private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentHashMap<String, String> keyNameOld, ConcurrentHashMap<String, String> keyNameNew) {
ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
Set<String> paths = keyNameNew.entrySet() Set<String> paths = keyNameNew.entrySet()
.stream() .stream()
@ -943,6 +980,71 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService {
return analyzerParameters; return analyzerParameters;
} }
/**
* #3.4, #6
* #6
* #6.1 - send update WriteAttribute
* #6.2 - send empty WriteAttribute
*
* @param attributeLwm2mOld -
* @param attributeLwm2mNew -
* @return
*/
private void getAnalyzerAttributeLwm2m(Set<String> registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) {
ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters();
ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType());
ConcurrentHashMap<String, Object> lwm2mAttributesNew = new Gson().fromJson(attributeLwm2mNew.toString(),
new TypeToken<ConcurrentHashMap<String, Object>>() {
}.getType());
Set<String> pathOld = lwm2mAttributesOld.keySet();
Set<String> pathNew = lwm2mAttributesNew.keySet();
analyzerParameters.setPathPostParametersAdd(pathNew
.stream().filter(p -> !pathOld.contains(p)).collect(Collectors.toSet()));
analyzerParameters.setPathPostParametersDel(pathOld
.stream().filter(p -> !pathNew.contains(p)).collect(Collectors.toSet()));
Set<String> pathCommon = pathNew
.stream().filter(p -> pathOld.contains(p)).collect(Collectors.toSet());
Set<String> pathCommonChange = pathCommon
.stream().filter(p -> !lwm2mAttributesOld.get(p).equals(lwm2mAttributesNew.get(p))).collect(Collectors.toSet());
analyzerParameters.getPathPostParametersAdd().addAll(pathCommonChange);
// #6
// #6.2
if (analyzerParameters.getPathPostParametersAdd().size() > 0) {
registrationIds.forEach(registrationId -> {
Registration registration = this.lwM2mClientContext.getRegistration(registrationId);
Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration);
Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew;
pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, PUT_TYPE_OPER_WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
null, finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()));
}
});
}
// #6.2
if (analyzerParameters.getPathPostParametersDel().size() > 0) {
registrationIds.forEach(registrationId -> {
Registration registration = this.lwM2mClientContext.getRegistration(registrationId);
Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration);
Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]))
.collect(Collectors.toUnmodifiableSet());
if (!pathSend.isEmpty()) {
pathSend.forEach(target -> {
Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target);
params.clear();
params.put(OBJECT_VERSION, "");
lwM2mTransportRequest.sendAllRequest(registration, target, PUT_TYPE_OPER_WRITE_ATTRIBUTES, ContentFormat.TLV.getName(),
null, params, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout());
});
}
});
}
}
private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) { private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) {
LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null);
paramAnallyzer.forEach(p -> { paramAnallyzer.forEach(p -> {

3
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContext.java

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
public interface LwM2mClientContext { public interface LwM2mClientContext {
@ -51,4 +52,6 @@ public interface LwM2mClientContext {
Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles); Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles);
boolean addUpdateProfileParameters(DeviceProfile deviceProfile); boolean addUpdateProfileParameters(DeviceProfile deviceProfile);
Set<String> getSupportedIdVerInClient(Registration registration);
} }

23
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.transport.lwm2m.server.client; package org.thingsboard.server.transport.lwm2m.server.client;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.security.EditableSecurityStore; import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -27,11 +28,14 @@ import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore;
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler;
import org.thingsboard.server.transport.lwm2m.utils.TypeServer; import org.thingsboard.server.transport.lwm2m.utils.TypeServer;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId;
@Service @Service
@TbLwM2mTransportComponent @TbLwM2mTransportComponent
@ -90,7 +94,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
@Override @Override
public LwM2mClient updateInSessionsLwM2MClient(Registration registration) { public LwM2mClient updateInSessionsLwM2MClient(Registration registration) {
if (this.lwM2mClients.get(registration.getEndpoint()) == null) { if (this.lwM2mClients.get(registration.getEndpoint()) == null) {
addLwM2mClientToSession(registration.getEndpoint()); this.addLwM2mClientToSession(registration.getEndpoint());
} }
LwM2mClient lwM2MClient = lwM2mClients.get(registration.getEndpoint()); LwM2mClient lwM2MClient = lwM2mClients.get(registration.getEndpoint());
lwM2MClient.setRegistration(registration); lwM2MClient.setRegistration(registration);
@ -169,4 +173,21 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
} }
return false; return false;
} }
/**
* if isVer - ok or default ver=DEFAULT_LWM2M_VERSION
* @param registration -
* @return - all objectIdVer in client
*/
@Override
public Set<String> getSupportedIdVerInClient(Registration registration) {
Set<String> clientObjects = ConcurrentHashMap.newKeySet();
Arrays.stream(registration.getObjectLinks()).forEach(url -> {
LwM2mPath pathIds = new LwM2mPath(url.getUrl());
if (!pathIds.isRoot()) {
clientObjects.add(convertToIdVerFromObjectId(url.getUrl(), registration));
}
});
return (clientObjects.size() > 0) ? clientObjects : null;
}
} }

8
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java

@ -56,6 +56,13 @@ public class LwM2mClientProfile {
*/ */
private JsonArray postObserveProfile; private JsonArray postObserveProfile;
/**
* "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"},
* "/3_1.0/0": {"gt": 17},
* "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}}
*/
private JsonObject postAttributeLwm2mProfile;
public LwM2mClientProfile clone() { public LwM2mClientProfile clone() {
LwM2mClientProfile lwM2mClientProfile = new LwM2mClientProfile(); LwM2mClientProfile lwM2mClientProfile = new LwM2mClientProfile();
lwM2mClientProfile.postClientLwM2mSettings = this.deepCopy(this.postClientLwM2mSettings, JsonObject.class); lwM2mClientProfile.postClientLwM2mSettings = this.deepCopy(this.postClientLwM2mSettings, JsonObject.class);
@ -63,6 +70,7 @@ public class LwM2mClientProfile {
lwM2mClientProfile.postAttributeProfile = this.deepCopy(this.postAttributeProfile, JsonArray.class); lwM2mClientProfile.postAttributeProfile = this.deepCopy(this.postAttributeProfile, JsonArray.class);
lwM2mClientProfile.postTelemetryProfile = this.deepCopy(this.postTelemetryProfile, JsonArray.class); lwM2mClientProfile.postTelemetryProfile = this.deepCopy(this.postTelemetryProfile, JsonArray.class);
lwM2mClientProfile.postObserveProfile = this.deepCopy(this.postObserveProfile, JsonArray.class); lwM2mClientProfile.postObserveProfile = this.deepCopy(this.postObserveProfile, JsonArray.class);
lwM2mClientProfile.postAttributeLwm2mProfile = this.deepCopy(this.postAttributeLwm2mProfile, JsonObject.class);
return lwM2mClientProfile; return lwM2mClientProfile;
} }

7
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -30,7 +31,6 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.DataValidator;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -111,6 +111,11 @@ public class BaseEventService implements EventService {
return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit); return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
} }
@Override
public PageData<Event> findEventsByFilter(TenantId tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink) {
return eventDao.findEventByFilter(tenantId.getId(), entityId, eventFilter, pageLink);
}
@Override @Override
public void removeEvents(TenantId tenantId, EntityId entityId) { public void removeEvents(TenantId tenantId, EntityId entityId) {
PageData<Event> eventPageData; PageData<Event> eventPageData;

3
dao/src/main/java/org/thingsboard/server/dao/event/EventDao.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.event;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -88,6 +89,8 @@ public interface EventDao extends Dao<Event> {
*/ */
PageData<Event> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink); PageData<Event> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink);
PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink);
/** /**
* Find latest events by tenantId, entityId and eventType. * Find latest events by tenantId, entityId and eventType.
* *

171
dao/src/main/java/org/thingsboard/server/dao/sql/event/EventRepository.java

@ -44,11 +44,11 @@ public interface EventRepository extends PagingAndSortingRepository<EventEntity,
@Query("SELECT e FROM EventEntity e WHERE e.tenantId = :tenantId AND e.entityType = :entityType " + @Query("SELECT e FROM EventEntity e WHERE e.tenantId = :tenantId AND e.entityType = :entityType " +
"AND e.entityId = :entityId AND e.eventType = :eventType ORDER BY e.eventType DESC, e.id DESC") "AND e.entityId = :entityId AND e.eventType = :eventType ORDER BY e.eventType DESC, e.id DESC")
List<EventEntity> findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType( List<EventEntity> findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType(
@Param("tenantId") UUID tenantId, @Param("tenantId") UUID tenantId,
@Param("entityType") EntityType entityType, @Param("entityType") EntityType entityType,
@Param("entityId") UUID entityId, @Param("entityId") UUID entityId,
@Param("eventType") String eventType, @Param("eventType") String eventType,
Pageable pageable); Pageable pageable);
@Query("SELECT e FROM EventEntity e WHERE " + @Query("SELECT e FROM EventEntity e WHERE " +
"e.tenantId = :tenantId " + "e.tenantId = :tenantId " +
@ -80,4 +80,165 @@ public interface EventRepository extends PagingAndSortingRepository<EventEntity,
@Param("endTime") Long endTime, @Param("endTime") Long endTime,
Pageable pageable); Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = :eventType " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:type IS NULL OR lower(json_body->>'type') LIKE concat('%', lower(:type\\:\\:varchar), '%')) " +
"AND (:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:entityName IS NULL OR lower(json_body->>'entityName') LIKE concat('%', lower(:entityName\\:\\:varchar), '%')) " +
"AND (:relationType IS NULL OR lower(json_body->>'relationType') LIKE concat('%', lower(:relationType\\:\\:varchar), '%')) " +
"AND (:bodyEntityId IS NULL OR lower(json_body->>'entityId') LIKE concat('%', lower(:bodyEntityId\\:\\:varchar), '%')) " +
"AND (:msgType IS NULL OR lower(json_body->>'msgType') LIKE concat('%', lower(:msgType\\:\\:varchar), '%')) " +
"AND ((:isError = FALSE) OR (json_body->>'error') IS NOT NULL) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%')) " +
"AND (:data IS NULL OR lower(json_body->>'data') LIKE concat('%', lower(:data\\:\\:varchar), '%')) " +
"AND (:metadata IS NULL OR lower(json_body->>'metadata') LIKE concat('%', lower(:metadata\\:\\:varchar), '%')) ",
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = :eventType " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:type IS NULL OR lower(json_body->>'type') LIKE concat('%', lower(:type\\:\\:varchar), '%')) " +
"AND (:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:entityName IS NULL OR lower(json_body->>'entityName') LIKE concat('%', lower(:entityName\\:\\:varchar), '%')) " +
"AND (:relationType IS NULL OR lower(json_body->>'relationType') LIKE concat('%', lower(:relationType\\:\\:varchar), '%')) " +
"AND (:bodyEntityId IS NULL OR lower(json_body->>'entityId') LIKE concat('%', lower(:bodyEntityId\\:\\:varchar), '%')) " +
"AND (:msgType IS NULL OR lower(json_body->>'msgType') LIKE concat('%', lower(:msgType\\:\\:varchar), '%')) " +
"AND ((:isError = FALSE) OR (json_body->>'error') IS NOT NULL) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%')) " +
"AND (:data IS NULL OR lower(json_body->>'data') LIKE concat('%', lower(:data\\:\\:varchar), '%')) " +
"AND (:metadata IS NULL OR lower(json_body->>'metadata') LIKE concat('%', lower(:metadata\\:\\:varchar), '%'))"
)
Page<EventEntity> findDebugRuleNodeEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("eventType") String eventType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTime,
@Param("type") String type,
@Param("server") String server,
@Param("entityName") String entityName,
@Param("relationType") String relationType,
@Param("bodyEntityId") String bodyEntityId,
@Param("msgType") String msgType,
@Param("isError") boolean isError,
@Param("error") String error,
@Param("data") String data,
@Param("metadata") String metadata,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'ERROR' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:method IS NULL OR lower(json_body->>'method') LIKE concat('%', lower(:method\\:\\:varchar), '%')) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))",
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'ERROR' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:method IS NULL OR lower(json_body->>'method') LIKE concat('%', lower(:method\\:\\:varchar), '%')) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))")
Page<EventEntity> findErrorEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTIme,
@Param("server") String server,
@Param("method") String method,
@Param("error") String error,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'LC_EVENT' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:event IS NULL OR lower(json_body->>'event') LIKE concat('%', lower(:event\\:\\:varchar), '%')) " +
"AND ((:statusFilterEnabled = FALSE) OR lower(json_body->>'success')\\:\\:boolean = :statusFilter) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))"
,
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'LC_EVENT' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:event IS NULL OR lower(json_body->>'event') LIKE concat('%', lower(:event\\:\\:varchar), '%')) " +
"AND ((:statusFilterEnabled = FALSE) OR lower(json_body->>'success')\\:\\:boolean = :statusFilter) " +
"AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))"
)
Page<EventEntity> findLifeCycleEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTIme,
@Param("server") String server,
@Param("event") String event,
@Param("statusFilterEnabled") boolean statusFilterEnabled,
@Param("statusFilter") boolean statusFilter,
@Param("error") String error,
Pageable pageable);
@Query(nativeQuery = true,
value = "SELECT e.id, e.created_time, e.body, e.entity_id, e.entity_type, e.event_type, e.event_uid, e.tenant_id, ts FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'STATS' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(e.body\\:\\:json->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:messagesProcessed = 0 OR (json_body->>'messagesProcessed')\\:\\:integer >= :messagesProcessed) " +
"AND (:errorsOccurred = 0 OR (json_body->>'errorsOccurred')\\:\\:integer >= :errorsOccurred) ",
countQuery = "SELECT count(*) FROM " +
"(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " +
"e.tenant_id = :tenantId " +
"AND e.entity_type = :entityType " +
"AND e.entity_id = :entityId " +
"AND e.event_type = 'LC_EVENT' " +
"AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " +
") AS e WHERE " +
"(:server IS NULL OR lower(e.body\\:\\:json->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " +
"AND (:messagesProcessed = 0 OR (json_body->>'messagesProcessed')\\:\\:integer >= :messagesProcessed) " +
"AND (:errorsOccurred = 0 OR (json_body->>'errorsOccurred')\\:\\:integer >= :errorsOccurred) ")
Page<EventEntity> findStatisticsEvents(@Param("tenantId") UUID tenantId,
@Param("entityId") UUID entityId,
@Param("entityType") String entityType,
@Param("startTime") Long startTime,
@Param("endTime") Long endTIme,
@Param("server") String server,
@Param("messagesProcessed") Integer messagesProcessed,
@Param("errorsOccurred") Integer errorsOccurred,
Pageable pageable);
} }

106
dao/src/main/java/org/thingsboard/server/dao/sql/event/JpaBaseEventDao.java

@ -24,6 +24,12 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.event.DebugEvent;
import org.thingsboard.server.common.data.event.ErrorEventFilter;
import org.thingsboard.server.common.data.event.EventFilter;
import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.event.LifeCycleEventFilter;
import org.thingsboard.server.common.data.event.StatisticsEventFilter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EventId; import org.thingsboard.server.common.data.id.EventId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
@ -147,6 +153,98 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
DaoUtil.toPageable(pageLink))); DaoUtil.toPageable(pageLink)));
} }
@Override
public PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink) {
if (eventFilter.hasFilterForJsonBody()) {
switch (eventFilter.getEventType()) {
case DEBUG_RULE_NODE:
case DEBUG_RULE_CHAIN:
return findEventByFilter(tenantId, entityId, (DebugEvent) eventFilter, pageLink);
case LC_EVENT:
return findEventByFilter(tenantId, entityId, (LifeCycleEventFilter) eventFilter, pageLink);
case ERROR:
return findEventByFilter(tenantId, entityId, (ErrorEventFilter) eventFilter, pageLink);
case STATS:
return findEventByFilter(tenantId, entityId, (StatisticsEventFilter) eventFilter, pageLink);
default:
throw new RuntimeException("Not supported event type: " + eventFilter.getEventType());
}
} else {
return findEvents(tenantId, entityId, eventFilter.getEventType().name(), pageLink);
}
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, DebugEvent eventFilter, TimePageLink pageLink) {
return DaoUtil.toPageData(
eventRepository.findDebugRuleNodeEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
eventFilter.getEventType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getMsgDirectionType(),
eventFilter.getServer(),
eventFilter.getEntityName(),
eventFilter.getRelationType(),
eventFilter.getEntityId(),
eventFilter.getMsgType(),
eventFilter.isError(),
eventFilter.getError(),
eventFilter.getDataSearch(),
eventFilter.getMetadataSearch(),
DaoUtil.toPageable(pageLink)));
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, ErrorEventFilter eventFilter, TimePageLink pageLink) {
return DaoUtil.toPageData(
eventRepository.findErrorEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getServer(),
eventFilter.getMethod(),
eventFilter.getError(),
DaoUtil.toPageable(pageLink))
);
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, LifeCycleEventFilter eventFilter, TimePageLink pageLink) {
boolean statusFilterEnabled = !StringUtils.isEmpty(eventFilter.getStatus());
boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase("Success");
return DaoUtil.toPageData(
eventRepository.findLifeCycleEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getServer(),
eventFilter.getEvent(),
statusFilterEnabled,
statusFilter,
eventFilter.getError(),
DaoUtil.toPageable(pageLink))
);
}
private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, StatisticsEventFilter eventFilter, TimePageLink pageLink) {
return DaoUtil.toPageData(
eventRepository.findStatisticsEvents(
tenantId,
entityId.getId(),
entityId.getEntityType().name(),
notNull(pageLink.getStartTime()),
notNull(pageLink.getEndTime()),
eventFilter.getServer(),
notNull(eventFilter.getMessagesProcessed()),
notNull(eventFilter.getErrorsOccurred()),
DaoUtil.toPageable(pageLink))
);
}
@Override @Override
public List<Event> findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit) { public List<Event> findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit) {
List<EventEntity> latest = eventRepository.findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType( List<EventEntity> latest = eventRepository.findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType(
@ -177,4 +275,12 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
return Optional.of(DaoUtil.getData(eventInsertRepository.saveOrUpdate(entity))); return Optional.of(DaoUtil.getData(eventInsertRepository.saveOrUpdate(entity)));
} }
private long notNull(Long value) {
return value != null ? value : 0;
}
private int notNull(Integer value) {
return value != null ? value : 0;
}
} }

8
ui-ngx/src/app/core/http/event.service.ts

@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http';
import { TimePageLink } from '@shared/models/page/page-link'; import { TimePageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { DebugEventType, Event, EventType } from '@shared/models/event.models'; import { DebugEventType, Event, EventType, FilterEventBody } from '@shared/models/event.models';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -39,4 +39,10 @@ export class EventService {
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public getFilterEvents(entityId: EntityId, eventType: EventType | DebugEventType, tenantId: string,
filters: FilterEventBody, pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<Event>> {
return this.http.post<PageData<Event>>(`/api/events/${entityId.entityType}/${entityId.id}` +
`${pageLink.toQuery()}&tenantId=${tenantId}`, {...filters, eventType}, defaultHttpOptionsFromConfig(config));
}
} }

7
ui-ngx/src/app/core/http/resource.service.ts

@ -43,14 +43,17 @@ export class ResourceService {
} }
public downloadResource(resourceId: string): Observable<any> { public downloadResource(resourceId: string): Observable<any> {
return this.http.get(`/api/resource/${resourceId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( return this.http.get(`/api/resource/${resourceId}/download`, {
responseType: 'arraybuffer',
observe: 'response'
}).pipe(
map((response) => { map((response) => {
const headers = response.headers; const headers = response.headers;
const filename = headers.get('x-filename'); const filename = headers.get('x-filename');
const contentType = headers.get('content-type'); const contentType = headers.get('content-type');
const linkElement = document.createElement('a'); const linkElement = document.createElement('a');
try { try {
const blob = new Blob([response.body], { type: contentType }); const blob = new Blob([response.body], {type: contentType});
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
linkElement.setAttribute('href', url); linkElement.setAttribute('href', url);
linkElement.setAttribute('download', filename); linkElement.setAttribute('download', filename);

18
ui-ngx/src/app/core/services/time.service.ts

@ -31,7 +31,7 @@ import { isDefined } from '@core/utils';
export interface TimeInterval { export interface TimeInterval {
name: string; name: string;
translateParams: {[key: string]: any}; translateParams: { [key: string]: any };
value: number; value: number;
} }
@ -56,14 +56,14 @@ export class TimeService {
public loadMaxDatapointsLimit(): Observable<number> { public loadMaxDatapointsLimit(): Observable<number> {
return this.http.get<number>('/api/dashboard/maxDatapointsLimit', return this.http.get<number>('/api/dashboard/maxDatapointsLimit',
defaultHttpOptions(true)).pipe( defaultHttpOptions(true)).pipe(
map( (limit) => { map((limit) => {
this.maxDatapointsLimit = limit; this.maxDatapointsLimit = limit;
if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) { if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) {
this.maxDatapointsLimit = MIN_LIMIT + 1; this.maxDatapointsLimit = MIN_LIMIT + 1;
} }
return this.maxDatapointsLimit; return this.maxDatapointsLimit;
}) })
); );
} }
public matchesExistingInterval(min: number, max: number, intervalMs: number): boolean { public matchesExistingInterval(min: number, max: number, intervalMs: number): boolean {

2
ui-ngx/src/app/core/utils.ts

@ -291,7 +291,7 @@ export function deepClone<T>(target: T, ignoreFields?: string[]): T {
return cp.map((n: any) => deepClone<any>(n)) as any; return cp.map((n: any) => deepClone<any>(n)) as any;
} }
if (typeof target === 'object' && target !== {}) { if (typeof target === 'object' && target !== {}) {
const cp = { ...(target as { [key: string]: any }) } as { [key: string]: any }; const cp = {...(target as { [key: string]: any })} as { [key: string]: any };
Object.keys(cp).forEach(k => { Object.keys(cp).forEach(k => {
if (!ignoreFields || ignoreFields.indexOf(k) === -1) { if (!ignoreFields || ignoreFields.indexOf(k) === -1) {
cp[k] = deepClone<any>(cp[k]); cp[k] = deepClone<any>(cp[k]);

64
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.html

@ -0,0 +1,64 @@
<!--
Copyright © 2016-2021 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.
-->
<form fxLayout="column" class="mat-content mat-padding" [formGroup]="eventFilterFormGroup" (ngSubmit)="update()">
<ng-container *ngFor="let column of showColumns">
<ng-container [ngSwitch]="column.key">
<ng-template [ngSwitchCase]="isSelector(column.key)">
<mat-form-field>
<mat-label>{{ column.title | translate}}</mat-label>
<mat-select [formControlName]="column.key">
<mat-option [value]="">{{ 'event.all-events' | translate}}</mat-option>
<mat-option *ngFor="let value of selectorValues(column.key)" [value]="value">
{{ value }}
</mat-option>
</mat-select>
</mat-form-field>
</ng-template>
<ng-template [ngSwitchCase]="'isError'">
<tb-checkbox formControlName="isError" [falseValue]="''">
{{ 'event.has-error' | translate }}
</tb-checkbox>
</ng-template>
<ng-template [ngSwitchCase]="'error'">
<mat-form-field fxHide [fxShow]="showErrorMsgFields()">
<mat-label>{{ column.title | translate}}</mat-label>
<input matInput type="text" name="errorSearchText" formControlName="error">
</mat-form-field>
</ng-template>
<ng-container *ngSwitchDefault>
<mat-form-field>
<mat-label>{{ column.title | translate}}</mat-label>
<input matInput type="text" [name]="column.key" [formControlName]="column.key">
</mat-form-field>
</ng-container>
</ng-container>
</ng-container>
<div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center">
<button type="button"
mat-button
(click)="cancel()">
{{ 'action.cancel' | translate }}
</button>
<button type="submit"
mat-raised-button
color="primary"
[disabled]="eventFilterFormGroup.invalid || !eventFilterFormGroup.dirty">
{{ 'action.update' | translate }}
</button>
</div>
</form>

35
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.scss

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2021 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%;
min-width: 300px;
overflow: auto;
background: #fff;
border-radius: 4px;
box-shadow:
0 7px 8px -4px rgba(0, 0, 0, .2),
0 13px 19px 2px rgba(0, 0, 0, .14),
0 5px 24px 4px rgba(0, 0, 0, .12);
.mat-content {
overflow: hidden;
background-color: #fff;
}
.mat-padding {
padding: 16px;
}
}

101
ui-ngx/src/app/modules/home/components/event/event-filter-panel.component.ts

@ -0,0 +1,101 @@
///
/// Copyright © 2016-2021 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, InjectionToken } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { OverlayRef } from '@angular/cdk/overlay';
import { EntityType } from '@shared/models/entity-type.models';
import { FilterEventBody } from '@shared/models/event.models';
import { deepTrim } from '@core/utils';
export const EVENT_FILTER_PANEL_DATA = new InjectionToken<any>('AlarmFilterPanelData');
export interface EventFilterPanelData {
filterParams: FilterEventBody;
columns: Array<FilterEntityColumn>;
}
export interface FilterEntityColumn {
key: string;
title: string;
}
@Component({
selector: 'tb-event-filter-panel',
templateUrl: './event-filter-panel.component.html',
styleUrls: ['./event-filter-panel.component.scss']
})
export class EventFilterPanelComponent {
eventFilterFormGroup: FormGroup;
result: EventFilterPanelData;
private conditionError = false;
private msgDirectionTypes = ['IN', 'OUT'];
private statusTypes = ['Success', 'Failure'];
private entityTypes = Object.keys(EntityType);
showColumns: FilterEntityColumn[] = [];
constructor(@Inject(EVENT_FILTER_PANEL_DATA)
public data: EventFilterPanelData,
public overlayRef: OverlayRef,
private fb: FormBuilder) {
this.eventFilterFormGroup = this.fb.group({});
this.data.columns.forEach((column) => {
this.showColumns.push(column);
this.eventFilterFormGroup.addControl(column.key, this.fb.control(this.data.filterParams[column.key] || ''));
if (column.key === 'isError') {
this.conditionError = true;
}
});
}
isSelector(key: string): string {
return ['msgDirectionType', 'status', 'entityName'].includes(key) ? key : '';
}
selectorValues(key: string): string[] {
switch (key) {
case 'msgDirectionType':
return this.msgDirectionTypes;
case 'status':
return this.statusTypes;
case 'entityName':
return this.entityTypes;
}
}
update() {
const filter = deepTrim(Object.fromEntries(Object.entries(this.eventFilterFormGroup.value).filter(([_, v]) => v !== '')));
this.result = {
filterParams: filter,
columns: this.data.columns
};
this.overlayRef.dispose();
}
showErrorMsgFields() {
return !this.conditionError || this.eventFilterFormGroup.get('isError').value !== '';
}
cancel() {
this.overlayRef.dispose();
}
}

122
ui-ngx/src/app/modules/home/components/event/event-table-config.ts

@ -20,7 +20,7 @@ import {
EntityTableColumn, EntityTableColumn,
EntityTableConfig EntityTableConfig
} from '@home/models/entity/entities-table-config.models'; } from '@home/models/entity/entities-table-config.models';
import { DebugEventType, Event, EventType } from '@shared/models/event.models'; import { DebugEventType, Event, EventType, FilterEventBody } from '@shared/models/event.models';
import { TimePageLink } from '@shared/models/page/page-link'; import { TimePageLink } from '@shared/models/page/page-link';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
@ -38,16 +38,29 @@ import {
EventContentDialogComponent, EventContentDialogComponent,
EventContentDialogData EventContentDialogData
} from '@home/components/event/event-content-dialog.component'; } from '@home/components/event/event-content-dialog.component';
import { sortObjectKeys } from '@core/utils'; import { isEqual, sortObjectKeys } from '@core/utils';
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ChangeDetectorRef, Injector, StaticProvider, ViewContainerRef } from '@angular/core';
import { ComponentPortal } from '@angular/cdk/portal';
import {
EVENT_FILTER_PANEL_DATA,
EventFilterPanelComponent,
EventFilterPanelData,
FilterEntityColumn
} from '@home/components/event/event-filter-panel.component';
export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
eventTypeValue: EventType | DebugEventType; eventTypeValue: EventType | DebugEventType;
private filterParams: FilterEventBody = {};
private filterColumns: FilterEntityColumn[] = [];
set eventType(eventType: EventType | DebugEventType) { set eventType(eventType: EventType | DebugEventType) {
if (this.eventTypeValue !== eventType) { if (this.eventTypeValue !== eventType) {
this.eventTypeValue = eventType; this.eventTypeValue = eventType;
this.updateColumns(true); this.updateColumns(true);
this.updateFilterColumns();
} }
} }
@ -66,7 +79,10 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
public tenantId: string, public tenantId: string,
private defaultEventType: EventType | DebugEventType, private defaultEventType: EventType | DebugEventType,
private disabledEventTypes: Array<EventType | DebugEventType> = null, private disabledEventTypes: Array<EventType | DebugEventType> = null,
private debugEventTypes: Array<DebugEventType> = null) { private debugEventTypes: Array<DebugEventType> = null,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef) {
super(); super();
this.loadDataOnInit = false; this.loadDataOnInit = false;
this.tableTitle = ''; this.tableTitle = '';
@ -101,10 +117,20 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC};
this.updateColumns(); this.updateColumns();
this.updateFilterColumns();
this.headerActionDescriptors.push({
name: this.translate.instant('event.events-filter'),
icon: 'filter_list',
isEnabled: () => true,
onAction: ($event) => {
this.editEventFilter($event);
}
});
} }
fetchEvents(pageLink: TimePageLink): Observable<PageData<Event>> { fetchEvents(pageLink: TimePageLink): Observable<PageData<Event>> {
return this.eventService.getEvents(this.entityId, this.eventType, this.tenantId, pageLink); return this.eventService.getFilterEvents(this.entityId, this.eventType, this.tenantId, this.filterParams, pageLink);
} }
updateColumns(updateTableColumns: boolean = false): void { updateColumns(updateTableColumns: boolean = false): void {
@ -169,7 +195,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
}), false, key => ({ }), false, key => ({
padding: '0 12px 0 0' padding: '0 12px 0 0'
})), })),
new EntityTableColumn<Event>('entity', 'event.entity', '100px', new EntityTableColumn<Event>('entityName', 'event.entity-type', '100px',
(entity) => entity.body.entityName, entity => ({ (entity) => entity.body.entityName, entity => ({
padding: '0 12px 0 0', padding: '0 12px 0 0',
}), false, key => ({ }), false, key => ({
@ -249,5 +275,91 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> {
} }
}); });
} }
private updateFilterColumns() {
this.filterParams = {};
this.filterColumns = [{key: 'server', title: 'event.server'}];
switch (this.eventType) {
case EventType.ERROR:
this.filterColumns.push(
{key: 'method', title: 'event.method'},
{key: 'error', title: 'event.error'}
);
break;
case EventType.LC_EVENT:
this.filterColumns.push(
{key: 'method', title: 'event.event'},
{key: 'status', title: 'event.status'},
{key: 'error', title: 'event.error'}
);
break;
case EventType.STATS:
this.filterColumns.push(
{key: 'messagesProcessed', title: 'event.messages-processed'},
{key: 'errorsOccurred', title: 'event.errors-occurred'}
);
break;
case DebugEventType.DEBUG_RULE_NODE:
case DebugEventType.DEBUG_RULE_CHAIN:
this.filterColumns.push(
{key: 'msgDirectionType', title: 'event.type'},
{key: 'entityId', title: 'event.entity-id'},
{key: 'entityName', title: 'event.entity-type'},
{key: 'msgType', title: 'event.message-type'},
{key: 'relationType', title: 'event.relation-type'},
{key: 'dataSearch', title: 'event.data'},
{key: 'metadataSearch', title: 'event.metadata'},
{key: 'isError', title: 'event.error'},
{key: 'error', title: 'event.error'}
);
break;
}
}
private editEventFilter($event: MouseEvent) {
if ($event) {
$event.stopPropagation();
}
const target = $event.target || $event.srcElement || $event.currentTarget;
const config = new OverlayConfig();
config.backdropClass = 'cdk-overlay-transparent-backdrop';
config.hasBackdrop = true;
const connectedPosition: ConnectedPosition = {
originX: 'end',
originY: 'bottom',
overlayX: 'end',
overlayY: 'top'
};
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement)
.withPositions([connectedPosition]);
const overlayRef = this.overlay.create(config);
overlayRef.backdropClick().subscribe(() => {
overlayRef.dispose();
});
const providers: StaticProvider[] = [
{
provide: EVENT_FILTER_PANEL_DATA,
useValue: {
columns: this.filterColumns,
filterParams: this.filterParams
} as EventFilterPanelData
},
{
provide: OverlayRef,
useValue: overlayRef
}
];
const injector = Injector.create({parent: this.viewContainerRef.injector, providers});
const componentRef = overlayRef.attach(new ComponentPortal(EventFilterPanelComponent,
this.viewContainerRef, injector));
componentRef.onDestroy(() => {
if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) {
this.filterParams = componentRef.instance.result.filterParams;
this.table.updateData();
}
});
this.cd.detectChanges();
}
} }

13
ui-ngx/src/app/modules/home/components/event/event-table.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
@ -24,6 +24,7 @@ import { EventTableConfig } from './event-table-config';
import { EventService } from '@core/http/event.service'; import { EventService } from '@core/http/event.service';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { DebugEventType, EventType } from '@shared/models/event.models'; import { DebugEventType, EventType } from '@shared/models/event.models';
import { Overlay } from '@angular/cdk/overlay';
@Component({ @Component({
selector: 'tb-event-table', selector: 'tb-event-table',
@ -80,7 +81,10 @@ export class EventTableComponent implements OnInit {
private dialogService: DialogService, private dialogService: DialogService,
private translate: TranslateService, private translate: TranslateService,
private datePipe: DatePipe, private datePipe: DatePipe,
private dialog: MatDialog) { private dialog: MatDialog,
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private cd: ChangeDetectorRef) {
} }
ngOnInit() { ngOnInit() {
@ -95,7 +99,10 @@ export class EventTableComponent implements OnInit {
this.tenantId, this.tenantId,
this.defaultEventType, this.defaultEventType,
this.disabledEventTypes, this.disabledEventTypes,
this.debugEventTypes this.debugEventTypes,
this.overlay,
this.viewContainerRef,
this.cd
); );
} }

2
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -25,6 +25,7 @@ import { AuditLogDetailsDialogComponent } from '@home/components/audit-log/audit
import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component'; import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component';
import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component'; import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component';
import { EventTableComponent } from '@home/components/event/event-table.component'; import { EventTableComponent } from '@home/components/event/event-table.component';
import { EventFilterPanelComponent } from '@home/components/event/event-filter-panel.component';
import { RelationTableComponent } from '@home/components/relation/relation-table.component'; import { RelationTableComponent } from '@home/components/relation/relation-table.component';
import { RelationDialogComponent } from '@home/components/relation/relation-dialog.component'; import { RelationDialogComponent } from '@home/components/relation/relation-dialog.component';
import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component'; import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component';
@ -149,6 +150,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag
EventContentDialogComponent, EventContentDialogComponent,
EventTableHeaderComponent, EventTableHeaderComponent,
EventTableComponent, EventTableComponent,
EventFilterPanelComponent,
EdgeDownlinkTableHeaderComponent, EdgeDownlinkTableHeaderComponent,
EdgeDownlinkTableComponent, EdgeDownlinkTableComponent,
RelationTableComponent, RelationTableComponent,

25
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts

@ -14,12 +14,12 @@
/// limitations under the License. /// limitations under the License.
/// ///
import {DeviceProfileTransportConfiguration, DeviceTransportType} from '@shared/models/device.models'; import { DeviceProfileTransportConfiguration } from '@shared/models/device.models';
import {Component, forwardRef, Inject, Input} from '@angular/core'; import { Component, forwardRef, Inject, Input } from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import {Store} from '@ngrx/store'; import { Store } from '@ngrx/store';
import {AppState} from '@app/core/core.state'; import { AppState } from '@app/core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion'; import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { import {
ATTRIBUTE, ATTRIBUTE,
DEFAULT_BINDING, DEFAULT_BINDING,
@ -34,11 +34,11 @@ import {
RESOURCES, RESOURCES,
TELEMETRY TELEMETRY
} from './lwm2m-profile-config.models'; } from './lwm2m-profile-config.models';
import {DeviceProfileService} from '@core/http/device-profile.service'; import { DeviceProfileService } from '@core/http/device-profile.service';
import {deepClone, isDefinedAndNotNull, isEmpty, isUndefined} from '@core/utils'; import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/utils';
import {WINDOW} from '@core/services/window.service'; import { WINDOW } from '@core/services/window.service';
import {JsonArray, JsonObject} from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point';
import {Direction} from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
@Component({ @Component({
selector: 'tb-profile-lwm2m-device-transport-configuration', selector: 'tb-profile-lwm2m-device-transport-configuration',
@ -173,7 +173,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
} }
private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => {
this.lwm2mDeviceProfileFormGroup.patchValue({ this.lwm2mDeviceProfileFormGroup.patchValue({
observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList)) observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList))
}, },
{emitEvent: false}); {emitEvent: false});
@ -358,6 +358,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
this.configurationValue.observeAttr.observe = observeArray; this.configurationValue.observeAttr.observe = observeArray;
this.configurationValue.observeAttr.attribute = attributeArray; this.configurationValue.observeAttr.attribute = attributeArray;
this.configurationValue.observeAttr.telemetry = telemetryArray; this.configurationValue.observeAttr.telemetry = telemetryArray;
this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew);
this.configurationValue.observeAttr.attributeLwm2m = attributeLwm2m; this.configurationValue.observeAttr.attributeLwm2m = attributeLwm2m;
} }
} }

3
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html

@ -47,6 +47,7 @@
<ng-template matExpansionPanelContent> <ng-template matExpansionPanelContent>
<div fxLayout="column" fxLayoutGap="8px" formArrayName="instances"> <div fxLayout="column" fxLayoutGap="8px" formArrayName="instances">
<mat-expansion-panel <mat-expansion-panel
class="instance-list"
*ngFor="let instances of instancesLwm2mFormArray(objectLwM2M).controls; let y = index;" *ngFor="let instances of instancesLwm2mFormArray(objectLwM2M).controls; let y = index;"
[formGroupName]="y" [formGroupName]="y"
[expanded]="getExpended(objectLwM2M)" [expanded]="getExpended(objectLwM2M)"
@ -96,7 +97,7 @@
</div> </div>
<div fxFlex="10"> <div fxFlex="10">
</div> </div>
<div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5" disabled="false"> <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5">
<tb-profile-lwm2m-attributes <tb-profile-lwm2m-attributes
formControlName="attributeLwm2m" formControlName="attributeLwm2m"
[attributeLwm2m]="instances.get('attributeLwm2m').value" [attributeLwm2m]="instances.get('attributeLwm2m').value"

8
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss

@ -22,3 +22,11 @@
padding-left: 22px; padding-left: 22px;
text-align:center; text-align:center;
} }
:host{
.instance-list {
mat-expansion-panel-header {
color: inherit;
}
}
}

6
ui-ngx/src/app/modules/home/components/widget/lib/json-input-widget.component.ts

@ -146,7 +146,7 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
this.attributeUpdateFormGroup = this.fb.group({ this.attributeUpdateFormGroup = this.fb.group({
currentValue: [{}, validators] currentValue: [{}, validators]
}); });
this.attributeUpdateFormGroup.valueChanges.subscribe( () => { this.attributeUpdateFormGroup.valueChanges.subscribe(() => {
this.ctx.detectChanges(); this.ctx.detectChanges();
}); });
} }
@ -191,14 +191,14 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit {
saveAttributeObservable = this.attributeService.saveEntityAttributes( saveAttributeObservable = this.attributeService.saveEntityAttributes(
entityId, entityId,
this.settings.attributeScope, this.settings.attributeScope,
[ attributeToSave ], [attributeToSave],
{} {}
); );
} else { } else {
saveAttributeObservable = this.attributeService.saveEntityTimeseries( saveAttributeObservable = this.attributeService.saveEntityTimeseries(
entityId, entityId,
LatestTelemetry.LATEST_TELEMETRY, LatestTelemetry.LATEST_TELEMETRY,
[ attributeToSave ], [attributeToSave],
{} {}
); );
} }

3
ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts

@ -87,4 +87,5 @@ import { JsonInputWidgetComponent } from '@home/components/widget/lib/json-input
ImportExportService ImportExportService
] ]
}) })
export class WidgetComponentsModule { } export class WidgetComponentsModule {
}

2
ui-ngx/src/app/modules/home/pages/asset/assets-table-config.resolver.ts

@ -89,7 +89,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse
this.config.entityTranslations = entityTypeTranslations.get(EntityType.ASSET); this.config.entityTranslations = entityTypeTranslations.get(EntityType.ASSET);
this.config.entityResources = entityTypeResources.get(EntityType.ASSET); this.config.entityResources = entityTypeResources.get(EntityType.ASSET);
this.config.deleteEntityTitle = asset => this.translate.instant('asset.delete-asset-title', { assetName: asset.name }); this.config.deleteEntityTitle = asset => this.translate.instant('asset.delete-asset-title', {assetName: asset.name});
this.config.deleteEntityContent = () => this.translate.instant('asset.delete-asset-text'); this.config.deleteEntityContent = () => this.translate.instant('asset.delete-asset-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('asset.delete-assets-title', {count}); this.config.deleteEntitiesTitle = count => this.translate.instant('asset.delete-assets-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('asset.delete-assets-text'); this.config.deleteEntitiesContent = () => this.translate.instant('asset.delete-assets-text');

7
ui-ngx/src/app/modules/home/pages/dashboard/dashboards-table-config.resolver.ts

@ -93,7 +93,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
this.config.entityResources = entityTypeResources.get(EntityType.DASHBOARD); this.config.entityResources = entityTypeResources.get(EntityType.DASHBOARD);
this.config.deleteEntityTitle = dashboard => this.config.deleteEntityTitle = dashboard =>
this.translate.instant('dashboard.delete-dashboard-title', { dashboardTitle: dashboard.title }); this.translate.instant('dashboard.delete-dashboard-title', {dashboardTitle: dashboard.title});
this.config.deleteEntityContent = () => this.translate.instant('dashboard.delete-dashboard-text'); this.config.deleteEntityContent = () => this.translate.instant('dashboard.delete-dashboard-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('dashboard.delete-dashboards-title', {count}); this.config.deleteEntitiesTitle = count => this.translate.instant('dashboard.delete-dashboards-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('dashboard.delete-dashboards-text'); this.config.deleteEntitiesContent = () => this.translate.instant('dashboard.delete-dashboards-text');
@ -363,8 +363,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
this.router.navigateByUrl(`customers/${this.config.componentsData.customerId}/dashboards/${dashboard.id.id}`); this.router.navigateByUrl(`customers/${this.config.componentsData.customerId}/dashboards/${dashboard.id.id}`);
} else if (this.config.componentsData.dashboardScope === 'edge') { } else if (this.config.componentsData.dashboardScope === 'edge') {
this.router.navigateByUrl(`edges/${this.config.componentsData.edgeId}/dashboards/${dashboard.id.id}`); this.router.navigateByUrl(`edges/${this.config.componentsData.edgeId}/dashboards/${dashboard.id.id}`);
} } else {
else {
this.router.navigateByUrl(`dashboards/${dashboard.id.id}`); this.router.navigateByUrl(`dashboards/${dashboard.id.id}`);
} }
} }
@ -421,7 +420,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig<
} }
}).afterClosed() }).afterClosed()
.subscribe(() => { .subscribe(() => {
this.config.table.updateData(); this.config.table.updateData();
}); });
} }
); );

54
ui-ngx/src/app/modules/home/pages/device/devices-table-config.resolver.ts

@ -97,7 +97,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
this.config.addDialogStyle = {width: '600px'}; this.config.addDialogStyle = {width: '600px'};
this.config.deleteEntityTitle = device => this.translate.instant('device.delete-device-title', { deviceName: device.name }); this.config.deleteEntityTitle = device => this.translate.instant('device.delete-device-title', {deviceName: device.name});
this.config.deleteEntityContent = () => this.translate.instant('device.delete-device-text'); this.config.deleteEntityContent = () => this.translate.instant('device.delete-device-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('device.delete-devices-title', {count}); this.config.deleteEntitiesTitle = count => this.translate.instant('device.delete-devices-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('device.delete-devices-text'); this.config.deleteEntitiesContent = () => this.translate.instant('device.delete-devices-text');
@ -109,7 +109,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
this.broadcast.broadcast('deviceSaved'); this.broadcast.broadcast('deviceSaved');
}), }),
mergeMap((savedDevice) => this.deviceService.getDeviceInfo(savedDevice.id.id) mergeMap((savedDevice) => this.deviceService.getDeviceInfo(savedDevice.id.id)
)); ));
}; };
this.config.onEntityAction = action => this.onDeviceAction(action); this.config.onEntityAction = action => this.onDeviceAction(action);
this.config.detailsReadonly = () => (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user'); this.config.detailsReadonly = () => (this.config.componentsData.deviceScope === 'customer_user' || this.config.componentsData.deviceScope === 'edge_customer_user');
@ -208,7 +208,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
this.config.entitiesFetchFunction = pageLink => this.config.entitiesFetchFunction = pageLink =>
this.deviceService.getCustomerDeviceInfosByDeviceProfileId(this.customerId, pageLink, this.deviceService.getCustomerDeviceInfosByDeviceProfileId(this.customerId, pageLink,
this.config.componentsData.deviceProfileId !== null ? this.config.componentsData.deviceProfileId !== null ?
this.config.componentsData.deviceProfileId.id : ''); this.config.componentsData.deviceProfileId.id : '');
this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id); this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id);
} }
} }
@ -250,26 +250,26 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
); );
} }
if (deviceScope === 'customer') { if (deviceScope === 'customer') {
actions.push( actions.push(
{ {
name: this.translate.instant('device.unassign-from-customer'), name: this.translate.instant('device.unassign-from-customer'),
icon: 'assignment_return', icon: 'assignment_return',
isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && !entity.customerIsPublic), isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && !entity.customerIsPublic),
onAction: ($event, entity) => this.unassignFromCustomer($event, entity) onAction: ($event, entity) => this.unassignFromCustomer($event, entity)
}, },
{ {
name: this.translate.instant('device.make-private'), name: this.translate.instant('device.make-private'),
icon: 'reply', icon: 'reply',
isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && entity.customerIsPublic), isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && entity.customerIsPublic),
onAction: ($event, entity) => this.unassignFromCustomer($event, entity) onAction: ($event, entity) => this.unassignFromCustomer($event, entity)
}, },
{ {
name: this.translate.instant('device.manage-credentials'), name: this.translate.instant('device.manage-credentials'),
icon: 'security', icon: 'security',
isEnabled: () => true, isEnabled: () => true,
onAction: ($event, entity) => this.manageCredentials($event, entity) onAction: ($event, entity) => this.manageCredentials($event, entity)
} }
); );
} }
if (deviceScope === 'customer_user' || deviceScope === 'edge_customer_user') { if (deviceScope === 'customer_user' || deviceScope === 'edge_customer_user') {
actions.push( actions.push(
@ -452,10 +452,10 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev
} }
}).afterClosed() }).afterClosed()
.subscribe((res) => { .subscribe((res) => {
if (res) { if (res) {
this.config.table.updateData(); this.config.table.updateData();
} }
}); });
} }
unassignFromCustomer($event: Event, device: DeviceInfo) { unassignFromCustomer($event: Event, device: DeviceInfo) {

12
ui-ngx/src/app/modules/home/pages/edge/edge-routing.module.ts

@ -33,8 +33,11 @@ import { RuleChainType } from '@shared/models/rule-chain.models';
import { import {
importRuleChainBreadcumbLabelFunction, importRuleChainBreadcumbLabelFunction,
ResolvedRuleChainMetaDataResolver, ResolvedRuleChainMetaDataResolver,
ruleChainBreadcumbLabelFunction, RuleChainImportGuard, ruleChainBreadcumbLabelFunction,
RuleChainResolver, RuleNodeComponentsResolver, TooltipsterResolver RuleChainImportGuard,
RuleChainResolver,
RuleNodeComponentsResolver,
TooltipsterResolver
} from '@home/pages/rulechain/rulechain-routing.module'; } from '@home/pages/rulechain/rulechain-routing.module';
const routes: Routes = [ const routes: Routes = [
@ -249,7 +252,7 @@ const routes: Routes = [
} }
] ]
} }
] ]
}]; }];
@NgModule({ @NgModule({
@ -259,4 +262,5 @@ const routes: Routes = [
EdgesTableConfigResolver EdgesTableConfigResolver
] ]
}) })
export class EdgeRoutingModule { } export class EdgeRoutingModule {
}

5
ui-ngx/src/app/modules/home/pages/entity-view/entity-views-table-config.resolver.ts

@ -89,7 +89,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
this.config.addDialogStyle = {maxWidth: '800px'}; this.config.addDialogStyle = {maxWidth: '800px'};
this.config.deleteEntityTitle = entityView => this.config.deleteEntityTitle = entityView =>
this.translate.instant('entity-view.delete-entity-view-title', { entityViewName: entityView.name }); this.translate.instant('entity-view.delete-entity-view-title', {entityViewName: entityView.name});
this.config.deleteEntityContent = () => this.translate.instant('entity-view.delete-entity-view-text'); this.config.deleteEntityContent = () => this.translate.instant('entity-view.delete-entity-view-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('entity-view.delete-entity-views-title', {count}); this.config.deleteEntitiesTitle = count => this.translate.instant('entity-view.delete-entity-views-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('entity-view.delete-entity-views-text'); this.config.deleteEntitiesContent = () => this.translate.instant('entity-view.delete-entity-views-text');
@ -143,8 +143,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig
this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe( this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe(
edge => this.config.tableTitle = edge.name + ': ' + this.translate.instant('entity-view.entity-views') edge => this.config.tableTitle = edge.name + ': ' + this.translate.instant('entity-view.entity-views')
); );
} } else {
else {
this.config.tableTitle = this.translate.instant('entity-view.entity-views'); this.config.tableTitle = this.translate.instant('entity-view.entity-views');
} }
this.config.columns = this.configureColumns(this.config.componentsData.entityViewScope); this.config.columns = this.configureColumns(this.config.componentsData.entityViewScope);

6
ui-ngx/src/app/modules/home/pages/resource/resources-library.component.ts

@ -90,8 +90,10 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
buildForm(entity: Resource): FormGroup { buildForm(entity: Resource): FormGroup {
return this.fb.group( return this.fb.group(
{ {
resourceType: [{value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, resourceType: [{
disabled: this.isEdit }, [Validators.required]], value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL,
disabled: this.isEdit
}, [Validators.required]],
data: [entity ? entity.data : null, [Validators.required]], data: [entity ? entity.data : null, [Validators.required]],
fileName: [entity ? entity.fileName : null, [Validators.required]], fileName: [entity ? entity.fileName : null, [Validators.required]],
title: [entity ? entity.title : '', []] title: [entity ? entity.title : '', []]

72
ui-ngx/src/app/modules/home/pages/rulechain/rulechains-table-config.resolver.ts

@ -73,7 +73,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
this.config.entityResources = entityTypeResources.get(EntityType.RULE_CHAIN); this.config.entityResources = entityTypeResources.get(EntityType.RULE_CHAIN);
this.config.deleteEntityTitle = ruleChain => this.translate.instant('rulechain.delete-rulechain-title', this.config.deleteEntityTitle = ruleChain => this.translate.instant('rulechain.delete-rulechain-title',
{ ruleChainName: ruleChain.name }); {ruleChainName: ruleChain.name});
this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text'); this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text');
this.config.deleteEntitiesTitle = count => this.translate.instant('rulechain.delete-rulechains-title', {count}); this.config.deleteEntitiesTitle = count => this.translate.instant('rulechain.delete-rulechains-title', {count});
this.config.deleteEntitiesContent = () => this.translate.instant('rulechain.delete-rulechains-text'); this.config.deleteEntitiesContent = () => this.translate.instant('rulechain.delete-rulechains-text');
@ -121,11 +121,11 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
columns.push( columns.push(
new EntityTableColumn<RuleChain>('root', 'rulechain.root', '60px', new EntityTableColumn<RuleChain>('root', 'rulechain.root', '60px',
entity => { entity => {
if (ruleChainScope === 'edge') { if (ruleChainScope === 'edge') {
return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id === entity.id.id)); return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id === entity.id.id));
} else { } else {
return checkBoxCell(entity.root); return checkBoxCell(entity.root);
} }
}) })
); );
} else if (ruleChainScope === 'edges') { } else if (ruleChainScope === 'edges') {
@ -421,32 +421,32 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
} }
}).afterClosed() }).afterClosed()
.subscribe((res) => { .subscribe((res) => {
if (res) { if (res) {
this.edgeService.findMissingToRelatedRuleChains(this.config.componentsData.edgeId).subscribe( this.edgeService.findMissingToRelatedRuleChains(this.config.componentsData.edgeId).subscribe(
(missingRuleChains) => { (missingRuleChains) => {
if (missingRuleChains && Object.keys(missingRuleChains).length > 0) { if (missingRuleChains && Object.keys(missingRuleChains).length > 0) {
const formattedMissingRuleChains: Array<string> = new Array<string>(); const formattedMissingRuleChains: Array<string> = new Array<string>();
for (const missingRuleChain of Object.keys(missingRuleChains)) { for (const missingRuleChain of Object.keys(missingRuleChains)) {
const arrayOfMissingRuleChains = missingRuleChains[missingRuleChain]; const arrayOfMissingRuleChains = missingRuleChains[missingRuleChain];
const tmp = '- \'' + missingRuleChain + '\': \'' + arrayOfMissingRuleChains.join('\', ') + '\''; const tmp = '- \'' + missingRuleChain + '\': \'' + arrayOfMissingRuleChains.join('\', ') + '\'';
formattedMissingRuleChains.push(tmp); formattedMissingRuleChains.push(tmp);
}
const message = this.translate.instant('edge.missing-related-rule-chains-text',
{missingRuleChains: formattedMissingRuleChains.join('<br>')});
this.dialogService.alert(this.translate.instant('edge.missing-related-rule-chains-title'),
message, this.translate.instant('action.close'), true).subscribe(
() => {
this.config.table.updateData();
} }
); const message = this.translate.instant('edge.missing-related-rule-chains-text',
} else { {missingRuleChains: formattedMissingRuleChains.join('<br>')});
this.config.table.updateData(); this.dialogService.alert(this.translate.instant('edge.missing-related-rule-chains-title'),
message, this.translate.instant('action.close'), true).subscribe(
() => {
this.config.table.updateData();
}
);
} else {
this.config.table.updateData();
}
} }
} );
); }
} }
} );
);
} }
unassignFromEdge($event: Event, ruleChain: RuleChain) { unassignFromEdge($event: Event, ruleChain: RuleChain) {
@ -510,13 +510,13 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<
this.translate.instant('action.yes'), this.translate.instant('action.yes'),
true true
).subscribe((res) => { ).subscribe((res) => {
if (res) { if (res) {
this.ruleChainService.setAutoAssignToEdgeRuleChain(ruleChain.id.id).subscribe( this.ruleChainService.setAutoAssignToEdgeRuleChain(ruleChain.id.id).subscribe(
() => { () => {
this.config.table.updateData(); this.config.table.updateData();
} }
); );
} }
} }
); );
} }

4
ui-ngx/src/app/shared/components/file-input.component.ts

@ -73,9 +73,11 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
contentConvertFunction: (content: string) => any; contentConvertFunction: (content: string) => any;
private requiredValue: boolean; private requiredValue: boolean;
get required(): boolean { get required(): boolean {
return this.requiredValue; return this.requiredValue;
} }
@Input() @Input()
set required(value: boolean) { set required(value: boolean) {
const newVal = coerceBooleanProperty(value); const newVal = coerceBooleanProperty(value);
@ -85,9 +87,11 @@ export class FileInputComponent extends PageComponent implements AfterViewInit,
} }
private requiredAsErrorValue: boolean; private requiredAsErrorValue: boolean;
get requiredAsError(): boolean { get requiredAsError(): boolean {
return this.requiredAsErrorValue; return this.requiredAsErrorValue;
} }
@Input() @Input()
set requiredAsError(value: boolean) { set requiredAsError(value: boolean) {
const newVal = coerceBooleanProperty(value); const newVal = coerceBooleanProperty(value);

2
ui-ngx/src/app/shared/components/json-object-edit.component.ts

@ -22,7 +22,7 @@ import { ActionNotificationHide, ActionNotificationShow } from '@core/notificati
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; import { CancelAnimationFrame, RafService } from '@core/services/raf.service';
import { guid, isUndefined, isDefinedAndNotNull, isLiteralObject } from '@core/utils'; import { guid, isDefinedAndNotNull, isLiteralObject, isUndefined } from '@core/utils';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { getAce } from '@shared/models/ace/ace.models'; import { getAce } from '@shared/models/ace/ace.models';

34
ui-ngx/src/app/shared/models/event.models.ts

@ -19,6 +19,7 @@ import { TenantId } from '@shared/models/id/tenant-id';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { EventId } from './id/event-id'; import { EventId } from './id/event-id';
import { ContentType } from '@shared/models/constants'; import { ContentType } from '@shared/models/constants';
import { EntityType } from '@shared/models/entity-type.models';
export enum EventType { export enum EventType {
ERROR = 'ERROR', ERROR = 'ERROR',
@ -83,3 +84,36 @@ export interface Event extends BaseData<EventId> {
uid: string; uid: string;
body: EventBody; body: EventBody;
} }
export interface BaseFilterEventBody {
server?: string;
}
export interface ErrorFilterEventBody extends BaseFilterEventBody {
method?: string;
}
export interface LcFilterEventEventBody extends BaseFilterEventBody {
method?: string;
status?: string;
isError?: boolean;
}
export interface StatsFilterEventBody extends BaseFilterEventBody {
messagesProcessed?: string;
errorsOccurred?: string;
}
export interface DebugFilterRuleNodeEventBody extends BaseFilterEventBody {
msgDirectionType?: string;
entityId?: string;
entityName?: EntityType;
msgId?: string;
msgType?: string;
relationType?: string;
dataSearch?: string;
metadataSearch?: string;
isError?: boolean;
}
export type FilterEventBody = ErrorFilterEventBody & LcFilterEventEventBody & StatsFilterEventBody & DebugFilterRuleNodeEventBody;

2
ui-ngx/src/app/shared/models/resource.models.ts

@ -45,7 +45,7 @@ export const ResourceTypeTranslationMap = new Map<ResourceType, string>(
[ResourceType.LWM2M_MODEL, 'LWM2M model'], [ResourceType.LWM2M_MODEL, 'LWM2M model'],
[ResourceType.PKCS_12, 'PKCS #12'], [ResourceType.PKCS_12, 'PKCS #12'],
[ResourceType.JKS, 'JKS'] [ResourceType.JKS, 'JKS']
] ]
); );
export interface ResourceInfo extends BaseData<TbResourceId> { export interface ResourceInfo extends BaseData<TbResourceId> {

5
ui-ngx/src/assets/locale/locale.constant-cs_CZ.json

@ -1332,7 +1332,6 @@
"body": "Tělo", "body": "Tělo",
"method": "Metoda", "method": "Metoda",
"type": "Typ", "type": "Typ",
"entity": "Entita",
"message-id": "Id zprávy", "message-id": "Id zprávy",
"message-type": "Typ zprávy", "message-type": "Typ zprávy",
"data-type": "Typ dat", "data-type": "Typ dat",
@ -1344,7 +1343,9 @@
"success": "Úspěch", "success": "Úspěch",
"failed": "Neúspěch", "failed": "Neúspěch",
"messages-processed": "Zpracované zprávy", "messages-processed": "Zpracované zprávy",
"errors-occurred": "Vyskytly se chyby" "errors-occurred": "Vyskytly se chyby",
"all-events": "Vše",
"entity-type": "Typ entity"
}, },
"extension": { "extension": {
"extensions": "Rozšíření", "extensions": "Rozšíření",

5
ui-ngx/src/assets/locale/locale.constant-de_DE.json

@ -1094,7 +1094,6 @@
"body": "Inhalt", "body": "Inhalt",
"method": "Methode", "method": "Methode",
"type": "Typ", "type": "Typ",
"entity": "Entität",
"message-id": "Nachrichten-Id", "message-id": "Nachrichten-Id",
"message-type": "Nachrichten-Typ", "message-type": "Nachrichten-Typ",
"data-type": "Datentyp", "data-type": "Datentyp",
@ -1106,7 +1105,9 @@
"success": "Erfolg", "success": "Erfolg",
"failed": "Fehlgeschlagen", "failed": "Fehlgeschlagen",
"messages-processed": "Nachrichten verarbeitet", "messages-processed": "Nachrichten verarbeitet",
"errors-occurred": "Fehler aufgetreten" "errors-occurred": "Fehler aufgetreten",
"all-events": "Alle",
"entity-type": "Entitätstyp"
}, },
"extension": { "extension": {
"extensions": "Erweiterungen", "extensions": "Erweiterungen",

5
ui-ngx/src/assets/locale/locale.constant-el_GR.json

@ -1247,7 +1247,6 @@
"out": "Έξοδος", "out": "Έξοδος",
"metadata": "Μεταδεδομένα", "metadata": "Μεταδεδομένα",
"message": "Μήνυμα", "message": "Μήνυμα",
"entity": "Οντότητα",
"message-id": "ID Μηνύματος", "message-id": "ID Μηνύματος",
"message-type": "Τύπος Μηνύματος", "message-type": "Τύπος Μηνύματος",
"data-type": "Τύπος Δεδομένων", "data-type": "Τύπος Δεδομένων",
@ -1258,7 +1257,9 @@
"success": "Επιτυχία", "success": "Επιτυχία",
"failed": "Απέτυχε", "failed": "Απέτυχε",
"messages-processed": "Επεξεργασμένα μηνύματα", "messages-processed": "Επεξεργασμένα μηνύματα",
"errors-occurred": "Παρουσιάστηκαν σφάλματα" "errors-occurred": "Παρουσιάστηκαν σφάλματα",
"all-events": "Όλοι",
"entity-type": "Τύπος οντοτήτων"
}, },
"extension": { "extension": {
"extensions": "Επεκτάσεις", "extensions": "Επεκτάσεις",

8
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -1616,6 +1616,7 @@
}, },
"event": { "event": {
"event-type": "Event type", "event-type": "Event type",
"events-filter": "Events Filter",
"type-error": "Error", "type-error": "Error",
"type-lc-event": "Lifecycle event", "type-lc-event": "Lifecycle event",
"type-stats": "Statistics", "type-stats": "Statistics",
@ -1629,7 +1630,6 @@
"body": "Body", "body": "Body",
"method": "Method", "method": "Method",
"type": "Type", "type": "Type",
"entity": "Entity",
"message-id": "Message Id", "message-id": "Message Id",
"message-type": "Message Type", "message-type": "Message Type",
"data-type": "Data Type", "data-type": "Data Type",
@ -1641,7 +1641,11 @@
"success": "Success", "success": "Success",
"failed": "Failed", "failed": "Failed",
"messages-processed": "Messages processed", "messages-processed": "Messages processed",
"errors-occurred": "Errors occurred" "errors-occurred": "Errors occurred",
"all-events": "All",
"has-error": "Has error",
"entity-id": "Entity Id",
"entity-type": "Entity type"
}, },
"extension": { "extension": {
"extensions": "Extensions", "extensions": "Extensions",

5
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -1521,7 +1521,6 @@
"body": "Cuerpo", "body": "Cuerpo",
"method": "Método", "method": "Método",
"type": "Tipo", "type": "Tipo",
"entity": "Entidad",
"message-id": "Id Mensaje", "message-id": "Id Mensaje",
"message-type": "Tipo Mensaje", "message-type": "Tipo Mensaje",
"data-type": "Tipo de Datos", "data-type": "Tipo de Datos",
@ -1533,7 +1532,9 @@
"success": "Éxito", "success": "Éxito",
"failed": "Fallo", "failed": "Fallo",
"messages-processed": "Mensajes procesados", "messages-processed": "Mensajes procesados",
"errors-occurred": "Ocurrieron errores" "errors-occurred": "Ocurrieron errores",
"all-events": "Todos",
"entity-type": "Tipo de entidad"
}, },
"extension": { "extension": {
"extensions": "Extensiones", "extensions": "Extensiones",

5
ui-ngx/src/assets/locale/locale.constant-fa_IR.json

@ -881,7 +881,6 @@
"body": "بدنه", "body": "بدنه",
"method": "روش", "method": "روش",
"type": "نوع", "type": "نوع",
"entity": "موجودي",
"message-id": "پيام ID", "message-id": "پيام ID",
"message-type": "نوع پيام", "message-type": "نوع پيام",
"data-type": "نوع داده", "data-type": "نوع داده",
@ -893,7 +892,9 @@
"success": "موفقيت", "success": "موفقيت",
"failed": "عدم موفقيت", "failed": "عدم موفقيت",
"messages-processed": "پيام پردازش شد", "messages-processed": "پيام پردازش شد",
"errors-occurred": "خطاها رخ دادند" "errors-occurred": "خطاها رخ دادند",
"all-events": "همه",
"entity-type": "نوع موجودي"
}, },
"extension": { "extension": {
"extensions": "دنباله ها", "extensions": "دنباله ها",

5
ui-ngx/src/assets/locale/locale.constant-fr_FR.json

@ -1115,7 +1115,6 @@
"body": "Corps", "body": "Corps",
"data": "Données", "data": "Données",
"data-type": "Type de données", "data-type": "Type de données",
"entity": "Entité",
"error": "erreur", "error": "erreur",
"type-edge-event": "Downlink", "type-edge-event": "Downlink",
"errors-occurred": "Des erreurs sont survenues", "errors-occurred": "Des erreurs sont survenues",
@ -1138,7 +1137,9 @@
"type-debug-rule-node": "Debug", "type-debug-rule-node": "Debug",
"type-error": "Erreur", "type-error": "Erreur",
"type-lc-event": "Evénement du cycle de vie", "type-lc-event": "Evénement du cycle de vie",
"type-stats": "Statistiques" "type-stats": "Statistiques",
"all-events": "Tout",
"entity-type": "Type d'entité"
}, },
"extension": { "extension": {
"add": "Ajouter une extension", "add": "Ajouter une extension",

5
ui-ngx/src/assets/locale/locale.constant-it_IT.json

@ -915,7 +915,6 @@
"body": "Body", "body": "Body",
"method": "Metodo", "method": "Metodo",
"type": "Tipo", "type": "Tipo",
"entity": "Entità",
"message-id": "Id Messaggio", "message-id": "Id Messaggio",
"message-type": "Tipo Messaggio", "message-type": "Tipo Messaggio",
"data-type": "Tipo di dato", "data-type": "Tipo di dato",
@ -927,7 +926,9 @@
"success": "Success", "success": "Success",
"failed": "Failed", "failed": "Failed",
"messages-processed": "Messaggi elaborati", "messages-processed": "Messaggi elaborati",
"errors-occurred": "Si sono verificati degli errori" "errors-occurred": "Si sono verificati degli errori",
"all-events": "Tutte",
"entity-type": "Tipo entità"
}, },
"extension": { "extension": {
"extensions": "Estensioni", "extensions": "Estensioni",

5
ui-ngx/src/assets/locale/locale.constant-ja_JA.json

@ -768,7 +768,6 @@
"body": "体", "body": "体",
"method": "方法", "method": "方法",
"type": "タイプ", "type": "タイプ",
"entity": "エンティティ",
"message-id": "メッセージID", "message-id": "メッセージID",
"message-type": "メッセージタイプ", "message-type": "メッセージタイプ",
"data-type": "データ・タイプ", "data-type": "データ・タイプ",
@ -780,7 +779,9 @@
"success": "成功", "success": "成功",
"failed": "失敗", "failed": "失敗",
"messages-processed": "処理されたメッセージ", "messages-processed": "処理されたメッセージ",
"errors-occurred": "エラーが発生しました" "errors-occurred": "エラーが発生しました",
"all-events": "すべて",
"entity-type": "エンティティタイプ"
}, },
"extension": { "extension": {
"extensions": "拡張機能", "extensions": "拡張機能",

5
ui-ngx/src/assets/locale/locale.constant-ka_GE.json

@ -959,7 +959,6 @@
"body": "სხეული", "body": "სხეული",
"method": "მეთოდი", "method": "მეთოდი",
"type": "ტიპი", "type": "ტიპი",
"entity": "ობიექტი",
"message-id": "მესიჯის-ID", "message-id": "მესიჯის-ID",
"message-type": "მესიჯის ტიპი", "message-type": "მესიჯის ტიპი",
"data-type": "მონაცემთა ტიპი", "data-type": "მონაცემთა ტიპი",
@ -971,7 +970,9 @@
"success": "წარმატება", "success": "წარმატება",
"failed": "ვერ მოხერხდა", "failed": "ვერ მოხერხდა",
"messages-processed": "შეტყობინებების დამუშავება", "messages-processed": "შეტყობინებების დამუშავება",
"errors-occurred": "შეცდომები მოხდა" "errors-occurred": "შეცდომები მოხდა",
"all-events": "ყველა",
"entity-type": "ობიექტის ტიპი"
}, },
"extension": { "extension": {
"extensions": "დამატებითი აპი", "extensions": "დამატებითი აპი",

5
ui-ngx/src/assets/locale/locale.constant-ko_KR.json

@ -1329,7 +1329,6 @@
"body": "Body", "body": "Body",
"method": "방법", "method": "방법",
"type": "유형", "type": "유형",
"entity": "개체",
"message-id": "메시지 ID", "message-id": "메시지 ID",
"message-type": "메시지 유형", "message-type": "메시지 유형",
"data-type": "데이터 유형", "data-type": "데이터 유형",
@ -1341,7 +1340,9 @@
"success": "성공", "success": "성공",
"failed": "실패", "failed": "실패",
"messages-processed": "처리된 메시지", "messages-processed": "처리된 메시지",
"errors-occurred": "오류가 발생했습니다" "errors-occurred": "오류가 발생했습니다",
"all-events": "모두",
"entity-type": "개체 유형"
}, },
"extension": { "extension": {
"extensions": "확장", "extensions": "확장",

5
ui-ngx/src/assets/locale/locale.constant-lv_LV.json

@ -894,7 +894,6 @@
"body": "Galvenā daļa", "body": "Galvenā daļa",
"method": "Metode", "method": "Metode",
"type": "Tips", "type": "Tips",
"entity": "Vienība",
"message-id": "Ziņojuma Id", "message-id": "Ziņojuma Id",
"message-type": "Ziņojuma tips", "message-type": "Ziņojuma tips",
"data-type": "Datu tips", "data-type": "Datu tips",
@ -906,7 +905,9 @@
"success": "Sekmīgi", "success": "Sekmīgi",
"failed": "Kļūda", "failed": "Kļūda",
"messages-processed": "Ziņojumi apstrādāti", "messages-processed": "Ziņojumi apstrādāti",
"errors-occurred": "Kļūdas konstatētas" "errors-occurred": "Kļūdas konstatētas",
"all-events": "Visi",
"entity-type": "Vienības tips"
}, },
"extension": { "extension": {
"extensions": "Paplašinājumi", "extensions": "Paplašinājumi",

5
ui-ngx/src/assets/locale/locale.constant-pt_BR.json

@ -997,7 +997,6 @@
"body": "Corpo", "body": "Corpo",
"method": "Método", "method": "Método",
"type": "Tipo", "type": "Tipo",
"entity": "Entidade",
"message-id": "ID de mensagem", "message-id": "ID de mensagem",
"message-type": "Tipo de Mensagem", "message-type": "Tipo de Mensagem",
"data-type": "Tipo de Dados", "data-type": "Tipo de Dados",
@ -1009,7 +1008,9 @@
"success": "Êxito", "success": "Êxito",
"failed": "Falhou", "failed": "Falhou",
"messages-processed": "Mensagens processadas", "messages-processed": "Mensagens processadas",
"errors-occurred": "Erros" "errors-occurred": "Erros",
"all-events": "Tudo",
"entity-type": "Tipo de entidade"
}, },
"extension": { "extension": {
"extensions": "Extensões", "extensions": "Extensões",

9
ui-ngx/src/assets/locale/locale.constant-ro_RO.json

@ -945,7 +945,6 @@
"body": "Corp", "body": "Corp",
"method": "Metodă", "method": "Metodă",
"type": "Tip", "type": "Tip",
"entity": "Entitate",
"message-id": "ID Mesaj", "message-id": "ID Mesaj",
"message-type": "Tip Mesaj", "message-type": "Tip Mesaj",
"data-type": "Tip Date", "data-type": "Tip Date",
@ -957,7 +956,9 @@
"success": "Succes", "success": "Succes",
"failed": "Eşuat", "failed": "Eşuat",
"messages-processed": "Mesaje procesate", "messages-processed": "Mesaje procesate",
"errors-occurred": "Au apărut erori" "errors-occurred": "Au apărut erori",
"all-events": "Toate",
"entity-type": "Tip Entitate"
}, },
"extension": { "extension": {
"extensions": "Extensii", "extensions": "Extensii",
@ -1174,7 +1175,7 @@
"entity-field": "Câmp Entitate", "entity-field": "Câmp Entitate",
"access-token": "Token De Acces" "access-token": "Token De Acces"
}, },
"stepper-text":{ "stepper-text": {
"select-file": "Selectează un fişier", "select-file": "Selectează un fişier",
"configuration": "Importă configurație", "configuration": "Importă configurație",
"column-type": "Selectează tipul de coloane", "column-type": "Selectează tipul de coloane",
@ -1796,4 +1797,4 @@
"language": { "language": {
"language": "Limba" "language": "Limba"
} }
} }

5
ui-ngx/src/assets/locale/locale.constant-ru_RU.json

@ -948,7 +948,6 @@
"body": "Тело", "body": "Тело",
"method": "Метод", "method": "Метод",
"type": "Тип", "type": "Тип",
"entity": "Объект",
"message-id": "ИД сообщения", "message-id": "ИД сообщения",
"message-type": "Тип сообщения", "message-type": "Тип сообщения",
"data-type": "Тип данных", "data-type": "Тип данных",
@ -960,7 +959,9 @@
"success": "Успех", "success": "Успех",
"failed": "Неудача", "failed": "Неудача",
"messages-processed": "Сообщения обработаны", "messages-processed": "Сообщения обработаны",
"errors-occurred": "Возникли ошибки" "errors-occurred": "Возникли ошибки",
"all-events": "Все",
"entity-type": "Тип объекта"
}, },
"extension": { "extension": {
"extensions": "Расширение", "extensions": "Расширение",

5
ui-ngx/src/assets/locale/locale.constant-sl_SI.json

@ -1329,7 +1329,6 @@
"body": "Vsebina", "body": "Vsebina",
"method": "Metoda", "method": "Metoda",
"type": "Vrsta", "type": "Vrsta",
"entity": "Entiteta",
"message-id": "ID sporočila", "message-id": "ID sporočila",
"message-type": "Vrsta sporočila", "message-type": "Vrsta sporočila",
"data-type": "Vrsta podatkov", "data-type": "Vrsta podatkov",
@ -1341,7 +1340,9 @@
"success": "Uspeh", "success": "Uspeh",
"failed": "Ni uspelo", "failed": "Ni uspelo",
"messages-processed": "Obdelana sporočila", "messages-processed": "Obdelana sporočila",
"errors-occurred": "Prišlo je do napak" "errors-occurred": "Prišlo je do napak",
"all-events": "Vse",
"entity-type": "Vrsta entitete"
}, },
"extension": { "extension": {
"extensions": "Razširitve", "extensions": "Razširitve",

5
ui-ngx/src/assets/locale/locale.constant-tr_TR.json

@ -915,7 +915,6 @@
"body": "İçerik //(Body)", "body": "İçerik //(Body)",
"method": "Yöntem", "method": "Yöntem",
"type": "Tür", "type": "Tür",
"entity": "Varlık",
"message-id": "Mesaj Kimliği", "message-id": "Mesaj Kimliği",
"message-type": "Mesaj tipi", "message-type": "Mesaj tipi",
"data-type": "Veri tipi", "data-type": "Veri tipi",
@ -927,7 +926,9 @@
"success": "Başarı", "success": "Başarı",
"failed": "Başarısız oldu", "failed": "Başarısız oldu",
"messages-processed": "Mesajlar işlendi", "messages-processed": "Mesajlar işlendi",
"errors-occurred": "Hatalar oluştu" "errors-occurred": "Hatalar oluştu",
"all-events": "Tümü",
"entity-type": "Öğe türü"
}, },
"extension": { "extension": {
"extensions": "Uzantılar", "extensions": "Uzantılar",

5
ui-ngx/src/assets/locale/locale.constant-uk_UA.json

@ -1201,7 +1201,6 @@
"out": "Out", "out": "Out",
"metadata": "Метадані", "metadata": "Метадані",
"message": "Повідомлення", "message": "Повідомлення",
"entity": "Сутність",
"message-id": "Id повідомлення", "message-id": "Id повідомлення",
"message-type": "Тип повідомлення", "message-type": "Тип повідомлення",
"data-type": "Тип даних", "data-type": "Тип даних",
@ -1212,7 +1211,9 @@
"success": "Успіх", "success": "Успіх",
"failed": "Невдача", "failed": "Невдача",
"messages-processed": "Повідомлення опрацьовані", "messages-processed": "Повідомлення опрацьовані",
"errors-occurred": "Виникли помилки" "errors-occurred": "Виникли помилки",
"all-events": "Всі",
"entity-type": "Тип сутності"
}, },
"extension": { "extension": {
"extensions": "Розширення", "extensions": "Розширення",

5
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -1334,7 +1334,6 @@
"body": "整体", "body": "整体",
"data": "数据", "data": "数据",
"data-type": "数据类型", "data-type": "数据类型",
"entity": "实体",
"error": "错误", "error": "错误",
"errors-occurred": "错误发生", "errors-occurred": "错误发生",
"event": "事件", "event": "事件",
@ -1356,7 +1355,9 @@
"type-debug-rule-node": "调试", "type-debug-rule-node": "调试",
"type-error": "错误", "type-error": "错误",
"type-lc-event": "生命周期事件", "type-lc-event": "生命周期事件",
"type-stats": "类型统计" "type-stats": "类型统计",
"all-events": "全部",
"entity-type": "实体类型"
}, },
"extension": { "extension": {
"add": "添加扩展", "add": "添加扩展",

5
ui-ngx/src/assets/locale/locale.constant-zh_TW.json

@ -865,7 +865,6 @@
"body": "整體", "body": "整體",
"method": "方法", "method": "方法",
"type": "類型", "type": "類型",
"entity": "實體",
"message-id": "消息ID", "message-id": "消息ID",
"message-type": "消息類型", "message-type": "消息類型",
"data-type": "資料類型", "data-type": "資料類型",
@ -877,7 +876,9 @@
"success": "成功", "success": "成功",
"failed": "失敗", "failed": "失敗",
"messages-processed": "消息處理", "messages-processed": "消息處理",
"errors-occurred": "錯誤發生" "errors-occurred": "錯誤發生",
"all-events": "所有",
"entity-type": "實體類型"
}, },
"extension": { "extension": {
"extensions": "擴展", "extensions": "擴展",

Loading…
Cancel
Save