Commit cc1b42f93d1842252b915e52d7dcd0abed329727
Merge branch 'master' of github.com:thingsboard/thingsboard into edge/refactoring
Showing
71 changed files
with
1459 additions
and
318 deletions
... | ... | @@ -18,12 +18,14 @@ package org.thingsboard.server.controller; |
18 | 18 | import org.springframework.beans.factory.annotation.Autowired; |
19 | 19 | import org.springframework.security.access.prepost.PreAuthorize; |
20 | 20 | import org.springframework.web.bind.annotation.PathVariable; |
21 | +import org.springframework.web.bind.annotation.RequestBody; | |
21 | 22 | import org.springframework.web.bind.annotation.RequestMapping; |
22 | 23 | import org.springframework.web.bind.annotation.RequestMethod; |
23 | 24 | import org.springframework.web.bind.annotation.RequestParam; |
24 | 25 | import org.springframework.web.bind.annotation.ResponseBody; |
25 | 26 | import org.springframework.web.bind.annotation.RestController; |
26 | 27 | import org.thingsboard.server.common.data.Event; |
28 | +import org.thingsboard.server.common.data.event.EventFilter; | |
27 | 29 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
28 | 30 | import org.thingsboard.server.common.data.id.EntityId; |
29 | 31 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
... | ... | @@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.id.TenantId; |
31 | 33 | import org.thingsboard.server.common.data.page.PageData; |
32 | 34 | import org.thingsboard.server.common.data.page.TimePageLink; |
33 | 35 | import org.thingsboard.server.dao.event.EventService; |
36 | +import org.thingsboard.server.dao.model.ModelConstants; | |
34 | 37 | import org.thingsboard.server.queue.util.TbCoreComponent; |
35 | 38 | import org.thingsboard.server.service.security.permission.Operation; |
36 | 39 | |
... | ... | @@ -101,4 +104,38 @@ public class EventController extends BaseController { |
101 | 104 | } |
102 | 105 | } |
103 | 106 | |
107 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | |
108 | + @RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST) | |
109 | + @ResponseBody | |
110 | + public PageData<Event> getEvents( | |
111 | + @PathVariable("entityType") String strEntityType, | |
112 | + @PathVariable("entityId") String strEntityId, | |
113 | + @RequestParam("tenantId") String strTenantId, | |
114 | + @RequestParam int pageSize, | |
115 | + @RequestParam int page, | |
116 | + @RequestBody EventFilter eventFilter, | |
117 | + @RequestParam(required = false) String textSearch, | |
118 | + @RequestParam(required = false) String sortProperty, | |
119 | + @RequestParam(required = false) String sortOrder, | |
120 | + @RequestParam(required = false) Long startTime, | |
121 | + @RequestParam(required = false) Long endTime) throws ThingsboardException { | |
122 | + checkParameter("EntityId", strEntityId); | |
123 | + checkParameter("EntityType", strEntityType); | |
124 | + try { | |
125 | + TenantId tenantId = new TenantId(toUUID(strTenantId)); | |
126 | + | |
127 | + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); | |
128 | + checkEntityId(entityId, Operation.READ); | |
129 | + | |
130 | + if(sortProperty != null && sortProperty.equals("createdTime") && eventFilter.hasFilterForJsonBody()) { | |
131 | + sortProperty = ModelConstants.CREATED_TIME_PROPERTY; | |
132 | + } | |
133 | + | |
134 | + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); | |
135 | + return checkNotNull(eventService.findEventsByFilter(tenantId, entityId, eventFilter, pageLink)); | |
136 | + } catch (Exception e) { | |
137 | + throw handleException(e); | |
138 | + } | |
139 | + } | |
140 | + | |
104 | 141 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -17,10 +17,8 @@ package org.thingsboard.server.service.install; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; |
19 | 19 | import com.fasterxml.jackson.databind.node.ObjectNode; |
20 | -import lombok.Getter; | |
21 | 20 | import lombok.extern.slf4j.Slf4j; |
22 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
23 | -import org.springframework.beans.factory.annotation.Value; | |
24 | 22 | import org.springframework.context.annotation.Bean; |
25 | 23 | import org.springframework.context.annotation.Profile; |
26 | 24 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
... | ... | @@ -60,11 +58,8 @@ import org.thingsboard.server.common.data.page.PageLink; |
60 | 58 | import org.thingsboard.server.common.data.query.BooleanFilterPredicate; |
61 | 59 | import org.thingsboard.server.common.data.query.DynamicValue; |
62 | 60 | import org.thingsboard.server.common.data.query.DynamicValueSourceType; |
63 | -import org.thingsboard.server.common.data.query.EntityKey; | |
64 | -import org.thingsboard.server.common.data.query.EntityKeyType; | |
65 | 61 | import org.thingsboard.server.common.data.query.EntityKeyValueType; |
66 | 62 | import org.thingsboard.server.common.data.query.FilterPredicateValue; |
67 | -import org.thingsboard.server.common.data.query.KeyFilter; | |
68 | 63 | import org.thingsboard.server.common.data.query.NumericFilterPredicate; |
69 | 64 | import org.thingsboard.server.common.data.rule.RuleChainType; |
70 | 65 | import org.thingsboard.server.common.data.security.Authority; | ... | ... |
... | ... | @@ -322,7 +322,7 @@ actors: |
322 | 322 | # Enable/disable actor statistics |
323 | 323 | enabled: "${ACTORS_STATISTICS_ENABLED:true}" |
324 | 324 | js_print_interval_ms: "${ACTORS_JS_STATISTICS_PRINT_INTERVAL_MS:10000}" |
325 | - persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:3600000}" | |
325 | + persist_frequency: "${ACTORS_STATISTICS_PERSIST_FREQUENCY:10000}" | |
326 | 326 | |
327 | 327 | cache: |
328 | 328 | # caffeine or redis |
... | ... | @@ -516,7 +516,7 @@ js: |
516 | 516 | # Built-in JVM JavaScript environment properties |
517 | 517 | local: |
518 | 518 | # Use Sandboxed (secured) JVM JavaScript environment |
519 | - use_js_sandbox: "${USE_LOCAL_JS_SANDBOX:false}" | |
519 | + use_js_sandbox: "${USE_LOCAL_JS_SANDBOX:true}" | |
520 | 520 | # Specify thread pool size for JavaScript sandbox resource monitor |
521 | 521 | monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" |
522 | 522 | # Maximum CPU time in milliseconds allowed for script execution | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.event; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.Event; |
20 | +import org.thingsboard.server.common.data.event.EventFilter; | |
20 | 21 | import org.thingsboard.server.common.data.id.EntityId; |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 23 | import org.thingsboard.server.common.data.page.PageData; |
... | ... | @@ -41,6 +42,8 @@ public interface EventService { |
41 | 42 | |
42 | 43 | List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit); |
43 | 44 | |
45 | + PageData<Event> findEventsByFilter(TenantId tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink); | |
46 | + | |
44 | 47 | void removeEvents(TenantId tenantId, EntityId entityId); |
45 | 48 | |
46 | 49 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.eclipse.leshan.core.util.StringUtils; | |
20 | + | |
21 | +@Data | |
22 | +public abstract class DebugEvent implements EventFilter { | |
23 | + | |
24 | + private String msgDirectionType; | |
25 | + private String server; | |
26 | + private String dataSearch; | |
27 | + private String metadataSearch; | |
28 | + private String entityName; | |
29 | + private String relationType; | |
30 | + private String entityId; | |
31 | + private String msgType; | |
32 | + private boolean isError; | |
33 | + private String error; | |
34 | + | |
35 | + public void setIsError(boolean isError) { | |
36 | + this.isError = isError; | |
37 | + } | |
38 | + | |
39 | + @Override | |
40 | + public boolean hasFilterForJsonBody() { | |
41 | + return !StringUtils.isEmpty(msgDirectionType) || !StringUtils.isEmpty(server) || !StringUtils.isEmpty(dataSearch) || !StringUtils.isEmpty(metadataSearch) | |
42 | + || !StringUtils.isEmpty(entityName) || !StringUtils.isEmpty(relationType) || !StringUtils.isEmpty(entityId) || !StringUtils.isEmpty(msgType) || !StringUtils.isEmpty(error) || isError; | |
43 | + } | |
44 | + | |
45 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleChainEventFilter.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +public class DebugRuleChainEventFilter extends DebugEvent { | |
19 | + @Override | |
20 | + public EventType getEventType() { | |
21 | + return EventType.DEBUG_RULE_CHAIN; | |
22 | + } | |
23 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/event/DebugRuleNodeEventFilter.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +public class DebugRuleNodeEventFilter extends DebugEvent { | |
19 | + @Override | |
20 | + public EventType getEventType() { | |
21 | + return EventType.DEBUG_RULE_NODE; | |
22 | + } | |
23 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.eclipse.leshan.core.util.StringUtils; | |
20 | + | |
21 | +@Data | |
22 | +public class ErrorEventFilter implements EventFilter { | |
23 | + private String server; | |
24 | + private String method; | |
25 | + private String error; | |
26 | + | |
27 | + @Override | |
28 | + public EventType getEventType() { | |
29 | + return EventType.ERROR; | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public boolean hasFilterForJsonBody() { | |
34 | + return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(method) || !StringUtils.isEmpty(error); | |
35 | + } | |
36 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |
20 | +import com.fasterxml.jackson.annotation.JsonSubTypes; | |
21 | +import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
22 | + | |
23 | +@JsonTypeInfo( | |
24 | + use = JsonTypeInfo.Id.NAME, | |
25 | + include = JsonTypeInfo.As.PROPERTY, | |
26 | + property = "eventType") | |
27 | +@JsonSubTypes({ | |
28 | + @JsonSubTypes.Type(value = DebugRuleNodeEventFilter.class, name = "DEBUG_RULE_NODE"), | |
29 | + @JsonSubTypes.Type(value = DebugRuleChainEventFilter.class, name = "DEBUG_RULE_CHAIN"), | |
30 | + @JsonSubTypes.Type(value = ErrorEventFilter.class, name = "ERROR"), | |
31 | + @JsonSubTypes.Type(value = LifeCycleEventFilter.class, name = "LC_EVENT"), | |
32 | + @JsonSubTypes.Type(value = StatisticsEventFilter.class, name = "STATS") | |
33 | +}) | |
34 | +public interface EventFilter { | |
35 | + @JsonIgnore | |
36 | + EventType getEventType(); | |
37 | + | |
38 | + boolean hasFilterForJsonBody(); | |
39 | + | |
40 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +public enum EventType { | |
19 | + ERROR, LC_EVENT, STATS, DEBUG_RULE_NODE, DEBUG_RULE_CHAIN | |
20 | +} | |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/event/LifeCycleEventFilter.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.eclipse.leshan.core.util.StringUtils; | |
20 | + | |
21 | +@Data | |
22 | +public class LifeCycleEventFilter implements EventFilter { | |
23 | + private String server; | |
24 | + private String event; | |
25 | + private String status; | |
26 | + private String error; | |
27 | + | |
28 | + @Override | |
29 | + public EventType getEventType() { | |
30 | + return EventType.LC_EVENT; | |
31 | + } | |
32 | + | |
33 | + @Override | |
34 | + public boolean hasFilterForJsonBody() { | |
35 | + return !StringUtils.isEmpty(server) || !StringUtils.isEmpty(event) || !StringUtils.isEmpty(status) || !StringUtils.isEmpty(error); | |
36 | + } | |
37 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/event/StatisticsEventFilter.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.event; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.eclipse.leshan.core.util.StringUtils; | |
20 | + | |
21 | +@Data | |
22 | +public class StatisticsEventFilter implements EventFilter { | |
23 | + private String server; | |
24 | + private Integer messagesProcessed; | |
25 | + private Integer errorsOccurred; | |
26 | + | |
27 | + @Override | |
28 | + public EventType getEventType() { | |
29 | + return EventType.STATS; | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public boolean hasFilterForJsonBody() { | |
34 | + return !StringUtils.isEmpty(server) || (messagesProcessed != null && messagesProcessed > 0) || (errorsOccurred != null && errorsOccurred > 0); | |
35 | + } | |
36 | +} | ... | ... |
... | ... | @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server; |
18 | 18 | import io.netty.util.concurrent.Future; |
19 | 19 | import io.netty.util.concurrent.GenericFutureListener; |
20 | 20 | import lombok.extern.slf4j.Slf4j; |
21 | +import org.jetbrains.annotations.NotNull; | |
21 | 22 | import org.thingsboard.server.common.data.Device; |
22 | 23 | import org.thingsboard.server.common.data.DeviceProfile; |
23 | 24 | import org.thingsboard.server.common.data.ResourceType; |
... | ... | @@ -74,12 +75,12 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s |
74 | 75 | |
75 | 76 | @Override |
76 | 77 | public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { |
77 | - log.info("[{}] toDeviceRpcRequest", toDeviceRequest); | |
78 | + this.service.onToDeviceRpcRequest(toDeviceRequest); | |
78 | 79 | } |
79 | 80 | |
80 | 81 | @Override |
81 | 82 | public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { |
82 | - log.info("[{}] toServerRpcResponse", toServerResponse); | |
83 | + this.service.onToServerRpcResponse(toServerResponse); | |
83 | 84 | } |
84 | 85 | |
85 | 86 | @Override |
... | ... | @@ -87,13 +88,15 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s |
87 | 88 | log.info("[{}] operationComplete", future); |
88 | 89 | } |
89 | 90 | |
90 | - public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { | |
91 | + @Override | |
92 | + public void onResourceUpdate(@NotNull Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { | |
91 | 93 | if (ResourceType.LWM2M_MODEL.name().equals(resourceUpdateMsgOpt.get().getResourceType())) { |
92 | 94 | this.service.onResourceUpdate(resourceUpdateMsgOpt); |
93 | 95 | } |
94 | 96 | } |
95 | 97 | |
96 | - public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) { | |
98 | + @Override | |
99 | + public void onResourceDelete(@NotNull Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) { | |
97 | 100 | if (ResourceType.LWM2M_MODEL.name().equals(resourceDeleteMsgOpt.get().getResourceType())) { |
98 | 101 | this.service.onResourceDelete(resourceDeleteMsgOpt); |
99 | 102 | } | ... | ... |
... | ... | @@ -22,15 +22,14 @@ import com.google.gson.JsonSyntaxException; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.apache.commons.lang3.StringUtils; |
24 | 24 | import org.eclipse.californium.core.network.config.NetworkConfig; |
25 | +import org.eclipse.leshan.core.attributes.Attribute; | |
26 | +import org.eclipse.leshan.core.attributes.AttributeSet; | |
25 | 27 | import org.eclipse.leshan.core.model.ObjectModel; |
26 | 28 | import org.eclipse.leshan.core.model.ResourceModel; |
27 | -import org.eclipse.leshan.core.node.LwM2mMultipleResource; | |
28 | -import org.eclipse.leshan.core.node.LwM2mNode; | |
29 | -import org.eclipse.leshan.core.node.LwM2mObject; | |
30 | -import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
31 | 29 | import org.eclipse.leshan.core.node.LwM2mPath; |
32 | -import org.eclipse.leshan.core.node.LwM2mSingleResource; | |
33 | 30 | import org.eclipse.leshan.core.node.codec.CodecException; |
31 | +import org.eclipse.leshan.core.request.DownlinkRequest; | |
32 | +import org.eclipse.leshan.core.request.WriteAttributesRequest; | |
34 | 33 | import org.eclipse.leshan.core.util.Hex; |
35 | 34 | import org.eclipse.leshan.server.californium.LeshanServerBuilder; |
36 | 35 | import org.eclipse.leshan.server.registration.Registration; |
... | ... | @@ -44,11 +43,19 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; |
44 | 43 | |
45 | 44 | import java.io.File; |
46 | 45 | import java.io.IOException; |
46 | +import java.util.ArrayList; | |
47 | 47 | import java.util.Arrays; |
48 | 48 | import java.util.Date; |
49 | 49 | import java.util.LinkedList; |
50 | +import java.util.List; | |
51 | +import java.util.Map; | |
50 | 52 | import java.util.Optional; |
53 | +import java.util.concurrent.ConcurrentHashMap; | |
51 | 54 | |
55 | +import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; | |
56 | +import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; | |
57 | +import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; | |
58 | +import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; | |
52 | 59 | import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; |
53 | 60 | import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; |
54 | 61 | |
... | ... | @@ -56,8 +63,19 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA |
56 | 63 | public class LwM2mTransportHandler { |
57 | 64 | |
58 | 65 | public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; |
66 | + | |
67 | + public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; | |
68 | + public static final String BOOTSTRAP = "bootstrap"; | |
69 | + public static final String SERVERS = "servers"; | |
70 | + public static final String LWM2M_SERVER = "lwm2mServer"; | |
71 | + public static final String BOOTSTRAP_SERVER = "bootstrapServer"; | |
72 | + public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr"; | |
59 | 73 | public static final String ATTRIBUTE = "attribute"; |
60 | 74 | public static final String TELEMETRY = "telemetry"; |
75 | + public static final String KEY_NAME = "keyName"; | |
76 | + public static final String OBSERVE = "observe"; | |
77 | + public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; | |
78 | + | |
61 | 79 | private static final String REQUEST = "/request"; |
62 | 80 | private static final String RESPONSE = "/response"; |
63 | 81 | private static final String ATTRIBUTES = "/" + ATTRIBUTE; |
... | ... | @@ -70,14 +88,6 @@ public class LwM2mTransportHandler { |
70 | 88 | public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES; |
71 | 89 | |
72 | 90 | public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms |
73 | - public static final String OBSERVE_ATTRIBUTE_TELEMETRY = "observeAttr"; | |
74 | - public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; | |
75 | - public static final String KEY_NAME = "keyName"; | |
76 | - public static final String OBSERVE = "observe"; | |
77 | - public static final String BOOTSTRAP = "bootstrap"; | |
78 | - public static final String SERVERS = "servers"; | |
79 | - public static final String LWM2M_SERVER = "lwm2mServer"; | |
80 | - public static final String BOOTSTRAP_SERVER = "bootstrapServer"; | |
81 | 91 | |
82 | 92 | public static final String LOG_LW2M_TELEMETRY = "logLwm2m"; |
83 | 93 | public static final String LOG_LW2M_INFO = "info"; |
... | ... | @@ -144,19 +154,19 @@ public class LwM2mTransportHandler { |
144 | 154 | throw new CodecException("Invalid value type for resource %s, type %s", resourcePath, type); |
145 | 155 | } |
146 | 156 | } |
147 | - | |
148 | - public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) { | |
149 | - if (content instanceof LwM2mObject) { | |
150 | - return (LwM2mObject) content; | |
151 | - } else if (content instanceof LwM2mObjectInstance) { | |
152 | - return (LwM2mObjectInstance) content; | |
153 | - } else if (content instanceof LwM2mSingleResource) { | |
154 | - return (LwM2mSingleResource) content; | |
155 | - } else if (content instanceof LwM2mMultipleResource) { | |
156 | - return (LwM2mMultipleResource) content; | |
157 | - } | |
158 | - return null; | |
159 | - } | |
157 | +// | |
158 | +// public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) { | |
159 | +// if (content instanceof LwM2mObject) { | |
160 | +// return (LwM2mObject) content; | |
161 | +// } else if (content instanceof LwM2mObjectInstance) { | |
162 | +// return (LwM2mObjectInstance) content; | |
163 | +// } else if (content instanceof LwM2mSingleResource) { | |
164 | +// return (LwM2mSingleResource) content; | |
165 | +// } else if (content instanceof LwM2mMultipleResource) { | |
166 | +// return (LwM2mMultipleResource) content; | |
167 | +// } | |
168 | +// return null; | |
169 | +// } | |
160 | 170 | |
161 | 171 | public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { |
162 | 172 | LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); |
... | ... | @@ -166,6 +176,7 @@ public class LwM2mTransportHandler { |
166 | 176 | lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); |
167 | 177 | lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); |
168 | 178 | lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).getAsJsonArray()); |
179 | + lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject()); | |
169 | 180 | return lwM2MClientProfile; |
170 | 181 | } |
171 | 182 | |
... | ... | @@ -184,6 +195,9 @@ public class LwM2mTransportHandler { |
184 | 195 | * "attribute":["/2/0/1","/3/0/9"], |
185 | 196 | * "telemetry":["/1/0/1","/2/0/1","/6/0/1"], |
186 | 197 | * "observe":["/2/0","/2/0/0","/4/0/2"]} |
198 | + * "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"}, | |
199 | + * "/3_1.0/0": {"gt": 17}, | |
200 | + * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} | |
187 | 201 | */ |
188 | 202 | public static LwM2mClientProfile getLwM2MClientProfileFromThingsboard(DeviceProfile deviceProfile) { |
189 | 203 | if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { |
... | ... | @@ -192,7 +206,7 @@ public class LwM2mTransportHandler { |
192 | 206 | ObjectMapper mapper = new ObjectMapper(); |
193 | 207 | String profileStr = mapper.writeValueAsString(profile); |
194 | 208 | JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; |
195 | - return (getValidateCredentialsBodyFromThingsboard(profileJson)) ? LwM2mTransportHandler.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; | |
209 | + return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportHandler.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; | |
196 | 210 | } catch (IOException e) { |
197 | 211 | log.error("", e); |
198 | 212 | } |
... | ... | @@ -240,7 +254,10 @@ public class LwM2mTransportHandler { |
240 | 254 | objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonArray() && |
241 | 255 | objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(OBSERVE) && |
242 | 256 | !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonNull() && |
243 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonArray()); | |
257 | + objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE).isJsonArray() && | |
258 | + objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE_LWM2M) && | |
259 | + !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonNull() && | |
260 | + objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonObject()); | |
244 | 261 | } |
245 | 262 | |
246 | 263 | private static boolean getValidateBootstrapProfileFromThingsboard(JsonObject objectMsg) { |
... | ... | @@ -360,4 +377,54 @@ public class LwM2mTransportHandler { |
360 | 377 | return ObjectModel.DEFAULT_VERSION; |
361 | 378 | } |
362 | 379 | } |
380 | + | |
381 | + /** | |
382 | + * As example: | |
383 | + * a)Write-Attributes/3/0/9?pmin=1 means the Battery Level value will be notified | |
384 | + * to the Server with a minimum interval of 1sec; | |
385 | + * this value is set at theResource level. | |
386 | + * b)Write-Attributes/3/0/9?pmin means the Battery Level will be notified | |
387 | + * to the Server with a minimum value (pmin) given by the default one | |
388 | + * (resource 2 of Object Server ID=1), | |
389 | + * or with another value if this Attribute has been set at another level | |
390 | + * (Object or Object Instance: see section5.1.1). | |
391 | + * c)Write-Attributes/3/0?pmin=10 means that all Resources of Instance 0 of the Object ‘Device (ID:3)’ | |
392 | + * will be notified to the Server with a minimum interval of 10 sec; | |
393 | + * this value is set at the Object Instance level. | |
394 | + * d)Write-Attributes /3/0/9?gt=45&st=10 means the Battery Level will be notified to the Server | |
395 | + * when: | |
396 | + * a.old value is 20 and new value is 35 due to step condition | |
397 | + * b.old value is 45 and new value is 50 due to gt condition | |
398 | + * c.old value is 50 and new value is 40 due to both gt and step conditions | |
399 | + * d.old value is 35 and new value is 20 due to step conditione) | |
400 | + * Write-Attributes /3/0/9?lt=20>=85&st=10 means the Battery Level will be notified to the Server | |
401 | + * when: | |
402 | + * a.old value is 17 and new value is 24 due to lt condition | |
403 | + * b.old value is 75 and new value is 90 due to both gt and step conditions | |
404 | + * String uriQueries = "pmin=10&pmax=60"; | |
405 | + * AttributeSet attributes = AttributeSet.parse(uriQueries); | |
406 | + * WriteAttributesRequest request = new WriteAttributesRequest(target, attributes); | |
407 | + * Attribute gt = new Attribute(GREATER_THAN, Double.valueOf("45")); | |
408 | + * Attribute st = new Attribute(LESSER_THAN, Double.valueOf("10")); | |
409 | + * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); | |
410 | + * Attribute [] attrs = {gt, st}; | |
411 | + */ | |
412 | + public static DownlinkRequest createWriteAttributeRequest(String target, Object params) { | |
413 | + AttributeSet attrSet = new AttributeSet(createWriteAttributes(params)); | |
414 | + return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; | |
415 | + } | |
416 | + | |
417 | + private static Attribute[] createWriteAttributes(Object params) { | |
418 | + List attributeLists = new ArrayList<Attribute>(); | |
419 | + ObjectMapper oMapper = new ObjectMapper(); | |
420 | + Map<String, Object> map = oMapper.convertValue(params, ConcurrentHashMap.class); | |
421 | + map.forEach((k, v) -> { | |
422 | + if (!v.toString().isEmpty() || (v.toString().isEmpty() && OBJECT_VERSION.equals(k))) { | |
423 | + attributeLists.add(new Attribute(k, | |
424 | + (DIMENSION.equals(k) || MINIMUM_PERIOD.equals(k) || MAXIMUM_PERIOD.equals(k)) ? | |
425 | + ((Double) v).longValue() : v)); | |
426 | + } | |
427 | + }); | |
428 | + return (Attribute[]) attributeLists.toArray(Attribute[]::new); | |
429 | + } | |
363 | 430 | } | ... | ... |
... | ... | @@ -18,8 +18,6 @@ package org.thingsboard.server.transport.lwm2m.server; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.eclipse.californium.core.coap.CoAP; |
20 | 20 | import org.eclipse.californium.core.coap.Response; |
21 | -import org.eclipse.leshan.core.attributes.Attribute; | |
22 | -import org.eclipse.leshan.core.attributes.AttributeSet; | |
23 | 21 | import org.eclipse.leshan.core.model.ResourceModel; |
24 | 22 | import org.eclipse.leshan.core.node.LwM2mNode; |
25 | 23 | import org.eclipse.leshan.core.node.LwM2mPath; |
... | ... | @@ -33,7 +31,6 @@ import org.eclipse.leshan.core.request.DownlinkRequest; |
33 | 31 | import org.eclipse.leshan.core.request.ExecuteRequest; |
34 | 32 | import org.eclipse.leshan.core.request.ObserveRequest; |
35 | 33 | import org.eclipse.leshan.core.request.ReadRequest; |
36 | -import org.eclipse.leshan.core.request.WriteAttributesRequest; | |
37 | 34 | import org.eclipse.leshan.core.request.WriteRequest; |
38 | 35 | import org.eclipse.leshan.core.request.exception.ClientSleepingException; |
39 | 36 | import org.eclipse.leshan.core.response.CancelObservationResponse; |
... | ... | @@ -60,7 +57,6 @@ import java.util.Date; |
60 | 57 | import java.util.concurrent.ExecutorService; |
61 | 58 | import java.util.concurrent.Executors; |
62 | 59 | |
63 | -import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; | |
64 | 60 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; |
65 | 61 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER; |
66 | 62 | 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 |
75 | 71 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.RESPONSE_CHANNEL; |
76 | 72 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId; |
77 | 73 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer; |
74 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.createWriteAttributeRequest; | |
78 | 75 | |
79 | 76 | @Slf4j |
80 | 77 | @Service |
... | ... | @@ -174,47 +171,7 @@ public class LwM2mTransportRequest { |
174 | 171 | } |
175 | 172 | break; |
176 | 173 | case PUT_TYPE_OPER_WRITE_ATTRIBUTES: |
177 | - /** | |
178 | - * As example: | |
179 | - * a)Write-Attributes/3/0/9?pmin=1 means the Battery Level value will be notified | |
180 | - * to the Server with a minimum interval of 1sec; | |
181 | - * this value is set at theResource level. | |
182 | - * b)Write-Attributes/3/0/9?pmin means the Battery Level will be notified | |
183 | - * to the Server with a minimum value (pmin) given by the default one | |
184 | - * (resource 2 of Object Server ID=1), | |
185 | - * or with another value if this Attribute has been set at another level | |
186 | - * (Object or Object Instance: see section5.1.1). | |
187 | - * c)Write-Attributes/3/0?pmin=10 means that all Resources of Instance 0 of the Object ‘Device (ID:3)’ | |
188 | - * will be notified to the Server with a minimum interval of 10 sec; | |
189 | - * this value is set at the Object Instance level. | |
190 | - * d)Write-Attributes /3/0/9?gt=45&st=10 means the Battery Level will be notified to the Server | |
191 | - * when: | |
192 | - * a.old value is 20 and new value is 35 due to step condition | |
193 | - * b.old value is 45 and new value is 50 due to gt condition | |
194 | - * c.old value is 50 and new value is 40 due to both gt and step conditions | |
195 | - * d.old value is 35 and new value is 20 due to step conditione) | |
196 | - * Write-Attributes /3/0/9?lt=20>=85&st=10 means the Battery Level will be notified to the Server | |
197 | - * when: | |
198 | - * a.old value is 17 and new value is 24 due to lt condition | |
199 | - * b.old value is 75 and new value is 90 due to both gt and step conditions | |
200 | - * String uriQueries = "pmin=10&pmax=60"; | |
201 | - * AttributeSet attributes = AttributeSet.parse(uriQueries); | |
202 | - * WriteAttributesRequest request = new WriteAttributesRequest(target, attributes); | |
203 | - * Attribute gt = new Attribute(GREATER_THAN, Double.valueOf("45")); | |
204 | - * Attribute st = new Attribute(LESSER_THAN, Double.valueOf("10")); | |
205 | - * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); | |
206 | - * Attribute [] attrs = {gt, st}; | |
207 | - */ | |
208 | - Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.parseInt("1"))); | |
209 | - Attribute[] attrs = {pmin}; | |
210 | - AttributeSet attrSet = new AttributeSet(attrs); | |
211 | - if (resultIds.isResource()) { | |
212 | - request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), attrSet); | |
213 | - } else if (resultIds.isObjectInstance()) { | |
214 | - request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), attrSet); | |
215 | - } else if (resultIds.getObjectId() >= 0) { | |
216 | - request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet); | |
217 | - } | |
174 | + request = createWriteAttributeRequest (target, params); | |
218 | 175 | break; |
219 | 176 | } |
220 | 177 | ... | ... |
... | ... | @@ -51,6 +51,10 @@ public interface LwM2mTransportService { |
51 | 51 | |
52 | 52 | void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt); |
53 | 53 | |
54 | + void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest); | |
55 | + | |
56 | + void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); | |
57 | + | |
54 | 58 | void doTrigger(Registration registration, String path); |
55 | 59 | |
56 | 60 | void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); | ... | ... |
... | ... | @@ -74,11 +74,14 @@ import java.util.concurrent.Executors; |
74 | 74 | import java.util.concurrent.TimeUnit; |
75 | 75 | import java.util.stream.Collectors; |
76 | 76 | |
77 | +import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; | |
78 | +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; | |
77 | 79 | import static org.thingsboard.server.common.transport.util.JsonUtils.getJsonObject; |
78 | 80 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; |
79 | 81 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; |
80 | 82 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_TOPIC; |
81 | 83 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_TELEMETRY_TOPIC; |
84 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_DISCOVER; | |
82 | 85 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_OBSERVE; |
83 | 86 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.GET_TYPE_OPER_READ; |
84 | 87 | 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 |
87 | 90 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LWM2M_STRATEGY_2; |
88 | 91 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_EXECUTE; |
89 | 92 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.POST_TYPE_OPER_WRITE_REPLACE; |
93 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.PUT_TYPE_OPER_WRITE_ATTRIBUTES; | |
90 | 94 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.SERVICE_CHANNEL; |
91 | 95 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId; |
92 | 96 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToObjectIdFromIdVer; |
... | ... | @@ -213,7 +217,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
213 | 217 | * !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect |
214 | 218 | */ |
215 | 219 | public void unReg(Registration registration, Collection<Observation> observations) { |
216 | - executorUnRegistered.submit(() -> { | |
220 | + executorUnRegistered.submit(() -> { | |
217 | 221 | try { |
218 | 222 | this.setCancelObservations(registration); |
219 | 223 | this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); |
... | ... | @@ -365,17 +369,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
365 | 369 | } |
366 | 370 | |
367 | 371 | /** |
368 | - * | |
369 | 372 | * @param resourceUpdateMsgOpt - |
370 | 373 | */ |
371 | 374 | @Override |
372 | - public void onResourceUpdate (Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { | |
375 | + public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { | |
373 | 376 | String idVer = resourceUpdateMsgOpt.get().getResourceKey(); |
374 | 377 | lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.updateResourceModel(idVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())); |
375 | 378 | } |
376 | 379 | |
377 | 380 | /** |
378 | - * | |
379 | 381 | * @param resourceDeleteMsgOpt - |
380 | 382 | */ |
381 | 383 | @Override |
... | ... | @@ -384,6 +386,14 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
384 | 386 | lwM2mClientContext.getLwM2mClients().values().stream().forEach(e -> e.deleteResources(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getModelProvider())); |
385 | 387 | } |
386 | 388 | |
389 | + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) { | |
390 | + log.info("[{}] toDeviceRpcRequest", toDeviceRequest); | |
391 | + } | |
392 | + | |
393 | + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { | |
394 | + log.info("[{}] toServerRpcResponse", toServerResponse); | |
395 | + } | |
396 | + | |
387 | 397 | /** |
388 | 398 | * Trigger Server path = "/1/0/8" |
389 | 399 | * <p> |
... | ... | @@ -496,15 +506,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
496 | 506 | */ |
497 | 507 | private void initLwM2mFromClientValue(Registration registration, LwM2mClient lwM2MClient) { |
498 | 508 | LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); |
499 | - Set<String> clientObjects = this.getAllOjectsInClient(registration); | |
500 | - if (clientObjects != null && LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { | |
501 | - // #2 | |
502 | - lwM2MClient.getPendingRequests().addAll(clientObjects); | |
503 | - clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), | |
504 | - null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout())); | |
509 | + Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration); | |
510 | + if (clientObjects != null && clientObjects.size() > 0) { | |
511 | + if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { | |
512 | + // #2 | |
513 | + lwM2MClient.getPendingRequests().addAll(clientObjects); | |
514 | + clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, GET_TYPE_OPER_READ, ContentFormat.TLV.getName(), | |
515 | + null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout())); | |
516 | + } | |
517 | + // #1 | |
518 | + this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_OBSERVE, clientObjects); | |
519 | + this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, PUT_TYPE_OPER_WRITE_ATTRIBUTES, clientObjects); | |
520 | + this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_DISCOVER, clientObjects); | |
505 | 521 | } |
506 | - // #1 | |
507 | - this.initReadAttrTelemetryObserveToClient(registration, lwM2MClient, GET_TYPE_OPER_OBSERVE); | |
508 | 522 | } |
509 | 523 | |
510 | 524 | /** |
... | ... | @@ -611,41 +625,61 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
611 | 625 | |
612 | 626 | /** |
613 | 627 | * Start observe/read: Attr/Telemetry |
614 | - * #1 - Analyze: | |
615 | - * #1.1 path in resource profile == client resource | |
628 | + * #1 - Analyze: path in resource profile == client resource | |
616 | 629 | * |
617 | 630 | * @param registration - |
618 | 631 | */ |
619 | - private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient, String typeOper) { | |
632 | + private void initReadAttrTelemetryObserveToClient(Registration registration, LwM2mClient lwM2MClient, | |
633 | + String typeOper, Set<String> clientObjects) { | |
620 | 634 | LwM2mClientProfile lwM2MClientProfile = lwM2mClientContext.getProfile(registration); |
621 | - Set<String> clientInstances = this.getAllInstancesInClient(registration); | |
622 | - Set<String> result; | |
635 | + Set<String> result = null; | |
636 | + ConcurrentHashMap<String, Object> params = null; | |
623 | 637 | if (GET_TYPE_OPER_READ.equals(typeOper)) { |
624 | - result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(), new TypeReference<>() { | |
625 | - }); | |
626 | - result.addAll(JacksonUtil.convertValue(lwM2MClientProfile.getPostTelemetryProfile().toString(), new TypeReference<>() { | |
627 | - })); | |
628 | - } else { | |
629 | - result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(), new TypeReference<>() { | |
630 | - }); | |
638 | + result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(), | |
639 | + new TypeReference<>() { | |
640 | + }); | |
641 | + result.addAll(JacksonUtil.fromString(lwM2MClientProfile.getPostTelemetryProfile().toString(), | |
642 | + new TypeReference<>() { | |
643 | + })); | |
644 | + } else if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { | |
645 | + result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(), | |
646 | + new TypeReference<>() { | |
647 | + }); | |
648 | + } else if (GET_TYPE_OPER_DISCOVER.equals(typeOper)) { | |
649 | + result = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()).keySet(); | |
650 | + ; | |
651 | + } else if (PUT_TYPE_OPER_WRITE_ATTRIBUTES.equals(typeOper)) { | |
652 | + params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); | |
653 | + result = params.keySet(); | |
631 | 654 | } |
632 | - Set<String> pathSend = ConcurrentHashMap.newKeySet(); | |
633 | - result.forEach(target -> { | |
634 | - // #1.1 | |
635 | - String[] resPath = target.split("/"); | |
636 | - String instance = "/" + resPath[1] + "/" + resPath[2]; | |
637 | - if (clientInstances != null && clientInstances.size() > 0 && clientInstances.contains(instance)) { | |
638 | - pathSend.add(target); | |
655 | + if (!result.isEmpty()) { | |
656 | + // #1 | |
657 | + Set<String> pathSend = result.stream().filter(target -> { | |
658 | + return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? | |
659 | + clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : | |
660 | + clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); | |
661 | + } | |
662 | + ) | |
663 | + .collect(Collectors.toUnmodifiableSet()); | |
664 | + if (!pathSend.isEmpty()) { | |
665 | + lwM2MClient.getPendingRequests().addAll(pathSend); | |
666 | + ConcurrentHashMap<String, Object> finalParams = params; | |
667 | + pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), | |
668 | + null, finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout())); | |
669 | + if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { | |
670 | + lwM2MClient.initValue(this, null); | |
671 | + } | |
639 | 672 | } |
640 | - }); | |
641 | - lwM2MClient.getPendingRequests().addAll(pathSend); | |
642 | - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), | |
643 | - null, null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout())); | |
644 | - if (GET_TYPE_OPER_OBSERVE.equals(typeOper)) { | |
645 | - lwM2MClient.initValue(this, null); | |
646 | 673 | } |
647 | 674 | } |
648 | 675 | |
676 | + private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) { | |
677 | + ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(), | |
678 | + new TypeToken<ConcurrentHashMap<String, Object>>() { | |
679 | + }.getType()); | |
680 | + return pathAttributes; | |
681 | + } | |
682 | + | |
649 | 683 | /** |
650 | 684 | * Update parameters device in LwM2MClient |
651 | 685 | * If new deviceProfile != old deviceProfile => update deviceProfile |
... | ... | @@ -667,21 +701,6 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
667 | 701 | |
668 | 702 | /** |
669 | 703 | * @param registration - |
670 | - * @return - all object in client | |
671 | - */ | |
672 | - private Set<String> getAllOjectsInClient(Registration registration) { | |
673 | - Set<String> clientObjects = ConcurrentHashMap.newKeySet(); | |
674 | - Arrays.stream(registration.getObjectLinks()).forEach(url -> { | |
675 | - LwM2mPath pathIds = new LwM2mPath(url.getUrl()); | |
676 | - if (pathIds.isObjectInstance()) { | |
677 | - clientObjects.add("/" + pathIds.getObjectId()); | |
678 | - } | |
679 | - }); | |
680 | - return (clientObjects.size() > 0) ? clientObjects : null; | |
681 | - } | |
682 | - | |
683 | - /** | |
684 | - * @param registration - | |
685 | 704 | * @return all instances in client |
686 | 705 | */ |
687 | 706 | private Set<String> getAllInstancesInClient(Registration registration) { |
... | ... | @@ -724,16 +743,20 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
724 | 743 | private void addParameters(String path, JsonObject parameters, Registration registration) { |
725 | 744 | LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); |
726 | 745 | JsonObject names = lwM2mClientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile(); |
727 | - String resName = names.get(path).getAsString(); | |
728 | - if (resName != null && !resName.isEmpty()) { | |
729 | - try { | |
730 | - String resValue = this.getResourceValueToString(lwM2MClient, path); | |
731 | - if (resValue != null) { | |
732 | - parameters.addProperty(resName, resValue); | |
746 | + if (names != null && names.has(path)) { | |
747 | + String resName = names.get(path).getAsString(); | |
748 | + if (resName != null && !resName.isEmpty()) { | |
749 | + try { | |
750 | + String resValue = this.getResourceValueToString(lwM2MClient, path); | |
751 | + if (resValue != null) { | |
752 | + parameters.addProperty(resName, resValue); | |
753 | + } | |
754 | + } catch (Exception e) { | |
755 | + log.error("Failed to add parameters.", e); | |
733 | 756 | } |
734 | - } catch (Exception e) { | |
735 | - log.error("Failed to add parameters.", e); | |
736 | 757 | } |
758 | + } else { | |
759 | + log.error("Failed to add parameters. path: [{}], names: [{}]", path, names); | |
737 | 760 | } |
738 | 761 | } |
739 | 762 | |
... | ... | @@ -750,7 +773,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
750 | 773 | |
751 | 774 | /** |
752 | 775 | * @param lwM2MClient - |
753 | - * @param path - | |
776 | + * @param path - | |
754 | 777 | * @return - return value of Resource by idPath |
755 | 778 | */ |
756 | 779 | private LwM2mResource returnResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { |
... | ... | @@ -779,6 +802,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
779 | 802 | * #3.1 Attribute isChange (add&del) |
780 | 803 | * #3.2 Telemetry isChange (add&del) |
781 | 804 | * #3.3 KeyName isChange (add) |
805 | + * #3.4 attributeLwm2m isChange (update WrightAttribute: add/update/del) | |
782 | 806 | * #4 update |
783 | 807 | * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and send Value to thingsboard |
784 | 808 | * #4.2 del |
... | ... | @@ -789,6 +813,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
789 | 813 | * -- path Attr/Telemetry includes newObserve and does not include oldObserve: send Request observe to Client |
790 | 814 | * #5.3 Observe.del |
791 | 815 | * -- different between newObserve and oldObserve: send Request cancel observe to client |
816 | + * #6 | |
817 | + * #6.1 - update WriteAttribute | |
818 | + * #6.2 - del WriteAttribute | |
792 | 819 | * |
793 | 820 | * @param registrationIds - |
794 | 821 | * @param deviceProfile - |
... | ... | @@ -803,6 +830,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
803 | 830 | Set<String> telemetrySetOld = this.convertJsonArrayToSet(telemetryOld); |
804 | 831 | JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile(); |
805 | 832 | JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile(); |
833 | + JsonObject attributeLwm2mOld = lwM2MClientProfileOld.getPostAttributeLwm2mProfile(); | |
806 | 834 | |
807 | 835 | LwM2mClientProfile lwM2MClientProfileNew = lwM2mClientContext.getProfiles().get(deviceProfile.getUuidId()); |
808 | 836 | JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile(); |
... | ... | @@ -811,32 +839,41 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
811 | 839 | Set<String> telemetrySetNew = this.convertJsonArrayToSet(telemetryNew); |
812 | 840 | JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile(); |
813 | 841 | JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile(); |
842 | + JsonObject attributeLwm2mNew = lwM2MClientProfileNew.getPostAttributeLwm2mProfile(); | |
814 | 843 | |
815 | 844 | // #3 |
816 | 845 | ResultsAnalyzerParameters sendAttrToThingsboard = new ResultsAnalyzerParameters(); |
817 | 846 | // #3.1 |
818 | 847 | if (!attributeOld.equals(attributeNew)) { |
819 | - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, new TypeToken<Set<String>>() { | |
820 | - }.getType()), attributeSetNew); | |
848 | + ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, | |
849 | + new TypeToken<Set<String>>() { | |
850 | + }.getType()), attributeSetNew); | |
821 | 851 | sendAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); |
822 | 852 | sendAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); |
823 | 853 | } |
824 | 854 | // #3.2 |
825 | 855 | if (!telemetryOld.equals(telemetryNew)) { |
826 | - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, new TypeToken<Set<String>>() { | |
827 | - }.getType()), telemetrySetNew); | |
856 | + ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, | |
857 | + new TypeToken<Set<String>>() { | |
858 | + }.getType()), telemetrySetNew); | |
828 | 859 | sendAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); |
829 | 860 | sendAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); |
830 | 861 | } |
831 | 862 | // #3.3 |
832 | 863 | if (!keyNameOld.equals(keyNameNew)) { |
833 | - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), new TypeToken<ConcurrentHashMap<String, String>>() { | |
864 | + ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), | |
865 | + new TypeToken<ConcurrentHashMap<String, String>>() { | |
834 | 866 | }.getType()), |
835 | 867 | new Gson().fromJson(keyNameNew.toString(), new TypeToken<ConcurrentHashMap<String, String>>() { |
836 | 868 | }.getType())); |
837 | 869 | sendAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); |
838 | 870 | } |
839 | 871 | |
872 | + // #3.4, #6 | |
873 | + if (!attributeLwm2mOld.equals(attributeLwm2mNew)) { | |
874 | + this.getAnalyzerAttributeLwm2m(registrationIds, attributeLwm2mOld, attributeLwm2mNew); | |
875 | + } | |
876 | + | |
840 | 877 | // #4.1 add |
841 | 878 | if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) { |
842 | 879 | // update value in Resources |
... | ... | @@ -933,7 +970,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
933 | 970 | }); |
934 | 971 | } |
935 | 972 | |
936 | - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentMap<String, String> keyNameOld, ConcurrentMap<String, String> keyNameNew) { | |
973 | + private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentHashMap<String, String> keyNameOld, ConcurrentHashMap<String, String> keyNameNew) { | |
937 | 974 | ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); |
938 | 975 | Set<String> paths = keyNameNew.entrySet() |
939 | 976 | .stream() |
... | ... | @@ -943,6 +980,71 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
943 | 980 | return analyzerParameters; |
944 | 981 | } |
945 | 982 | |
983 | + /** | |
984 | + * #3.4, #6 | |
985 | + * #6 | |
986 | + * #6.1 - send update WriteAttribute | |
987 | + * #6.2 - send empty WriteAttribute | |
988 | + * | |
989 | + * @param attributeLwm2mOld - | |
990 | + * @param attributeLwm2mNew - | |
991 | + * @return | |
992 | + */ | |
993 | + private void getAnalyzerAttributeLwm2m(Set<String> registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) { | |
994 | + ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); | |
995 | + ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(), | |
996 | + new TypeToken<ConcurrentHashMap<String, Object>>() { | |
997 | + }.getType()); | |
998 | + ConcurrentHashMap<String, Object> lwm2mAttributesNew = new Gson().fromJson(attributeLwm2mNew.toString(), | |
999 | + new TypeToken<ConcurrentHashMap<String, Object>>() { | |
1000 | + }.getType()); | |
1001 | + Set<String> pathOld = lwm2mAttributesOld.keySet(); | |
1002 | + Set<String> pathNew = lwm2mAttributesNew.keySet(); | |
1003 | + analyzerParameters.setPathPostParametersAdd(pathNew | |
1004 | + .stream().filter(p -> !pathOld.contains(p)).collect(Collectors.toSet())); | |
1005 | + analyzerParameters.setPathPostParametersDel(pathOld | |
1006 | + .stream().filter(p -> !pathNew.contains(p)).collect(Collectors.toSet())); | |
1007 | + Set<String> pathCommon = pathNew | |
1008 | + .stream().filter(p -> pathOld.contains(p)).collect(Collectors.toSet()); | |
1009 | + Set<String> pathCommonChange = pathCommon | |
1010 | + .stream().filter(p -> !lwm2mAttributesOld.get(p).equals(lwm2mAttributesNew.get(p))).collect(Collectors.toSet()); | |
1011 | + analyzerParameters.getPathPostParametersAdd().addAll(pathCommonChange); | |
1012 | + // #6 | |
1013 | + // #6.2 | |
1014 | + if (analyzerParameters.getPathPostParametersAdd().size() > 0) { | |
1015 | + registrationIds.forEach(registrationId -> { | |
1016 | + Registration registration = this.lwM2mClientContext.getRegistration(registrationId); | |
1017 | + Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration); | |
1018 | + Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) | |
1019 | + .collect(Collectors.toUnmodifiableSet()); | |
1020 | + if (!pathSend.isEmpty()) { | |
1021 | + ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew; | |
1022 | + pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, PUT_TYPE_OPER_WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), | |
1023 | + null, finalParams.get(target), this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout())); | |
1024 | + } | |
1025 | + }); | |
1026 | + } | |
1027 | + // #6.2 | |
1028 | + if (analyzerParameters.getPathPostParametersDel().size() > 0) { | |
1029 | + registrationIds.forEach(registrationId -> { | |
1030 | + Registration registration = this.lwM2mClientContext.getRegistration(registrationId); | |
1031 | + Set<String> clientObjects = lwM2mClientContext.getSupportedIdVerInClient(registration); | |
1032 | + Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) | |
1033 | + .collect(Collectors.toUnmodifiableSet()); | |
1034 | + if (!pathSend.isEmpty()) { | |
1035 | + pathSend.forEach(target -> { | |
1036 | + Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target); | |
1037 | + params.clear(); | |
1038 | + params.put(OBJECT_VERSION, ""); | |
1039 | + lwM2mTransportRequest.sendAllRequest(registration, target, PUT_TYPE_OPER_WRITE_ATTRIBUTES, ContentFormat.TLV.getName(), | |
1040 | + null, params, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout()); | |
1041 | + }); | |
1042 | + } | |
1043 | + }); | |
1044 | + } | |
1045 | + | |
1046 | + } | |
1047 | + | |
946 | 1048 | private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) { |
947 | 1049 | LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); |
948 | 1050 | paramAnallyzer.forEach(p -> { | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.DeviceProfile; |
20 | 20 | import org.thingsboard.server.gen.transport.TransportProtos; |
21 | 21 | |
22 | 22 | import java.util.Map; |
23 | +import java.util.Set; | |
23 | 24 | import java.util.UUID; |
24 | 25 | |
25 | 26 | public interface LwM2mClientContext { |
... | ... | @@ -51,4 +52,6 @@ public interface LwM2mClientContext { |
51 | 52 | Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles); |
52 | 53 | |
53 | 54 | boolean addUpdateProfileParameters(DeviceProfile deviceProfile); |
55 | + | |
56 | + Set<String> getSupportedIdVerInClient(Registration registration); | |
54 | 57 | } | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.transport.lwm2m.server.client; |
17 | 17 | |
18 | +import org.eclipse.leshan.core.node.LwM2mPath; | |
18 | 19 | import org.eclipse.leshan.server.registration.Registration; |
19 | 20 | import org.eclipse.leshan.server.security.EditableSecurityStore; |
20 | 21 | import org.springframework.stereotype.Service; |
... | ... | @@ -27,11 +28,14 @@ import org.thingsboard.server.transport.lwm2m.secure.ReadResultSecurityStore; |
27 | 28 | import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler; |
28 | 29 | import org.thingsboard.server.transport.lwm2m.utils.TypeServer; |
29 | 30 | |
31 | +import java.util.Arrays; | |
30 | 32 | import java.util.Map; |
33 | +import java.util.Set; | |
31 | 34 | import java.util.UUID; |
32 | 35 | import java.util.concurrent.ConcurrentHashMap; |
33 | 36 | |
34 | 37 | import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC; |
38 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertToIdVerFromObjectId; | |
35 | 39 | |
36 | 40 | @Service |
37 | 41 | @TbLwM2mTransportComponent |
... | ... | @@ -90,7 +94,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
90 | 94 | @Override |
91 | 95 | public LwM2mClient updateInSessionsLwM2MClient(Registration registration) { |
92 | 96 | if (this.lwM2mClients.get(registration.getEndpoint()) == null) { |
93 | - addLwM2mClientToSession(registration.getEndpoint()); | |
97 | + this.addLwM2mClientToSession(registration.getEndpoint()); | |
94 | 98 | } |
95 | 99 | LwM2mClient lwM2MClient = lwM2mClients.get(registration.getEndpoint()); |
96 | 100 | lwM2MClient.setRegistration(registration); |
... | ... | @@ -169,4 +173,21 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
169 | 173 | } |
170 | 174 | return false; |
171 | 175 | } |
176 | + | |
177 | + /** | |
178 | + * if isVer - ok or default ver=DEFAULT_LWM2M_VERSION | |
179 | + * @param registration - | |
180 | + * @return - all objectIdVer in client | |
181 | + */ | |
182 | + @Override | |
183 | + public Set<String> getSupportedIdVerInClient(Registration registration) { | |
184 | + Set<String> clientObjects = ConcurrentHashMap.newKeySet(); | |
185 | + Arrays.stream(registration.getObjectLinks()).forEach(url -> { | |
186 | + LwM2mPath pathIds = new LwM2mPath(url.getUrl()); | |
187 | + if (!pathIds.isRoot()) { | |
188 | + clientObjects.add(convertToIdVerFromObjectId(url.getUrl(), registration)); | |
189 | + } | |
190 | + }); | |
191 | + return (clientObjects.size() > 0) ? clientObjects : null; | |
192 | + } | |
172 | 193 | } | ... | ... |
... | ... | @@ -56,6 +56,13 @@ public class LwM2mClientProfile { |
56 | 56 | */ |
57 | 57 | private JsonArray postObserveProfile; |
58 | 58 | |
59 | + /** | |
60 | + * "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"}, | |
61 | + * "/3_1.0/0": {"gt": 17}, | |
62 | + * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} | |
63 | + */ | |
64 | + private JsonObject postAttributeLwm2mProfile; | |
65 | + | |
59 | 66 | public LwM2mClientProfile clone() { |
60 | 67 | LwM2mClientProfile lwM2mClientProfile = new LwM2mClientProfile(); |
61 | 68 | lwM2mClientProfile.postClientLwM2mSettings = this.deepCopy(this.postClientLwM2mSettings, JsonObject.class); |
... | ... | @@ -63,6 +70,7 @@ public class LwM2mClientProfile { |
63 | 70 | lwM2mClientProfile.postAttributeProfile = this.deepCopy(this.postAttributeProfile, JsonArray.class); |
64 | 71 | lwM2mClientProfile.postTelemetryProfile = this.deepCopy(this.postTelemetryProfile, JsonArray.class); |
65 | 72 | lwM2mClientProfile.postObserveProfile = this.deepCopy(this.postObserveProfile, JsonArray.class); |
73 | + lwM2mClientProfile.postAttributeLwm2mProfile = this.deepCopy(this.postAttributeLwm2mProfile, JsonObject.class); | |
66 | 74 | return lwM2mClientProfile; |
67 | 75 | } |
68 | 76 | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
23 | 23 | import org.springframework.beans.factory.annotation.Value; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | 25 | import org.thingsboard.server.common.data.Event; |
26 | +import org.thingsboard.server.common.data.event.EventFilter; | |
26 | 27 | import org.thingsboard.server.common.data.id.EntityId; |
27 | 28 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 29 | import org.thingsboard.server.common.data.page.PageData; |
... | ... | @@ -30,7 +31,6 @@ import org.thingsboard.server.common.data.page.TimePageLink; |
30 | 31 | import org.thingsboard.server.dao.exception.DataValidationException; |
31 | 32 | import org.thingsboard.server.dao.service.DataValidator; |
32 | 33 | |
33 | -import java.nio.charset.StandardCharsets; | |
34 | 34 | import java.util.List; |
35 | 35 | import java.util.Optional; |
36 | 36 | |
... | ... | @@ -112,6 +112,11 @@ public class BaseEventService implements EventService { |
112 | 112 | } |
113 | 113 | |
114 | 114 | @Override |
115 | + public PageData<Event> findEventsByFilter(TenantId tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink) { | |
116 | + return eventDao.findEventByFilter(tenantId.getId(), entityId, eventFilter, pageLink); | |
117 | + } | |
118 | + | |
119 | + @Override | |
115 | 120 | public void removeEvents(TenantId tenantId, EntityId entityId) { |
116 | 121 | PageData<Event> eventPageData; |
117 | 122 | TimePageLink eventPageLink = new TimePageLink(1000); | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.event; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.Event; |
20 | +import org.thingsboard.server.common.data.event.EventFilter; | |
20 | 21 | import org.thingsboard.server.common.data.id.EntityId; |
21 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
22 | 23 | import org.thingsboard.server.common.data.page.PageData; |
... | ... | @@ -88,6 +89,8 @@ public interface EventDao extends Dao<Event> { |
88 | 89 | */ |
89 | 90 | PageData<Event> findEvents(UUID tenantId, EntityId entityId, String eventType, TimePageLink pageLink); |
90 | 91 | |
92 | + PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink); | |
93 | + | |
91 | 94 | /** |
92 | 95 | * Find latest events by tenantId, entityId and eventType. |
93 | 96 | * | ... | ... |
... | ... | @@ -44,11 +44,11 @@ public interface EventRepository extends PagingAndSortingRepository<EventEntity, |
44 | 44 | @Query("SELECT e FROM EventEntity e WHERE e.tenantId = :tenantId AND e.entityType = :entityType " + |
45 | 45 | "AND e.entityId = :entityId AND e.eventType = :eventType ORDER BY e.eventType DESC, e.id DESC") |
46 | 46 | List<EventEntity> findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType( |
47 | - @Param("tenantId") UUID tenantId, | |
48 | - @Param("entityType") EntityType entityType, | |
49 | - @Param("entityId") UUID entityId, | |
50 | - @Param("eventType") String eventType, | |
51 | - Pageable pageable); | |
47 | + @Param("tenantId") UUID tenantId, | |
48 | + @Param("entityType") EntityType entityType, | |
49 | + @Param("entityId") UUID entityId, | |
50 | + @Param("eventType") String eventType, | |
51 | + Pageable pageable); | |
52 | 52 | |
53 | 53 | @Query("SELECT e FROM EventEntity e WHERE " + |
54 | 54 | "e.tenantId = :tenantId " + |
... | ... | @@ -80,4 +80,165 @@ public interface EventRepository extends PagingAndSortingRepository<EventEntity, |
80 | 80 | @Param("endTime") Long endTime, |
81 | 81 | Pageable pageable); |
82 | 82 | |
83 | + @Query(nativeQuery = true, | |
84 | + 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 " + | |
85 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
86 | + "e.tenant_id = :tenantId " + | |
87 | + "AND e.entity_type = :entityType " + | |
88 | + "AND e.entity_id = :entityId " + | |
89 | + "AND e.event_type = :eventType " + | |
90 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
91 | + ") AS e WHERE " + | |
92 | + "(:type IS NULL OR lower(json_body->>'type') LIKE concat('%', lower(:type\\:\\:varchar), '%')) " + | |
93 | + "AND (:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
94 | + "AND (:entityName IS NULL OR lower(json_body->>'entityName') LIKE concat('%', lower(:entityName\\:\\:varchar), '%')) " + | |
95 | + "AND (:relationType IS NULL OR lower(json_body->>'relationType') LIKE concat('%', lower(:relationType\\:\\:varchar), '%')) " + | |
96 | + "AND (:bodyEntityId IS NULL OR lower(json_body->>'entityId') LIKE concat('%', lower(:bodyEntityId\\:\\:varchar), '%')) " + | |
97 | + "AND (:msgType IS NULL OR lower(json_body->>'msgType') LIKE concat('%', lower(:msgType\\:\\:varchar), '%')) " + | |
98 | + "AND ((:isError = FALSE) OR (json_body->>'error') IS NOT NULL) " + | |
99 | + "AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%')) " + | |
100 | + "AND (:data IS NULL OR lower(json_body->>'data') LIKE concat('%', lower(:data\\:\\:varchar), '%')) " + | |
101 | + "AND (:metadata IS NULL OR lower(json_body->>'metadata') LIKE concat('%', lower(:metadata\\:\\:varchar), '%')) ", | |
102 | + countQuery = "SELECT count(*) FROM " + | |
103 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
104 | + "e.tenant_id = :tenantId " + | |
105 | + "AND e.entity_type = :entityType " + | |
106 | + "AND e.entity_id = :entityId " + | |
107 | + "AND e.event_type = :eventType " + | |
108 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
109 | + ") AS e WHERE " + | |
110 | + "(:type IS NULL OR lower(json_body->>'type') LIKE concat('%', lower(:type\\:\\:varchar), '%')) " + | |
111 | + "AND (:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
112 | + "AND (:entityName IS NULL OR lower(json_body->>'entityName') LIKE concat('%', lower(:entityName\\:\\:varchar), '%')) " + | |
113 | + "AND (:relationType IS NULL OR lower(json_body->>'relationType') LIKE concat('%', lower(:relationType\\:\\:varchar), '%')) " + | |
114 | + "AND (:bodyEntityId IS NULL OR lower(json_body->>'entityId') LIKE concat('%', lower(:bodyEntityId\\:\\:varchar), '%')) " + | |
115 | + "AND (:msgType IS NULL OR lower(json_body->>'msgType') LIKE concat('%', lower(:msgType\\:\\:varchar), '%')) " + | |
116 | + "AND ((:isError = FALSE) OR (json_body->>'error') IS NOT NULL) " + | |
117 | + "AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%')) " + | |
118 | + "AND (:data IS NULL OR lower(json_body->>'data') LIKE concat('%', lower(:data\\:\\:varchar), '%')) " + | |
119 | + "AND (:metadata IS NULL OR lower(json_body->>'metadata') LIKE concat('%', lower(:metadata\\:\\:varchar), '%'))" | |
120 | + ) | |
121 | + Page<EventEntity> findDebugRuleNodeEvents(@Param("tenantId") UUID tenantId, | |
122 | + @Param("entityId") UUID entityId, | |
123 | + @Param("entityType") String entityType, | |
124 | + @Param("eventType") String eventType, | |
125 | + @Param("startTime") Long startTime, | |
126 | + @Param("endTime") Long endTime, | |
127 | + @Param("type") String type, | |
128 | + @Param("server") String server, | |
129 | + @Param("entityName") String entityName, | |
130 | + @Param("relationType") String relationType, | |
131 | + @Param("bodyEntityId") String bodyEntityId, | |
132 | + @Param("msgType") String msgType, | |
133 | + @Param("isError") boolean isError, | |
134 | + @Param("error") String error, | |
135 | + @Param("data") String data, | |
136 | + @Param("metadata") String metadata, | |
137 | + Pageable pageable); | |
138 | + | |
139 | + @Query(nativeQuery = true, | |
140 | + 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 " + | |
141 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
142 | + "e.tenant_id = :tenantId " + | |
143 | + "AND e.entity_type = :entityType " + | |
144 | + "AND e.entity_id = :entityId " + | |
145 | + "AND e.event_type = 'ERROR' " + | |
146 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
147 | + ") AS e WHERE " + | |
148 | + "(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
149 | + "AND (:method IS NULL OR lower(json_body->>'method') LIKE concat('%', lower(:method\\:\\:varchar), '%')) " + | |
150 | + "AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))", | |
151 | + countQuery = "SELECT count(*) FROM " + | |
152 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
153 | + "e.tenant_id = :tenantId " + | |
154 | + "AND e.entity_type = :entityType " + | |
155 | + "AND e.entity_id = :entityId " + | |
156 | + "AND e.event_type = 'ERROR' " + | |
157 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
158 | + ") AS e WHERE " + | |
159 | + "(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
160 | + "AND (:method IS NULL OR lower(json_body->>'method') LIKE concat('%', lower(:method\\:\\:varchar), '%')) " + | |
161 | + "AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))") | |
162 | + Page<EventEntity> findErrorEvents(@Param("tenantId") UUID tenantId, | |
163 | + @Param("entityId") UUID entityId, | |
164 | + @Param("entityType") String entityType, | |
165 | + @Param("startTime") Long startTime, | |
166 | + @Param("endTime") Long endTIme, | |
167 | + @Param("server") String server, | |
168 | + @Param("method") String method, | |
169 | + @Param("error") String error, | |
170 | + Pageable pageable); | |
171 | + | |
172 | + @Query(nativeQuery = true, | |
173 | + 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 " + | |
174 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
175 | + "e.tenant_id = :tenantId " + | |
176 | + "AND e.entity_type = :entityType " + | |
177 | + "AND e.entity_id = :entityId " + | |
178 | + "AND e.event_type = 'LC_EVENT' " + | |
179 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
180 | + ") AS e WHERE " + | |
181 | + "(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
182 | + "AND (:event IS NULL OR lower(json_body->>'event') LIKE concat('%', lower(:event\\:\\:varchar), '%')) " + | |
183 | + "AND ((:statusFilterEnabled = FALSE) OR lower(json_body->>'success')\\:\\:boolean = :statusFilter) " + | |
184 | + "AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))" | |
185 | + , | |
186 | + countQuery = "SELECT count(*) FROM " + | |
187 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
188 | + "e.tenant_id = :tenantId " + | |
189 | + "AND e.entity_type = :entityType " + | |
190 | + "AND e.entity_id = :entityId " + | |
191 | + "AND e.event_type = 'LC_EVENT' " + | |
192 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
193 | + ") AS e WHERE " + | |
194 | + "(:server IS NULL OR lower(json_body->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
195 | + "AND (:event IS NULL OR lower(json_body->>'event') LIKE concat('%', lower(:event\\:\\:varchar), '%')) " + | |
196 | + "AND ((:statusFilterEnabled = FALSE) OR lower(json_body->>'success')\\:\\:boolean = :statusFilter) " + | |
197 | + "AND (:error IS NULL OR lower(json_body->>'error') LIKE concat('%', lower(:error\\:\\:varchar), '%'))" | |
198 | + ) | |
199 | + Page<EventEntity> findLifeCycleEvents(@Param("tenantId") UUID tenantId, | |
200 | + @Param("entityId") UUID entityId, | |
201 | + @Param("entityType") String entityType, | |
202 | + @Param("startTime") Long startTime, | |
203 | + @Param("endTime") Long endTIme, | |
204 | + @Param("server") String server, | |
205 | + @Param("event") String event, | |
206 | + @Param("statusFilterEnabled") boolean statusFilterEnabled, | |
207 | + @Param("statusFilter") boolean statusFilter, | |
208 | + @Param("error") String error, | |
209 | + Pageable pageable); | |
210 | + | |
211 | + @Query(nativeQuery = true, | |
212 | + 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 " + | |
213 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
214 | + "e.tenant_id = :tenantId " + | |
215 | + "AND e.entity_type = :entityType " + | |
216 | + "AND e.entity_id = :entityId " + | |
217 | + "AND e.event_type = 'STATS' " + | |
218 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
219 | + ") AS e WHERE " + | |
220 | + "(:server IS NULL OR lower(e.body\\:\\:json->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
221 | + "AND (:messagesProcessed = 0 OR (json_body->>'messagesProcessed')\\:\\:integer >= :messagesProcessed) " + | |
222 | + "AND (:errorsOccurred = 0 OR (json_body->>'errorsOccurred')\\:\\:integer >= :errorsOccurred) ", | |
223 | + countQuery = "SELECT count(*) FROM " + | |
224 | + "(SELECT *, e.body\\:\\:jsonb as json_body FROM event e WHERE " + | |
225 | + "e.tenant_id = :tenantId " + | |
226 | + "AND e.entity_type = :entityType " + | |
227 | + "AND e.entity_id = :entityId " + | |
228 | + "AND e.event_type = 'LC_EVENT' " + | |
229 | + "AND e.created_time >= :startTime AND (:endTime = 0 OR e.created_time <= :endTime) " + | |
230 | + ") AS e WHERE " + | |
231 | + "(:server IS NULL OR lower(e.body\\:\\:json->>'server') LIKE concat('%', lower(:server\\:\\:varchar), '%')) " + | |
232 | + "AND (:messagesProcessed = 0 OR (json_body->>'messagesProcessed')\\:\\:integer >= :messagesProcessed) " + | |
233 | + "AND (:errorsOccurred = 0 OR (json_body->>'errorsOccurred')\\:\\:integer >= :errorsOccurred) ") | |
234 | + Page<EventEntity> findStatisticsEvents(@Param("tenantId") UUID tenantId, | |
235 | + @Param("entityId") UUID entityId, | |
236 | + @Param("entityType") String entityType, | |
237 | + @Param("startTime") Long startTime, | |
238 | + @Param("endTime") Long endTIme, | |
239 | + @Param("server") String server, | |
240 | + @Param("messagesProcessed") Integer messagesProcessed, | |
241 | + @Param("errorsOccurred") Integer errorsOccurred, | |
242 | + Pageable pageable); | |
243 | + | |
83 | 244 | } | ... | ... |
... | ... | @@ -24,6 +24,12 @@ import org.springframework.data.domain.PageRequest; |
24 | 24 | import org.springframework.data.repository.CrudRepository; |
25 | 25 | import org.springframework.stereotype.Component; |
26 | 26 | import org.thingsboard.server.common.data.Event; |
27 | +import org.thingsboard.server.common.data.event.DebugEvent; | |
28 | +import org.thingsboard.server.common.data.event.ErrorEventFilter; | |
29 | +import org.thingsboard.server.common.data.event.EventFilter; | |
30 | +import org.thingsboard.server.common.data.event.EventType; | |
31 | +import org.thingsboard.server.common.data.event.LifeCycleEventFilter; | |
32 | +import org.thingsboard.server.common.data.event.StatisticsEventFilter; | |
27 | 33 | import org.thingsboard.server.common.data.id.EntityId; |
28 | 34 | import org.thingsboard.server.common.data.id.EventId; |
29 | 35 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -148,6 +154,98 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen |
148 | 154 | } |
149 | 155 | |
150 | 156 | @Override |
157 | + public PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, EventFilter eventFilter, TimePageLink pageLink) { | |
158 | + if (eventFilter.hasFilterForJsonBody()) { | |
159 | + switch (eventFilter.getEventType()) { | |
160 | + case DEBUG_RULE_NODE: | |
161 | + case DEBUG_RULE_CHAIN: | |
162 | + return findEventByFilter(tenantId, entityId, (DebugEvent) eventFilter, pageLink); | |
163 | + case LC_EVENT: | |
164 | + return findEventByFilter(tenantId, entityId, (LifeCycleEventFilter) eventFilter, pageLink); | |
165 | + case ERROR: | |
166 | + return findEventByFilter(tenantId, entityId, (ErrorEventFilter) eventFilter, pageLink); | |
167 | + case STATS: | |
168 | + return findEventByFilter(tenantId, entityId, (StatisticsEventFilter) eventFilter, pageLink); | |
169 | + default: | |
170 | + throw new RuntimeException("Not supported event type: " + eventFilter.getEventType()); | |
171 | + } | |
172 | + } else { | |
173 | + return findEvents(tenantId, entityId, eventFilter.getEventType().name(), pageLink); | |
174 | + } | |
175 | + } | |
176 | + | |
177 | + private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, DebugEvent eventFilter, TimePageLink pageLink) { | |
178 | + return DaoUtil.toPageData( | |
179 | + eventRepository.findDebugRuleNodeEvents( | |
180 | + tenantId, | |
181 | + entityId.getId(), | |
182 | + entityId.getEntityType().name(), | |
183 | + eventFilter.getEventType().name(), | |
184 | + notNull(pageLink.getStartTime()), | |
185 | + notNull(pageLink.getEndTime()), | |
186 | + eventFilter.getMsgDirectionType(), | |
187 | + eventFilter.getServer(), | |
188 | + eventFilter.getEntityName(), | |
189 | + eventFilter.getRelationType(), | |
190 | + eventFilter.getEntityId(), | |
191 | + eventFilter.getMsgType(), | |
192 | + eventFilter.isError(), | |
193 | + eventFilter.getError(), | |
194 | + eventFilter.getDataSearch(), | |
195 | + eventFilter.getMetadataSearch(), | |
196 | + DaoUtil.toPageable(pageLink))); | |
197 | + } | |
198 | + | |
199 | + private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, ErrorEventFilter eventFilter, TimePageLink pageLink) { | |
200 | + return DaoUtil.toPageData( | |
201 | + eventRepository.findErrorEvents( | |
202 | + tenantId, | |
203 | + entityId.getId(), | |
204 | + entityId.getEntityType().name(), | |
205 | + notNull(pageLink.getStartTime()), | |
206 | + notNull(pageLink.getEndTime()), | |
207 | + eventFilter.getServer(), | |
208 | + eventFilter.getMethod(), | |
209 | + eventFilter.getError(), | |
210 | + DaoUtil.toPageable(pageLink)) | |
211 | + ); | |
212 | + } | |
213 | + | |
214 | + private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, LifeCycleEventFilter eventFilter, TimePageLink pageLink) { | |
215 | + boolean statusFilterEnabled = !StringUtils.isEmpty(eventFilter.getStatus()); | |
216 | + boolean statusFilter = statusFilterEnabled && eventFilter.getStatus().equalsIgnoreCase("Success"); | |
217 | + return DaoUtil.toPageData( | |
218 | + eventRepository.findLifeCycleEvents( | |
219 | + tenantId, | |
220 | + entityId.getId(), | |
221 | + entityId.getEntityType().name(), | |
222 | + notNull(pageLink.getStartTime()), | |
223 | + notNull(pageLink.getEndTime()), | |
224 | + eventFilter.getServer(), | |
225 | + eventFilter.getEvent(), | |
226 | + statusFilterEnabled, | |
227 | + statusFilter, | |
228 | + eventFilter.getError(), | |
229 | + DaoUtil.toPageable(pageLink)) | |
230 | + ); | |
231 | + } | |
232 | + | |
233 | + private PageData<Event> findEventByFilter(UUID tenantId, EntityId entityId, StatisticsEventFilter eventFilter, TimePageLink pageLink) { | |
234 | + return DaoUtil.toPageData( | |
235 | + eventRepository.findStatisticsEvents( | |
236 | + tenantId, | |
237 | + entityId.getId(), | |
238 | + entityId.getEntityType().name(), | |
239 | + notNull(pageLink.getStartTime()), | |
240 | + notNull(pageLink.getEndTime()), | |
241 | + eventFilter.getServer(), | |
242 | + notNull(eventFilter.getMessagesProcessed()), | |
243 | + notNull(eventFilter.getErrorsOccurred()), | |
244 | + DaoUtil.toPageable(pageLink)) | |
245 | + ); | |
246 | + } | |
247 | + | |
248 | + @Override | |
151 | 249 | public List<Event> findLatestEvents(UUID tenantId, EntityId entityId, String eventType, int limit) { |
152 | 250 | List<EventEntity> latest = eventRepository.findLatestByTenantIdAndEntityTypeAndEntityIdAndEventType( |
153 | 251 | tenantId, |
... | ... | @@ -177,4 +275,12 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen |
177 | 275 | return Optional.of(DaoUtil.getData(eventInsertRepository.saveOrUpdate(entity))); |
178 | 276 | } |
179 | 277 | |
278 | + private long notNull(Long value) { | |
279 | + return value != null ? value : 0; | |
280 | + } | |
281 | + | |
282 | + private int notNull(Integer value) { | |
283 | + return value != null ? value : 0; | |
284 | + } | |
285 | + | |
180 | 286 | } | ... | ... |
... | ... | @@ -21,7 +21,7 @@ import { HttpClient } from '@angular/common/http'; |
21 | 21 | import { TimePageLink } from '@shared/models/page/page-link'; |
22 | 22 | import { PageData } from '@shared/models/page/page-data'; |
23 | 23 | import { EntityId } from '@shared/models/id/entity-id'; |
24 | -import { DebugEventType, Event, EventType } from '@shared/models/event.models'; | |
24 | +import { DebugEventType, Event, EventType, FilterEventBody } from '@shared/models/event.models'; | |
25 | 25 | |
26 | 26 | @Injectable({ |
27 | 27 | providedIn: 'root' |
... | ... | @@ -39,4 +39,10 @@ export class EventService { |
39 | 39 | defaultHttpOptionsFromConfig(config)); |
40 | 40 | } |
41 | 41 | |
42 | + public getFilterEvents(entityId: EntityId, eventType: EventType | DebugEventType, tenantId: string, | |
43 | + filters: FilterEventBody, pageLink: TimePageLink, config?: RequestConfig): Observable<PageData<Event>> { | |
44 | + return this.http.post<PageData<Event>>(`/api/events/${entityId.entityType}/${entityId.id}` + | |
45 | + `${pageLink.toQuery()}&tenantId=${tenantId}`, {...filters, eventType}, defaultHttpOptionsFromConfig(config)); | |
46 | + } | |
47 | + | |
42 | 48 | } | ... | ... |
... | ... | @@ -43,14 +43,17 @@ export class ResourceService { |
43 | 43 | } |
44 | 44 | |
45 | 45 | public downloadResource(resourceId: string): Observable<any> { |
46 | - return this.http.get(`/api/resource/${resourceId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( | |
46 | + return this.http.get(`/api/resource/${resourceId}/download`, { | |
47 | + responseType: 'arraybuffer', | |
48 | + observe: 'response' | |
49 | + }).pipe( | |
47 | 50 | map((response) => { |
48 | 51 | const headers = response.headers; |
49 | 52 | const filename = headers.get('x-filename'); |
50 | 53 | const contentType = headers.get('content-type'); |
51 | 54 | const linkElement = document.createElement('a'); |
52 | 55 | try { |
53 | - const blob = new Blob([response.body], { type: contentType }); | |
56 | + const blob = new Blob([response.body], {type: contentType}); | |
54 | 57 | const url = URL.createObjectURL(blob); |
55 | 58 | linkElement.setAttribute('href', url); |
56 | 59 | linkElement.setAttribute('download', filename); | ... | ... |
... | ... | @@ -31,7 +31,7 @@ import { isDefined } from '@core/utils'; |
31 | 31 | |
32 | 32 | export interface TimeInterval { |
33 | 33 | name: string; |
34 | - translateParams: {[key: string]: any}; | |
34 | + translateParams: { [key: string]: any }; | |
35 | 35 | value: number; |
36 | 36 | } |
37 | 37 | |
... | ... | @@ -56,14 +56,14 @@ export class TimeService { |
56 | 56 | public loadMaxDatapointsLimit(): Observable<number> { |
57 | 57 | return this.http.get<number>('/api/dashboard/maxDatapointsLimit', |
58 | 58 | defaultHttpOptions(true)).pipe( |
59 | - map( (limit) => { | |
60 | - this.maxDatapointsLimit = limit; | |
61 | - if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) { | |
62 | - this.maxDatapointsLimit = MIN_LIMIT + 1; | |
63 | - } | |
64 | - return this.maxDatapointsLimit; | |
65 | - }) | |
66 | - ); | |
59 | + map((limit) => { | |
60 | + this.maxDatapointsLimit = limit; | |
61 | + if (!this.maxDatapointsLimit || this.maxDatapointsLimit <= MIN_LIMIT) { | |
62 | + this.maxDatapointsLimit = MIN_LIMIT + 1; | |
63 | + } | |
64 | + return this.maxDatapointsLimit; | |
65 | + }) | |
66 | + ); | |
67 | 67 | } |
68 | 68 | |
69 | 69 | public matchesExistingInterval(min: number, max: number, intervalMs: number): boolean { | ... | ... |
... | ... | @@ -291,7 +291,7 @@ export function deepClone<T>(target: T, ignoreFields?: string[]): T { |
291 | 291 | return cp.map((n: any) => deepClone<any>(n)) as any; |
292 | 292 | } |
293 | 293 | if (typeof target === 'object' && target !== {}) { |
294 | - const cp = { ...(target as { [key: string]: any }) } as { [key: string]: any }; | |
294 | + const cp = {...(target as { [key: string]: any })} as { [key: string]: any }; | |
295 | 295 | Object.keys(cp).forEach(k => { |
296 | 296 | if (!ignoreFields || ignoreFields.indexOf(k) === -1) { |
297 | 297 | cp[k] = deepClone<any>(cp[k]); | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2021 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<form fxLayout="column" class="mat-content mat-padding" [formGroup]="eventFilterFormGroup" (ngSubmit)="update()"> | |
19 | + <ng-container *ngFor="let column of showColumns"> | |
20 | + <ng-container [ngSwitch]="column.key"> | |
21 | + <ng-template [ngSwitchCase]="isSelector(column.key)"> | |
22 | + <mat-form-field> | |
23 | + <mat-label>{{ column.title | translate}}</mat-label> | |
24 | + <mat-select [formControlName]="column.key"> | |
25 | + <mat-option [value]="">{{ 'event.all-events' | translate}}</mat-option> | |
26 | + <mat-option *ngFor="let value of selectorValues(column.key)" [value]="value"> | |
27 | + {{ value }} | |
28 | + </mat-option> | |
29 | + </mat-select> | |
30 | + </mat-form-field> | |
31 | + </ng-template> | |
32 | + <ng-template [ngSwitchCase]="'isError'"> | |
33 | + <tb-checkbox formControlName="isError" [falseValue]="''"> | |
34 | + {{ 'event.has-error' | translate }} | |
35 | + </tb-checkbox> | |
36 | + </ng-template> | |
37 | + <ng-template [ngSwitchCase]="'error'"> | |
38 | + <mat-form-field fxHide [fxShow]="showErrorMsgFields()"> | |
39 | + <mat-label>{{ column.title | translate}}</mat-label> | |
40 | + <input matInput type="text" name="errorSearchText" formControlName="error"> | |
41 | + </mat-form-field> | |
42 | + </ng-template> | |
43 | + <ng-container *ngSwitchDefault> | |
44 | + <mat-form-field> | |
45 | + <mat-label>{{ column.title | translate}}</mat-label> | |
46 | + <input matInput type="text" [name]="column.key" [formControlName]="column.key"> | |
47 | + </mat-form-field> | |
48 | + </ng-container> | |
49 | + </ng-container> | |
50 | + </ng-container> | |
51 | + <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> | |
52 | + <button type="button" | |
53 | + mat-button | |
54 | + (click)="cancel()"> | |
55 | + {{ 'action.cancel' | translate }} | |
56 | + </button> | |
57 | + <button type="submit" | |
58 | + mat-raised-button | |
59 | + color="primary" | |
60 | + [disabled]="eventFilterFormGroup.invalid || !eventFilterFormGroup.dirty"> | |
61 | + {{ 'action.update' | translate }} | |
62 | + </button> | |
63 | + </div> | |
64 | +</form> | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +:host { | |
17 | + width: 100%; | |
18 | + min-width: 300px; | |
19 | + overflow: auto; | |
20 | + background: #fff; | |
21 | + border-radius: 4px; | |
22 | + box-shadow: | |
23 | + 0 7px 8px -4px rgba(0, 0, 0, .2), | |
24 | + 0 13px 19px 2px rgba(0, 0, 0, .14), | |
25 | + 0 5px 24px 4px rgba(0, 0, 0, .12); | |
26 | + | |
27 | + .mat-content { | |
28 | + overflow: hidden; | |
29 | + background-color: #fff; | |
30 | + } | |
31 | + | |
32 | + .mat-padding { | |
33 | + padding: 16px; | |
34 | + } | |
35 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Component, Inject, InjectionToken } from '@angular/core'; | |
18 | +import { FormBuilder, FormGroup } from '@angular/forms'; | |
19 | +import { OverlayRef } from '@angular/cdk/overlay'; | |
20 | +import { EntityType } from '@shared/models/entity-type.models'; | |
21 | +import { FilterEventBody } from '@shared/models/event.models'; | |
22 | +import { deepTrim } from '@core/utils'; | |
23 | + | |
24 | +export const EVENT_FILTER_PANEL_DATA = new InjectionToken<any>('AlarmFilterPanelData'); | |
25 | + | |
26 | +export interface EventFilterPanelData { | |
27 | + filterParams: FilterEventBody; | |
28 | + columns: Array<FilterEntityColumn>; | |
29 | +} | |
30 | + | |
31 | +export interface FilterEntityColumn { | |
32 | + key: string; | |
33 | + title: string; | |
34 | +} | |
35 | + | |
36 | + | |
37 | +@Component({ | |
38 | + selector: 'tb-event-filter-panel', | |
39 | + templateUrl: './event-filter-panel.component.html', | |
40 | + styleUrls: ['./event-filter-panel.component.scss'] | |
41 | +}) | |
42 | +export class EventFilterPanelComponent { | |
43 | + | |
44 | + eventFilterFormGroup: FormGroup; | |
45 | + result: EventFilterPanelData; | |
46 | + | |
47 | + private conditionError = false; | |
48 | + | |
49 | + private msgDirectionTypes = ['IN', 'OUT']; | |
50 | + private statusTypes = ['Success', 'Failure']; | |
51 | + private entityTypes = Object.keys(EntityType); | |
52 | + | |
53 | + showColumns: FilterEntityColumn[] = []; | |
54 | + | |
55 | + constructor(@Inject(EVENT_FILTER_PANEL_DATA) | |
56 | + public data: EventFilterPanelData, | |
57 | + public overlayRef: OverlayRef, | |
58 | + private fb: FormBuilder) { | |
59 | + this.eventFilterFormGroup = this.fb.group({}); | |
60 | + this.data.columns.forEach((column) => { | |
61 | + this.showColumns.push(column); | |
62 | + this.eventFilterFormGroup.addControl(column.key, this.fb.control(this.data.filterParams[column.key] || '')); | |
63 | + if (column.key === 'isError') { | |
64 | + this.conditionError = true; | |
65 | + } | |
66 | + }); | |
67 | + } | |
68 | + | |
69 | + isSelector(key: string): string { | |
70 | + return ['msgDirectionType', 'status', 'entityName'].includes(key) ? key : ''; | |
71 | + } | |
72 | + | |
73 | + selectorValues(key: string): string[] { | |
74 | + switch (key) { | |
75 | + case 'msgDirectionType': | |
76 | + return this.msgDirectionTypes; | |
77 | + case 'status': | |
78 | + return this.statusTypes; | |
79 | + case 'entityName': | |
80 | + return this.entityTypes; | |
81 | + } | |
82 | + } | |
83 | + | |
84 | + update() { | |
85 | + const filter = deepTrim(Object.fromEntries(Object.entries(this.eventFilterFormGroup.value).filter(([_, v]) => v !== ''))); | |
86 | + this.result = { | |
87 | + filterParams: filter, | |
88 | + columns: this.data.columns | |
89 | + }; | |
90 | + this.overlayRef.dispose(); | |
91 | + } | |
92 | + | |
93 | + showErrorMsgFields() { | |
94 | + return !this.conditionError || this.eventFilterFormGroup.get('isError').value !== ''; | |
95 | + } | |
96 | + | |
97 | + cancel() { | |
98 | + this.overlayRef.dispose(); | |
99 | + } | |
100 | +} | |
101 | + | ... | ... |
... | ... | @@ -20,7 +20,7 @@ import { |
20 | 20 | EntityTableColumn, |
21 | 21 | EntityTableConfig |
22 | 22 | } from '@home/models/entity/entities-table-config.models'; |
23 | -import { DebugEventType, Event, EventType } from '@shared/models/event.models'; | |
23 | +import { DebugEventType, Event, EventType, FilterEventBody } from '@shared/models/event.models'; | |
24 | 24 | import { TimePageLink } from '@shared/models/page/page-link'; |
25 | 25 | import { TranslateService } from '@ngx-translate/core'; |
26 | 26 | import { DatePipe } from '@angular/common'; |
... | ... | @@ -38,16 +38,29 @@ import { |
38 | 38 | EventContentDialogComponent, |
39 | 39 | EventContentDialogData |
40 | 40 | } from '@home/components/event/event-content-dialog.component'; |
41 | -import { sortObjectKeys } from '@core/utils'; | |
41 | +import { isEqual, sortObjectKeys } from '@core/utils'; | |
42 | +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; | |
43 | +import { ChangeDetectorRef, Injector, StaticProvider, ViewContainerRef } from '@angular/core'; | |
44 | +import { ComponentPortal } from '@angular/cdk/portal'; | |
45 | +import { | |
46 | + EVENT_FILTER_PANEL_DATA, | |
47 | + EventFilterPanelComponent, | |
48 | + EventFilterPanelData, | |
49 | + FilterEntityColumn | |
50 | +} from '@home/components/event/event-filter-panel.component'; | |
42 | 51 | |
43 | 52 | export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { |
44 | 53 | |
45 | 54 | eventTypeValue: EventType | DebugEventType; |
46 | 55 | |
56 | + private filterParams: FilterEventBody = {}; | |
57 | + private filterColumns: FilterEntityColumn[] = []; | |
58 | + | |
47 | 59 | set eventType(eventType: EventType | DebugEventType) { |
48 | 60 | if (this.eventTypeValue !== eventType) { |
49 | 61 | this.eventTypeValue = eventType; |
50 | 62 | this.updateColumns(true); |
63 | + this.updateFilterColumns(); | |
51 | 64 | } |
52 | 65 | } |
53 | 66 | |
... | ... | @@ -66,7 +79,10 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { |
66 | 79 | public tenantId: string, |
67 | 80 | private defaultEventType: EventType | DebugEventType, |
68 | 81 | private disabledEventTypes: Array<EventType | DebugEventType> = null, |
69 | - private debugEventTypes: Array<DebugEventType> = null) { | |
82 | + private debugEventTypes: Array<DebugEventType> = null, | |
83 | + private overlay: Overlay, | |
84 | + private viewContainerRef: ViewContainerRef, | |
85 | + private cd: ChangeDetectorRef) { | |
70 | 86 | super(); |
71 | 87 | this.loadDataOnInit = false; |
72 | 88 | this.tableTitle = ''; |
... | ... | @@ -101,10 +117,20 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { |
101 | 117 | this.defaultSortOrder = {property: 'createdTime', direction: Direction.DESC}; |
102 | 118 | |
103 | 119 | this.updateColumns(); |
120 | + this.updateFilterColumns(); | |
121 | + | |
122 | + this.headerActionDescriptors.push({ | |
123 | + name: this.translate.instant('event.events-filter'), | |
124 | + icon: 'filter_list', | |
125 | + isEnabled: () => true, | |
126 | + onAction: ($event) => { | |
127 | + this.editEventFilter($event); | |
128 | + } | |
129 | + }); | |
104 | 130 | } |
105 | 131 | |
106 | 132 | fetchEvents(pageLink: TimePageLink): Observable<PageData<Event>> { |
107 | - return this.eventService.getEvents(this.entityId, this.eventType, this.tenantId, pageLink); | |
133 | + return this.eventService.getFilterEvents(this.entityId, this.eventType, this.tenantId, this.filterParams, pageLink); | |
108 | 134 | } |
109 | 135 | |
110 | 136 | updateColumns(updateTableColumns: boolean = false): void { |
... | ... | @@ -169,7 +195,7 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { |
169 | 195 | }), false, key => ({ |
170 | 196 | padding: '0 12px 0 0' |
171 | 197 | })), |
172 | - new EntityTableColumn<Event>('entity', 'event.entity', '100px', | |
198 | + new EntityTableColumn<Event>('entityName', 'event.entity-type', '100px', | |
173 | 199 | (entity) => entity.body.entityName, entity => ({ |
174 | 200 | padding: '0 12px 0 0', |
175 | 201 | }), false, key => ({ |
... | ... | @@ -249,5 +275,91 @@ export class EventTableConfig extends EntityTableConfig<Event, TimePageLink> { |
249 | 275 | } |
250 | 276 | }); |
251 | 277 | } |
278 | + | |
279 | + private updateFilterColumns() { | |
280 | + this.filterParams = {}; | |
281 | + this.filterColumns = [{key: 'server', title: 'event.server'}]; | |
282 | + switch (this.eventType) { | |
283 | + case EventType.ERROR: | |
284 | + this.filterColumns.push( | |
285 | + {key: 'method', title: 'event.method'}, | |
286 | + {key: 'error', title: 'event.error'} | |
287 | + ); | |
288 | + break; | |
289 | + case EventType.LC_EVENT: | |
290 | + this.filterColumns.push( | |
291 | + {key: 'method', title: 'event.event'}, | |
292 | + {key: 'status', title: 'event.status'}, | |
293 | + {key: 'error', title: 'event.error'} | |
294 | + ); | |
295 | + break; | |
296 | + case EventType.STATS: | |
297 | + this.filterColumns.push( | |
298 | + {key: 'messagesProcessed', title: 'event.messages-processed'}, | |
299 | + {key: 'errorsOccurred', title: 'event.errors-occurred'} | |
300 | + ); | |
301 | + break; | |
302 | + case DebugEventType.DEBUG_RULE_NODE: | |
303 | + case DebugEventType.DEBUG_RULE_CHAIN: | |
304 | + this.filterColumns.push( | |
305 | + {key: 'msgDirectionType', title: 'event.type'}, | |
306 | + {key: 'entityId', title: 'event.entity-id'}, | |
307 | + {key: 'entityName', title: 'event.entity-type'}, | |
308 | + {key: 'msgType', title: 'event.message-type'}, | |
309 | + {key: 'relationType', title: 'event.relation-type'}, | |
310 | + {key: 'dataSearch', title: 'event.data'}, | |
311 | + {key: 'metadataSearch', title: 'event.metadata'}, | |
312 | + {key: 'isError', title: 'event.error'}, | |
313 | + {key: 'error', title: 'event.error'} | |
314 | + ); | |
315 | + break; | |
316 | + } | |
317 | + } | |
318 | + | |
319 | + private editEventFilter($event: MouseEvent) { | |
320 | + if ($event) { | |
321 | + $event.stopPropagation(); | |
322 | + } | |
323 | + const target = $event.target || $event.srcElement || $event.currentTarget; | |
324 | + const config = new OverlayConfig(); | |
325 | + config.backdropClass = 'cdk-overlay-transparent-backdrop'; | |
326 | + config.hasBackdrop = true; | |
327 | + const connectedPosition: ConnectedPosition = { | |
328 | + originX: 'end', | |
329 | + originY: 'bottom', | |
330 | + overlayX: 'end', | |
331 | + overlayY: 'top' | |
332 | + }; | |
333 | + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) | |
334 | + .withPositions([connectedPosition]); | |
335 | + | |
336 | + const overlayRef = this.overlay.create(config); | |
337 | + overlayRef.backdropClick().subscribe(() => { | |
338 | + overlayRef.dispose(); | |
339 | + }); | |
340 | + const providers: StaticProvider[] = [ | |
341 | + { | |
342 | + provide: EVENT_FILTER_PANEL_DATA, | |
343 | + useValue: { | |
344 | + columns: this.filterColumns, | |
345 | + filterParams: this.filterParams | |
346 | + } as EventFilterPanelData | |
347 | + }, | |
348 | + { | |
349 | + provide: OverlayRef, | |
350 | + useValue: overlayRef | |
351 | + } | |
352 | + ]; | |
353 | + const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); | |
354 | + const componentRef = overlayRef.attach(new ComponentPortal(EventFilterPanelComponent, | |
355 | + this.viewContainerRef, injector)); | |
356 | + componentRef.onDestroy(() => { | |
357 | + if (componentRef.instance.result && !isEqual(this.filterParams, componentRef.instance.result.filterParams)) { | |
358 | + this.filterParams = componentRef.instance.result.filterParams; | |
359 | + this.table.updateData(); | |
360 | + } | |
361 | + }); | |
362 | + this.cd.detectChanges(); | |
363 | + } | |
252 | 364 | } |
253 | 365 | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { Component, Input, OnInit, ViewChild } from '@angular/core'; | |
17 | +import { ChangeDetectorRef, Component, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; | |
18 | 18 | import { TranslateService } from '@ngx-translate/core'; |
19 | 19 | import { DatePipe } from '@angular/common'; |
20 | 20 | import { MatDialog } from '@angular/material/dialog'; |
... | ... | @@ -24,6 +24,7 @@ import { EventTableConfig } from './event-table-config'; |
24 | 24 | import { EventService } from '@core/http/event.service'; |
25 | 25 | import { DialogService } from '@core/services/dialog.service'; |
26 | 26 | import { DebugEventType, EventType } from '@shared/models/event.models'; |
27 | +import { Overlay } from '@angular/cdk/overlay'; | |
27 | 28 | |
28 | 29 | @Component({ |
29 | 30 | selector: 'tb-event-table', |
... | ... | @@ -80,7 +81,10 @@ export class EventTableComponent implements OnInit { |
80 | 81 | private dialogService: DialogService, |
81 | 82 | private translate: TranslateService, |
82 | 83 | private datePipe: DatePipe, |
83 | - private dialog: MatDialog) { | |
84 | + private dialog: MatDialog, | |
85 | + private overlay: Overlay, | |
86 | + private viewContainerRef: ViewContainerRef, | |
87 | + private cd: ChangeDetectorRef) { | |
84 | 88 | } |
85 | 89 | |
86 | 90 | ngOnInit() { |
... | ... | @@ -95,7 +99,10 @@ export class EventTableComponent implements OnInit { |
95 | 99 | this.tenantId, |
96 | 100 | this.defaultEventType, |
97 | 101 | this.disabledEventTypes, |
98 | - this.debugEventTypes | |
102 | + this.debugEventTypes, | |
103 | + this.overlay, | |
104 | + this.viewContainerRef, | |
105 | + this.cd | |
99 | 106 | ); |
100 | 107 | } |
101 | 108 | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import { AuditLogDetailsDialogComponent } from '@home/components/audit-log/audit |
25 | 25 | import { AuditLogTableComponent } from '@home/components/audit-log/audit-log-table.component'; |
26 | 26 | import { EventTableHeaderComponent } from '@home/components/event/event-table-header.component'; |
27 | 27 | import { EventTableComponent } from '@home/components/event/event-table.component'; |
28 | +import { EventFilterPanelComponent } from '@home/components/event/event-filter-panel.component'; | |
28 | 29 | import { RelationTableComponent } from '@home/components/relation/relation-table.component'; |
29 | 30 | import { RelationDialogComponent } from '@home/components/relation/relation-dialog.component'; |
30 | 31 | import { AlarmTableHeaderComponent } from '@home/components/alarm/alarm-table-header.component'; |
... | ... | @@ -149,6 +150,7 @@ import { DisplayWidgetTypesPanelComponent } from '@home/components/dashboard-pag |
149 | 150 | EventContentDialogComponent, |
150 | 151 | EventTableHeaderComponent, |
151 | 152 | EventTableComponent, |
153 | + EventFilterPanelComponent, | |
152 | 154 | EdgeDownlinkTableHeaderComponent, |
153 | 155 | EdgeDownlinkTableComponent, |
154 | 156 | RelationTableComponent, | ... | ... |
... | ... | @@ -14,12 +14,12 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import {DeviceProfileTransportConfiguration, DeviceTransportType} from '@shared/models/device.models'; | |
18 | -import {Component, forwardRef, Inject, Input} from '@angular/core'; | |
19 | -import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; | |
20 | -import {Store} from '@ngrx/store'; | |
21 | -import {AppState} from '@app/core/core.state'; | |
22 | -import {coerceBooleanProperty} from '@angular/cdk/coercion'; | |
17 | +import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; | |
18 | +import { Component, forwardRef, Inject, Input } from '@angular/core'; | |
19 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | |
20 | +import { Store } from '@ngrx/store'; | |
21 | +import { AppState } from '@app/core/core.state'; | |
22 | +import { coerceBooleanProperty } from '@angular/cdk/coercion'; | |
23 | 23 | import { |
24 | 24 | ATTRIBUTE, |
25 | 25 | DEFAULT_BINDING, |
... | ... | @@ -34,11 +34,11 @@ import { |
34 | 34 | RESOURCES, |
35 | 35 | TELEMETRY |
36 | 36 | } from './lwm2m-profile-config.models'; |
37 | -import {DeviceProfileService} from '@core/http/device-profile.service'; | |
38 | -import {deepClone, isDefinedAndNotNull, isEmpty, isUndefined} from '@core/utils'; | |
39 | -import {WINDOW} from '@core/services/window.service'; | |
40 | -import {JsonArray, JsonObject} from '@angular/compiler-cli/ngcc/src/packages/entry_point'; | |
41 | -import {Direction} from '@shared/models/page/sort-order'; | |
37 | +import { DeviceProfileService } from '@core/http/device-profile.service'; | |
38 | +import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/utils'; | |
39 | +import { WINDOW } from '@core/services/window.service'; | |
40 | +import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; | |
41 | +import { Direction } from '@shared/models/page/sort-order'; | |
42 | 42 | |
43 | 43 | @Component({ |
44 | 44 | selector: 'tb-profile-lwm2m-device-transport-configuration', |
... | ... | @@ -173,7 +173,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
173 | 173 | } |
174 | 174 | |
175 | 175 | private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { |
176 | - this.lwm2mDeviceProfileFormGroup.patchValue({ | |
176 | + this.lwm2mDeviceProfileFormGroup.patchValue({ | |
177 | 177 | observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList)) |
178 | 178 | }, |
179 | 179 | {emitEvent: false}); |
... | ... | @@ -358,6 +358,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro |
358 | 358 | this.configurationValue.observeAttr.observe = observeArray; |
359 | 359 | this.configurationValue.observeAttr.attribute = attributeArray; |
360 | 360 | this.configurationValue.observeAttr.telemetry = telemetryArray; |
361 | + this.configurationValue.observeAttr.keyName = this.sortObjectKeyPathJson(KEY_NAME, keyNameNew); | |
361 | 362 | this.configurationValue.observeAttr.attributeLwm2m = attributeLwm2m; |
362 | 363 | } |
363 | 364 | } | ... | ... |
... | ... | @@ -47,6 +47,7 @@ |
47 | 47 | <ng-template matExpansionPanelContent> |
48 | 48 | <div fxLayout="column" fxLayoutGap="8px" formArrayName="instances"> |
49 | 49 | <mat-expansion-panel |
50 | + class="instance-list" | |
50 | 51 | *ngFor="let instances of instancesLwm2mFormArray(objectLwM2M).controls; let y = index;" |
51 | 52 | [formGroupName]="y" |
52 | 53 | [expanded]="getExpended(objectLwM2M)" |
... | ... | @@ -96,7 +97,7 @@ |
96 | 97 | </div> |
97 | 98 | <div fxFlex="10"> |
98 | 99 | </div> |
99 | - <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5" disabled="false"> | |
100 | + <div fxFlex="37" class="resource-name-lw-end" fxFlexOffset="5"> | |
100 | 101 | <tb-profile-lwm2m-attributes |
101 | 102 | formControlName="attributeLwm2m" |
102 | 103 | [attributeLwm2m]="instances.get('attributeLwm2m').value" | ... | ... |
... | ... | @@ -146,7 +146,7 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit { |
146 | 146 | this.attributeUpdateFormGroup = this.fb.group({ |
147 | 147 | currentValue: [{}, validators] |
148 | 148 | }); |
149 | - this.attributeUpdateFormGroup.valueChanges.subscribe( () => { | |
149 | + this.attributeUpdateFormGroup.valueChanges.subscribe(() => { | |
150 | 150 | this.ctx.detectChanges(); |
151 | 151 | }); |
152 | 152 | } |
... | ... | @@ -191,14 +191,14 @@ export class JsonInputWidgetComponent extends PageComponent implements OnInit { |
191 | 191 | saveAttributeObservable = this.attributeService.saveEntityAttributes( |
192 | 192 | entityId, |
193 | 193 | this.settings.attributeScope, |
194 | - [ attributeToSave ], | |
194 | + [attributeToSave], | |
195 | 195 | {} |
196 | 196 | ); |
197 | 197 | } else { |
198 | 198 | saveAttributeObservable = this.attributeService.saveEntityTimeseries( |
199 | 199 | entityId, |
200 | 200 | LatestTelemetry.LATEST_TELEMETRY, |
201 | - [ attributeToSave ], | |
201 | + [attributeToSave], | |
202 | 202 | {} |
203 | 203 | ); |
204 | 204 | } | ... | ... |
... | ... | @@ -89,7 +89,7 @@ export class AssetsTableConfigResolver implements Resolve<EntityTableConfig<Asse |
89 | 89 | this.config.entityTranslations = entityTypeTranslations.get(EntityType.ASSET); |
90 | 90 | this.config.entityResources = entityTypeResources.get(EntityType.ASSET); |
91 | 91 | |
92 | - this.config.deleteEntityTitle = asset => this.translate.instant('asset.delete-asset-title', { assetName: asset.name }); | |
92 | + this.config.deleteEntityTitle = asset => this.translate.instant('asset.delete-asset-title', {assetName: asset.name}); | |
93 | 93 | this.config.deleteEntityContent = () => this.translate.instant('asset.delete-asset-text'); |
94 | 94 | this.config.deleteEntitiesTitle = count => this.translate.instant('asset.delete-assets-title', {count}); |
95 | 95 | this.config.deleteEntitiesContent = () => this.translate.instant('asset.delete-assets-text'); | ... | ... |
... | ... | @@ -93,7 +93,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< |
93 | 93 | this.config.entityResources = entityTypeResources.get(EntityType.DASHBOARD); |
94 | 94 | |
95 | 95 | this.config.deleteEntityTitle = dashboard => |
96 | - this.translate.instant('dashboard.delete-dashboard-title', { dashboardTitle: dashboard.title }); | |
96 | + this.translate.instant('dashboard.delete-dashboard-title', {dashboardTitle: dashboard.title}); | |
97 | 97 | this.config.deleteEntityContent = () => this.translate.instant('dashboard.delete-dashboard-text'); |
98 | 98 | this.config.deleteEntitiesTitle = count => this.translate.instant('dashboard.delete-dashboards-title', {count}); |
99 | 99 | this.config.deleteEntitiesContent = () => this.translate.instant('dashboard.delete-dashboards-text'); |
... | ... | @@ -363,8 +363,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< |
363 | 363 | this.router.navigateByUrl(`customers/${this.config.componentsData.customerId}/dashboards/${dashboard.id.id}`); |
364 | 364 | } else if (this.config.componentsData.dashboardScope === 'edge') { |
365 | 365 | this.router.navigateByUrl(`edges/${this.config.componentsData.edgeId}/dashboards/${dashboard.id.id}`); |
366 | - } | |
367 | - else { | |
366 | + } else { | |
368 | 367 | this.router.navigateByUrl(`dashboards/${dashboard.id.id}`); |
369 | 368 | } |
370 | 369 | } |
... | ... | @@ -421,7 +420,7 @@ export class DashboardsTableConfigResolver implements Resolve<EntityTableConfig< |
421 | 420 | } |
422 | 421 | }).afterClosed() |
423 | 422 | .subscribe(() => { |
424 | - this.config.table.updateData(); | |
423 | + this.config.table.updateData(); | |
425 | 424 | }); |
426 | 425 | } |
427 | 426 | ); | ... | ... |
... | ... | @@ -97,7 +97,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev |
97 | 97 | |
98 | 98 | this.config.addDialogStyle = {width: '600px'}; |
99 | 99 | |
100 | - this.config.deleteEntityTitle = device => this.translate.instant('device.delete-device-title', { deviceName: device.name }); | |
100 | + this.config.deleteEntityTitle = device => this.translate.instant('device.delete-device-title', {deviceName: device.name}); | |
101 | 101 | this.config.deleteEntityContent = () => this.translate.instant('device.delete-device-text'); |
102 | 102 | this.config.deleteEntitiesTitle = count => this.translate.instant('device.delete-devices-title', {count}); |
103 | 103 | this.config.deleteEntitiesContent = () => this.translate.instant('device.delete-devices-text'); |
... | ... | @@ -109,7 +109,7 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev |
109 | 109 | this.broadcast.broadcast('deviceSaved'); |
110 | 110 | }), |
111 | 111 | mergeMap((savedDevice) => this.deviceService.getDeviceInfo(savedDevice.id.id) |
112 | - )); | |
112 | + )); | |
113 | 113 | }; |
114 | 114 | this.config.onEntityAction = action => this.onDeviceAction(action); |
115 | 115 | 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 |
208 | 208 | this.config.entitiesFetchFunction = pageLink => |
209 | 209 | this.deviceService.getCustomerDeviceInfosByDeviceProfileId(this.customerId, pageLink, |
210 | 210 | this.config.componentsData.deviceProfileId !== null ? |
211 | - this.config.componentsData.deviceProfileId.id : ''); | |
211 | + this.config.componentsData.deviceProfileId.id : ''); | |
212 | 212 | this.config.deleteEntity = id => this.deviceService.unassignDeviceFromCustomer(id.id); |
213 | 213 | } |
214 | 214 | } |
... | ... | @@ -250,26 +250,26 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev |
250 | 250 | ); |
251 | 251 | } |
252 | 252 | if (deviceScope === 'customer') { |
253 | - actions.push( | |
254 | - { | |
255 | - name: this.translate.instant('device.unassign-from-customer'), | |
256 | - icon: 'assignment_return', | |
257 | - isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && !entity.customerIsPublic), | |
258 | - onAction: ($event, entity) => this.unassignFromCustomer($event, entity) | |
259 | - }, | |
260 | - { | |
261 | - name: this.translate.instant('device.make-private'), | |
262 | - icon: 'reply', | |
263 | - isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && entity.customerIsPublic), | |
264 | - onAction: ($event, entity) => this.unassignFromCustomer($event, entity) | |
265 | - }, | |
266 | - { | |
267 | - name: this.translate.instant('device.manage-credentials'), | |
268 | - icon: 'security', | |
269 | - isEnabled: () => true, | |
270 | - onAction: ($event, entity) => this.manageCredentials($event, entity) | |
271 | - } | |
272 | - ); | |
253 | + actions.push( | |
254 | + { | |
255 | + name: this.translate.instant('device.unassign-from-customer'), | |
256 | + icon: 'assignment_return', | |
257 | + isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && !entity.customerIsPublic), | |
258 | + onAction: ($event, entity) => this.unassignFromCustomer($event, entity) | |
259 | + }, | |
260 | + { | |
261 | + name: this.translate.instant('device.make-private'), | |
262 | + icon: 'reply', | |
263 | + isEnabled: (entity) => (entity.customerId && entity.customerId.id !== NULL_UUID && entity.customerIsPublic), | |
264 | + onAction: ($event, entity) => this.unassignFromCustomer($event, entity) | |
265 | + }, | |
266 | + { | |
267 | + name: this.translate.instant('device.manage-credentials'), | |
268 | + icon: 'security', | |
269 | + isEnabled: () => true, | |
270 | + onAction: ($event, entity) => this.manageCredentials($event, entity) | |
271 | + } | |
272 | + ); | |
273 | 273 | } |
274 | 274 | if (deviceScope === 'customer_user' || deviceScope === 'edge_customer_user') { |
275 | 275 | actions.push( |
... | ... | @@ -452,10 +452,10 @@ export class DevicesTableConfigResolver implements Resolve<EntityTableConfig<Dev |
452 | 452 | } |
453 | 453 | }).afterClosed() |
454 | 454 | .subscribe((res) => { |
455 | - if (res) { | |
456 | - this.config.table.updateData(); | |
457 | - } | |
458 | - }); | |
455 | + if (res) { | |
456 | + this.config.table.updateData(); | |
457 | + } | |
458 | + }); | |
459 | 459 | } |
460 | 460 | |
461 | 461 | unassignFromCustomer($event: Event, device: DeviceInfo) { | ... | ... |
... | ... | @@ -33,8 +33,11 @@ import { RuleChainType } from '@shared/models/rule-chain.models'; |
33 | 33 | import { |
34 | 34 | importRuleChainBreadcumbLabelFunction, |
35 | 35 | ResolvedRuleChainMetaDataResolver, |
36 | - ruleChainBreadcumbLabelFunction, RuleChainImportGuard, | |
37 | - RuleChainResolver, RuleNodeComponentsResolver, TooltipsterResolver | |
36 | + ruleChainBreadcumbLabelFunction, | |
37 | + RuleChainImportGuard, | |
38 | + RuleChainResolver, | |
39 | + RuleNodeComponentsResolver, | |
40 | + TooltipsterResolver | |
38 | 41 | } from '@home/pages/rulechain/rulechain-routing.module'; |
39 | 42 | |
40 | 43 | const routes: Routes = [ |
... | ... | @@ -249,7 +252,7 @@ const routes: Routes = [ |
249 | 252 | } |
250 | 253 | ] |
251 | 254 | } |
252 | - ] | |
255 | + ] | |
253 | 256 | }]; |
254 | 257 | |
255 | 258 | @NgModule({ |
... | ... | @@ -259,4 +262,5 @@ const routes: Routes = [ |
259 | 262 | EdgesTableConfigResolver |
260 | 263 | ] |
261 | 264 | }) |
262 | -export class EdgeRoutingModule { } | |
265 | +export class EdgeRoutingModule { | |
266 | +} | ... | ... |
... | ... | @@ -89,7 +89,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig |
89 | 89 | this.config.addDialogStyle = {maxWidth: '800px'}; |
90 | 90 | |
91 | 91 | this.config.deleteEntityTitle = entityView => |
92 | - this.translate.instant('entity-view.delete-entity-view-title', { entityViewName: entityView.name }); | |
92 | + this.translate.instant('entity-view.delete-entity-view-title', {entityViewName: entityView.name}); | |
93 | 93 | this.config.deleteEntityContent = () => this.translate.instant('entity-view.delete-entity-view-text'); |
94 | 94 | this.config.deleteEntitiesTitle = count => this.translate.instant('entity-view.delete-entity-views-title', {count}); |
95 | 95 | this.config.deleteEntitiesContent = () => this.translate.instant('entity-view.delete-entity-views-text'); |
... | ... | @@ -143,8 +143,7 @@ export class EntityViewsTableConfigResolver implements Resolve<EntityTableConfig |
143 | 143 | this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe( |
144 | 144 | edge => this.config.tableTitle = edge.name + ': ' + this.translate.instant('entity-view.entity-views') |
145 | 145 | ); |
146 | - } | |
147 | - else { | |
146 | + } else { | |
148 | 147 | this.config.tableTitle = this.translate.instant('entity-view.entity-views'); |
149 | 148 | } |
150 | 149 | this.config.columns = this.configureColumns(this.config.componentsData.entityViewScope); | ... | ... |
... | ... | @@ -90,8 +90,10 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme |
90 | 90 | buildForm(entity: Resource): FormGroup { |
91 | 91 | return this.fb.group( |
92 | 92 | { |
93 | - resourceType: [{value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, | |
94 | - disabled: this.isEdit }, [Validators.required]], | |
93 | + resourceType: [{ | |
94 | + value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, | |
95 | + disabled: this.isEdit | |
96 | + }, [Validators.required]], | |
95 | 97 | data: [entity ? entity.data : null, [Validators.required]], |
96 | 98 | fileName: [entity ? entity.fileName : null, [Validators.required]], |
97 | 99 | title: [entity ? entity.title : '', []] | ... | ... |
... | ... | @@ -73,7 +73,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
73 | 73 | this.config.entityResources = entityTypeResources.get(EntityType.RULE_CHAIN); |
74 | 74 | |
75 | 75 | this.config.deleteEntityTitle = ruleChain => this.translate.instant('rulechain.delete-rulechain-title', |
76 | - { ruleChainName: ruleChain.name }); | |
76 | + {ruleChainName: ruleChain.name}); | |
77 | 77 | this.config.deleteEntityContent = () => this.translate.instant('rulechain.delete-rulechain-text'); |
78 | 78 | this.config.deleteEntitiesTitle = count => this.translate.instant('rulechain.delete-rulechains-title', {count}); |
79 | 79 | this.config.deleteEntitiesContent = () => this.translate.instant('rulechain.delete-rulechains-text'); |
... | ... | @@ -121,11 +121,11 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
121 | 121 | columns.push( |
122 | 122 | new EntityTableColumn<RuleChain>('root', 'rulechain.root', '60px', |
123 | 123 | entity => { |
124 | - if (ruleChainScope === 'edge') { | |
125 | - return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id === entity.id.id)); | |
126 | - } else { | |
127 | - return checkBoxCell(entity.root); | |
128 | - } | |
124 | + if (ruleChainScope === 'edge') { | |
125 | + return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id === entity.id.id)); | |
126 | + } else { | |
127 | + return checkBoxCell(entity.root); | |
128 | + } | |
129 | 129 | }) |
130 | 130 | ); |
131 | 131 | } else if (ruleChainScope === 'edges') { |
... | ... | @@ -421,32 +421,32 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
421 | 421 | } |
422 | 422 | }).afterClosed() |
423 | 423 | .subscribe((res) => { |
424 | - if (res) { | |
425 | - this.edgeService.findMissingToRelatedRuleChains(this.config.componentsData.edgeId).subscribe( | |
426 | - (missingRuleChains) => { | |
427 | - if (missingRuleChains && Object.keys(missingRuleChains).length > 0) { | |
428 | - const formattedMissingRuleChains: Array<string> = new Array<string>(); | |
429 | - for (const missingRuleChain of Object.keys(missingRuleChains)) { | |
430 | - const arrayOfMissingRuleChains = missingRuleChains[missingRuleChain]; | |
431 | - const tmp = '- \'' + missingRuleChain + '\': \'' + arrayOfMissingRuleChains.join('\', ') + '\''; | |
432 | - formattedMissingRuleChains.push(tmp); | |
433 | - } | |
434 | - const message = this.translate.instant('edge.missing-related-rule-chains-text', | |
435 | - {missingRuleChains: formattedMissingRuleChains.join('<br>')}); | |
436 | - this.dialogService.alert(this.translate.instant('edge.missing-related-rule-chains-title'), | |
437 | - message, this.translate.instant('action.close'), true).subscribe( | |
438 | - () => { | |
439 | - this.config.table.updateData(); | |
424 | + if (res) { | |
425 | + this.edgeService.findMissingToRelatedRuleChains(this.config.componentsData.edgeId).subscribe( | |
426 | + (missingRuleChains) => { | |
427 | + if (missingRuleChains && Object.keys(missingRuleChains).length > 0) { | |
428 | + const formattedMissingRuleChains: Array<string> = new Array<string>(); | |
429 | + for (const missingRuleChain of Object.keys(missingRuleChains)) { | |
430 | + const arrayOfMissingRuleChains = missingRuleChains[missingRuleChain]; | |
431 | + const tmp = '- \'' + missingRuleChain + '\': \'' + arrayOfMissingRuleChains.join('\', ') + '\''; | |
432 | + formattedMissingRuleChains.push(tmp); | |
440 | 433 | } |
441 | - ); | |
442 | - } else { | |
443 | - this.config.table.updateData(); | |
434 | + const message = this.translate.instant('edge.missing-related-rule-chains-text', | |
435 | + {missingRuleChains: formattedMissingRuleChains.join('<br>')}); | |
436 | + this.dialogService.alert(this.translate.instant('edge.missing-related-rule-chains-title'), | |
437 | + message, this.translate.instant('action.close'), true).subscribe( | |
438 | + () => { | |
439 | + this.config.table.updateData(); | |
440 | + } | |
441 | + ); | |
442 | + } else { | |
443 | + this.config.table.updateData(); | |
444 | + } | |
444 | 445 | } |
445 | - } | |
446 | - ); | |
446 | + ); | |
447 | + } | |
447 | 448 | } |
448 | - } | |
449 | - ); | |
449 | + ); | |
450 | 450 | } |
451 | 451 | |
452 | 452 | unassignFromEdge($event: Event, ruleChain: RuleChain) { |
... | ... | @@ -510,13 +510,13 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
510 | 510 | this.translate.instant('action.yes'), |
511 | 511 | true |
512 | 512 | ).subscribe((res) => { |
513 | - if (res) { | |
514 | - this.ruleChainService.setAutoAssignToEdgeRuleChain(ruleChain.id.id).subscribe( | |
515 | - () => { | |
516 | - this.config.table.updateData(); | |
517 | - } | |
518 | - ); | |
519 | - } | |
513 | + if (res) { | |
514 | + this.ruleChainService.setAutoAssignToEdgeRuleChain(ruleChain.id.id).subscribe( | |
515 | + () => { | |
516 | + this.config.table.updateData(); | |
517 | + } | |
518 | + ); | |
519 | + } | |
520 | 520 | } |
521 | 521 | ); |
522 | 522 | } | ... | ... |
... | ... | @@ -73,9 +73,11 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, |
73 | 73 | contentConvertFunction: (content: string) => any; |
74 | 74 | |
75 | 75 | private requiredValue: boolean; |
76 | + | |
76 | 77 | get required(): boolean { |
77 | 78 | return this.requiredValue; |
78 | 79 | } |
80 | + | |
79 | 81 | @Input() |
80 | 82 | set required(value: boolean) { |
81 | 83 | const newVal = coerceBooleanProperty(value); |
... | ... | @@ -85,9 +87,11 @@ export class FileInputComponent extends PageComponent implements AfterViewInit, |
85 | 87 | } |
86 | 88 | |
87 | 89 | private requiredAsErrorValue: boolean; |
90 | + | |
88 | 91 | get requiredAsError(): boolean { |
89 | 92 | return this.requiredAsErrorValue; |
90 | 93 | } |
94 | + | |
91 | 95 | @Input() |
92 | 96 | set requiredAsError(value: boolean) { |
93 | 97 | const newVal = coerceBooleanProperty(value); | ... | ... |
... | ... | @@ -22,7 +22,7 @@ import { ActionNotificationHide, ActionNotificationShow } from '@core/notificati |
22 | 22 | import { Store } from '@ngrx/store'; |
23 | 23 | import { AppState } from '@core/core.state'; |
24 | 24 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
25 | -import { guid, isUndefined, isDefinedAndNotNull, isLiteralObject } from '@core/utils'; | |
25 | +import { guid, isDefinedAndNotNull, isLiteralObject, isUndefined } from '@core/utils'; | |
26 | 26 | import { ResizeObserver } from '@juggle/resize-observer'; |
27 | 27 | import { getAce } from '@shared/models/ace/ace.models'; |
28 | 28 | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import { TenantId } from '@shared/models/id/tenant-id'; |
19 | 19 | import { EntityId } from '@shared/models/id/entity-id'; |
20 | 20 | import { EventId } from './id/event-id'; |
21 | 21 | import { ContentType } from '@shared/models/constants'; |
22 | +import { EntityType } from '@shared/models/entity-type.models'; | |
22 | 23 | |
23 | 24 | export enum EventType { |
24 | 25 | ERROR = 'ERROR', |
... | ... | @@ -83,3 +84,36 @@ export interface Event extends BaseData<EventId> { |
83 | 84 | uid: string; |
84 | 85 | body: EventBody; |
85 | 86 | } |
87 | + | |
88 | +export interface BaseFilterEventBody { | |
89 | + server?: string; | |
90 | +} | |
91 | + | |
92 | +export interface ErrorFilterEventBody extends BaseFilterEventBody { | |
93 | + method?: string; | |
94 | +} | |
95 | + | |
96 | +export interface LcFilterEventEventBody extends BaseFilterEventBody { | |
97 | + method?: string; | |
98 | + status?: string; | |
99 | + isError?: boolean; | |
100 | +} | |
101 | + | |
102 | +export interface StatsFilterEventBody extends BaseFilterEventBody { | |
103 | + messagesProcessed?: string; | |
104 | + errorsOccurred?: string; | |
105 | +} | |
106 | + | |
107 | +export interface DebugFilterRuleNodeEventBody extends BaseFilterEventBody { | |
108 | + msgDirectionType?: string; | |
109 | + entityId?: string; | |
110 | + entityName?: EntityType; | |
111 | + msgId?: string; | |
112 | + msgType?: string; | |
113 | + relationType?: string; | |
114 | + dataSearch?: string; | |
115 | + metadataSearch?: string; | |
116 | + isError?: boolean; | |
117 | +} | |
118 | + | |
119 | +export type FilterEventBody = ErrorFilterEventBody & LcFilterEventEventBody & StatsFilterEventBody & DebugFilterRuleNodeEventBody; | ... | ... |
... | ... | @@ -45,7 +45,7 @@ export const ResourceTypeTranslationMap = new Map<ResourceType, string>( |
45 | 45 | [ResourceType.LWM2M_MODEL, 'LWM2M model'], |
46 | 46 | [ResourceType.PKCS_12, 'PKCS #12'], |
47 | 47 | [ResourceType.JKS, 'JKS'] |
48 | - ] | |
48 | + ] | |
49 | 49 | ); |
50 | 50 | |
51 | 51 | export interface ResourceInfo extends BaseData<TbResourceId> { | ... | ... |
... | ... | @@ -1332,7 +1332,6 @@ |
1332 | 1332 | "body": "Tělo", |
1333 | 1333 | "method": "Metoda", |
1334 | 1334 | "type": "Typ", |
1335 | - "entity": "Entita", | |
1336 | 1335 | "message-id": "Id zprávy", |
1337 | 1336 | "message-type": "Typ zprávy", |
1338 | 1337 | "data-type": "Typ dat", |
... | ... | @@ -1344,7 +1343,9 @@ |
1344 | 1343 | "success": "Úspěch", |
1345 | 1344 | "failed": "Neúspěch", |
1346 | 1345 | "messages-processed": "Zpracované zprávy", |
1347 | - "errors-occurred": "Vyskytly se chyby" | |
1346 | + "errors-occurred": "Vyskytly se chyby", | |
1347 | + "all-events": "Vše", | |
1348 | + "entity-type": "Typ entity" | |
1348 | 1349 | }, |
1349 | 1350 | "extension": { |
1350 | 1351 | "extensions": "Rozšíření", | ... | ... |
... | ... | @@ -1094,7 +1094,6 @@ |
1094 | 1094 | "body": "Inhalt", |
1095 | 1095 | "method": "Methode", |
1096 | 1096 | "type": "Typ", |
1097 | - "entity": "Entität", | |
1098 | 1097 | "message-id": "Nachrichten-Id", |
1099 | 1098 | "message-type": "Nachrichten-Typ", |
1100 | 1099 | "data-type": "Datentyp", |
... | ... | @@ -1106,7 +1105,9 @@ |
1106 | 1105 | "success": "Erfolg", |
1107 | 1106 | "failed": "Fehlgeschlagen", |
1108 | 1107 | "messages-processed": "Nachrichten verarbeitet", |
1109 | - "errors-occurred": "Fehler aufgetreten" | |
1108 | + "errors-occurred": "Fehler aufgetreten", | |
1109 | + "all-events": "Alle", | |
1110 | + "entity-type": "Entitätstyp" | |
1110 | 1111 | }, |
1111 | 1112 | "extension": { |
1112 | 1113 | "extensions": "Erweiterungen", | ... | ... |
... | ... | @@ -1247,7 +1247,6 @@ |
1247 | 1247 | "out": "Έξοδος", |
1248 | 1248 | "metadata": "Μεταδεδομένα", |
1249 | 1249 | "message": "Μήνυμα", |
1250 | - "entity": "Οντότητα", | |
1251 | 1250 | "message-id": "ID Μηνύματος", |
1252 | 1251 | "message-type": "Τύπος Μηνύματος", |
1253 | 1252 | "data-type": "Τύπος Δεδομένων", |
... | ... | @@ -1258,7 +1257,9 @@ |
1258 | 1257 | "success": "Επιτυχία", |
1259 | 1258 | "failed": "Απέτυχε", |
1260 | 1259 | "messages-processed": "Επεξεργασμένα μηνύματα", |
1261 | - "errors-occurred": "Παρουσιάστηκαν σφάλματα" | |
1260 | + "errors-occurred": "Παρουσιάστηκαν σφάλματα", | |
1261 | + "all-events": "Όλοι", | |
1262 | + "entity-type": "Τύπος οντοτήτων" | |
1262 | 1263 | }, |
1263 | 1264 | "extension": { |
1264 | 1265 | "extensions": "Επεκτάσεις", | ... | ... |
... | ... | @@ -1616,6 +1616,7 @@ |
1616 | 1616 | }, |
1617 | 1617 | "event": { |
1618 | 1618 | "event-type": "Event type", |
1619 | + "events-filter": "Events Filter", | |
1619 | 1620 | "type-error": "Error", |
1620 | 1621 | "type-lc-event": "Lifecycle event", |
1621 | 1622 | "type-stats": "Statistics", |
... | ... | @@ -1629,7 +1630,6 @@ |
1629 | 1630 | "body": "Body", |
1630 | 1631 | "method": "Method", |
1631 | 1632 | "type": "Type", |
1632 | - "entity": "Entity", | |
1633 | 1633 | "message-id": "Message Id", |
1634 | 1634 | "message-type": "Message Type", |
1635 | 1635 | "data-type": "Data Type", |
... | ... | @@ -1641,7 +1641,11 @@ |
1641 | 1641 | "success": "Success", |
1642 | 1642 | "failed": "Failed", |
1643 | 1643 | "messages-processed": "Messages processed", |
1644 | - "errors-occurred": "Errors occurred" | |
1644 | + "errors-occurred": "Errors occurred", | |
1645 | + "all-events": "All", | |
1646 | + "has-error": "Has error", | |
1647 | + "entity-id": "Entity Id", | |
1648 | + "entity-type": "Entity type" | |
1645 | 1649 | }, |
1646 | 1650 | "extension": { |
1647 | 1651 | "extensions": "Extensions", | ... | ... |
... | ... | @@ -1521,7 +1521,6 @@ |
1521 | 1521 | "body": "Cuerpo", |
1522 | 1522 | "method": "Método", |
1523 | 1523 | "type": "Tipo", |
1524 | - "entity": "Entidad", | |
1525 | 1524 | "message-id": "Id Mensaje", |
1526 | 1525 | "message-type": "Tipo Mensaje", |
1527 | 1526 | "data-type": "Tipo de Datos", |
... | ... | @@ -1533,7 +1532,9 @@ |
1533 | 1532 | "success": "Éxito", |
1534 | 1533 | "failed": "Fallo", |
1535 | 1534 | "messages-processed": "Mensajes procesados", |
1536 | - "errors-occurred": "Ocurrieron errores" | |
1535 | + "errors-occurred": "Ocurrieron errores", | |
1536 | + "all-events": "Todos", | |
1537 | + "entity-type": "Tipo de entidad" | |
1537 | 1538 | }, |
1538 | 1539 | "extension": { |
1539 | 1540 | "extensions": "Extensiones", | ... | ... |
... | ... | @@ -881,7 +881,6 @@ |
881 | 881 | "body": "بدنه", |
882 | 882 | "method": "روش", |
883 | 883 | "type": "نوع", |
884 | - "entity": "موجودي", | |
885 | 884 | "message-id": "پيام ID", |
886 | 885 | "message-type": "نوع پيام", |
887 | 886 | "data-type": "نوع داده", |
... | ... | @@ -893,7 +892,9 @@ |
893 | 892 | "success": "موفقيت", |
894 | 893 | "failed": "عدم موفقيت", |
895 | 894 | "messages-processed": "پيام پردازش شد", |
896 | - "errors-occurred": "خطاها رخ دادند" | |
895 | + "errors-occurred": "خطاها رخ دادند", | |
896 | + "all-events": "همه", | |
897 | + "entity-type": "نوع موجودي" | |
897 | 898 | }, |
898 | 899 | "extension": { |
899 | 900 | "extensions": "دنباله ها", | ... | ... |
... | ... | @@ -1115,7 +1115,6 @@ |
1115 | 1115 | "body": "Corps", |
1116 | 1116 | "data": "Données", |
1117 | 1117 | "data-type": "Type de données", |
1118 | - "entity": "Entité", | |
1119 | 1118 | "error": "erreur", |
1120 | 1119 | "type-edge-event": "Downlink", |
1121 | 1120 | "errors-occurred": "Des erreurs sont survenues", |
... | ... | @@ -1138,7 +1137,9 @@ |
1138 | 1137 | "type-debug-rule-node": "Debug", |
1139 | 1138 | "type-error": "Erreur", |
1140 | 1139 | "type-lc-event": "Evénement du cycle de vie", |
1141 | - "type-stats": "Statistiques" | |
1140 | + "type-stats": "Statistiques", | |
1141 | + "all-events": "Tout", | |
1142 | + "entity-type": "Type d'entité" | |
1142 | 1143 | }, |
1143 | 1144 | "extension": { |
1144 | 1145 | "add": "Ajouter une extension", | ... | ... |
... | ... | @@ -915,7 +915,6 @@ |
915 | 915 | "body": "Body", |
916 | 916 | "method": "Metodo", |
917 | 917 | "type": "Tipo", |
918 | - "entity": "Entità", | |
919 | 918 | "message-id": "Id Messaggio", |
920 | 919 | "message-type": "Tipo Messaggio", |
921 | 920 | "data-type": "Tipo di dato", |
... | ... | @@ -927,7 +926,9 @@ |
927 | 926 | "success": "Success", |
928 | 927 | "failed": "Failed", |
929 | 928 | "messages-processed": "Messaggi elaborati", |
930 | - "errors-occurred": "Si sono verificati degli errori" | |
929 | + "errors-occurred": "Si sono verificati degli errori", | |
930 | + "all-events": "Tutte", | |
931 | + "entity-type": "Tipo entità" | |
931 | 932 | }, |
932 | 933 | "extension": { |
933 | 934 | "extensions": "Estensioni", | ... | ... |
... | ... | @@ -768,7 +768,6 @@ |
768 | 768 | "body": "体", |
769 | 769 | "method": "方法", |
770 | 770 | "type": "タイプ", |
771 | - "entity": "エンティティ", | |
772 | 771 | "message-id": "メッセージID", |
773 | 772 | "message-type": "メッセージタイプ", |
774 | 773 | "data-type": "データ・タイプ", |
... | ... | @@ -780,7 +779,9 @@ |
780 | 779 | "success": "成功", |
781 | 780 | "failed": "失敗", |
782 | 781 | "messages-processed": "処理されたメッセージ", |
783 | - "errors-occurred": "エラーが発生しました" | |
782 | + "errors-occurred": "エラーが発生しました", | |
783 | + "all-events": "すべて", | |
784 | + "entity-type": "エンティティタイプ" | |
784 | 785 | }, |
785 | 786 | "extension": { |
786 | 787 | "extensions": "拡張機能", | ... | ... |
... | ... | @@ -959,7 +959,6 @@ |
959 | 959 | "body": "სხეული", |
960 | 960 | "method": "მეთოდი", |
961 | 961 | "type": "ტიპი", |
962 | - "entity": "ობიექტი", | |
963 | 962 | "message-id": "მესიჯის-ID", |
964 | 963 | "message-type": "მესიჯის ტიპი", |
965 | 964 | "data-type": "მონაცემთა ტიპი", |
... | ... | @@ -971,7 +970,9 @@ |
971 | 970 | "success": "წარმატება", |
972 | 971 | "failed": "ვერ მოხერხდა", |
973 | 972 | "messages-processed": "შეტყობინებების დამუშავება", |
974 | - "errors-occurred": "შეცდომები მოხდა" | |
973 | + "errors-occurred": "შეცდომები მოხდა", | |
974 | + "all-events": "ყველა", | |
975 | + "entity-type": "ობიექტის ტიპი" | |
975 | 976 | }, |
976 | 977 | "extension": { |
977 | 978 | "extensions": "დამატებითი აპი", | ... | ... |
... | ... | @@ -1329,7 +1329,6 @@ |
1329 | 1329 | "body": "Body", |
1330 | 1330 | "method": "방법", |
1331 | 1331 | "type": "유형", |
1332 | - "entity": "개체", | |
1333 | 1332 | "message-id": "메시지 ID", |
1334 | 1333 | "message-type": "메시지 유형", |
1335 | 1334 | "data-type": "데이터 유형", |
... | ... | @@ -1341,7 +1340,9 @@ |
1341 | 1340 | "success": "성공", |
1342 | 1341 | "failed": "실패", |
1343 | 1342 | "messages-processed": "처리된 메시지", |
1344 | - "errors-occurred": "오류가 발생했습니다" | |
1343 | + "errors-occurred": "오류가 발생했습니다", | |
1344 | + "all-events": "모두", | |
1345 | + "entity-type": "개체 유형" | |
1345 | 1346 | }, |
1346 | 1347 | "extension": { |
1347 | 1348 | "extensions": "확장", | ... | ... |
... | ... | @@ -894,7 +894,6 @@ |
894 | 894 | "body": "Galvenā daļa", |
895 | 895 | "method": "Metode", |
896 | 896 | "type": "Tips", |
897 | - "entity": "Vienība", | |
898 | 897 | "message-id": "Ziņojuma Id", |
899 | 898 | "message-type": "Ziņojuma tips", |
900 | 899 | "data-type": "Datu tips", |
... | ... | @@ -906,7 +905,9 @@ |
906 | 905 | "success": "Sekmīgi", |
907 | 906 | "failed": "Kļūda", |
908 | 907 | "messages-processed": "Ziņojumi apstrādāti", |
909 | - "errors-occurred": "Kļūdas konstatētas" | |
908 | + "errors-occurred": "Kļūdas konstatētas", | |
909 | + "all-events": "Visi", | |
910 | + "entity-type": "Vienības tips" | |
910 | 911 | }, |
911 | 912 | "extension": { |
912 | 913 | "extensions": "Paplašinājumi", | ... | ... |
... | ... | @@ -997,7 +997,6 @@ |
997 | 997 | "body": "Corpo", |
998 | 998 | "method": "Método", |
999 | 999 | "type": "Tipo", |
1000 | - "entity": "Entidade", | |
1001 | 1000 | "message-id": "ID de mensagem", |
1002 | 1001 | "message-type": "Tipo de Mensagem", |
1003 | 1002 | "data-type": "Tipo de Dados", |
... | ... | @@ -1009,7 +1008,9 @@ |
1009 | 1008 | "success": "Êxito", |
1010 | 1009 | "failed": "Falhou", |
1011 | 1010 | "messages-processed": "Mensagens processadas", |
1012 | - "errors-occurred": "Erros" | |
1011 | + "errors-occurred": "Erros", | |
1012 | + "all-events": "Tudo", | |
1013 | + "entity-type": "Tipo de entidade" | |
1013 | 1014 | }, |
1014 | 1015 | "extension": { |
1015 | 1016 | "extensions": "Extensões", | ... | ... |
... | ... | @@ -945,7 +945,6 @@ |
945 | 945 | "body": "Corp", |
946 | 946 | "method": "Metodă", |
947 | 947 | "type": "Tip", |
948 | - "entity": "Entitate", | |
949 | 948 | "message-id": "ID Mesaj", |
950 | 949 | "message-type": "Tip Mesaj", |
951 | 950 | "data-type": "Tip Date", |
... | ... | @@ -957,7 +956,9 @@ |
957 | 956 | "success": "Succes", |
958 | 957 | "failed": "Eşuat", |
959 | 958 | "messages-processed": "Mesaje procesate", |
960 | - "errors-occurred": "Au apărut erori" | |
959 | + "errors-occurred": "Au apărut erori", | |
960 | + "all-events": "Toate", | |
961 | + "entity-type": "Tip Entitate" | |
961 | 962 | }, |
962 | 963 | "extension": { |
963 | 964 | "extensions": "Extensii", |
... | ... | @@ -1174,7 +1175,7 @@ |
1174 | 1175 | "entity-field": "Câmp Entitate", |
1175 | 1176 | "access-token": "Token De Acces" |
1176 | 1177 | }, |
1177 | - "stepper-text":{ | |
1178 | + "stepper-text": { | |
1178 | 1179 | "select-file": "Selectează un fişier", |
1179 | 1180 | "configuration": "Importă configurație", |
1180 | 1181 | "column-type": "Selectează tipul de coloane", |
... | ... | @@ -1796,4 +1797,4 @@ |
1796 | 1797 | "language": { |
1797 | 1798 | "language": "Limba" |
1798 | 1799 | } |
1799 | -} | |
1800 | +} | |
\ No newline at end of file | ... | ... |
... | ... | @@ -948,7 +948,6 @@ |
948 | 948 | "body": "Тело", |
949 | 949 | "method": "Метод", |
950 | 950 | "type": "Тип", |
951 | - "entity": "Объект", | |
952 | 951 | "message-id": "ИД сообщения", |
953 | 952 | "message-type": "Тип сообщения", |
954 | 953 | "data-type": "Тип данных", |
... | ... | @@ -960,7 +959,9 @@ |
960 | 959 | "success": "Успех", |
961 | 960 | "failed": "Неудача", |
962 | 961 | "messages-processed": "Сообщения обработаны", |
963 | - "errors-occurred": "Возникли ошибки" | |
962 | + "errors-occurred": "Возникли ошибки", | |
963 | + "all-events": "Все", | |
964 | + "entity-type": "Тип объекта" | |
964 | 965 | }, |
965 | 966 | "extension": { |
966 | 967 | "extensions": "Расширение", | ... | ... |
... | ... | @@ -1329,7 +1329,6 @@ |
1329 | 1329 | "body": "Vsebina", |
1330 | 1330 | "method": "Metoda", |
1331 | 1331 | "type": "Vrsta", |
1332 | - "entity": "Entiteta", | |
1333 | 1332 | "message-id": "ID sporočila", |
1334 | 1333 | "message-type": "Vrsta sporočila", |
1335 | 1334 | "data-type": "Vrsta podatkov", |
... | ... | @@ -1341,7 +1340,9 @@ |
1341 | 1340 | "success": "Uspeh", |
1342 | 1341 | "failed": "Ni uspelo", |
1343 | 1342 | "messages-processed": "Obdelana sporočila", |
1344 | - "errors-occurred": "Prišlo je do napak" | |
1343 | + "errors-occurred": "Prišlo je do napak", | |
1344 | + "all-events": "Vse", | |
1345 | + "entity-type": "Vrsta entitete" | |
1345 | 1346 | }, |
1346 | 1347 | "extension": { |
1347 | 1348 | "extensions": "Razširitve", | ... | ... |
... | ... | @@ -915,7 +915,6 @@ |
915 | 915 | "body": "İçerik //(Body)", |
916 | 916 | "method": "Yöntem", |
917 | 917 | "type": "Tür", |
918 | - "entity": "Varlık", | |
919 | 918 | "message-id": "Mesaj Kimliği", |
920 | 919 | "message-type": "Mesaj tipi", |
921 | 920 | "data-type": "Veri tipi", |
... | ... | @@ -927,7 +926,9 @@ |
927 | 926 | "success": "Başarı", |
928 | 927 | "failed": "Başarısız oldu", |
929 | 928 | "messages-processed": "Mesajlar işlendi", |
930 | - "errors-occurred": "Hatalar oluştu" | |
929 | + "errors-occurred": "Hatalar oluştu", | |
930 | + "all-events": "Tümü", | |
931 | + "entity-type": "Öğe türü" | |
931 | 932 | }, |
932 | 933 | "extension": { |
933 | 934 | "extensions": "Uzantılar", | ... | ... |
... | ... | @@ -1201,7 +1201,6 @@ |
1201 | 1201 | "out": "Out", |
1202 | 1202 | "metadata": "Метадані", |
1203 | 1203 | "message": "Повідомлення", |
1204 | - "entity": "Сутність", | |
1205 | 1204 | "message-id": "Id повідомлення", |
1206 | 1205 | "message-type": "Тип повідомлення", |
1207 | 1206 | "data-type": "Тип даних", |
... | ... | @@ -1212,7 +1211,9 @@ |
1212 | 1211 | "success": "Успіх", |
1213 | 1212 | "failed": "Невдача", |
1214 | 1213 | "messages-processed": "Повідомлення опрацьовані", |
1215 | - "errors-occurred": "Виникли помилки" | |
1214 | + "errors-occurred": "Виникли помилки", | |
1215 | + "all-events": "Всі", | |
1216 | + "entity-type": "Тип сутності" | |
1216 | 1217 | }, |
1217 | 1218 | "extension": { |
1218 | 1219 | "extensions": "Розширення", | ... | ... |
... | ... | @@ -1334,7 +1334,6 @@ |
1334 | 1334 | "body": "整体", |
1335 | 1335 | "data": "数据", |
1336 | 1336 | "data-type": "数据类型", |
1337 | - "entity": "实体", | |
1338 | 1337 | "error": "错误", |
1339 | 1338 | "errors-occurred": "错误发生", |
1340 | 1339 | "event": "事件", |
... | ... | @@ -1356,7 +1355,9 @@ |
1356 | 1355 | "type-debug-rule-node": "调试", |
1357 | 1356 | "type-error": "错误", |
1358 | 1357 | "type-lc-event": "生命周期事件", |
1359 | - "type-stats": "类型统计" | |
1358 | + "type-stats": "类型统计", | |
1359 | + "all-events": "全部", | |
1360 | + "entity-type": "实体类型" | |
1360 | 1361 | }, |
1361 | 1362 | "extension": { |
1362 | 1363 | "add": "添加扩展", | ... | ... |
... | ... | @@ -865,7 +865,6 @@ |
865 | 865 | "body": "整體", |
866 | 866 | "method": "方法", |
867 | 867 | "type": "類型", |
868 | - "entity": "實體", | |
869 | 868 | "message-id": "消息ID", |
870 | 869 | "message-type": "消息類型", |
871 | 870 | "data-type": "資料類型", |
... | ... | @@ -877,7 +876,9 @@ |
877 | 876 | "success": "成功", |
878 | 877 | "failed": "失敗", |
879 | 878 | "messages-processed": "消息處理", |
880 | - "errors-occurred": "錯誤發生" | |
879 | + "errors-occurred": "錯誤發生", | |
880 | + "all-events": "所有", | |
881 | + "entity-type": "實體類型" | |
881 | 882 | }, |
882 | 883 | "extension": { |
883 | 884 | "extensions": "擴展", | ... | ... |