Commit d7e65dae3909b14afc9e6dfdc81a1ced23573c1a

Authored by xp.Huang
2 parents 61f4b8fa 9e675fcc

Merge branch '20230710' into 'master_dev'

refactor: 命令下发逻辑调整

See merge request yunteng/thingskit!209
Showing 33 changed files with 1829 additions and 1422 deletions
@@ -72,6 +72,7 @@ import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -72,6 +72,7 @@ import org.thingsboard.server.dao.tenant.TenantProfileService;
72 import org.thingsboard.server.dao.tenant.TenantService; 72 import org.thingsboard.server.dao.tenant.TenantService;
73 import org.thingsboard.server.dao.timeseries.TimeseriesService; 73 import org.thingsboard.server.dao.timeseries.TimeseriesService;
74 import org.thingsboard.server.dao.user.UserService; 74 import org.thingsboard.server.dao.user.UserService;
  75 +import org.thingsboard.server.dao.yunteng.service.TkDeviceService;
75 import org.thingsboard.server.queue.discovery.PartitionService; 76 import org.thingsboard.server.queue.discovery.PartitionService;
76 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 77 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
77 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 78 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
@@ -645,4 +646,10 @@ public class ActorSystemContext { @@ -645,4 +646,10 @@ public class ActorSystemContext {
645 } 646 }
646 } 647 }
647 648
  649 +
  650 + //Thingskit function
  651 + @Autowired
  652 + @Getter
  653 + private TkDeviceService tkDeviceService;
  654 +
648 } 655 }
1 /** 1 /**
2 * Copyright © 2016-2022 The Thingsboard Authors 2 * Copyright © 2016-2022 The Thingsboard Authors
3 * 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 4 + * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  5 + * except in compliance with the License. You may obtain a copy of the License at
7 * 6 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 7 + * <p>http://www.apache.org/licenses/LICENSE-2.0
9 * 8 *
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 9 + * <p>Unless required by applicable law or agreed to in writing, software distributed under the
  10 + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  11 + * express or implied. See the License for the specific language governing permissions and
14 * limitations under the License. 12 * limitations under the License.
15 */ 13 */
16 package org.thingsboard.server.actors.device; 14 package org.thingsboard.server.actors.device;
@@ -22,6 +20,21 @@ import com.google.common.util.concurrent.Futures; @@ -22,6 +20,21 @@ import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.common.util.concurrent.MoreExecutors; 21 import com.google.common.util.concurrent.MoreExecutors;
24 import com.google.protobuf.InvalidProtocolBufferException; 22 import com.google.protobuf.InvalidProtocolBufferException;
  23 +import java.util.ArrayList;
  24 +import java.util.Arrays;
  25 +import java.util.Collections;
  26 +import java.util.HashMap;
  27 +import java.util.HashSet;
  28 +import java.util.LinkedHashMap;
  29 +import java.util.List;
  30 +import java.util.Map;
  31 +import java.util.Objects;
  32 +import java.util.Optional;
  33 +import java.util.Set;
  34 +import java.util.UUID;
  35 +import java.util.function.Consumer;
  36 +import java.util.stream.Collectors;
  37 +import javax.annotation.Nullable;
25 import lombok.extern.slf4j.Slf4j; 38 import lombok.extern.slf4j.Slf4j;
26 import org.apache.commons.collections.CollectionUtils; 39 import org.apache.commons.collections.CollectionUtils;
27 import org.thingsboard.common.util.JacksonUtil; 40 import org.thingsboard.common.util.JacksonUtil;
@@ -35,7 +48,6 @@ import org.thingsboard.server.actors.TbActorCtx; @@ -35,7 +48,6 @@ import org.thingsboard.server.actors.TbActorCtx;
35 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 48 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
36 import org.thingsboard.server.common.data.DataConstants; 49 import org.thingsboard.server.common.data.DataConstants;
37 import org.thingsboard.server.common.data.Device; 50 import org.thingsboard.server.common.data.Device;
38 -import org.thingsboard.server.common.data.DeviceTransportType;  
39 import org.thingsboard.server.common.data.StringUtils; 51 import org.thingsboard.server.common.data.StringUtils;
40 import org.thingsboard.server.common.data.edge.EdgeEvent; 52 import org.thingsboard.server.common.data.edge.EdgeEvent;
41 import org.thingsboard.server.common.data.edge.EdgeEventActionType; 53 import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@@ -59,8 +71,6 @@ import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; @@ -59,8 +71,6 @@ import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
59 import org.thingsboard.server.common.data.security.DeviceCredentials; 71 import org.thingsboard.server.common.data.security.DeviceCredentials;
60 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 72 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
61 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; 73 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
62 -import org.thingsboard.server.common.data.yunteng.constant.ModelConstants;  
63 -import org.thingsboard.server.common.data.yunteng.enums.CmdTypeEnum;  
64 import org.thingsboard.server.common.msg.TbActorMsg; 74 import org.thingsboard.server.common.msg.TbActorMsg;
65 import org.thingsboard.server.common.msg.TbMsgMetaData; 75 import org.thingsboard.server.common.msg.TbMsgMetaData;
66 import org.thingsboard.server.common.msg.queue.TbCallback; 76 import org.thingsboard.server.common.msg.queue.TbCallback;
@@ -97,921 +107,1109 @@ import org.thingsboard.server.service.rpc.RemoveRpcActorMsg; @@ -97,921 +107,1109 @@ import org.thingsboard.server.service.rpc.RemoveRpcActorMsg;
97 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 107 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
98 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 108 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
99 109
100 -import javax.annotation.Nullable;  
101 -import java.util.ArrayList;  
102 -import java.util.Arrays;  
103 -import java.util.Collections;  
104 -import java.util.ConcurrentModificationException;  
105 -import java.util.HashMap;  
106 -import java.util.HashSet;  
107 -import java.util.LinkedHashMap;  
108 -import java.util.List;  
109 -import java.util.Map;  
110 -import java.util.Objects;  
111 -import java.util.Optional;  
112 -import java.util.Set;  
113 -import java.util.UUID;  
114 -import java.util.function.Consumer;  
115 -import java.util.stream.Collectors;  
116 -  
117 -  
118 /** 110 /**
119 * @author Andrew Shvayka 111 * @author Andrew Shvayka
120 */ 112 */
121 @Slf4j 113 @Slf4j
122 class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { 114 class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
123 115
124 - static final String SESSION_TIMEOUT_MESSAGE = "session timeout!";  
125 - final TenantId tenantId;  
126 - final DeviceId deviceId;  
127 - final LinkedHashMapRemoveEldest<UUID, SessionInfoMetaData> sessions;  
128 - private final Map<UUID, SessionInfo> attributeSubscriptions;  
129 - private final Map<UUID, SessionInfo> rpcSubscriptions;  
130 - private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;  
131 - private final boolean rpcSequential;  
132 -  
133 - private int rpcSeq = 0;  
134 - private String deviceName;  
135 - private String deviceType;  
136 - private TbMsgMetaData defaultMetaData;  
137 - private EdgeId edgeId;  
138 -  
139 - DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {  
140 - super(systemContext);  
141 - this.tenantId = tenantId;  
142 - this.deviceId = deviceId;  
143 - this.rpcSequential = systemContext.isRpcSequential();  
144 - this.attributeSubscriptions = new HashMap<>();  
145 - this.rpcSubscriptions = new HashMap<>();  
146 - this.toDeviceRpcPendingMap = new LinkedHashMap<>();  
147 - this.sessions = new LinkedHashMapRemoveEldest<>(systemContext.getMaxConcurrentSessionsPerDevice(), this::notifyTransportAboutClosedSessionMaxSessionsLimit);  
148 - if (initAttributes()) {  
149 - restoreSessions();  
150 - } 116 + static final String SESSION_TIMEOUT_MESSAGE = "session timeout!";
  117 + final TenantId tenantId;
  118 + final DeviceId deviceId;
  119 + final LinkedHashMapRemoveEldest<UUID, SessionInfoMetaData> sessions;
  120 + private final Map<UUID, SessionInfo> attributeSubscriptions;
  121 + private final Map<UUID, SessionInfo> rpcSubscriptions;
  122 + private final Map<Integer, ToDeviceRpcRequestMetadata> toDeviceRpcPendingMap;
  123 + private final boolean rpcSequential;
  124 +
  125 + private int rpcSeq = 0;
  126 + private String deviceName;
  127 + private String deviceType;
  128 + private TbMsgMetaData defaultMetaData;
  129 + private EdgeId edgeId;
  130 +
  131 + DeviceActorMessageProcessor(
  132 + ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
  133 + super(systemContext);
  134 + this.tenantId = tenantId;
  135 + this.deviceId = deviceId;
  136 + this.rpcSequential = systemContext.isRpcSequential();
  137 + this.attributeSubscriptions = new HashMap<>();
  138 + this.rpcSubscriptions = new HashMap<>();
  139 + this.toDeviceRpcPendingMap = new LinkedHashMap<>();
  140 + this.sessions =
  141 + new LinkedHashMapRemoveEldest<>(
  142 + systemContext.getMaxConcurrentSessionsPerDevice(),
  143 + this::notifyTransportAboutClosedSessionMaxSessionsLimit);
  144 + if (initAttributes()) {
  145 + restoreSessions();
151 } 146 }
152 -  
153 - boolean initAttributes() {  
154 - Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId);  
155 - if (device != null) {  
156 - this.deviceName = device.getName();  
157 - this.deviceType = device.getType();  
158 - this.defaultMetaData = new TbMsgMetaData();  
159 - this.defaultMetaData.putValue("deviceName", deviceName);  
160 - this.defaultMetaData.putValue("deviceType", deviceType);  
161 - if (systemContext.isEdgesEnabled()) {  
162 - this.edgeId = findRelatedEdgeId();  
163 - }  
164 - return true;  
165 - } else {  
166 - return false;  
167 - } 147 + }
  148 +
  149 + boolean initAttributes() {
  150 + Device device = systemContext.getDeviceService().findDeviceById(tenantId, deviceId);
  151 + if (device != null) {
  152 + this.deviceName = device.getName();
  153 + this.deviceType = device.getType();
  154 + this.defaultMetaData = new TbMsgMetaData();
  155 + this.defaultMetaData.putValue("deviceName", deviceName);
  156 + this.defaultMetaData.putValue("deviceType", deviceType);
  157 + if (systemContext.isEdgesEnabled()) {
  158 + this.edgeId = findRelatedEdgeId();
  159 + }
  160 + return true;
  161 + } else {
  162 + return false;
168 } 163 }
169 -  
170 - private EdgeId findRelatedEdgeId() {  
171 - List<EntityRelation> result =  
172 - systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);  
173 - if (result != null && result.size() > 0) {  
174 - EntityRelation relationToEdge = result.get(0);  
175 - if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {  
176 - log.trace("[{}][{}] found edge [{}] for device", tenantId, deviceId, relationToEdge.getFrom().getId());  
177 - return new EdgeId(relationToEdge.getFrom().getId());  
178 - } else {  
179 - log.trace("[{}][{}] edge relation is empty {}", tenantId, deviceId, relationToEdge);  
180 - }  
181 - } else {  
182 - log.trace("[{}][{}] device doesn't have any related edge", tenantId, deviceId);  
183 - }  
184 - return null; 164 + }
  165 +
  166 + private EdgeId findRelatedEdgeId() {
  167 + List<EntityRelation> result =
  168 + systemContext
  169 + .getRelationService()
  170 + .findByToAndType(
  171 + tenantId, deviceId, EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);
  172 + if (result != null && result.size() > 0) {
  173 + EntityRelation relationToEdge = result.get(0);
  174 + if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {
  175 + log.trace(
  176 + "[{}][{}] found edge [{}] for device",
  177 + tenantId,
  178 + deviceId,
  179 + relationToEdge.getFrom().getId());
  180 + return new EdgeId(relationToEdge.getFrom().getId());
  181 + } else {
  182 + log.trace("[{}][{}] edge relation is empty {}", tenantId, deviceId, relationToEdge);
  183 + }
  184 + } else {
  185 + log.trace("[{}][{}] device doesn't have any related edge", tenantId, deviceId);
  186 + }
  187 + return null;
  188 + }
  189 +
  190 + void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
  191 + ToDeviceRpcRequest request = msg.getMsg();
  192 + ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request);
  193 +
  194 + long timeout = request.getExpirationTime() - System.currentTimeMillis();
  195 + boolean persisted = request.isPersisted();
  196 +
  197 + if (timeout <= 0) {
  198 + log.debug(
  199 + "[{}][{}] Ignoring message due to exp time reached, {}",
  200 + deviceId,
  201 + request.getId(),
  202 + request.getExpirationTime());
  203 + if (persisted) {
  204 + createRpc(request, RpcStatus.EXPIRED);
  205 + }
  206 + return;
  207 + } else if (persisted) {
  208 + createRpc(request, RpcStatus.QUEUED);
185 } 209 }
186 210
187 - void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {  
188 - ToDeviceRpcRequest request = msg.getMsg();  
189 - ToDeviceRpcRequestMsg rpcRequest = creteToDeviceRpcRequestMsg(request);  
190 -  
191 - long timeout = request.getExpirationTime() - System.currentTimeMillis();  
192 - boolean persisted = request.isPersisted();  
193 -  
194 - if (timeout <= 0) {  
195 - log.debug("[{}][{}] Ignoring message due to exp time reached, {}", deviceId, request.getId(), request.getExpirationTime());  
196 - if (persisted) {  
197 - createRpc(request, RpcStatus.EXPIRED); 211 + boolean sent = false;
  212 + if (systemContext.isEdgesEnabled() && edgeId != null) {
  213 + log.debug(
  214 + "[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue",
  215 + tenantId,
  216 + deviceId,
  217 + edgeId.getId());
  218 + saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId());
  219 + sent = true;
  220 + } else if (isSendNewRpcAvailable()) {
  221 + sent = rpcSubscriptions.size() > 0;
  222 + Set<UUID> syncSessionSet = new HashSet<>();
  223 + rpcSubscriptions.forEach(
  224 + (key, value) -> {
  225 + sendToTransport(rpcRequest, key, value.getNodeId());
  226 + if (SessionType.SYNC == value.getType()) {
  227 + syncSessionSet.add(key);
198 } 228 }
199 - return;  
200 - } else if (persisted) {  
201 - createRpc(request, RpcStatus.QUEUED);  
202 - }  
203 -  
204 - boolean sent = false;  
205 - if (systemContext.isEdgesEnabled() && edgeId != null) {  
206 - log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId());  
207 - saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId());  
208 - sent = true;  
209 - } else if (isSendNewRpcAvailable()) {  
210 - sent = rpcSubscriptions.size() > 0;  
211 - Set<UUID> syncSessionSet = new HashSet<>();  
212 - rpcSubscriptions.forEach((key, value) -> {  
213 - sendToTransport(rpcRequest, key, value.getNodeId());  
214 - if (SessionType.SYNC == value.getType()) {  
215 - syncSessionSet.add(key);  
216 - }  
217 - });  
218 - log.trace("Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);  
219 - syncSessionSet.forEach(rpcSubscriptions::remove);  
220 - }  
221 -  
222 - if (persisted) {  
223 - ObjectNode response = JacksonUtil.newObjectNode();  
224 - response.put("rpcId", request.getId().toString());  
225 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), JacksonUtil.toString(response), null));  
226 - }  
227 -  
228 - if (!persisted && request.isOneway() && sent) {  
229 - log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());  
230 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));  
231 - } else {  
232 - registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);  
233 - }  
234 - if (sent) {  
235 - log.debug("[{}] RPC request {} is sent!", deviceId, request.getId());  
236 - } else {  
237 - log.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId());  
238 - } 229 + });
  230 + log.trace(
  231 + "Rpc syncSessionSet [{}] subscription after sent [{}]", syncSessionSet, rpcSubscriptions);
  232 + syncSessionSet.forEach(rpcSubscriptions::remove);
239 } 233 }
240 234
241 - private boolean isSendNewRpcAvailable() {  
242 - return !rpcSequential || toDeviceRpcPendingMap.values().stream().filter(md -> !md.isDelivered()).findAny().isEmpty(); 235 + if (persisted) {
  236 + ObjectNode response = JacksonUtil.newObjectNode();
  237 + response.put("rpcId", request.getId().toString());
  238 + systemContext
  239 + .getTbCoreDeviceRpcService()
  240 + .processRpcResponseFromDeviceActor(
  241 + new FromDeviceRpcResponse(
  242 + msg.getMsg().getId(), JacksonUtil.toString(response), null));
243 } 243 }
244 244
245 - private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) {  
246 - //Thingskit function  
247 - JsonNode old = JacksonUtil.toJsonNode(request.getAdditionalInfo());  
248 - ObjectNode additional = (old == null || old.isEmpty()) ?mapper.createObjectNode():(ObjectNode)old;  
249 - if(!additional.has(ModelConstants.TablePropertyMapping.COMMAND_TYPE)){  
250 - additional.put(ModelConstants.TablePropertyMapping.COMMAND_TYPE, CmdTypeEnum.DIY.ordinal());  
251 - }  
252 - DeviceId saveDeviceId = deviceId;  
253 - if(additional.has(FastIotConstants.Rpc.TARGET_ID)){  
254 - saveDeviceId = new DeviceId(UUID.fromString(additional.get(FastIotConstants.Rpc.TARGET_ID).asText()));  
255 - }  
256 -  
257 - Rpc rpc = new Rpc(new RpcId(request.getId()));  
258 - rpc.setCreatedTime(System.currentTimeMillis());  
259 - rpc.setTenantId(tenantId);  
260 - rpc.setDeviceId(saveDeviceId);  
261 - rpc.setExpirationTime(request.getExpirationTime());  
262 - rpc.setRequest(JacksonUtil.valueToTree(request));  
263 - rpc.setStatus(status);  
264 - rpc.setAdditionalInfo(JacksonUtil.toJsonNode(request.getAdditionalInfo()));  
265 -  
266 - rpc.setAdditionalInfo(additional);  
267 -  
268 - return systemContext.getTbRpcService().save(tenantId, rpc);  
269 - }  
270 -  
271 - private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) {  
272 - ToDeviceRpcRequestBody body = request.getBody();  
273 - return ToDeviceRpcRequestMsg.newBuilder()  
274 - .setRequestId(rpcSeq++)  
275 - .setMethodName(body.getMethod())  
276 - .setParams(body.getParams())  
277 - .setExpirationTime(request.getExpirationTime())  
278 - .setRequestIdMSB(request.getId().getMostSignificantBits())  
279 - .setRequestIdLSB(request.getId().getLeastSignificantBits())  
280 - .setOneway(request.isOneway())  
281 - .setPersisted(request.isPersisted())  
282 - .build();  
283 - }  
284 -  
285 - void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {  
286 - log.debug("[{}] Processing rpc command response from edge session", deviceId);  
287 - ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());  
288 - boolean success = requestMd != null;  
289 - if (success) {  
290 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(responseMsg.getMsg());  
291 - } else {  
292 - log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());  
293 - } 245 + if (!persisted && request.isOneway() && sent) {
  246 + log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
  247 + systemContext
  248 + .getTbCoreDeviceRpcService()
  249 + .processRpcResponseFromDeviceActor(
  250 + new FromDeviceRpcResponse(msg.getMsg().getId(), null, null));
  251 + } else {
  252 + registerPendingRpcRequest(context, msg, sent, rpcRequest, timeout);
294 } 253 }
295 -  
296 - void processRemoveRpc(TbActorCtx context, RemoveRpcActorMsg msg) {  
297 - log.debug("[{}] Processing remove rpc command", msg.getRequestId());  
298 - Map.Entry<Integer, ToDeviceRpcRequestMetadata> entry = null;  
299 - for (Map.Entry<Integer, ToDeviceRpcRequestMetadata> e : toDeviceRpcPendingMap.entrySet()) {  
300 - if (e.getValue().getMsg().getMsg().getId().equals(msg.getRequestId())) {  
301 - entry = e;  
302 - break;  
303 - }  
304 - }  
305 -  
306 - if (entry != null) {  
307 - if (entry.getValue().isDelivered()) {  
308 - toDeviceRpcPendingMap.remove(entry.getKey());  
309 - } else {  
310 - Optional<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> firstRpc = getFirstRpc();  
311 - if (firstRpc.isPresent() && entry.getKey().equals(firstRpc.get().getKey())) {  
312 - toDeviceRpcPendingMap.remove(entry.getKey());  
313 - sendNextPendingRequest(context);  
314 - } else {  
315 - toDeviceRpcPendingMap.remove(entry.getKey());  
316 - }  
317 - }  
318 - } 254 + if (sent) {
  255 + log.debug("[{}] RPC request {} is sent!", deviceId, request.getId());
  256 + } else {
  257 + log.debug("[{}] RPC request {} is NOT sent!", deviceId, request.getId());
319 } 258 }
320 -  
321 - private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {  
322 - toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));  
323 - DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);  
324 - scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout()); 259 + }
  260 +
  261 + private boolean isSendNewRpcAvailable() {
  262 + return !rpcSequential
  263 + || toDeviceRpcPendingMap.values().stream()
  264 + .filter(md -> !md.isDelivered())
  265 + .findAny()
  266 + .isEmpty();
  267 + }
  268 +
  269 + private Rpc createRpc(ToDeviceRpcRequest request, RpcStatus status) {
  270 + // Thingskit function
  271 + ObjectNode additional = (ObjectNode) JacksonUtil.toJsonNode(request.getAdditionalInfo());
  272 + DeviceId saveDeviceId = deviceId;
  273 + if (additional.has(FastIotConstants.Rpc.TARGET_ID)) {
  274 + saveDeviceId =
  275 + new DeviceId(UUID.fromString(additional.get(FastIotConstants.Rpc.TARGET_ID).asText()));
325 } 276 }
326 277
327 - void processServerSideRpcTimeout(TbActorCtx context, DeviceActorServerSideRpcTimeoutMsg msg) {  
328 - ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());  
329 - if (requestMd != null) {  
330 - log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());  
331 - if (requestMd.getMsg().getMsg().isPersisted()) {  
332 - systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.EXPIRED, null);  
333 - }  
334 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),  
335 - null, requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));  
336 - if (!requestMd.isDelivered()) {  
337 - sendNextPendingRequest(context);  
338 - }  
339 - } 278 + Rpc rpc = new Rpc(new RpcId(request.getId()));
  279 + rpc.setCreatedTime(System.currentTimeMillis());
  280 + rpc.setTenantId(tenantId);
  281 + rpc.setDeviceId(saveDeviceId);
  282 + rpc.setExpirationTime(request.getExpirationTime());
  283 + rpc.setRequest(JacksonUtil.valueToTree(request));
  284 + rpc.setStatus(status);
  285 + rpc.setAdditionalInfo(JacksonUtil.toJsonNode(request.getAdditionalInfo()));
  286 +
  287 + rpc.setAdditionalInfo(additional);
  288 +
  289 + return systemContext.getTbRpcService().save(tenantId, rpc);
  290 + }
  291 +
  292 + private ToDeviceRpcRequestMsg creteToDeviceRpcRequestMsg(ToDeviceRpcRequest request) {
  293 + ToDeviceRpcRequestBody body = request.getBody();
  294 + return ToDeviceRpcRequestMsg.newBuilder()
  295 + .setRequestId(rpcSeq++)
  296 + .setMethodName(body.getMethod())
  297 + .setParams(body.getParams())
  298 + .setExpirationTime(request.getExpirationTime())
  299 + .setRequestIdMSB(request.getId().getMostSignificantBits())
  300 + .setRequestIdLSB(request.getId().getLeastSignificantBits())
  301 + .setOneway(request.isOneway())
  302 + .setPersisted(request.isPersisted())
  303 + .build();
  304 + }
  305 +
  306 + void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
  307 + log.debug("[{}] Processing rpc command response from edge session", deviceId);
  308 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
  309 + boolean success = requestMd != null;
  310 + if (success) {
  311 + systemContext
  312 + .getTbCoreDeviceRpcService()
  313 + .processRpcResponseFromDeviceActor(responseMsg.getMsg());
  314 + } else {
  315 + log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
  316 + }
  317 + }
  318 +
  319 + void processRemoveRpc(TbActorCtx context, RemoveRpcActorMsg msg) {
  320 + log.debug("[{}] Processing remove rpc command", msg.getRequestId());
  321 + Map.Entry<Integer, ToDeviceRpcRequestMetadata> entry = null;
  322 + for (Map.Entry<Integer, ToDeviceRpcRequestMetadata> e : toDeviceRpcPendingMap.entrySet()) {
  323 + if (e.getValue().getMsg().getMsg().getId().equals(msg.getRequestId())) {
  324 + entry = e;
  325 + break;
  326 + }
340 } 327 }
341 328
342 - private void sendPendingRequests(TbActorCtx context, UUID sessionId, String nodeId) {  
343 - SessionType sessionType = getSessionType(sessionId);  
344 - if (!toDeviceRpcPendingMap.isEmpty()) {  
345 - log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId);  
346 - if (sessionType == SessionType.SYNC) {  
347 - log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);  
348 - rpcSubscriptions.remove(sessionId);  
349 - }  
350 - } else {  
351 - log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);  
352 - }  
353 - Set<Integer> sentOneWayIds = new HashSet<>();  
354 -  
355 - if (rpcSequential) {  
356 - getFirstRpc().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));  
357 - } else if (sessionType == SessionType.ASYNC) {  
358 - toDeviceRpcPendingMap.entrySet().forEach(processPendingRpc(context, sessionId, nodeId, sentOneWayIds)); 329 + if (entry != null) {
  330 + if (entry.getValue().isDelivered()) {
  331 + toDeviceRpcPendingMap.remove(entry.getKey());
  332 + } else {
  333 + Optional<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> firstRpc = getFirstRpc();
  334 + if (firstRpc.isPresent() && entry.getKey().equals(firstRpc.get().getKey())) {
  335 + toDeviceRpcPendingMap.remove(entry.getKey());
  336 + sendNextPendingRequest(context);
359 } else { 337 } else {
360 - toDeviceRpcPendingMap.entrySet().stream().findFirst().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds)); 338 + toDeviceRpcPendingMap.remove(entry.getKey());
361 } 339 }
362 -  
363 - sentOneWayIds.stream().filter(id -> !toDeviceRpcPendingMap.get(id).getMsg().getMsg().isPersisted()).forEach(toDeviceRpcPendingMap::remove); 340 + }
364 } 341 }
365 -  
366 - private Optional<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> getFirstRpc() {  
367 - return toDeviceRpcPendingMap.entrySet().stream().filter(e -> !e.getValue().isDelivered()).findFirst(); 342 + }
  343 +
  344 + private void registerPendingRpcRequest(
  345 + TbActorCtx context,
  346 + ToDeviceRpcRequestActorMsg msg,
  347 + boolean sent,
  348 + ToDeviceRpcRequestMsg rpcRequest,
  349 + long timeout) {
  350 + toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
  351 + DeviceActorServerSideRpcTimeoutMsg timeoutMsg =
  352 + new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
  353 + scheduleMsgWithDelay(context, timeoutMsg, timeoutMsg.getTimeout());
  354 + }
  355 +
  356 + void processServerSideRpcTimeout(TbActorCtx context, DeviceActorServerSideRpcTimeoutMsg msg) {
  357 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(msg.getId());
  358 + if (requestMd != null) {
  359 + log.debug("[{}] RPC request [{}] timeout detected!", deviceId, msg.getId());
  360 + if (requestMd.getMsg().getMsg().isPersisted()) {
  361 + systemContext
  362 + .getTbRpcService()
  363 + .save(
  364 + tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), RpcStatus.EXPIRED, null);
  365 + }
  366 + systemContext
  367 + .getTbCoreDeviceRpcService()
  368 + .processRpcResponseFromDeviceActor(
  369 + new FromDeviceRpcResponse(
  370 + requestMd.getMsg().getMsg().getId(),
  371 + null,
  372 + requestMd.isSent() ? RpcError.TIMEOUT : RpcError.NO_ACTIVE_CONNECTION));
  373 + if (!requestMd.isDelivered()) {
  374 + sendNextPendingRequest(context);
  375 + }
368 } 376 }
369 -  
370 - private void sendNextPendingRequest(TbActorCtx context) {  
371 - if (rpcSequential) {  
372 - rpcSubscriptions.forEach((id, s) -> sendPendingRequests(context, id, s.getNodeId()));  
373 - } 377 + }
  378 +
  379 + private void sendPendingRequests(TbActorCtx context, UUID sessionId, String nodeId) {
  380 + SessionType sessionType = getSessionType(sessionId);
  381 + if (!toDeviceRpcPendingMap.isEmpty()) {
  382 + log.debug(
  383 + "[{}] Pushing {} pending RPC messages to new async session [{}]",
  384 + deviceId,
  385 + toDeviceRpcPendingMap.size(),
  386 + sessionId);
  387 + if (sessionType == SessionType.SYNC) {
  388 + log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId);
  389 + rpcSubscriptions.remove(sessionId);
  390 + }
  391 + } else {
  392 + log.debug("[{}] No pending RPC messages for new async session [{}]", deviceId, sessionId);
374 } 393 }
375 -  
376 - private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {  
377 - return entry -> {  
378 - ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();  
379 - ToDeviceRpcRequestBody body = request.getBody();  
380 - if (request.isOneway() && !rpcSequential) {  
381 - sentOneWayIds.add(entry.getKey());  
382 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(request.getId(), null, null));  
383 - }  
384 - ToDeviceRpcRequestMsg rpcRequest = ToDeviceRpcRequestMsg.newBuilder()  
385 - .setRequestId(entry.getKey())  
386 - .setMethodName(body.getMethod())  
387 - .setParams(body.getParams())  
388 - .setExpirationTime(request.getExpirationTime())  
389 - .setRequestIdMSB(request.getId().getMostSignificantBits())  
390 - .setRequestIdLSB(request.getId().getLeastSignificantBits())  
391 - .setOneway(request.isOneway())  
392 - .setPersisted(request.isPersisted())  
393 - .build();  
394 - sendToTransport(rpcRequest, sessionId, nodeId);  
395 - };  
396 - }  
397 -  
398 - void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {  
399 - TransportToDeviceActorMsg msg = wrapper.getMsg();  
400 - TbCallback callback = wrapper.getCallback();  
401 - var sessionInfo = msg.getSessionInfo();  
402 -  
403 - if (msg.hasSessionEvent()) {  
404 - processSessionStateMsgs(sessionInfo, msg.getSessionEvent());  
405 - }  
406 - if (msg.hasSubscribeToAttributes()) {  
407 - processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes());  
408 - }  
409 - if (msg.hasSubscribeToRPC()) {  
410 - processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC());  
411 - }  
412 - if (msg.hasSendPendingRPC()) {  
413 - sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo.getNodeId());  
414 - }  
415 - if (msg.hasGetAttributes()) {  
416 - handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes());  
417 - }  
418 - if (msg.hasToDeviceRPCCallResponse()) {  
419 - processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse());  
420 - }  
421 - if (msg.hasSubscriptionInfo()) {  
422 - handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo());  
423 - }  
424 - if (msg.hasClaimDevice()) {  
425 - handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice());  
426 - }  
427 - if (msg.hasRpcResponseStatusMsg()) {  
428 - processRpcResponseStatus(context, sessionInfo, msg.getRpcResponseStatusMsg());  
429 - }  
430 - if (msg.hasUplinkNotificationMsg()) {  
431 - processUplinkNotificationMsg(context, sessionInfo, msg.getUplinkNotificationMsg());  
432 - }  
433 - callback.onSuccess(); 394 + Set<Integer> sentOneWayIds = new HashSet<>();
  395 +
  396 + if (rpcSequential) {
  397 + getFirstRpc().ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));
  398 + } else if (sessionType == SessionType.ASYNC) {
  399 + toDeviceRpcPendingMap
  400 + .entrySet()
  401 + .forEach(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));
  402 + } else {
  403 + toDeviceRpcPendingMap.entrySet().stream()
  404 + .findFirst()
  405 + .ifPresent(processPendingRpc(context, sessionId, nodeId, sentOneWayIds));
434 } 406 }
435 407
436 - private void processUplinkNotificationMsg(TbActorCtx context, SessionInfoProto sessionInfo, TransportProtos.UplinkNotificationMsg uplinkNotificationMsg) {  
437 - String nodeId = sessionInfo.getNodeId();  
438 - sessions.entrySet().stream()  
439 - .filter(kv -> kv.getValue().getSessionInfo().getNodeId().equals(nodeId) && (kv.getValue().isSubscribedToAttributes() || kv.getValue().isSubscribedToRPC()))  
440 - .forEach(kv -> {  
441 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
442 - .setSessionIdMSB(kv.getKey().getMostSignificantBits())  
443 - .setSessionIdLSB(kv.getKey().getLeastSignificantBits())  
444 - .setUplinkNotificationMsg(uplinkNotificationMsg)  
445 - .build();  
446 - systemContext.getTbCoreToTransportService().process(kv.getValue().getSessionInfo().getNodeId(), msg);  
447 - });  
448 - } 408 + sentOneWayIds.stream()
  409 + .filter(id -> !toDeviceRpcPendingMap.get(id).getMsg().getMsg().isPersisted())
  410 + .forEach(toDeviceRpcPendingMap::remove);
  411 + }
449 412
450 - private void handleClaimDeviceMsg(TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {  
451 - DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));  
452 - systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());  
453 - } 413 + private Optional<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> getFirstRpc() {
  414 + return toDeviceRpcPendingMap.entrySet().stream()
  415 + .filter(e -> !e.getValue().isDelivered())
  416 + .findFirst();
  417 + }
454 418
455 - private void reportSessionOpen() {  
456 - systemContext.getDeviceStateService().onDeviceConnect(tenantId, deviceId); 419 + private void sendNextPendingRequest(TbActorCtx context) {
  420 + if (rpcSequential) {
  421 + rpcSubscriptions.forEach((id, s) -> sendPendingRequests(context, id, s.getNodeId()));
457 } 422 }
458 -  
459 - private void reportSessionClose() {  
460 - systemContext.getDeviceStateService().onDeviceDisconnect(tenantId, deviceId); 423 + }
  424 +
  425 + private Consumer<Map.Entry<Integer, ToDeviceRpcRequestMetadata>> processPendingRpc(
  426 + TbActorCtx context, UUID sessionId, String nodeId, Set<Integer> sentOneWayIds) {
  427 + return entry -> {
  428 + ToDeviceRpcRequest request = entry.getValue().getMsg().getMsg();
  429 + ToDeviceRpcRequestBody body = request.getBody();
  430 + if (request.isOneway() && !rpcSequential) {
  431 + sentOneWayIds.add(entry.getKey());
  432 + systemContext
  433 + .getTbCoreDeviceRpcService()
  434 + .processRpcResponseFromDeviceActor(
  435 + new FromDeviceRpcResponse(request.getId(), null, null));
  436 + }
  437 + ToDeviceRpcRequestMsg rpcRequest =
  438 + ToDeviceRpcRequestMsg.newBuilder()
  439 + .setRequestId(entry.getKey())
  440 + .setMethodName(body.getMethod())
  441 + .setParams(body.getParams())
  442 + .setExpirationTime(request.getExpirationTime())
  443 + .setRequestIdMSB(request.getId().getMostSignificantBits())
  444 + .setRequestIdLSB(request.getId().getLeastSignificantBits())
  445 + .setOneway(request.isOneway())
  446 + .setPersisted(request.isPersisted())
  447 + .build();
  448 + sendToTransport(rpcRequest, sessionId, nodeId);
  449 + };
  450 + }
  451 +
  452 + void process(TbActorCtx context, TransportToDeviceActorMsgWrapper wrapper) {
  453 + TransportToDeviceActorMsg msg = wrapper.getMsg();
  454 + TbCallback callback = wrapper.getCallback();
  455 + var sessionInfo = msg.getSessionInfo();
  456 +
  457 + if (msg.hasSessionEvent()) {
  458 + processSessionStateMsgs(sessionInfo, msg.getSessionEvent());
461 } 459 }
462 -  
463 - private void handleGetAttributesRequest(TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {  
464 - int requestId = request.getRequestId();  
465 - if (request.getOnlyShared()) {  
466 - Futures.addCallback(findAllAttributesByScope(DataConstants.SHARED_SCOPE), new FutureCallback<>() {  
467 - @Override  
468 - public void onSuccess(@Nullable List<AttributeKvEntry> result) {  
469 - GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()  
470 - .setRequestId(requestId)  
471 - .setSharedStateMsg(true)  
472 - .addAllSharedAttributeList(toTsKvProtos(result))  
473 - .setIsMultipleAttributesRequest(request.getSharedAttributeNamesCount() > 1)  
474 - .build();  
475 - sendToTransport(responseMsg, sessionInfo);  
476 - }  
477 -  
478 - @Override  
479 - public void onFailure(Throwable t) {  
480 - GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()  
481 - .setError(t.getMessage())  
482 - .setSharedStateMsg(true)  
483 - .build();  
484 - sendToTransport(responseMsg, sessionInfo);  
485 - }  
486 - }, MoreExecutors.directExecutor());  
487 - } else {  
488 - Futures.addCallback(getAttributesKvEntries(request), new FutureCallback<>() {  
489 - @Override  
490 - public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {  
491 - GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()  
492 - .setRequestId(requestId)  
493 - .addAllClientAttributeList(toTsKvProtos(result.get(0)))  
494 - .addAllSharedAttributeList(toTsKvProtos(result.get(1)))  
495 - .setIsMultipleAttributesRequest(  
496 - request.getSharedAttributeNamesCount() + request.getClientAttributeNamesCount() > 1)  
497 - .build();  
498 - sendToTransport(responseMsg, sessionInfo);  
499 - }  
500 -  
501 - @Override  
502 - public void onFailure(Throwable t) {  
503 - GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()  
504 - .setError(t.getMessage())  
505 - .build();  
506 - sendToTransport(responseMsg, sessionInfo);  
507 - }  
508 - }, MoreExecutors.directExecutor());  
509 - } 460 + if (msg.hasSubscribeToAttributes()) {
  461 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToAttributes());
510 } 462 }
511 -  
512 - private ListenableFuture<List<List<AttributeKvEntry>>> getAttributesKvEntries(GetAttributeRequestMsg request) {  
513 - ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture;  
514 - ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture;  
515 - if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {  
516 - clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE);  
517 - sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE);  
518 - } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {  
519 - clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);  
520 - sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);  
521 - } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {  
522 - clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());  
523 - sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);  
524 - } else {  
525 - sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());  
526 - clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);  
527 - }  
528 - return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)); 463 + if (msg.hasSubscribeToRPC()) {
  464 + processSubscriptionCommands(context, sessionInfo, msg.getSubscribeToRPC());
529 } 465 }
530 -  
531 - private ListenableFuture<List<AttributeKvEntry>> findAllAttributesByScope(String scope) {  
532 - return systemContext.getAttributesService().findAll(tenantId, deviceId, scope); 466 + if (msg.hasSendPendingRPC()) {
  467 + sendPendingRequests(context, getSessionId(sessionInfo), sessionInfo.getNodeId());
533 } 468 }
534 -  
535 - private ListenableFuture<List<AttributeKvEntry>> findAttributesByScope(Set<String> attributesSet, String scope) {  
536 - return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet); 469 + if (msg.hasGetAttributes()) {
  470 + handleGetAttributesRequest(context, sessionInfo, msg.getGetAttributes());
537 } 471 }
538 -  
539 - private Set<String> toSet(List<String> strings) {  
540 - return new HashSet<>(strings); 472 + if (msg.hasToDeviceRPCCallResponse()) {
  473 + processRpcResponses(context, sessionInfo, msg.getToDeviceRPCCallResponse());
541 } 474 }
542 -  
543 - private SessionType getSessionType(UUID sessionId) {  
544 - return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC; 475 + if (msg.hasSubscriptionInfo()) {
  476 + handleSessionActivity(context, sessionInfo, msg.getSubscriptionInfo());
545 } 477 }
546 -  
547 - void processAttributesUpdate(TbActorCtx context, DeviceAttributesEventNotificationMsg msg) {  
548 - if (attributeSubscriptions.size() > 0) {  
549 - boolean hasNotificationData = false;  
550 - AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();  
551 - if (msg.isDeleted()) {  
552 - List<String> sharedKeys = msg.getDeletedKeys().stream()  
553 - .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))  
554 - .map(AttributeKey::getAttributeKey)  
555 - .collect(Collectors.toList());  
556 - if (!sharedKeys.isEmpty()) {  
557 - notification.addAllSharedDeleted(sharedKeys);  
558 - hasNotificationData = true;  
559 - }  
560 - } else {  
561 - if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {  
562 - List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());  
563 - if (attributes.size() > 0) {  
564 - List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)  
565 - .collect(Collectors.toList());  
566 - if (!sharedUpdated.isEmpty()) {  
567 - notification.addAllSharedUpdated(sharedUpdated);  
568 - hasNotificationData = true;  
569 - }  
570 - } else {  
571 - log.debug("[{}] No public shared side attributes changed!", deviceId);  
572 - }  
573 - }  
574 - }  
575 - if (hasNotificationData) {  
576 - AttributeUpdateNotificationMsg finalNotification = notification.build();  
577 - attributeSubscriptions.forEach((key, value) -> sendToTransport(finalNotification, key, value.getNodeId()));  
578 - }  
579 - } else {  
580 - log.debug("[{}] No registered attributes subscriptions to process!", deviceId);  
581 - } 478 + if (msg.hasClaimDevice()) {
  479 + handleClaimDeviceMsg(context, sessionInfo, msg.getClaimDevice());
582 } 480 }
583 -  
584 - private void processRpcResponses(TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {  
585 - UUID sessionId = getSessionId(sessionInfo);  
586 - log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);  
587 - ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());  
588 - boolean success = requestMd != null;  
589 - if (success) {  
590 - boolean hasError = StringUtils.isNotEmpty(responseMsg.getError());  
591 - try {  
592 - String payload = hasError ? responseMsg.getError() : responseMsg.getPayload();  
593 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(  
594 - new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(),  
595 - payload, null));  
596 - if (requestMd.getMsg().getMsg().isPersisted()) {  
597 - RpcStatus status = hasError ? RpcStatus.FAILED : RpcStatus.SUCCESSFUL;  
598 - JsonNode response;  
599 - try {  
600 - response = JacksonUtil.toJsonNode(payload);  
601 - } catch (IllegalArgumentException e) {  
602 - response = JacksonUtil.newObjectNode().put("error", payload);  
603 - }  
604 - systemContext.getTbRpcService().save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), status, response);  
605 - }  
606 - } finally {  
607 - if (hasError && !requestMd.isDelivered()) {  
608 - sendNextPendingRequest(context);  
609 - }  
610 - }  
611 - } else {  
612 - log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());  
613 - } 481 + if (msg.hasRpcResponseStatusMsg()) {
  482 + processRpcResponseStatus(context, sessionInfo, msg.getRpcResponseStatusMsg());
614 } 483 }
615 -  
616 - private void processRpcResponseStatus(TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseStatusMsg responseMsg) {  
617 - UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB());  
618 - RpcStatus status = RpcStatus.valueOf(responseMsg.getStatus());  
619 - ToDeviceRpcRequestMetadata md = toDeviceRpcPendingMap.get(responseMsg.getRequestId());  
620 -  
621 - if (md != null) {  
622 - JsonNode response = null;  
623 - if (status.equals(RpcStatus.DELIVERED)) {  
624 - if (md.getMsg().getMsg().isOneway()) {  
625 - toDeviceRpcPendingMap.remove(responseMsg.getRequestId());  
626 - if (rpcSequential) {  
627 - systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, null, null));  
628 - }  
629 - } else {  
630 - md.setDelivered(true);  
631 - }  
632 - } else if (status.equals(RpcStatus.TIMEOUT)) {  
633 - Integer maxRpcRetries = md.getMsg().getMsg().getRetries();  
634 - maxRpcRetries = maxRpcRetries == null ? systemContext.getMaxRpcRetries() : Math.min(maxRpcRetries, systemContext.getMaxRpcRetries());  
635 - if (maxRpcRetries <= md.getRetries()) {  
636 - toDeviceRpcPendingMap.remove(responseMsg.getRequestId());  
637 - status = RpcStatus.FAILED;  
638 - response = JacksonUtil.newObjectNode().put("error", "There was a Timeout and all retry attempts have been exhausted. Retry attempts set: " + maxRpcRetries);  
639 - } else {  
640 - md.setRetries(md.getRetries() + 1);  
641 - } 484 + if (msg.hasUplinkNotificationMsg()) {
  485 + processUplinkNotificationMsg(context, sessionInfo, msg.getUplinkNotificationMsg());
  486 + }
  487 + callback.onSuccess();
  488 + }
  489 +
  490 + private void processUplinkNotificationMsg(
  491 + TbActorCtx context,
  492 + SessionInfoProto sessionInfo,
  493 + TransportProtos.UplinkNotificationMsg uplinkNotificationMsg) {
  494 + String nodeId = sessionInfo.getNodeId();
  495 + sessions.entrySet().stream()
  496 + .filter(
  497 + kv ->
  498 + kv.getValue().getSessionInfo().getNodeId().equals(nodeId)
  499 + && (kv.getValue().isSubscribedToAttributes()
  500 + || kv.getValue().isSubscribedToRPC()))
  501 + .forEach(
  502 + kv -> {
  503 + ToTransportMsg msg =
  504 + ToTransportMsg.newBuilder()
  505 + .setSessionIdMSB(kv.getKey().getMostSignificantBits())
  506 + .setSessionIdLSB(kv.getKey().getLeastSignificantBits())
  507 + .setUplinkNotificationMsg(uplinkNotificationMsg)
  508 + .build();
  509 + systemContext
  510 + .getTbCoreToTransportService()
  511 + .process(kv.getValue().getSessionInfo().getNodeId(), msg);
  512 + });
  513 + }
  514 +
  515 + private void handleClaimDeviceMsg(
  516 + TbActorCtx context, SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
  517 + DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
  518 + systemContext
  519 + .getClaimDevicesService()
  520 + .registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
  521 + }
  522 +
  523 + private void reportSessionOpen() {
  524 + systemContext.getDeviceStateService().onDeviceConnect(tenantId, deviceId);
  525 + }
  526 +
  527 + private void reportSessionClose() {
  528 + systemContext.getDeviceStateService().onDeviceDisconnect(tenantId, deviceId);
  529 + }
  530 +
  531 + private void handleGetAttributesRequest(
  532 + TbActorCtx context, SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
  533 + int requestId = request.getRequestId();
  534 + if (request.getOnlyShared()) {
  535 + Futures.addCallback(
  536 + findAllAttributesByScope(DataConstants.SHARED_SCOPE),
  537 + new FutureCallback<>() {
  538 + @Override
  539 + public void onSuccess(@Nullable List<AttributeKvEntry> result) {
  540 + GetAttributeResponseMsg responseMsg =
  541 + GetAttributeResponseMsg.newBuilder()
  542 + .setRequestId(requestId)
  543 + .setSharedStateMsg(true)
  544 + .addAllSharedAttributeList(toTsKvProtos(result))
  545 + .setIsMultipleAttributesRequest(request.getSharedAttributeNamesCount() > 1)
  546 + .build();
  547 + sendToTransport(responseMsg, sessionInfo);
642 } 548 }
643 549
644 - if (md.getMsg().getMsg().isPersisted()) {  
645 - systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, response); 550 + @Override
  551 + public void onFailure(Throwable t) {
  552 + GetAttributeResponseMsg responseMsg =
  553 + GetAttributeResponseMsg.newBuilder()
  554 + .setError(t.getMessage())
  555 + .setSharedStateMsg(true)
  556 + .build();
  557 + sendToTransport(responseMsg, sessionInfo);
646 } 558 }
647 - if (status != RpcStatus.SENT) {  
648 - sendNextPendingRequest(context); 559 + },
  560 + MoreExecutors.directExecutor());
  561 + } else {
  562 + Futures.addCallback(
  563 + getAttributesKvEntries(request),
  564 + new FutureCallback<>() {
  565 + @Override
  566 + public void onSuccess(@Nullable List<List<AttributeKvEntry>> result) {
  567 + GetAttributeResponseMsg responseMsg =
  568 + GetAttributeResponseMsg.newBuilder()
  569 + .setRequestId(requestId)
  570 + .addAllClientAttributeList(toTsKvProtos(result.get(0)))
  571 + .addAllSharedAttributeList(toTsKvProtos(result.get(1)))
  572 + .setIsMultipleAttributesRequest(
  573 + request.getSharedAttributeNamesCount()
  574 + + request.getClientAttributeNamesCount()
  575 + > 1)
  576 + .build();
  577 + sendToTransport(responseMsg, sessionInfo);
649 } 578 }
650 - } else {  
651 - log.info("[{}][{}] Rpc has already removed from pending map.", deviceId, rpcId);  
652 - }  
653 - }  
654 579
655 - private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToAttributeUpdatesMsg subscribeCmd) {  
656 - UUID sessionId = getSessionId(sessionInfo);  
657 - if (subscribeCmd.getUnsubscribe()) {  
658 - log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);  
659 - attributeSubscriptions.remove(sessionId);  
660 - } else {  
661 - SessionInfoMetaData sessionMD = sessions.get(sessionId);  
662 - if (sessionMD == null) {  
663 - sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); 580 + @Override
  581 + public void onFailure(Throwable t) {
  582 + GetAttributeResponseMsg responseMsg =
  583 + GetAttributeResponseMsg.newBuilder().setError(t.getMessage()).build();
  584 + sendToTransport(responseMsg, sessionInfo);
664 } 585 }
665 - sessionMD.setSubscribedToAttributes(true);  
666 - log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);  
667 - attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo());  
668 - dumpSessions();  
669 - } 586 + },
  587 + MoreExecutors.directExecutor());
670 } 588 }
671 -  
672 - private UUID getSessionId(SessionInfoProto sessionInfo) {  
673 - return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); 589 + }
  590 +
  591 + private ListenableFuture<List<List<AttributeKvEntry>>> getAttributesKvEntries(
  592 + GetAttributeRequestMsg request) {
  593 + ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture;
  594 + ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture;
  595 + if (CollectionUtils.isEmpty(request.getClientAttributeNamesList())
  596 + && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
  597 + clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE);
  598 + sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE);
  599 + } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList())
  600 + && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
  601 + clientAttributesFuture =
  602 + findAttributesByScope(
  603 + toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
  604 + sharedAttributesFuture =
  605 + findAttributesByScope(
  606 + toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
  607 + } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList())
  608 + && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
  609 + clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
  610 + sharedAttributesFuture =
  611 + findAttributesByScope(
  612 + toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
  613 + } else {
  614 + sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
  615 + clientAttributesFuture =
  616 + findAttributesByScope(
  617 + toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
674 } 618 }
675 -  
676 - private void processSubscriptionCommands(TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {  
677 - UUID sessionId = getSessionId(sessionInfo);  
678 - if (subscribeCmd.getUnsubscribe()) {  
679 - log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);  
680 - rpcSubscriptions.remove(sessionId);  
681 - } else {  
682 - SessionInfoMetaData sessionMD = sessions.get(sessionId);  
683 - if (sessionMD == null) {  
684 - sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));  
685 - }  
686 - sessionMD.setSubscribedToRPC(true);  
687 - log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);  
688 - rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo());  
689 - sendPendingRequests(context, sessionId, sessionInfo.getNodeId());  
690 - dumpSessions(); 619 + return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
  620 + }
  621 +
  622 + private ListenableFuture<List<AttributeKvEntry>> findAllAttributesByScope(String scope) {
  623 + return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
  624 + }
  625 +
  626 + private ListenableFuture<List<AttributeKvEntry>> findAttributesByScope(
  627 + Set<String> attributesSet, String scope) {
  628 + return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet);
  629 + }
  630 +
  631 + private Set<String> toSet(List<String> strings) {
  632 + return new HashSet<>(strings);
  633 + }
  634 +
  635 + private SessionType getSessionType(UUID sessionId) {
  636 + return sessions.containsKey(sessionId) ? SessionType.ASYNC : SessionType.SYNC;
  637 + }
  638 +
  639 + void processAttributesUpdate(TbActorCtx context, DeviceAttributesEventNotificationMsg msg) {
  640 + if (attributeSubscriptions.size() > 0) {
  641 + boolean hasNotificationData = false;
  642 + AttributeUpdateNotificationMsg.Builder notification =
  643 + AttributeUpdateNotificationMsg.newBuilder();
  644 + if (msg.isDeleted()) {
  645 + List<String> sharedKeys =
  646 + msg.getDeletedKeys().stream()
  647 + .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
  648 + .map(AttributeKey::getAttributeKey)
  649 + .collect(Collectors.toList());
  650 + if (!sharedKeys.isEmpty()) {
  651 + notification.addAllSharedDeleted(sharedKeys);
  652 + hasNotificationData = true;
691 } 653 }
692 - }  
693 -  
694 - private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {  
695 - UUID sessionId = getSessionId(sessionInfo);  
696 - Objects.requireNonNull(sessionId);  
697 - if (msg.getEvent() == SessionEvent.OPEN) {  
698 - if (sessions.containsKey(sessionId)) {  
699 - log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);  
700 - return;  
701 - }  
702 - log.debug("[{}] Processing new session [{}]. Current sessions size {}", deviceId, sessionId, sessions.size());  
703 -  
704 - sessions.put(sessionId, new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));  
705 - if (sessions.size() == 1) {  
706 - reportSessionOpen(); 654 + } else {
  655 + if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
  656 + List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
  657 + if (attributes.size() > 0) {
  658 + List<TsKvProto> sharedUpdated =
  659 + msg.getValues().stream().map(this::toTsKvProto).collect(Collectors.toList());
  660 + if (!sharedUpdated.isEmpty()) {
  661 + notification.addAllSharedUpdated(sharedUpdated);
  662 + hasNotificationData = true;
707 } 663 }
708 - systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, System.currentTimeMillis());  
709 - dumpSessions();  
710 - } else if (msg.getEvent() == SessionEvent.CLOSED) {  
711 - log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);  
712 - sessions.remove(sessionId);  
713 - attributeSubscriptions.remove(sessionId);  
714 - rpcSubscriptions.remove(sessionId);  
715 - if (sessions.isEmpty()) {  
716 - reportSessionClose();  
717 - }  
718 - dumpSessions(); 664 + } else {
  665 + log.debug("[{}] No public shared side attributes changed!", deviceId);
  666 + }
719 } 667 }
  668 + }
  669 + if (hasNotificationData) {
  670 + AttributeUpdateNotificationMsg finalNotification = notification.build();
  671 + attributeSubscriptions.forEach(
  672 + (key, value) -> sendToTransport(finalNotification, key, value.getNodeId()));
  673 + }
  674 + } else {
  675 + log.debug("[{}] No registered attributes subscriptions to process!", deviceId);
720 } 676 }
721 -  
722 - private void handleSessionActivity(TbActorCtx context, SessionInfoProto sessionInfoProto, SubscriptionInfoProto subscriptionInfo) {  
723 - UUID sessionId = getSessionId(sessionInfoProto);  
724 - Objects.requireNonNull(sessionId);  
725 -  
726 - SessionInfoMetaData sessionMD = sessions.get(sessionId);  
727 - if (sessionMD != null) {  
728 - sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());  
729 - sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());  
730 - sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription());  
731 - if (subscriptionInfo.getAttributeSubscription()) {  
732 - attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());  
733 - }  
734 - if (subscriptionInfo.getRpcSubscription()) {  
735 - rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());  
736 - } 677 + }
  678 +
  679 + private void processRpcResponses(
  680 + TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseMsg responseMsg) {
  681 + UUID sessionId = getSessionId(sessionInfo);
  682 + log.debug("[{}] Processing rpc command response [{}]", deviceId, sessionId);
  683 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
  684 + boolean success = requestMd != null;
  685 + if (success) {
  686 + boolean hasError = StringUtils.isNotEmpty(responseMsg.getError());
  687 + try {
  688 + String payload = hasError ? responseMsg.getError() : responseMsg.getPayload();
  689 + systemContext
  690 + .getTbCoreDeviceRpcService()
  691 + .processRpcResponseFromDeviceActor(
  692 + new FromDeviceRpcResponse(requestMd.getMsg().getMsg().getId(), payload, null));
  693 + if (requestMd.getMsg().getMsg().isPersisted()) {
  694 + RpcStatus status = hasError ? RpcStatus.FAILED : RpcStatus.SUCCESSFUL;
  695 + JsonNode response;
  696 + try {
  697 + response = JacksonUtil.toJsonNode(payload);
  698 + } catch (IllegalArgumentException e) {
  699 + response = JacksonUtil.newObjectNode().put("error", payload);
  700 + }
  701 + systemContext
  702 + .getTbRpcService()
  703 + .save(tenantId, new RpcId(requestMd.getMsg().getMsg().getId()), status, response);
737 } 704 }
738 - systemContext.getDeviceStateService().onDeviceActivity(tenantId, deviceId, subscriptionInfo.getLastActivityTime());  
739 - if (sessionMD != null) {  
740 - dumpSessions(); 705 + } finally {
  706 + if (hasError && !requestMd.isDelivered()) {
  707 + sendNextPendingRequest(context);
741 } 708 }
  709 + }
  710 + } else {
  711 + log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
742 } 712 }
743 -  
744 - void processCredentialsUpdate(TbActorMsg msg) {  
745 - if (((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials().getCredentialsType() == DeviceCredentialsType.LWM2M_CREDENTIALS) {  
746 - sessions.forEach((k, v) -> {  
747 - notifyTransportAboutDeviceCredentialsUpdate(k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials());  
748 - }); 713 + }
  714 +
  715 + private void processRpcResponseStatus(
  716 + TbActorCtx context, SessionInfoProto sessionInfo, ToDeviceRpcResponseStatusMsg responseMsg) {
  717 + UUID rpcId = new UUID(responseMsg.getRequestIdMSB(), responseMsg.getRequestIdLSB());
  718 + RpcStatus status = RpcStatus.valueOf(responseMsg.getStatus());
  719 + ToDeviceRpcRequestMetadata md = toDeviceRpcPendingMap.get(responseMsg.getRequestId());
  720 +
  721 + if (md != null) {
  722 + JsonNode response = null;
  723 + if (status.equals(RpcStatus.DELIVERED)) {
  724 + if (md.getMsg().getMsg().isOneway()) {
  725 + toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
  726 + if (rpcSequential) {
  727 + systemContext
  728 + .getTbCoreDeviceRpcService()
  729 + .processRpcResponseFromDeviceActor(new FromDeviceRpcResponse(rpcId, null, null));
  730 + }
749 } else { 731 } else {
750 - sessions.forEach((sessionId, sessionMd) -> notifyTransportAboutClosedSession(sessionId, sessionMd, "device credentials updated!"));  
751 - attributeSubscriptions.clear();  
752 - rpcSubscriptions.clear();  
753 - dumpSessions();  
754 - 732 + md.setDelivered(true);
755 } 733 }
756 - }  
757 -  
758 - private void notifyTransportAboutClosedSessionMaxSessionsLimit(UUID sessionId, SessionInfoMetaData sessionMd) {  
759 - log.debug("remove eldest session (max concurrent sessions limit reached per device) sessionId [{}] sessionMd [{}]", sessionId, sessionMd);  
760 - notifyTransportAboutClosedSession(sessionId, sessionMd, "max concurrent sessions limit reached per device!");  
761 - }  
762 -  
763 - private void notifyTransportAboutClosedSession(UUID sessionId, SessionInfoMetaData sessionMd, String message) {  
764 - SessionCloseNotificationProto sessionCloseNotificationProto = SessionCloseNotificationProto  
765 - .newBuilder()  
766 - .setMessage(message).build();  
767 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
768 - .setSessionIdMSB(sessionId.getMostSignificantBits())  
769 - .setSessionIdLSB(sessionId.getLeastSignificantBits())  
770 - .setSessionCloseNotification(sessionCloseNotificationProto)  
771 - .build();  
772 - systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);  
773 - }  
774 -  
775 - void notifyTransportAboutDeviceCredentialsUpdate(UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {  
776 - ToTransportUpdateCredentialsProto.Builder notification = ToTransportUpdateCredentialsProto.newBuilder();  
777 - notification.addCredentialsId(deviceCredentials.getCredentialsId());  
778 - notification.addCredentialsValue(deviceCredentials.getCredentialsValue());  
779 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
780 - .setSessionIdMSB(sessionId.getMostSignificantBits())  
781 - .setSessionIdLSB(sessionId.getLeastSignificantBits())  
782 - .setToTransportUpdateCredentialsNotification(notification).build();  
783 - systemContext.getTbCoreToTransportService().process(sessionMd.getSessionInfo().getNodeId(), msg);  
784 - }  
785 -  
786 - void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {  
787 - this.deviceName = msg.getDeviceName();  
788 - this.deviceType = msg.getDeviceType();  
789 - this.defaultMetaData = new TbMsgMetaData();  
790 - this.defaultMetaData.putValue("deviceName", deviceName);  
791 - this.defaultMetaData.putValue("deviceType", deviceType);  
792 - }  
793 -  
794 - void processEdgeUpdate(DeviceEdgeUpdateMsg msg) {  
795 - log.trace("[{}] Processing edge update {}", deviceId, msg);  
796 - this.edgeId = msg.getEdgeId();  
797 - }  
798 -  
799 - private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {  
800 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
801 - .setSessionIdMSB(sessionInfo.getSessionIdMSB())  
802 - .setSessionIdLSB(sessionInfo.getSessionIdLSB())  
803 - .setGetAttributesResponse(responseMsg).build();  
804 - systemContext.getTbCoreToTransportService().process(sessionInfo.getNodeId(), msg);  
805 - }  
806 -  
807 - private void sendToTransport(AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {  
808 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
809 - .setSessionIdMSB(sessionId.getMostSignificantBits())  
810 - .setSessionIdLSB(sessionId.getLeastSignificantBits())  
811 - .setAttributeUpdateNotification(notificationMsg).build();  
812 - systemContext.getTbCoreToTransportService().process(nodeId, msg);  
813 - }  
814 -  
815 - private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {  
816 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
817 - .setSessionIdMSB(sessionId.getMostSignificantBits())  
818 - .setSessionIdLSB(sessionId.getLeastSignificantBits())  
819 - .setToDeviceRequest(rpcMsg).build();  
820 - systemContext.getTbCoreToTransportService().process(nodeId, msg);  
821 - }  
822 -  
823 - private void sendToTransport(ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {  
824 - ToTransportMsg msg = ToTransportMsg.newBuilder()  
825 - .setSessionIdMSB(sessionId.getMostSignificantBits())  
826 - .setSessionIdLSB(sessionId.getLeastSignificantBits())  
827 - .setToServerResponse(rpcMsg).build();  
828 - systemContext.getTbCoreToTransportService().process(nodeId, msg);  
829 - }  
830 -  
831 - private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {  
832 - EdgeEvent edgeEvent = new EdgeEvent();  
833 - edgeEvent.setTenantId(tenantId);  
834 - edgeEvent.setAction(EdgeEventActionType.RPC_CALL);  
835 - edgeEvent.setEntityId(deviceId.getId());  
836 - edgeEvent.setType(EdgeEventType.DEVICE);  
837 -  
838 - ObjectNode body = mapper.createObjectNode();  
839 - body.put("requestId", requestId);  
840 - body.put("requestUUID", msg.getId().toString());  
841 - body.put("oneway", msg.isOneway());  
842 - body.put("expirationTime", msg.getExpirationTime());  
843 - body.put("method", msg.getBody().getMethod());  
844 - body.put("params", msg.getBody().getParams());  
845 - edgeEvent.setBody(body);  
846 -  
847 - edgeEvent.setEdgeId(edgeId);  
848 - systemContext.getEdgeEventService().save(edgeEvent);  
849 - systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);  
850 - }  
851 -  
852 - private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {  
853 - List<TsKvProto> clientAttributes;  
854 - if (result == null || result.isEmpty()) {  
855 - clientAttributes = Collections.emptyList(); 734 + } else if (status.equals(RpcStatus.TIMEOUT)) {
  735 + Integer maxRpcRetries = md.getMsg().getMsg().getRetries();
  736 + maxRpcRetries =
  737 + maxRpcRetries == null
  738 + ? systemContext.getMaxRpcRetries()
  739 + : Math.min(maxRpcRetries, systemContext.getMaxRpcRetries());
  740 + if (maxRpcRetries <= md.getRetries()) {
  741 + toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
  742 + status = RpcStatus.FAILED;
  743 + response =
  744 + JacksonUtil.newObjectNode()
  745 + .put(
  746 + "error",
  747 + "There was a Timeout and all retry attempts have been exhausted. Retry attempts set: "
  748 + + maxRpcRetries);
856 } else { 749 } else {
857 - clientAttributes = new ArrayList<>(result.size());  
858 - for (AttributeKvEntry attrEntry : result) {  
859 - clientAttributes.add(toTsKvProto(attrEntry));  
860 - }  
861 - }  
862 - return clientAttributes;  
863 - }  
864 -  
865 - private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {  
866 - return TsKvProto.newBuilder().setTs(attrEntry.getLastUpdateTs())  
867 - .setKv(toKeyValueProto(attrEntry)).build();  
868 - }  
869 -  
870 - private KeyValueProto toKeyValueProto(KvEntry kvEntry) {  
871 - KeyValueProto.Builder builder = KeyValueProto.newBuilder();  
872 - builder.setKey(kvEntry.getKey());  
873 - switch (kvEntry.getDataType()) {  
874 - case BOOLEAN:  
875 - builder.setType(KeyValueType.BOOLEAN_V);  
876 - builder.setBoolV(kvEntry.getBooleanValue().get());  
877 - break;  
878 - case DOUBLE:  
879 - builder.setType(KeyValueType.DOUBLE_V);  
880 - builder.setDoubleV(kvEntry.getDoubleValue().get());  
881 - break;  
882 - case LONG:  
883 - builder.setType(KeyValueType.LONG_V);  
884 - builder.setLongV(kvEntry.getLongValue().get());  
885 - break;  
886 - case STRING:  
887 - builder.setType(KeyValueType.STRING_V);  
888 - builder.setStringV(kvEntry.getStrValue().get());  
889 - break;  
890 - case JSON:  
891 - builder.setType(KeyValueType.JSON_V);  
892 - builder.setJsonV(kvEntry.getJsonValue().get());  
893 - break; 750 + md.setRetries(md.getRetries() + 1);
894 } 751 }
895 - return builder.build(); 752 + }
  753 +
  754 + if (md.getMsg().getMsg().isPersisted()) {
  755 + systemContext.getTbRpcService().save(tenantId, new RpcId(rpcId), status, response);
  756 + }
  757 + if (status != RpcStatus.SENT) {
  758 + sendNextPendingRequest(context);
  759 + }
  760 + } else {
  761 + log.info("[{}][{}] Rpc has already removed from pending map.", deviceId, rpcId);
896 } 762 }
897 -  
898 - void restoreSessions() {  
899 - if (systemContext.isLocalCacheType()) {  
900 - return;  
901 - }  
902 - log.debug("[{}] Restoring sessions from cache", deviceId);  
903 - DeviceSessionsCacheEntry sessionsDump = null;  
904 - try {  
905 - sessionsDump = DeviceSessionsCacheEntry.parseFrom(systemContext.getDeviceSessionCacheService().get(deviceId));  
906 - } catch (InvalidProtocolBufferException e) {  
907 - log.warn("[{}] Failed to decode device sessions from cache", deviceId);  
908 - return;  
909 - }  
910 - if (sessionsDump.getSessionsCount() == 0) {  
911 - log.debug("[{}] No session information found", deviceId);  
912 - return;  
913 - }  
914 - // TODO: Take latest max allowed sessions size from cache  
915 - for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto : sessionsDump.getSessionsList()) {  
916 - SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();  
917 - UUID sessionId = getSessionId(sessionInfoProto);  
918 - SessionInfo sessionInfo = new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId());  
919 - SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo();  
920 - SessionInfoMetaData sessionMD = new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime());  
921 - sessions.put(sessionId, sessionMD);  
922 - if (subInfo.getAttributeSubscription()) {  
923 - attributeSubscriptions.put(sessionId, sessionInfo);  
924 - sessionMD.setSubscribedToAttributes(true);  
925 - }  
926 - if (subInfo.getRpcSubscription()) {  
927 - rpcSubscriptions.put(sessionId, sessionInfo);  
928 - sessionMD.setSubscribedToRPC(true);  
929 - }  
930 - log.debug("[{}] Restored session: {}", deviceId, sessionMD);  
931 - }  
932 - log.debug("[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size()); 763 + }
  764 +
  765 + private void processSubscriptionCommands(
  766 + TbActorCtx context,
  767 + SessionInfoProto sessionInfo,
  768 + SubscribeToAttributeUpdatesMsg subscribeCmd) {
  769 + UUID sessionId = getSessionId(sessionInfo);
  770 + if (subscribeCmd.getUnsubscribe()) {
  771 + log.debug("[{}] Canceling attributes subscription for session [{}]", deviceId, sessionId);
  772 + attributeSubscriptions.remove(sessionId);
  773 + } else {
  774 + SessionInfoMetaData sessionMD = sessions.get(sessionId);
  775 + if (sessionMD == null) {
  776 + sessionMD =
  777 + new SessionInfoMetaData(
  778 + new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));
  779 + }
  780 + sessionMD.setSubscribedToAttributes(true);
  781 + log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId);
  782 + attributeSubscriptions.put(sessionId, sessionMD.getSessionInfo());
  783 + dumpSessions();
  784 + }
  785 + }
  786 +
  787 + private UUID getSessionId(SessionInfoProto sessionInfo) {
  788 + return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
  789 + }
  790 +
  791 + private void processSubscriptionCommands(
  792 + TbActorCtx context, SessionInfoProto sessionInfo, SubscribeToRPCMsg subscribeCmd) {
  793 + UUID sessionId = getSessionId(sessionInfo);
  794 + if (subscribeCmd.getUnsubscribe()) {
  795 + log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId);
  796 + rpcSubscriptions.remove(sessionId);
  797 + } else {
  798 + SessionInfoMetaData sessionMD = sessions.get(sessionId);
  799 + if (sessionMD == null) {
  800 + sessionMD =
  801 + new SessionInfoMetaData(
  802 + new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId()));
  803 + }
  804 + sessionMD.setSubscribedToRPC(true);
  805 + log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId);
  806 + rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo());
  807 + sendPendingRequests(context, sessionId, sessionInfo.getNodeId());
  808 + dumpSessions();
  809 + }
  810 + }
  811 +
  812 + private void processSessionStateMsgs(SessionInfoProto sessionInfo, SessionEventMsg msg) {
  813 + UUID sessionId = getSessionId(sessionInfo);
  814 + Objects.requireNonNull(sessionId);
  815 + if (msg.getEvent() == SessionEvent.OPEN) {
  816 + if (sessions.containsKey(sessionId)) {
  817 + log.debug("[{}] Received duplicate session open event [{}]", deviceId, sessionId);
  818 + return;
  819 + }
  820 + log.debug(
  821 + "[{}] Processing new session [{}]. Current sessions size {}",
  822 + deviceId,
  823 + sessionId,
  824 + sessions.size());
  825 +
  826 + sessions.put(
  827 + sessionId,
  828 + new SessionInfoMetaData(new SessionInfo(SessionType.ASYNC, sessionInfo.getNodeId())));
  829 + if (sessions.size() == 1) {
  830 + reportSessionOpen();
  831 + }
  832 + systemContext
  833 + .getDeviceStateService()
  834 + .onDeviceActivity(tenantId, deviceId, System.currentTimeMillis());
  835 + dumpSessions();
  836 + } else if (msg.getEvent() == SessionEvent.CLOSED) {
  837 + log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId);
  838 + sessions.remove(sessionId);
  839 + attributeSubscriptions.remove(sessionId);
  840 + rpcSubscriptions.remove(sessionId);
  841 + if (sessions.isEmpty()) {
  842 + reportSessionClose();
  843 + }
  844 + dumpSessions();
  845 + }
  846 + }
  847 +
  848 + private void handleSessionActivity(
  849 + TbActorCtx context,
  850 + SessionInfoProto sessionInfoProto,
  851 + SubscriptionInfoProto subscriptionInfo) {
  852 + UUID sessionId = getSessionId(sessionInfoProto);
  853 + Objects.requireNonNull(sessionId);
  854 +
  855 + SessionInfoMetaData sessionMD = sessions.get(sessionId);
  856 + if (sessionMD != null) {
  857 + sessionMD.setLastActivityTime(subscriptionInfo.getLastActivityTime());
  858 + sessionMD.setSubscribedToAttributes(subscriptionInfo.getAttributeSubscription());
  859 + sessionMD.setSubscribedToRPC(subscriptionInfo.getRpcSubscription());
  860 + if (subscriptionInfo.getAttributeSubscription()) {
  861 + attributeSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
  862 + }
  863 + if (subscriptionInfo.getRpcSubscription()) {
  864 + rpcSubscriptions.putIfAbsent(sessionId, sessionMD.getSessionInfo());
  865 + }
933 } 866 }
  867 + systemContext
  868 + .getDeviceStateService()
  869 + .onDeviceActivity(tenantId, deviceId, subscriptionInfo.getLastActivityTime());
  870 + if (sessionMD != null) {
  871 + dumpSessions();
  872 + }
  873 + }
  874 +
  875 + void processCredentialsUpdate(TbActorMsg msg) {
  876 + if (((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials().getCredentialsType()
  877 + == DeviceCredentialsType.LWM2M_CREDENTIALS) {
  878 + sessions.forEach(
  879 + (k, v) -> {
  880 + notifyTransportAboutDeviceCredentialsUpdate(
  881 + k, v, ((DeviceCredentialsUpdateNotificationMsg) msg).getDeviceCredentials());
  882 + });
  883 + } else {
  884 + sessions.forEach(
  885 + (sessionId, sessionMd) ->
  886 + notifyTransportAboutClosedSession(
  887 + sessionId, sessionMd, "device credentials updated!"));
  888 + attributeSubscriptions.clear();
  889 + rpcSubscriptions.clear();
  890 + dumpSessions();
  891 + }
  892 + }
  893 +
  894 + private void notifyTransportAboutClosedSessionMaxSessionsLimit(
  895 + UUID sessionId, SessionInfoMetaData sessionMd) {
  896 + log.debug(
  897 + "remove eldest session (max concurrent sessions limit reached per device) sessionId [{}] sessionMd [{}]",
  898 + sessionId,
  899 + sessionMd);
  900 + notifyTransportAboutClosedSession(
  901 + sessionId, sessionMd, "max concurrent sessions limit reached per device!");
  902 + }
  903 +
  904 + private void notifyTransportAboutClosedSession(
  905 + UUID sessionId, SessionInfoMetaData sessionMd, String message) {
  906 + SessionCloseNotificationProto sessionCloseNotificationProto =
  907 + SessionCloseNotificationProto.newBuilder().setMessage(message).build();
  908 + ToTransportMsg msg =
  909 + ToTransportMsg.newBuilder()
  910 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  911 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  912 + .setSessionCloseNotification(sessionCloseNotificationProto)
  913 + .build();
  914 + systemContext
  915 + .getTbCoreToTransportService()
  916 + .process(sessionMd.getSessionInfo().getNodeId(), msg);
  917 + }
  918 +
  919 + void notifyTransportAboutDeviceCredentialsUpdate(
  920 + UUID sessionId, SessionInfoMetaData sessionMd, DeviceCredentials deviceCredentials) {
  921 + ToTransportUpdateCredentialsProto.Builder notification =
  922 + ToTransportUpdateCredentialsProto.newBuilder();
  923 + notification.addCredentialsId(deviceCredentials.getCredentialsId());
  924 + notification.addCredentialsValue(deviceCredentials.getCredentialsValue());
  925 + ToTransportMsg msg =
  926 + ToTransportMsg.newBuilder()
  927 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  928 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  929 + .setToTransportUpdateCredentialsNotification(notification)
  930 + .build();
  931 + systemContext
  932 + .getTbCoreToTransportService()
  933 + .process(sessionMd.getSessionInfo().getNodeId(), msg);
  934 + }
  935 +
  936 + void processNameOrTypeUpdate(DeviceNameOrTypeUpdateMsg msg) {
  937 + this.deviceName = msg.getDeviceName();
  938 + this.deviceType = msg.getDeviceType();
  939 + this.defaultMetaData = new TbMsgMetaData();
  940 + this.defaultMetaData.putValue("deviceName", deviceName);
  941 + this.defaultMetaData.putValue("deviceType", deviceType);
  942 + }
  943 +
  944 + void processEdgeUpdate(DeviceEdgeUpdateMsg msg) {
  945 + log.trace("[{}] Processing edge update {}", deviceId, msg);
  946 + this.edgeId = msg.getEdgeId();
  947 + }
  948 +
  949 + private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
  950 + ToTransportMsg msg =
  951 + ToTransportMsg.newBuilder()
  952 + .setSessionIdMSB(sessionInfo.getSessionIdMSB())
  953 + .setSessionIdLSB(sessionInfo.getSessionIdLSB())
  954 + .setGetAttributesResponse(responseMsg)
  955 + .build();
  956 + systemContext.getTbCoreToTransportService().process(sessionInfo.getNodeId(), msg);
  957 + }
  958 +
  959 + private void sendToTransport(
  960 + AttributeUpdateNotificationMsg notificationMsg, UUID sessionId, String nodeId) {
  961 + ToTransportMsg msg =
  962 + ToTransportMsg.newBuilder()
  963 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  964 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  965 + .setAttributeUpdateNotification(notificationMsg)
  966 + .build();
  967 + systemContext.getTbCoreToTransportService().process(nodeId, msg);
  968 + }
  969 +
  970 + private void sendToTransport(ToDeviceRpcRequestMsg rpcMsg, UUID sessionId, String nodeId) {
  971 + ToTransportMsg msg =
  972 + ToTransportMsg.newBuilder()
  973 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  974 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  975 + .setToDeviceRequest(rpcMsg)
  976 + .build();
  977 + systemContext.getTbCoreToTransportService().process(nodeId, msg);
  978 + }
  979 +
  980 + private void sendToTransport(ToServerRpcResponseMsg rpcMsg, UUID sessionId, String nodeId) {
  981 + ToTransportMsg msg =
  982 + ToTransportMsg.newBuilder()
  983 + .setSessionIdMSB(sessionId.getMostSignificantBits())
  984 + .setSessionIdLSB(sessionId.getLeastSignificantBits())
  985 + .setToServerResponse(rpcMsg)
  986 + .build();
  987 + systemContext.getTbCoreToTransportService().process(nodeId, msg);
  988 + }
  989 +
  990 + private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
  991 + EdgeEvent edgeEvent = new EdgeEvent();
  992 + edgeEvent.setTenantId(tenantId);
  993 + edgeEvent.setAction(EdgeEventActionType.RPC_CALL);
  994 + edgeEvent.setEntityId(deviceId.getId());
  995 + edgeEvent.setType(EdgeEventType.DEVICE);
  996 +
  997 + ObjectNode body = mapper.createObjectNode();
  998 + body.put("requestId", requestId);
  999 + body.put("requestUUID", msg.getId().toString());
  1000 + body.put("oneway", msg.isOneway());
  1001 + body.put("expirationTime", msg.getExpirationTime());
  1002 + body.put("method", msg.getBody().getMethod());
  1003 + body.put("params", msg.getBody().getParams());
  1004 + edgeEvent.setBody(body);
  1005 +
  1006 + edgeEvent.setEdgeId(edgeId);
  1007 + systemContext.getEdgeEventService().save(edgeEvent);
  1008 + systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
  1009 + }
  1010 +
  1011 + private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
  1012 + List<TsKvProto> clientAttributes;
  1013 + if (result == null || result.isEmpty()) {
  1014 + clientAttributes = Collections.emptyList();
  1015 + } else {
  1016 + clientAttributes = new ArrayList<>(result.size());
  1017 + for (AttributeKvEntry attrEntry : result) {
  1018 + clientAttributes.add(toTsKvProto(attrEntry));
  1019 + }
  1020 + }
  1021 + return clientAttributes;
  1022 + }
  1023 +
  1024 + private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {
  1025 + return TsKvProto.newBuilder()
  1026 + .setTs(attrEntry.getLastUpdateTs())
  1027 + .setKv(toKeyValueProto(attrEntry))
  1028 + .build();
  1029 + }
  1030 +
  1031 + private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
  1032 + KeyValueProto.Builder builder = KeyValueProto.newBuilder();
  1033 + builder.setKey(kvEntry.getKey());
  1034 + switch (kvEntry.getDataType()) {
  1035 + case BOOLEAN:
  1036 + builder.setType(KeyValueType.BOOLEAN_V);
  1037 + builder.setBoolV(kvEntry.getBooleanValue().get());
  1038 + break;
  1039 + case DOUBLE:
  1040 + builder.setType(KeyValueType.DOUBLE_V);
  1041 + builder.setDoubleV(kvEntry.getDoubleValue().get());
  1042 + break;
  1043 + case LONG:
  1044 + builder.setType(KeyValueType.LONG_V);
  1045 + builder.setLongV(kvEntry.getLongValue().get());
  1046 + break;
  1047 + case STRING:
  1048 + builder.setType(KeyValueType.STRING_V);
  1049 + builder.setStringV(kvEntry.getStrValue().get());
  1050 + break;
  1051 + case JSON:
  1052 + builder.setType(KeyValueType.JSON_V);
  1053 + builder.setJsonV(kvEntry.getJsonValue().get());
  1054 + break;
  1055 + }
  1056 + return builder.build();
  1057 + }
934 1058
935 - private void dumpSessions() {  
936 - if (systemContext.isLocalCacheType()) { 1059 + void restoreSessions() {
  1060 + if (systemContext.isLocalCacheType()) {
  1061 + return;
  1062 + }
  1063 + log.debug("[{}] Restoring sessions from cache", deviceId);
  1064 + DeviceSessionsCacheEntry sessionsDump = null;
  1065 + try {
  1066 + sessionsDump =
  1067 + DeviceSessionsCacheEntry.parseFrom(
  1068 + systemContext.getDeviceSessionCacheService().get(deviceId));
  1069 + } catch (InvalidProtocolBufferException e) {
  1070 + log.warn("[{}] Failed to decode device sessions from cache", deviceId);
  1071 + return;
  1072 + }
  1073 + if (sessionsDump.getSessionsCount() == 0) {
  1074 + log.debug("[{}] No session information found", deviceId);
  1075 + return;
  1076 + }
  1077 + // TODO: Take latest max allowed sessions size from cache
  1078 + for (SessionSubscriptionInfoProto sessionSubscriptionInfoProto :
  1079 + sessionsDump.getSessionsList()) {
  1080 + SessionInfoProto sessionInfoProto = sessionSubscriptionInfoProto.getSessionInfo();
  1081 + UUID sessionId = getSessionId(sessionInfoProto);
  1082 + SessionInfo sessionInfo = new SessionInfo(SessionType.ASYNC, sessionInfoProto.getNodeId());
  1083 + SubscriptionInfoProto subInfo = sessionSubscriptionInfoProto.getSubscriptionInfo();
  1084 + SessionInfoMetaData sessionMD =
  1085 + new SessionInfoMetaData(sessionInfo, subInfo.getLastActivityTime());
  1086 + sessions.put(sessionId, sessionMD);
  1087 + if (subInfo.getAttributeSubscription()) {
  1088 + attributeSubscriptions.put(sessionId, sessionInfo);
  1089 + sessionMD.setSubscribedToAttributes(true);
  1090 + }
  1091 + if (subInfo.getRpcSubscription()) {
  1092 + rpcSubscriptions.put(sessionId, sessionInfo);
  1093 + sessionMD.setSubscribedToRPC(true);
  1094 + }
  1095 + log.debug("[{}] Restored session: {}", deviceId, sessionMD);
  1096 + }
  1097 + log.debug(
  1098 + "[{}] Restored sessions: {}, rpc subscriptions: {}, attribute subscriptions: {}",
  1099 + deviceId,
  1100 + sessions.size(),
  1101 + rpcSubscriptions.size(),
  1102 + attributeSubscriptions.size());
  1103 + }
  1104 +
  1105 + private void dumpSessions() {
  1106 + if (systemContext.isLocalCacheType()) {
  1107 + return;
  1108 + }
  1109 + log.debug(
  1110 + "[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache",
  1111 + deviceId,
  1112 + sessions.size(),
  1113 + rpcSubscriptions.size(),
  1114 + attributeSubscriptions.size());
  1115 + List<SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size());
  1116 + sessions.forEach(
  1117 + (uuid, sessionMD) -> {
  1118 + if (sessionMD.getSessionInfo().getType() == SessionType.SYNC) {
937 return; 1119 return;
938 - }  
939 - log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());  
940 - List<SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size());  
941 - sessions.forEach((uuid, sessionMD) -> {  
942 - if (sessionMD.getSessionInfo().getType() == SessionType.SYNC) {  
943 - return;  
944 - }  
945 - SessionInfo sessionInfo = sessionMD.getSessionInfo();  
946 - SubscriptionInfoProto subscriptionInfoProto = SubscriptionInfoProto.newBuilder()  
947 - .setLastActivityTime(sessionMD.getLastActivityTime())  
948 - .setAttributeSubscription(sessionMD.isSubscribedToAttributes())  
949 - .setRpcSubscription(sessionMD.isSubscribedToRPC()).build();  
950 - SessionInfoProto sessionInfoProto = SessionInfoProto.newBuilder()  
951 - .setSessionIdMSB(uuid.getMostSignificantBits())  
952 - .setSessionIdLSB(uuid.getLeastSignificantBits())  
953 - .setNodeId(sessionInfo.getNodeId()).build();  
954 - sessionsList.add(SessionSubscriptionInfoProto.newBuilder()  
955 - .setSessionInfo(sessionInfoProto)  
956 - .setSubscriptionInfo(subscriptionInfoProto).build());  
957 - log.debug("[{}] Dumping session: {}", deviceId, sessionMD); 1120 + }
  1121 + SessionInfo sessionInfo = sessionMD.getSessionInfo();
  1122 + SubscriptionInfoProto subscriptionInfoProto =
  1123 + SubscriptionInfoProto.newBuilder()
  1124 + .setLastActivityTime(sessionMD.getLastActivityTime())
  1125 + .setAttributeSubscription(sessionMD.isSubscribedToAttributes())
  1126 + .setRpcSubscription(sessionMD.isSubscribedToRPC())
  1127 + .build();
  1128 + SessionInfoProto sessionInfoProto =
  1129 + SessionInfoProto.newBuilder()
  1130 + .setSessionIdMSB(uuid.getMostSignificantBits())
  1131 + .setSessionIdLSB(uuid.getLeastSignificantBits())
  1132 + .setNodeId(sessionInfo.getNodeId())
  1133 + .build();
  1134 + sessionsList.add(
  1135 + SessionSubscriptionInfoProto.newBuilder()
  1136 + .setSessionInfo(sessionInfoProto)
  1137 + .setSubscriptionInfo(subscriptionInfoProto)
  1138 + .build());
  1139 + log.debug("[{}] Dumping session: {}", deviceId, sessionMD);
958 }); 1140 });
959 - systemContext.getDeviceSessionCacheService()  
960 - .put(deviceId, DeviceSessionsCacheEntry.newBuilder()  
961 - .addAllSessions(sessionsList).build().toByteArray());  
962 - }  
963 -  
964 - void init(TbActorCtx ctx) {  
965 - PageLink pageLink = new PageLink(1024, 0, null, new SortOrder("createdTime"));  
966 - PageData<Rpc> pageData;  
967 - do {  
968 - pageData = systemContext.getTbRpcService().findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink);  
969 - pageData.getData().forEach(rpc -> {  
970 - ToDeviceRpcRequest msg = JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class); 1141 + systemContext
  1142 + .getDeviceSessionCacheService()
  1143 + .put(
  1144 + deviceId,
  1145 + DeviceSessionsCacheEntry.newBuilder()
  1146 + .addAllSessions(sessionsList)
  1147 + .build()
  1148 + .toByteArray());
  1149 + }
  1150 +
  1151 + void init(TbActorCtx ctx) {
  1152 + PageLink pageLink = new PageLink(1024, 0, null, new SortOrder("createdTime"));
  1153 + PageData<Rpc> pageData;
  1154 + do {
  1155 + pageData =
  1156 + systemContext
  1157 + .getTbRpcService()
  1158 + .findAllByDeviceIdAndStatus(tenantId, deviceId, RpcStatus.QUEUED, pageLink);
  1159 + pageData
  1160 + .getData()
  1161 + .forEach(
  1162 + rpc -> {
  1163 + ToDeviceRpcRequest msg =
  1164 + JacksonUtil.convertValue(rpc.getRequest(), ToDeviceRpcRequest.class);
971 long timeout = rpc.getExpirationTime() - System.currentTimeMillis(); 1165 long timeout = rpc.getExpirationTime() - System.currentTimeMillis();
972 if (timeout <= 0) { 1166 if (timeout <= 0) {
973 - rpc.setStatus(RpcStatus.EXPIRED);  
974 - systemContext.getTbRpcService().save(tenantId, rpc); 1167 + rpc.setStatus(RpcStatus.EXPIRED);
  1168 + systemContext.getTbRpcService().save(tenantId, rpc);
975 } else { 1169 } else {
976 - registerPendingRpcRequest(ctx, new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg), false, creteToDeviceRpcRequestMsg(msg), timeout);  
977 - }  
978 - });  
979 - if (pageData.hasNext()) {  
980 - pageLink = pageLink.nextPageLink();  
981 - }  
982 - } while (pageData.hasNext());  
983 - }  
984 -  
985 - void checkSessionsTimeout() {  
986 - final long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout();  
987 - List<UUID> expiredIds = null;  
988 -  
989 - for (Map.Entry<UUID, SessionInfoMetaData> kv : sessions.entrySet()) { //entry set are cached for stable sessions  
990 - if (kv.getValue().getLastActivityTime() < expTime) {  
991 - final UUID id = kv.getKey();  
992 - if (expiredIds == null) {  
993 - expiredIds = new ArrayList<>(1); //most of the expired sessions is a single event 1170 + registerPendingRpcRequest(
  1171 + ctx,
  1172 + new ToDeviceRpcRequestActorMsg(systemContext.getServiceId(), msg),
  1173 + false,
  1174 + creteToDeviceRpcRequestMsg(msg),
  1175 + timeout);
994 } 1176 }
995 - expiredIds.add(id);  
996 - } 1177 + });
  1178 + if (pageData.hasNext()) {
  1179 + pageLink = pageLink.nextPageLink();
  1180 + }
  1181 + } while (pageData.hasNext());
  1182 + }
  1183 +
  1184 + void checkSessionsTimeout() {
  1185 + final long expTime = System.currentTimeMillis() - systemContext.getSessionInactivityTimeout();
  1186 + List<UUID> expiredIds = null;
  1187 +
  1188 + for (Map.Entry<UUID, SessionInfoMetaData> kv :
  1189 + sessions.entrySet()) { // entry set are cached for stable sessions
  1190 + if (kv.getValue().getLastActivityTime() < expTime) {
  1191 + final UUID id = kv.getKey();
  1192 + if (expiredIds == null) {
  1193 + expiredIds = new ArrayList<>(1); // most of the expired sessions is a single event
997 } 1194 }
  1195 + expiredIds.add(id);
  1196 + }
  1197 + }
998 1198
999 - if (expiredIds != null) {  
1000 - int removed = 0;  
1001 - for (UUID id : expiredIds) {  
1002 - final SessionInfoMetaData session = sessions.remove(id);  
1003 - rpcSubscriptions.remove(id);  
1004 - attributeSubscriptions.remove(id);  
1005 - if (session != null) {  
1006 - removed++;  
1007 - notifyTransportAboutClosedSession(id, session, SESSION_TIMEOUT_MESSAGE);  
1008 - }  
1009 - }  
1010 - if (removed != 0) {  
1011 - dumpSessions();  
1012 - } 1199 + if (expiredIds != null) {
  1200 + int removed = 0;
  1201 + for (UUID id : expiredIds) {
  1202 + final SessionInfoMetaData session = sessions.remove(id);
  1203 + rpcSubscriptions.remove(id);
  1204 + attributeSubscriptions.remove(id);
  1205 + if (session != null) {
  1206 + removed++;
  1207 + notifyTransportAboutClosedSession(id, session, SESSION_TIMEOUT_MESSAGE);
1013 } 1208 }
1014 - 1209 + }
  1210 + if (removed != 0) {
  1211 + dumpSessions();
  1212 + }
1015 } 1213 }
1016 - 1214 + }
1017 } 1215 }
@@ -78,6 +78,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; @@ -78,6 +78,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
78 import org.thingsboard.server.dao.tenant.TenantService; 78 import org.thingsboard.server.dao.tenant.TenantService;
79 import org.thingsboard.server.dao.timeseries.TimeseriesService; 79 import org.thingsboard.server.dao.timeseries.TimeseriesService;
80 import org.thingsboard.server.dao.user.UserService; 80 import org.thingsboard.server.dao.user.UserService;
  81 +import org.thingsboard.server.dao.yunteng.service.TkDeviceService;
81 import org.thingsboard.server.gen.transport.TransportProtos; 82 import org.thingsboard.server.gen.transport.TransportProtos;
82 import org.thingsboard.server.queue.TbQueueCallback; 83 import org.thingsboard.server.queue.TbQueueCallback;
83 import org.thingsboard.server.queue.TbQueueMsgMetadata; 84 import org.thingsboard.server.queue.TbQueueMsgMetadata;
@@ -689,4 +690,10 @@ class DefaultTbContext implements TbContext { @@ -689,4 +690,10 @@ class DefaultTbContext implements TbContext {
689 } 690 }
690 } 691 }
691 } 692 }
  693 +
  694 + //Thingskit function
  695 + @Override
  696 + public TkDeviceService getTkDeviceService() {
  697 + return mainCtx.getTkDeviceService();
  698 + }
692 } 699 }
@@ -17,8 +17,8 @@ import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; @@ -17,8 +17,8 @@ import org.thingsboard.server.common.data.yunteng.common.DeleteGroup;
17 import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; 17 import org.thingsboard.server.common.data.yunteng.common.UpdateGroup;
18 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; 18 import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO;
19 import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; 19 import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO;
20 -import org.thingsboard.server.common.data.yunteng.dto.SceneLinkageDTO;  
21 -import org.thingsboard.server.common.data.yunteng.dto.TriggerDTO; 20 +import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDTO;
  21 +import org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO;
22 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; 22 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
23 import org.thingsboard.server.common.data.yunteng.id.SceneId; 23 import org.thingsboard.server.common.data.yunteng.id.SceneId;
24 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; 24 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData;
@@ -21,27 +21,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode; @@ -21,27 +21,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
21 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
24 -import org.thingsboard.common.util.JacksonUtil;  
25 import org.thingsboard.common.util.ThingsBoardThreadFactory; 24 import org.thingsboard.common.util.ThingsBoardThreadFactory;
26 import org.thingsboard.server.actors.ActorSystemContext; 25 import org.thingsboard.server.actors.ActorSystemContext;
27 import org.thingsboard.server.cluster.TbClusterService; 26 import org.thingsboard.server.cluster.TbClusterService;
28 import org.thingsboard.server.common.data.DataConstants; 27 import org.thingsboard.server.common.data.DataConstants;
29 import org.thingsboard.server.common.data.Device; 28 import org.thingsboard.server.common.data.Device;
30 -import org.thingsboard.server.common.data.StringUtils;  
31 import org.thingsboard.server.common.data.User; 29 import org.thingsboard.server.common.data.User;
32 -import org.thingsboard.server.common.data.id.DeviceId;  
33 import org.thingsboard.server.common.data.rpc.RpcError; 30 import org.thingsboard.server.common.data.rpc.RpcError;
34 -import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;  
35 -import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;  
36 -import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO;  
37 -import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;  
38 import org.thingsboard.server.common.msg.TbMsg; 31 import org.thingsboard.server.common.msg.TbMsg;
39 import org.thingsboard.server.common.msg.TbMsgDataType; 32 import org.thingsboard.server.common.msg.TbMsgDataType;
40 import org.thingsboard.server.common.msg.TbMsgMetaData; 33 import org.thingsboard.server.common.msg.TbMsgMetaData;
41 import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; 34 import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse;
42 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; 35 import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest;
43 import org.thingsboard.server.dao.device.DeviceService; 36 import org.thingsboard.server.dao.device.DeviceService;
44 -import org.thingsboard.server.dao.yunteng.service.TkDeviceService;  
45 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 37 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
46 import org.thingsboard.server.queue.util.TbCoreComponent; 38 import org.thingsboard.server.queue.util.TbCoreComponent;
47 import org.thingsboard.server.service.security.model.SecurityUser; 39 import org.thingsboard.server.service.security.model.SecurityUser;
@@ -79,8 +71,6 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { @@ -79,8 +71,6 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
79 private ScheduledExecutorService scheduler; 71 private ScheduledExecutorService scheduler;
80 private String serviceId; 72 private String serviceId;
81 73
82 - @Autowired  
83 - protected TkDeviceService tkDeviceService;  
84 public DefaultTbCoreDeviceRpcService(DeviceService deviceService, TbClusterService clusterService, TbServiceInfoProvider serviceInfoProvider, 74 public DefaultTbCoreDeviceRpcService(DeviceService deviceService, TbClusterService clusterService, TbServiceInfoProvider serviceInfoProvider,
85 ActorSystemContext actorContext) { 75 ActorSystemContext actorContext) {
86 this.deviceService = deviceService; 76 this.deviceService = deviceService;
@@ -110,32 +100,6 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService { @@ -110,32 +100,6 @@ public class DefaultTbCoreDeviceRpcService implements TbCoreDeviceRpcService {
110 @Override 100 @Override
111 public void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer, SecurityUser currentUser) { 101 public void processRestApiRpcRequest(ToDeviceRpcRequest request, Consumer<FromDeviceRpcResponse> responseConsumer, SecurityUser currentUser) {
112 log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId()); 102 log.trace("[{}][{}] Processing REST API call to rule engine [{}]", request.getTenantId(), request.getId(), request.getDeviceId());
113 -  
114 - //Thingskit function 修改RPC三要素:设备ID、参数、附加信息  
115 - //只有命令下发的时候走此逻辑,其他method的时候不走以下逻辑  
116 - ToDeviceRpcRequestBody body = request.getBody();  
117 - if(body.getMethod().contains("method"))  
118 - {  
119 - DeviceId targetId = request.getDeviceId();  
120 - DeviceDTO targetDevice = tkDeviceService.findDeviceInfoByTbDeviceId(currentUser.getCurrentTenantId(), targetId.getId().toString());  
121 - DeviceId netEnableId = DeviceTypeEnum.SENSOR == targetDevice.getDeviceType()?new DeviceId(UUID.fromString(targetDevice.getGatewayId())):targetId;  
122 - String paramStr = body.getParams();  
123 - if(DeviceTypeEnum.SENSOR == targetDevice.getDeviceType() && paramStr.contains("}")){  
124 - ObjectNode methodParams = (ObjectNode) JacksonUtil.toJsonNode(paramStr);  
125 - methodParams.put(FastIotConstants.Rpc.TARGET_NAME,targetDevice.getName());  
126 - body = new ToDeviceRpcRequestBody(body.getMethod(),JacksonUtil.toString(methodParams));  
127 - }  
128 -  
129 - ObjectNode additional = JacksonUtil.newObjectNode();  
130 - if(StringUtils.isNotBlank(request.getAdditionalInfo())){  
131 - additional = (ObjectNode) JacksonUtil.toJsonNode(request.getAdditionalInfo());  
132 - }  
133 - if(!additional.has(FastIotConstants.Rpc.TARGET_ID)){  
134 - additional.put(FastIotConstants.Rpc.TARGET_ID, targetId.getId().toString());  
135 - }  
136 - request = new ToDeviceRpcRequest(request.getId(),request.getTenantId(),netEnableId,request.isOneway(),request.getExpirationTime(),body,request.isPersisted(),request.getRetries(),JacksonUtil.toString(additional));  
137 -  
138 - }  
139 UUID requestId = request.getId(); 103 UUID requestId = request.getId();
140 localToRuleEngineRpcRequests.put(requestId, responseConsumer); 104 localToRuleEngineRpcRequests.put(requestId, responseConsumer);
141 sendRpcRequestToRuleEngine(request, currentUser); 105 sendRpcRequestToRuleEngine(request, currentUser);
@@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.yunteng.dto; @@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.yunteng.dto;
3 import io.swagger.annotations.ApiModelProperty; 3 import io.swagger.annotations.ApiModelProperty;
4 import lombok.Data; 4 import lombok.Data;
5 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 5 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  6 +import org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO;
6 7
7 import java.util.List; 8 import java.util.List;
8 9
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/scene/DoActionDTO.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/DoActionDTO.java
1 -package org.thingsboard.server.common.data.yunteng.dto; 1 +package org.thingsboard.server.common.data.yunteng.dto.scene;
2 2
3 import com.fasterxml.jackson.databind.JsonNode; 3 import com.fasterxml.jackson.databind.JsonNode;
4 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 5 import lombok.Data;
6 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
  7 +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO;
7 import org.thingsboard.server.common.data.yunteng.enums.ActionTypeEnum; 8 import org.thingsboard.server.common.data.yunteng.enums.ActionTypeEnum;
8 import org.thingsboard.server.common.data.yunteng.enums.CallTypeEnum; 9 import org.thingsboard.server.common.data.yunteng.enums.CallTypeEnum;
9 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; 10 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/scene/DoConditionDTO.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/DoConditionDTO.java
1 -package org.thingsboard.server.common.data.yunteng.dto; 1 +package org.thingsboard.server.common.data.yunteng.dto.scene;
2 2
3 import io.swagger.annotations.ApiModelProperty; 3 import io.swagger.annotations.ApiModelProperty;
  4 +import java.util.List;
4 import lombok.Data; 5 import lombok.Data;
5 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
6 -import org.thingsboard.server.common.data.device.profile.AlarmCondition;  
7 import org.thingsboard.server.common.data.device.profile.AlarmRule; 7 import org.thingsboard.server.common.data.device.profile.AlarmRule;
  8 +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO;
8 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; 9 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
9 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum; 10 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum;
10 import org.thingsboard.server.common.data.yunteng.enums.TriggerTypeEnum; 11 import org.thingsboard.server.common.data.yunteng.enums.TriggerTypeEnum;
11 12
12 -import java.util.List;  
13 -  
14 /** 13 /**
15 * @Description 场景联动执行条件数据传输表 @Author cxy @Date 2021/11/24 17:33 14 * @Description 场景联动执行条件数据传输表 @Author cxy @Date 2021/11/24 17:33
16 */ 15 */
  1 +package org.thingsboard.server.common.data.yunteng.dto.scene;
  2 +
  3 +import com.fasterxml.jackson.databind.JsonNode;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +
  6 +import java.io.Serializable;
  7 +import java.util.List;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO;
  11 +import org.thingsboard.server.common.data.yunteng.enums.ActionTypeEnum;
  12 +import org.thingsboard.server.common.data.yunteng.enums.CallTypeEnum;
  13 +import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
  14 +import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum;
  15 +
  16 +/**
  17 + * @Description 场景联动执行动作数据传输表 @Author cxy @Date 2021/11/24 17:32
  18 + */
  19 +@Data
  20 +public class RuleFilterDTO implements Serializable {
  21 +
  22 + @ApiModelProperty(value = "触发条件ID")
  23 + private String ruleId;
  24 +
  25 + @ApiModelProperty(value = "场景联动ID")
  26 + private String scenId;
  27 +
  28 +}
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/scene/SceneLinkageDTO.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/SceneLinkageDTO.java
1 -package org.thingsboard.server.common.data.yunteng.dto; 1 +package org.thingsboard.server.common.data.yunteng.dto.scene;
2 2
3 import io.swagger.annotations.ApiModelProperty; 3 import io.swagger.annotations.ApiModelProperty;
4 import lombok.Data; 4 import lombok.Data;
5 import lombok.EqualsAndHashCode; 5 import lombok.EqualsAndHashCode;
6 import org.thingsboard.server.common.data.HasName; 6 import org.thingsboard.server.common.data.HasName;
7 import org.thingsboard.server.common.data.yunteng.common.AddGroup; 7 import org.thingsboard.server.common.data.yunteng.common.AddGroup;
  8 +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO;
8 9
9 import javax.validation.constraints.NotEmpty; 10 import javax.validation.constraints.NotEmpty;
10 import java.util.List; 11 import java.util.List;
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/scene/TriggerDTO.java renamed from common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/TriggerDTO.java
1 -package org.thingsboard.server.common.data.yunteng.dto; 1 +package org.thingsboard.server.common.data.yunteng.dto.scene;
2 2
3 import io.swagger.annotations.ApiModelProperty; 3 import io.swagger.annotations.ApiModelProperty;
4 import lombok.Data; 4 import lombok.Data;
5 import lombok.EqualsAndHashCode; 5 import lombok.EqualsAndHashCode;
6 import org.thingsboard.server.common.data.device.profile.AlarmRule; 6 import org.thingsboard.server.common.data.device.profile.AlarmRule;
  7 +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO;
7 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; 8 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
8 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum; 9 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum;
9 import org.thingsboard.server.common.data.yunteng.enums.TriggerTypeEnum; 10 import org.thingsboard.server.common.data.yunteng.enums.TriggerTypeEnum;
@@ -13,6 +13,7 @@ import org.thingsboard.server.common.data.yunteng.core.exception.NoneTenantAsset @@ -13,6 +13,7 @@ import org.thingsboard.server.common.data.yunteng.core.exception.NoneTenantAsset
13 import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; 13 import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException;
14 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; 14 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
15 import org.thingsboard.server.common.data.yunteng.dto.*; 15 import org.thingsboard.server.common.data.yunteng.dto.*;
  16 +import org.thingsboard.server.common.data.yunteng.dto.scene.DoActionDTO;
16 import org.thingsboard.server.common.data.yunteng.utils.ReflectUtils; 17 import org.thingsboard.server.common.data.yunteng.utils.ReflectUtils;
17 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; 18 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData;
18 import org.thingsboard.server.dao.yunteng.entities.TkAlarmProfileEntity; 19 import org.thingsboard.server.dao.yunteng.entities.TkAlarmProfileEntity;
@@ -7,7 +7,7 @@ import java.util.Set; @@ -7,7 +7,7 @@ import java.util.Set;
7 import java.util.stream.Collectors; 7 import java.util.stream.Collectors;
8 import lombok.RequiredArgsConstructor; 8 import lombok.RequiredArgsConstructor;
9 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
10 -import org.thingsboard.server.common.data.yunteng.dto.DoActionDTO; 10 +import org.thingsboard.server.common.data.yunteng.dto.scene.DoActionDTO;
11 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum; 11 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum;
12 import org.thingsboard.server.dao.yunteng.entities.TenantBaseEntity; 12 import org.thingsboard.server.dao.yunteng.entities.TenantBaseEntity;
13 import org.thingsboard.server.dao.yunteng.entities.TkDeviceEntity; 13 import org.thingsboard.server.dao.yunteng.entities.TkDeviceEntity;
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 import com.baomidou.mybatisplus.core.metadata.IPage; 5 import com.baomidou.mybatisplus.core.metadata.IPage;
6 import com.fasterxml.jackson.databind.JsonNode; 6 import com.fasterxml.jackson.databind.JsonNode;
7 import com.fasterxml.jackson.databind.node.ObjectNode; 7 import com.fasterxml.jackson.databind.node.ObjectNode;
  8 +import java.util.*;
  9 +import java.util.stream.Collectors;
8 import lombok.RequiredArgsConstructor; 10 import lombok.RequiredArgsConstructor;
9 import org.apache.commons.lang3.StringUtils; 11 import org.apache.commons.lang3.StringUtils;
10 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
@@ -16,6 +18,7 @@ import org.thingsboard.server.common.data.yunteng.constant.ModelConstants; @@ -16,6 +18,7 @@ import org.thingsboard.server.common.data.yunteng.constant.ModelConstants;
16 import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; 18 import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException;
17 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; 19 import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage;
18 import org.thingsboard.server.common.data.yunteng.dto.*; 20 import org.thingsboard.server.common.data.yunteng.dto.*;
  21 +import org.thingsboard.server.common.data.yunteng.dto.scene.*;
19 import org.thingsboard.server.common.data.yunteng.enums.ActionTypeEnum; 22 import org.thingsboard.server.common.data.yunteng.enums.ActionTypeEnum;
20 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; 23 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
21 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum; 24 import org.thingsboard.server.common.data.yunteng.enums.ScopeEnum;
@@ -27,13 +30,13 @@ import org.thingsboard.server.dao.yunteng.entities.*; @@ -27,13 +30,13 @@ import org.thingsboard.server.dao.yunteng.entities.*;
27 import org.thingsboard.server.dao.yunteng.mapper.*; 30 import org.thingsboard.server.dao.yunteng.mapper.*;
28 import org.thingsboard.server.dao.yunteng.service.*; 31 import org.thingsboard.server.dao.yunteng.service.*;
29 32
30 -import java.util.*;  
31 -import java.util.stream.Collectors;  
32 -  
33 -/** @Description 场景联动业务实现层 @Author cxy @Date 2021/11/25 11:22 */ 33 +/**
  34 + * @Description 场景联动业务实现层 @Author cxy @Date 2021/11/25 11:22
  35 + */
34 @Service 36 @Service
35 @RequiredArgsConstructor 37 @RequiredArgsConstructor
36 -public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageMapper, TkSceneLinkageEntity> 38 +public class TkSceneLinkageServiceImpl
  39 + extends AbstractBaseService<SceneLinkageMapper, TkSceneLinkageEntity>
37 implements SceneLinkageService { 40 implements SceneLinkageService {
38 41
39 private final SceneLinkageMapper sceneLinkageMapper; 42 private final SceneLinkageMapper sceneLinkageMapper;
@@ -74,14 +77,20 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -74,14 +77,20 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
74 77
75 /** 78 /**
76 * 更新场景联动附加信息,触发器、执行条件、动作 79 * 更新场景联动附加信息,触发器、执行条件、动作
77 - * @param sceneLinkageDTO 场景联动表单数据  
78 - * @param tenantId 租户ID 80 + *
  81 + * @param sceneLinkageDTO 场景联动表单数据
  82 + * @param tenantId 租户ID
79 * @param customerId 客户ID 83 * @param customerId 客户ID
80 * @param sceneLinkage 场景联动主表实体 84 * @param sceneLinkage 场景联动主表实体
81 */ 85 */
82 - private void updateScene(SceneLinkageDTO sceneLinkageDTO, String tenantId, String customerId, TkSceneLinkageEntity sceneLinkage) { 86 + private void updateScene(
  87 + SceneLinkageDTO sceneLinkageDTO,
  88 + String tenantId,
  89 + String customerId,
  90 + TkSceneLinkageEntity sceneLinkage) {
83 String organizationId = sceneLinkage.getOrganizationId(); 91 String organizationId = sceneLinkage.getOrganizationId();
84 - List<DeviceDTO> organizationDevices = findDeviceList(organizationId, tenantId, customerId,new ArrayList<>()); 92 + List<DeviceDTO> organizationDevices =
  93 + findDeviceList(organizationId, tenantId, customerId, new ArrayList<>());
85 94
86 List<String> tbDeviceIds = new ArrayList<>(); 95 List<String> tbDeviceIds = new ArrayList<>();
87 for (DeviceDTO item : organizationDevices) { 96 for (DeviceDTO item : organizationDevices) {
@@ -105,7 +114,8 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -105,7 +114,8 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
105 new QueryWrapper<TkSceneLinkageEntity>() 114 new QueryWrapper<TkSceneLinkageEntity>()
106 .lambda() 115 .lambda()
107 .eq(TkSceneLinkageEntity::getTenantId, tenantId) 116 .eq(TkSceneLinkageEntity::getTenantId, tenantId)
108 -// .eq(StringUtils.isNotBlank(currentUserId),SceneLinkage::getCreator, currentUserId) 117 + // .eq(StringUtils.isNotBlank(currentUserId),SceneLinkage::getCreator,
  118 + // currentUserId)
109 .in(TkSceneLinkageEntity::getId, ids); 119 .in(TkSceneLinkageEntity::getId, ids);
110 int result = sceneLinkageMapper.delete(Wrapper); 120 int result = sceneLinkageMapper.delete(Wrapper);
111 if (result != ids.size()) { 121 if (result != ids.size()) {
@@ -239,19 +249,22 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -239,19 +249,22 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
239 doActionDTO.setTenantId(sceneLinkageDTO.getTenantId()); 249 doActionDTO.setTenantId(sceneLinkageDTO.getTenantId());
240 doActionDTO.setSceneLinkageId(sceneLinkageDTO.getId()); 250 doActionDTO.setSceneLinkageId(sceneLinkageDTO.getId());
241 if (ActionTypeEnum.DEVICE_OUT.equals(doActionDTO.getOutTarget())) { 251 if (ActionTypeEnum.DEVICE_OUT.equals(doActionDTO.getOutTarget())) {
242 - JsonNode inputContext = doActionDTO.getDoContext().get(FastIotConstants.Rpc.PARAMS_NAME); 252 + JsonNode inputContext =
  253 + doActionDTO.getDoContext().get(FastIotConstants.Rpc.PARAMS_NAME);
243 ObjectNode outputContext = JacksonUtil.newObjectNode(); 254 ObjectNode outputContext = JacksonUtil.newObjectNode();
244 outputContext.put(FastIotConstants.Rpc.METHOD_NAME, "methodThingskit"); 255 outputContext.put(FastIotConstants.Rpc.METHOD_NAME, "methodThingskit");
245 - if(inputContext == null){  
246 - outputContext.put(FastIotConstants.Rpc.PARAMS_NAME,"");  
247 - }else if(inputContext.isTextual()){  
248 - outputContext.put(FastIotConstants.Rpc.PARAMS_NAME,inputContext.asText());  
249 - }else{ 256 + if (inputContext == null) {
  257 + outputContext.put(FastIotConstants.Rpc.PARAMS_NAME, "");
  258 + } else if (inputContext.isTextual()) {
  259 + outputContext.put(FastIotConstants.Rpc.PARAMS_NAME, inputContext.asText());
  260 + } else {
250 outputContext.set(FastIotConstants.Rpc.PARAMS_NAME, inputContext); 261 outputContext.set(FastIotConstants.Rpc.PARAMS_NAME, inputContext);
251 } 262 }
252 263
253 ObjectNode addtionalInfo = JacksonUtil.newObjectNode(); 264 ObjectNode addtionalInfo = JacksonUtil.newObjectNode();
254 - addtionalInfo.put(ModelConstants.TablePropertyMapping.COMMAND_TYPE, doActionDTO.getCommandType()); 265 + addtionalInfo.put(
  266 + ModelConstants.TablePropertyMapping.COMMAND_TYPE,
  267 + doActionDTO.getCommandType());
255 outputContext.set(DataConstants.ADDITIONAL_INFO, addtionalInfo); 268 outputContext.set(DataConstants.ADDITIONAL_INFO, addtionalInfo);
256 doActionDTO.setDoContext(outputContext); 269 doActionDTO.setDoContext(outputContext);
257 } 270 }
@@ -264,11 +277,13 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -264,11 +277,13 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
264 277
265 /** 278 /**
266 * 验证设备输出的目标设备是否合法有效 279 * 验证设备输出的目标设备是否合法有效
267 - * @param tbDeviceIds 场景联动所属组织的全部设备ID  
268 - * @param deviceIds 场景联动选择的设备ID  
269 - * @param entityType 设备输出类型 280 + *
  281 + * @param tbDeviceIds 场景联动所属组织的全部设备ID
  282 + * @param deviceIds 场景联动选择的设备ID
  283 + * @param entityType 设备输出类型
270 */ 284 */
271 - private void validateRpcDevice(List<String> tbDeviceIds, List<String> deviceIds, ScopeEnum entityType) { 285 + private void validateRpcDevice(
  286 + List<String> tbDeviceIds, List<String> deviceIds, ScopeEnum entityType) {
272 if (ScopeEnum.PART.equals(entityType)) { 287 if (ScopeEnum.PART.equals(entityType)) {
273 if (deviceIds == null || deviceIds.isEmpty()) { 288 if (deviceIds == null || deviceIds.isEmpty()) {
274 throw new TkDataValidationException(ErrorMessage.DEVICE_LOSED.getMessage()); 289 throw new TkDataValidationException(ErrorMessage.DEVICE_LOSED.getMessage());
@@ -338,13 +353,13 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -338,13 +353,13 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
338 String organizationId = (String) queryMap.get("organizationId"); 353 String organizationId = (String) queryMap.get("organizationId");
339 // 不为空 354 // 不为空
340 if (null != organizationId && !StringUtils.isEmpty(organizationId)) { 355 if (null != organizationId && !StringUtils.isEmpty(organizationId)) {
341 - queryMap.put(  
342 - "organizationIds", getQueryOrganizationIds(tenantId, List.of(organizationId))); 356 + queryMap.put("organizationIds", getQueryOrganizationIds(tenantId, List.of(organizationId)));
343 } 357 }
344 if (!isCustomerUser) { 358 if (!isCustomerUser) {
345 queryMap.remove("currentUser"); 359 queryMap.remove("currentUser");
346 } 360 }
347 - IPage<TkSceneLinkageEntity> page = getPage(queryMap, FastIotConstants.DefaultOrder.CREATE_TIME, false); 361 + IPage<TkSceneLinkageEntity> page =
  362 + getPage(queryMap, FastIotConstants.DefaultOrder.CREATE_TIME, false);
348 IPage<SceneLinkageDTO> scenePage = baseMapper.getScenePage(page, queryMap); 363 IPage<SceneLinkageDTO> scenePage = baseMapper.getScenePage(page, queryMap);
349 return getPageData(scenePage, SceneLinkageDTO.class); 364 return getPageData(scenePage, SceneLinkageDTO.class);
350 } 365 }
@@ -392,7 +407,8 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -392,7 +407,8 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
392 * @return 设备集合 407 * @return 设备集合
393 */ 408 */
394 @Override 409 @Override
395 - public List<DeviceDTO> findDeviceList(String organizationId, String tenantId, String customerId,List<DeviceTypeEnum> typeFilter) { 410 + public List<DeviceDTO> findDeviceList(
  411 + String organizationId, String tenantId, String customerId, List<DeviceTypeEnum> typeFilter) {
396 List<String> organizationFilter = new ArrayList<>(); 412 List<String> organizationFilter = new ArrayList<>();
397 organizationFilter.add(organizationId); 413 organizationFilter.add(organizationId);
398 // 查询该组织的所有子类 414 // 查询该组织的所有子类
@@ -405,7 +421,6 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -405,7 +421,6 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
405 throw new TkDataValidationException(ErrorMessage.ORGANIZATION_NOT_EXTIED.getMessage()); 421 throw new TkDataValidationException(ErrorMessage.ORGANIZATION_NOT_EXTIED.getMessage());
406 } 422 }
407 423
408 -  
409 List<String> customerDevices = null; 424 List<String> customerDevices = null;
410 // 不等于默认的UUID就是客户用户 425 // 不等于默认的UUID就是客户用户
411 if (!EntityId.NULL_UUID.toString().equals(customerId)) { 426 if (!EntityId.NULL_UUID.toString().equals(customerId)) {
@@ -416,18 +431,19 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -416,18 +431,19 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
416 } 431 }
417 432
418 List<TkDeviceEntity> orgDevices = 433 List<TkDeviceEntity> orgDevices =
419 - deviceMapper.selectList(  
420 - new QueryWrapper<TkDeviceEntity>().lambda().in(TkDeviceEntity::getOrganizationId, orgIds)); 434 + deviceMapper.selectList(
  435 + new QueryWrapper<TkDeviceEntity>()
  436 + .lambda()
  437 + .in(TkDeviceEntity::getOrganizationId, orgIds));
421 List<DeviceDTO> result = 438 List<DeviceDTO> result =
422 orgDevices.stream() 439 orgDevices.stream()
423 - .filter(t -> typeFilter.isEmpty() || typeFilter.contains(t.getDeviceType())) 440 + .filter(t -> typeFilter.isEmpty() || typeFilter.contains(t.getDeviceType()))
424 .map(device -> device.getDTO(DeviceDTO.class)) 441 .map(device -> device.getDTO(DeviceDTO.class))
425 .collect(Collectors.toList()); 442 .collect(Collectors.toList());
426 443
427 -  
428 if (null != customerDevices && customerDevices.size() > 0) { 444 if (null != customerDevices && customerDevices.size() > 0) {
429 List<DeviceDTO> customerAllDevice = new ArrayList<>(); 445 List<DeviceDTO> customerAllDevice = new ArrayList<>();
430 - for(DeviceDTO item: result){ 446 + for (DeviceDTO item : result) {
431 if (customerDevices.contains(item.getTbDeviceId())) { 447 if (customerDevices.contains(item.getTbDeviceId())) {
432 customerAllDevice.add(item); 448 customerAllDevice.add(item);
433 } 449 }
@@ -480,12 +496,10 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -480,12 +496,10 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
480 return null; 496 return null;
481 } 497 }
482 498
  499 + Map<String, List<RuleFilterDTO>> matchedDevices = new HashMap<>();
  500 + Map<String, List<RuleFilterDTO>> matchedProjectes = new HashMap<>();
483 501
484 -  
485 -  
486 - Map<String, List<String>> matchedDevices = new HashMap<>();  
487 - Map<String, List<String>> matchedProjectes = new HashMap<>();  
488 - 502 + Set<String> projectFilter = new HashSet<>();
489 List<TkTriggerEntity> triggers = 503 List<TkTriggerEntity> triggers =
490 triggerMapper.selectList( 504 triggerMapper.selectList(
491 new QueryWrapper<TkTriggerEntity>() 505 new QueryWrapper<TkTriggerEntity>()
@@ -493,27 +507,51 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM @@ -493,27 +507,51 @@ public class TkSceneLinkageServiceImpl extends AbstractBaseService<SceneLinkageM
493 .eq(TkTriggerEntity::getTenantId, tenantId) 507 .eq(TkTriggerEntity::getTenantId, tenantId)
494 .eq(TkTriggerEntity::getTriggerType, TriggerTypeEnum.DEVICE_TRIGGER) 508 .eq(TkTriggerEntity::getTriggerType, TriggerTypeEnum.DEVICE_TRIGGER)
495 .in(TkTriggerEntity::getSceneLinkageId, enableIds)); 509 .in(TkTriggerEntity::getSceneLinkageId, enableIds));
496 -  
497 triggers.forEach( 510 triggers.forEach(
498 trigger -> { 511 trigger -> {
499 - String scenId = trigger.getSceneLinkageId();  
500 - if (ScopeEnum.ALL.equals(trigger.getEntityType()) ) {  
501 - List<String> triggerIds = new ArrayList<>();  
502 - triggerIds.add(trigger.getDeviceProfileId());  
503 - deviceSceneMap(matchedProjectes, triggerIds, scenId);  
504 - }else{  
505 - deviceSceneMap(matchedDevices, trigger.getEntityId(), scenId);  
506 - } 512 + projectFilter.add(trigger.getDeviceProfileId());
  513 + deviceSceneMap(
  514 + matchedProjectes,
  515 + matchedDevices,
  516 + trigger.getId(),
  517 + trigger.getSceneLinkageId(),
  518 + trigger.getEntityType(),
  519 + trigger.getDeviceProfileId(),
  520 + trigger.getEntityId());
  521 + });
507 522
  523 + List<TkDoConditionEntity> conditions =
  524 + doConditionMapper.selectList(
  525 + new QueryWrapper<TkDoConditionEntity>()
  526 + .lambda()
  527 + .eq(TkDoConditionEntity::getTenantId, tenantId)
  528 + .eq(TkDoConditionEntity::getTriggerType, TriggerTypeEnum.DEVICE_TRIGGER)
  529 + .in(TkDoConditionEntity::getSceneLinkageId, enableIds));
  530 + conditions.forEach(
  531 + condition -> {
  532 + projectFilter.add(condition.getDeviceProfileId());
  533 + deviceSceneMap(
  534 + matchedProjectes,
  535 + matchedDevices,
  536 + condition.getId(),
  537 + condition.getSceneLinkageId(),
  538 + condition.getEntityType(),
  539 + condition.getDeviceProfileId(),
  540 + condition.getEntityId());
508 }); 541 });
509 542
510 - if (matchedDevices.isEmpty()&&matchedProjectes.isEmpty()) { 543 + if (matchedDevices.isEmpty() && matchedProjectes.isEmpty()) {
511 return null; 544 return null;
512 } 545 }
513 546
514 -List<TkDeviceProfileEntity> profiles = profileMapper.selectList(new LambdaQueryWrapper<TkDeviceProfileEntity>().eq(TkDeviceProfileEntity::getTenantId,tenantId)); 547 +
  548 +
  549 + List<TkDeviceProfileEntity> profiles =
  550 + profileMapper.selectList(
  551 + new LambdaQueryWrapper<TkDeviceProfileEntity>()
  552 + .eq(TkDeviceProfileEntity::getTenantId, tenantId).in(TkDeviceProfileEntity::getId, projectFilter));
515 Map<String, String> projects = new HashMap<>(); 553 Map<String, String> projects = new HashMap<>();
516 - profiles.forEach(f->projects.put(f.getTbProfileId(),f.getId())); 554 + profiles.forEach(f -> projects.put(f.getTbProfileId(), f.getId()));
517 555
518 Map<String, Map> engineConfig = new HashMap<>(); 556 Map<String, Map> engineConfig = new HashMap<>();
519 engineConfig.put("scenes", matchedDevices); 557 engineConfig.put("scenes", matchedDevices);
@@ -525,21 +563,38 @@ List<TkDeviceProfileEntity> profiles = profileMapper.selectList(new LambdaQueryW @@ -525,21 +563,38 @@ List<TkDeviceProfileEntity> profiles = profileMapper.selectList(new LambdaQueryW
525 return JacksonUtil.convertValue(engineConfig, JsonNode.class); 563 return JacksonUtil.convertValue(engineConfig, JsonNode.class);
526 } 564 }
527 565
528 - 566 + private void deviceSceneMap(
  567 + Map<String, List<RuleFilterDTO>> matchedProjectes,
  568 + Map<String, List<RuleFilterDTO>> matchedDevices,String ruleId,
  569 + String scenId,
  570 + ScopeEnum scope,
  571 + String profileId,
  572 + List<String> deviceIds) {
  573 + if (ScopeEnum.ALL.equals(scope)) {
  574 + List<String> profileIds = new ArrayList<>();
  575 + profileIds.add(profileId);
  576 + deviceSceneMap(matchedProjectes, profileIds,ruleId, scenId);
  577 + } else {
  578 + deviceSceneMap(matchedDevices, deviceIds,ruleId, scenId);
  579 + }
  580 + }
529 581
530 /** 582 /**
531 * 设备与场景联动的映射集合 583 * 设备与场景联动的映射集合
532 * 584 *
533 * @param resultMap 缓存设备和场景联动映射结果的集合 585 * @param resultMap 缓存设备和场景联动映射结果的集合
534 - * @param devices 设备主键集合 586 + * @param postfixIds 设备或产品主键集合
535 * @param scenId 场景联动主键 587 * @param scenId 场景联动主键
536 */ 588 */
537 private void deviceSceneMap( 589 private void deviceSceneMap(
538 - Map<String, List<String>> resultMap, List<String> devices, String scenId) {  
539 - for (String deviceId : devices) {  
540 - List<String> scenes = resultMap.computeIfAbsent(deviceId, k -> new ArrayList<String>());  
541 - if (!scenes.contains(scenId)) {  
542 - scenes.add(scenId); 590 + Map<String, List<RuleFilterDTO>> resultMap, List<String> postfixIds, String prefixId, String scenId) {
  591 + for (String deviceId : postfixIds) {
  592 + List<RuleFilterDTO> scenes = resultMap.computeIfAbsent(deviceId, k -> new ArrayList<RuleFilterDTO>());
  593 + RuleFilterDTO filter = new RuleFilterDTO();
  594 + filter.setRuleId(prefixId);
  595 + filter.setScenId(scenId);
  596 + if (!scenes.contains(filter)) {
  597 + scenes.add(filter);
543 } 598 }
544 resultMap.put(deviceId, scenes); 599 resultMap.put(deviceId, scenes);
545 } 600 }
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
5 import lombok.RequiredArgsConstructor; 5 import lombok.RequiredArgsConstructor;
6 import org.springframework.stereotype.Service; 6 import org.springframework.stereotype.Service;
7 -import org.thingsboard.server.common.data.yunteng.dto.TriggerDTO; 7 +import org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO;
8 import org.thingsboard.server.dao.yunteng.entities.TkTriggerEntity; 8 import org.thingsboard.server.dao.yunteng.entities.TkTriggerEntity;
9 import org.thingsboard.server.dao.yunteng.mapper.TriggerMapper; 9 import org.thingsboard.server.dao.yunteng.mapper.TriggerMapper;
10 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService; 10 import org.thingsboard.server.dao.yunteng.service.AbstractBaseService;
@@ -3,7 +3,7 @@ package org.thingsboard.server.dao.yunteng.mapper; @@ -3,7 +3,7 @@ package org.thingsboard.server.dao.yunteng.mapper;
3 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Mapper;
5 import org.apache.ibatis.annotations.Param; 5 import org.apache.ibatis.annotations.Param;
6 -import org.thingsboard.server.common.data.yunteng.dto.DoActionDTO; 6 +import org.thingsboard.server.common.data.yunteng.dto.scene.DoActionDTO;
7 import org.thingsboard.server.dao.yunteng.entities.TkDoActionEntity; 7 import org.thingsboard.server.dao.yunteng.entities.TkDoActionEntity;
8 8
9 import java.util.List; 9 import java.util.List;
@@ -3,7 +3,7 @@ package org.thingsboard.server.dao.yunteng.mapper; @@ -3,7 +3,7 @@ package org.thingsboard.server.dao.yunteng.mapper;
3 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Mapper;
5 import org.apache.ibatis.annotations.Param; 5 import org.apache.ibatis.annotations.Param;
6 -import org.thingsboard.server.common.data.yunteng.dto.DoConditionDTO; 6 +import org.thingsboard.server.common.data.yunteng.dto.scene.DoConditionDTO;
7 import org.thingsboard.server.dao.yunteng.entities.TkDoConditionEntity; 7 import org.thingsboard.server.dao.yunteng.entities.TkDoConditionEntity;
8 8
9 import java.util.List; 9 import java.util.List;
@@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; @@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 import com.baomidou.mybatisplus.core.metadata.IPage; 4 import com.baomidou.mybatisplus.core.metadata.IPage;
5 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
6 import org.apache.ibatis.annotations.Param; 6 import org.apache.ibatis.annotations.Param;
7 -import org.thingsboard.server.common.data.yunteng.dto.SceneLinkageDTO; 7 +import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDTO;
8 import org.thingsboard.server.dao.yunteng.entities.TkSceneLinkageEntity; 8 import org.thingsboard.server.dao.yunteng.entities.TkSceneLinkageEntity;
9 9
10 import java.util.Map; 10 import java.util.Map;
@@ -3,7 +3,7 @@ package org.thingsboard.server.dao.yunteng.mapper; @@ -3,7 +3,7 @@ package org.thingsboard.server.dao.yunteng.mapper;
3 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 3 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Mapper;
5 import org.apache.ibatis.annotations.Param; 5 import org.apache.ibatis.annotations.Param;
6 -import org.thingsboard.server.common.data.yunteng.dto.TriggerDTO; 6 +import org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO;
7 import org.thingsboard.server.dao.yunteng.entities.TkTriggerEntity; 7 import org.thingsboard.server.dao.yunteng.entities.TkTriggerEntity;
8 8
9 import java.util.List; 9 import java.util.List;
1 package org.thingsboard.server.dao.yunteng.service; 1 package org.thingsboard.server.dao.yunteng.service;
2 2
3 -import org.thingsboard.server.common.data.yunteng.dto.DoActionDTO; 3 +import org.thingsboard.server.common.data.yunteng.dto.scene.DoActionDTO;
4 import org.thingsboard.server.dao.yunteng.entities.TkDoActionEntity; 4 import org.thingsboard.server.dao.yunteng.entities.TkDoActionEntity;
5 5
6 import java.util.List; 6 import java.util.List;
1 package org.thingsboard.server.dao.yunteng.service; 1 package org.thingsboard.server.dao.yunteng.service;
2 import com.fasterxml.jackson.databind.JsonNode; 2 import com.fasterxml.jackson.databind.JsonNode;
3 import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; 3 import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO;
4 -import org.thingsboard.server.common.data.yunteng.dto.SceneLinkageDTO; 4 +import org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDTO;
5 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; 5 import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
6 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; 6 import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData;
7 import org.thingsboard.server.dao.yunteng.entities.TkSceneLinkageEntity; 7 import org.thingsboard.server.dao.yunteng.entities.TkSceneLinkageEntity;
1 package org.thingsboard.server.dao.yunteng.service; 1 package org.thingsboard.server.dao.yunteng.service;
2 2
3 -import org.thingsboard.server.common.data.yunteng.dto.TriggerDTO; 3 +import org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO;
4 import org.thingsboard.server.dao.yunteng.entities.TkTriggerEntity; 4 import org.thingsboard.server.dao.yunteng.entities.TkTriggerEntity;
5 5
6 import java.util.List; 6 import java.util.List;
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.DoActionMapper"> 4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.DoActionMapper">
5 - <resultMap id="actionDTO" type="org.thingsboard.server.common.data.yunteng.dto.DoActionDTO"> 5 + <resultMap id="actionDTO" type="org.thingsboard.server.common.data.yunteng.dto.scene.DoActionDTO">
6 <result property="id" column="id"/> 6 <result property="id" column="id"/>
7 <result property="deviceId" column="device_id" 7 <result property="deviceId" column="device_id"
8 typeHandler="org.thingsboard.server.dao.yunteng.mapper.ListStringTypeHandler"/> 8 typeHandler="org.thingsboard.server.dao.yunteng.mapper.ListStringTypeHandler"/>
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.DoConditionMapper"> 4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.DoConditionMapper">
5 - <resultMap id="conditionDTO" type="org.thingsboard.server.common.data.yunteng.dto.DoConditionDTO"> 5 + <resultMap id="conditionDTO" type="org.thingsboard.server.common.data.yunteng.dto.scene.DoConditionDTO">
6 <result property="id" column="id"/> 6 <result property="id" column="id"/>
7 <result property="entityId" column="entity_id" 7 <result property="entityId" column="entity_id"
8 typeHandler="org.thingsboard.server.dao.yunteng.mapper.ListStringTypeHandler"/> 8 typeHandler="org.thingsboard.server.dao.yunteng.mapper.ListStringTypeHandler"/>
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.SceneLinkageMapper"> 4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.SceneLinkageMapper">
5 - <resultMap id="sceneLinkageMap" type="org.thingsboard.server.common.data.yunteng.dto.SceneLinkageDTO"> 5 + <resultMap id="sceneLinkageMap" type="org.thingsboard.server.common.data.yunteng.dto.scene.SceneLinkageDTO">
6 <result property="id" column="id"/> 6 <result property="id" column="id"/>
7 <result property="name" column="name"/> 7 <result property="name" column="name"/>
8 <result property="organizationId" column="organization_id"/> 8 <result property="organizationId" column="organization_id"/>
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 3
4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.TriggerMapper"> 4 <mapper namespace="org.thingsboard.server.dao.yunteng.mapper.TriggerMapper">
5 - <resultMap id="triggerDTO" type="org.thingsboard.server.common.data.yunteng.dto.TriggerDTO"> 5 + <resultMap id="triggerDTO" type="org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO">
6 <result property="id" column="id"/> 6 <result property="id" column="id"/>
7 <result property="entityId" column="entity_id" 7 <result property="entityId" column="entity_id"
8 typeHandler="org.thingsboard.server.dao.yunteng.mapper.ListStringTypeHandler"/> 8 typeHandler="org.thingsboard.server.dao.yunteng.mapper.ListStringTypeHandler"/>
@@ -44,8 +44,8 @@ @@ -44,8 +44,8 @@
44 <scope>provided</scope> 44 <scope>provided</scope>
45 </dependency> 45 </dependency>
46 <dependency> 46 <dependency>
47 - <groupId>org.thingsboard.common</groupId>  
48 - <artifactId>dao-api</artifactId> 47 + <groupId>org.thingsboard</groupId>
  48 + <artifactId>dao</artifactId>
49 <scope>provided</scope> 49 <scope>provided</scope>
50 </dependency> 50 </dependency>
51 <dependency> 51 <dependency>
@@ -56,6 +56,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; @@ -56,6 +56,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
56 import org.thingsboard.server.dao.tenant.TenantService; 56 import org.thingsboard.server.dao.tenant.TenantService;
57 import org.thingsboard.server.dao.timeseries.TimeseriesService; 57 import org.thingsboard.server.dao.timeseries.TimeseriesService;
58 import org.thingsboard.server.dao.user.UserService; 58 import org.thingsboard.server.dao.user.UserService;
  59 +import org.thingsboard.server.dao.yunteng.service.TkDeviceService;
59 60
60 import java.util.Set; 61 import java.util.Set;
61 import java.util.function.BiConsumer; 62 import java.util.function.BiConsumer;
@@ -285,4 +286,8 @@ public interface TbContext { @@ -285,4 +286,8 @@ public interface TbContext {
285 void removeListeners(); 286 void removeListeners();
286 287
287 TenantProfile getTenantProfile(); 288 TenantProfile getTenantProfile();
  289 +
  290 +
  291 + //Thingskit function
  292 + TkDeviceService getTkDeviceService();
288 } 293 }
@@ -16,12 +16,13 @@ @@ -16,12 +16,13 @@
16 package org.thingsboard.rule.engine.rpc; 16 package org.thingsboard.rule.engine.rpc;
17 17
18 import com.datastax.oss.driver.api.core.uuid.Uuids; 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 import com.google.gson.Gson; 20 import com.google.gson.Gson;
20 import com.google.gson.JsonElement; 21 import com.google.gson.JsonElement;
21 import com.google.gson.JsonObject; 22 import com.google.gson.JsonObject;
22 import com.google.gson.JsonParser; 23 import com.google.gson.JsonParser;
23 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
24 -import org.springframework.util.StringUtils; 25 +import org.thingsboard.common.util.JacksonUtil;
25 import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; 26 import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
26 import org.thingsboard.rule.engine.api.RuleNode; 27 import org.thingsboard.rule.engine.api.RuleNode;
27 import org.thingsboard.rule.engine.api.TbContext; 28 import org.thingsboard.rule.engine.api.TbContext;
@@ -31,9 +32,17 @@ import org.thingsboard.rule.engine.api.TbNodeException; @@ -31,9 +32,17 @@ import org.thingsboard.rule.engine.api.TbNodeException;
31 import org.thingsboard.rule.engine.api.TbRelationTypes; 32 import org.thingsboard.rule.engine.api.TbRelationTypes;
32 import org.thingsboard.rule.engine.api.util.TbNodeUtils; 33 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
33 import org.thingsboard.server.common.data.DataConstants; 34 import org.thingsboard.server.common.data.DataConstants;
  35 +import org.thingsboard.server.common.data.Device;
34 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
  37 +import org.thingsboard.server.common.data.StringUtils;
35 import org.thingsboard.server.common.data.id.DeviceId; 38 import org.thingsboard.server.common.data.id.DeviceId;
36 import org.thingsboard.server.common.data.plugin.ComponentType; 39 import org.thingsboard.server.common.data.plugin.ComponentType;
  40 +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
  41 +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
  42 +import org.thingsboard.server.common.data.yunteng.constant.ModelConstants;
  43 +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO;
  44 +import org.thingsboard.server.common.data.yunteng.enums.CmdTypeEnum;
  45 +import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum;
37 import org.thingsboard.server.common.msg.TbMsg; 46 import org.thingsboard.server.common.msg.TbMsg;
38 47
39 import java.util.Random; 48 import java.util.Random;
@@ -98,12 +107,41 @@ public class TbSendRPCRequestNode implements TbNode { @@ -98,12 +107,41 @@ public class TbSendRPCRequestNode implements TbNode {
98 String params = parseJsonData(json.get("params")); 107 String params = parseJsonData(json.get("params"));
99 String additionalInfo = parseJsonData(json.get(DataConstants.ADDITIONAL_INFO)); 108 String additionalInfo = parseJsonData(json.get(DataConstants.ADDITIONAL_INFO));
100 109
  110 +
  111 + /** Thingskit function
  112 + * 1.1、目标设备为网关子设备时,表单数据增加属性deviceName。
  113 + * 1.2、目标设备为网关子设备时,附加信息增加属性target,便于命令下发记录挂靠。
  114 + * 1.3、目标设备为网关子设备时,将RuleEngineDeviceRpcRequest对象的deviceId改为网关设备。
  115 + * 2、附加信息增加属性cmdType,记录命令下发类型,例如:定时任务。
  116 + */
  117 + UUID orginatorId = msg.getOriginator().getId();
  118 + DeviceId targetId = new DeviceId(orginatorId);
  119 + DeviceDTO targetDevice = ctx.getTkDeviceService().findDeviceInfoByTbDeviceId(ctx.getTenantId().getId().toString(), orginatorId.toString());
  120 + DeviceId netEnableId = DeviceTypeEnum.SENSOR == targetDevice.getDeviceType()?new DeviceId(UUID.fromString(targetDevice.getGatewayId())):targetId;
  121 + if(DeviceTypeEnum.SENSOR == targetDevice.getDeviceType() && params.contains("}")){
  122 + ObjectNode methodParams = (ObjectNode) JacksonUtil.toJsonNode(params);
  123 + methodParams.put(FastIotConstants.Rpc.TARGET_NAME,targetDevice.getName());
  124 + params = JacksonUtil.toString(methodParams);
  125 + }
  126 + ObjectNode additional = JacksonUtil.newObjectNode();
  127 + if(StringUtils.isNotBlank(additionalInfo)){
  128 + additional = (ObjectNode) JacksonUtil.toJsonNode(additionalInfo);
  129 + }
  130 + if(!additional.has(FastIotConstants.Rpc.TARGET_ID)){
  131 + additional.put(FastIotConstants.Rpc.TARGET_ID, orginatorId.toString());
  132 + }
  133 + if(!additional.has(ModelConstants.TablePropertyMapping.COMMAND_TYPE)){
  134 + additional.put(ModelConstants.TablePropertyMapping.COMMAND_TYPE, CmdTypeEnum.DIY.ordinal());
  135 + }
  136 + additionalInfo = JacksonUtil.toString(additional);
  137 +
  138 +
101 RuleEngineDeviceRpcRequest request = RuleEngineDeviceRpcRequest.builder() 139 RuleEngineDeviceRpcRequest request = RuleEngineDeviceRpcRequest.builder()
102 .oneway(oneway) 140 .oneway(oneway)
103 .method(json.get("method").getAsString()) 141 .method(json.get("method").getAsString())
104 .body(params) 142 .body(params)
105 .tenantId(ctx.getTenantId()) 143 .tenantId(ctx.getTenantId())
106 - .deviceId(new DeviceId(msg.getOriginator().getId())) 144 + .deviceId(netEnableId)
107 .requestId(requestId) 145 .requestId(requestId)
108 .requestUUID(requestUUID) 146 .requestUUID(requestUUID)
109 .originServiceId(originServiceId) 147 .originServiceId(originServiceId)
@@ -3,12 +3,10 @@ package org.thingsboard.rule.engine.yunteng.scene; @@ -3,12 +3,10 @@ package org.thingsboard.rule.engine.yunteng.scene;
3 3
4 import com.fasterxml.jackson.databind.JsonNode; 4 import com.fasterxml.jackson.databind.JsonNode;
5 import com.fasterxml.jackson.databind.node.ObjectNode; 5 import com.fasterxml.jackson.databind.node.ObjectNode;
6 -import java.util.HashSet;  
7 -import java.util.List;  
8 -import java.util.Set;  
9 -import java.util.UUID; 6 +import java.util.*;
10 import java.util.concurrent.ConcurrentHashMap; 7 import java.util.concurrent.ConcurrentHashMap;
11 import java.util.concurrent.ExecutionException; 8 import java.util.concurrent.ExecutionException;
  9 +import java.util.concurrent.atomic.AtomicBoolean;
12 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
13 import org.apache.commons.lang3.StringUtils; 11 import org.apache.commons.lang3.StringUtils;
14 import org.jetbrains.annotations.NotNull; 12 import org.jetbrains.annotations.NotNull;
@@ -23,7 +21,7 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -23,7 +21,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
23 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; 21 import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants;
24 import org.thingsboard.server.common.data.yunteng.dto.ActionAlarmDTO; 22 import org.thingsboard.server.common.data.yunteng.dto.ActionAlarmDTO;
25 import org.thingsboard.server.common.data.yunteng.dto.AlarmInfoDTO; 23 import org.thingsboard.server.common.data.yunteng.dto.AlarmInfoDTO;
26 -import org.thingsboard.server.common.data.yunteng.dto.TriggerDTO; 24 +import org.thingsboard.server.common.data.yunteng.dto.scene.TriggerDTO;
27 import org.thingsboard.server.common.data.yunteng.enums.*; 25 import org.thingsboard.server.common.data.yunteng.enums.*;
28 import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; 26 import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil;
29 import org.thingsboard.server.common.data.yunteng.utils.SpringBeanUtils; 27 import org.thingsboard.server.common.data.yunteng.utils.SpringBeanUtils;
@@ -86,7 +84,16 @@ class ReactState { @@ -86,7 +84,16 @@ class ReactState {
86 this.ytDeviceService = SpringBeanUtils.getBean(TkDeviceService.class); 84 this.ytDeviceService = SpringBeanUtils.getBean(TkDeviceService.class);
87 } 85 }
88 86
89 - public void process(TbContext ctx, TbMsg msg, String profileId, String deviceId) 87 + /**
  88 + * 触发器内部逻辑:或 执行条件内部逻辑:且 触发器和执行条件之间的逻辑:且
  89 + *
  90 + * @param ctx
  91 + * @param msg 设备推送的遥测数据
  92 + * @param deviceId 遥测数据的来源设备的TB设备ID
  93 + * @throws ExecutionException
  94 + * @throws InterruptedException
  95 + */
  96 + public void process(TbContext ctx, TbMsg msg, String prefixId,String deviceId)
90 throws ExecutionException, InterruptedException { 97 throws ExecutionException, InterruptedException {
91 98
92 StringBuilder detail = new StringBuilder(); 99 StringBuilder detail = new StringBuilder();
@@ -94,74 +101,120 @@ class ReactState { @@ -94,74 +101,120 @@ class ReactState {
94 ctx.tellSuccess(msg); 101 ctx.tellSuccess(msg);
95 } 102 }
96 103
97 - boolean matched = false;  
98 - if (triggers == null || triggers.isEmpty()) {  
99 - matched = true;  
100 - } else {  
101 - boolean itemMatched = false;  
102 - for (TriggerDTO trigger : triggers) {  
103 - TriggerState triggerState = getOrCreateTriggerState(trigger, profileId, deviceId);  
104 - if (triggerState == null) {  
105 - continue;  
106 - }  
107 - itemMatched = triggerState.process(ctx, msg);  
108 - if (itemMatched) {  
109 - matched = true;  
110 - detail.append(  
111 - triggerState.getAlarmDetails() == null ? "" : triggerState.getAlarmDetails());  
112 - if (this.alarmAction != null) {  
113 - noticeMsg(  
114 - ctx,  
115 - msg,  
116 - alarmAction,  
117 - deviceId,  
118 - triggerState.getAlarmDetails(),  
119 - triggerState.getLatestValues().getTs());  
120 - }  
121 - } else if (currentAlarms.containsKey(deviceId) && this.alarmAction != null) {  
122 - // 清除设备告警  
123 - for (AlarmConditionFilterKey entityKey : triggerState.getEntityKeys()) {  
124 - clearAlarm(ctx, msg, deviceId, entityKey.getKey());  
125 - }  
126 - }  
127 - }  
128 - } 104 + AtomicBoolean triggerMatched = new AtomicBoolean(true);
  105 + Optional.ofNullable(triggers)
  106 + .ifPresent(
  107 + t -> {
  108 + triggerMatched.set(false);
  109 + t.forEach(
  110 + trigger -> {
  111 + ScopeEnum entityType = trigger.getEntityType();
  112 + List<String> trifggerDevices = trigger.getEntityId();
  113 + String tkProjectId = trigger.getDeviceProfileId();
  114 + if (ScopeEnum.ALL.equals(entityType)) {
  115 + trifggerDevices =
  116 + ytDeviceService.findTbDeviceIdsByDeviceProfileId(
  117 + tkProjectId, trigger.getTenantId());
  118 + }
  119 + triggerMatched.set(
  120 + trifggerDevices.stream()
  121 + .anyMatch(
  122 + id -> {
  123 + TriggerState triggerState =
  124 + getOrCreateTriggerState(trigger, tkProjectId, id);
  125 + if (triggerState == null) {
  126 + return false;
  127 + }
  128 + try {
  129 + boolean fresh = false;
  130 + if(trigger.getId().equals(prefixId)&&msg.getOriginator().getId().toString().equals(id)){
  131 + fresh=true;
  132 + }
  133 + boolean result = triggerState.process(ctx, msg,fresh);
  134 + log.error(String.format("触发器【%s】刷新【%s】结果【%s】触发器设备【%s】数据设备【%s】数据内容【%s】",trigger.getId(),fresh,result,id,msg.getOriginator(),msg.getData()));
  135 + if (result) {
  136 + detail.append(
  137 + triggerState.getAlarmDetails() == null
  138 + ? ""
  139 + : triggerState.getAlarmDetails());
  140 + return true;
  141 + } else if (currentAlarms.containsKey(deviceId)) {
  142 + // 清除设备告警
  143 + for (AlarmConditionFilterKey entityKey :
  144 + triggerState.getEntityKeys()) {
  145 + clearAlarm(ctx, msg, deviceId, entityKey.getKey());
  146 + }
  147 + }
  148 + } catch (ExecutionException e) {
  149 + throw new RuntimeException(e);
  150 + } catch (InterruptedException e) {
  151 + throw new RuntimeException(e);
  152 + }
  153 + return false;
  154 + }));
  155 + });
  156 + });
129 157
130 - if (matched && conditions.size() > 0) {  
131 - matched = false;  
132 - for (TkDoConditionEntity item : conditions) {  
133 - List<String> entityIds = item.getEntityId();  
134 - if (entityIds == null || entityIds.isEmpty()) {  
135 - matched = true;  
136 - break;  
137 - }  
138 - for (String id : entityIds) {  
139 - TriggerState conditionState = getOrCreateConditionState(item, profileId, id);  
140 - if (conditionState == null || conditionState.process(ctx, msg)) {  
141 - detail.append(";");  
142 - detail.append(conditionState.getAlarmDetails());  
143 - matched = true;  
144 - break;  
145 - }  
146 - }  
147 - if (matched) {  
148 - break;  
149 - }  
150 - }  
151 - } 158 + /** 执行条件的所有设备都满足才为true */
  159 + AtomicBoolean conditionMatched = new AtomicBoolean(true);
  160 + Optional.ofNullable(conditions)
  161 + .ifPresent(
  162 + t -> {
  163 + t.forEach(
  164 + condition -> {
  165 + ScopeEnum entityType = condition.getEntityType();
  166 + List<String> conditionDevices = condition.getEntityId();
  167 + String tkProjectId = condition.getDeviceProfileId();
  168 + if (ScopeEnum.ALL.equals(entityType)) {
  169 + conditionDevices =
  170 + ytDeviceService.findTbDeviceIdsByDeviceProfileId(
  171 + tkProjectId, condition.getTenantId());
  172 + }
  173 + conditionMatched.set(
  174 + !conditionDevices.stream()
  175 + .anyMatch(
  176 + id -> {
  177 + TriggerState conditionState =
  178 + getOrCreateConditionState(condition, tkProjectId, id);
  179 + try {
  180 + boolean fresh = false;
  181 + if(msg.getOriginator().getId().toString().equals(id)){
  182 + fresh=true;
  183 + }
  184 + boolean result = conditionState.process(ctx, msg,fresh);
  185 + log.warn(String.format("执行器【%s】刷新【%s】结果【%s】执行器设备【%s】数据设备【%s】数据内容【%s】",condition.getId(),fresh,result,id,msg.getOriginator(),msg.getData()));
  186 + return !result;
  187 + } catch (ExecutionException e) {
  188 + throw new RuntimeException(e);
  189 + } catch (InterruptedException e) {
  190 + throw new RuntimeException(e);
  191 + }
  192 + }));
  193 + });
  194 + });
152 195
153 - if (matched) { 196 + if (triggerMatched.get() && conditionMatched.get()) {
  197 + log.error(String.format("设备【%s】的消息内容【%s】触发动作",deviceId,msg.getData()));
154 for (TkDoActionEntity item : actions) { 198 for (TkDoActionEntity item : actions) {
155 if (ActionTypeEnum.MSG_NOTIFY.equals(item.getOutTarget())) { 199 if (ActionTypeEnum.MSG_NOTIFY.equals(item.getOutTarget())) {
156 - continue; 200 + noticeMsg(ctx, msg, item, deviceId, detail.toString(), msg.getTs());
  201 + } else {
  202 + pushMsg(ctx, msg, item, detail.toString());
157 } 203 }
158 - pushMsg(ctx, msg, item, detail.toString());  
159 } 204 }
160 } else { 205 } else {
161 ctx.tellSuccess(msg); 206 ctx.tellSuccess(msg);
162 } 207 }
163 } 208 }
164 209
  210 + /**
  211 + * 获取触发器状态
  212 + *
  213 + * @param trigger 触发器数据
  214 + * @param profileId 遥测数据的来源设备的TK产品ID
  215 + * @param deviceId 遥测数据的来源设备的TB设备ID
  216 + * @return
  217 + */
165 protected TriggerState getOrCreateTriggerState( 218 protected TriggerState getOrCreateTriggerState(
166 TriggerDTO trigger, String profileId, String deviceId) { 219 TriggerDTO trigger, String profileId, String deviceId) {
167 String triggerId = trigger.getId(); 220 String triggerId = trigger.getId();
@@ -173,6 +226,7 @@ class ReactState { @@ -173,6 +226,7 @@ class ReactState {
173 || (trigger.getEntityType().equals(ScopeEnum.ALL) 226 || (trigger.getEntityType().equals(ScopeEnum.ALL)
174 && trigger.getDeviceProfileId().equals(profileId))) { 227 && trigger.getDeviceProfileId().equals(profileId))) {
175 TriggerState state = createTriggerState(deviceId, trigger.getTriggerCondition()); 228 TriggerState state = createTriggerState(deviceId, trigger.getTriggerCondition());
  229 + log.error(String.format("新建设备【%s】的触发器",deviceId));
176 triggerState.put(cacheKey, state); 230 triggerState.put(cacheKey, state);
177 return state; 231 return state;
178 } 232 }
@@ -224,7 +278,7 @@ class ReactState { @@ -224,7 +278,7 @@ class ReactState {
224 || (condition.getEntityType().equals(ScopeEnum.ALL) 278 || (condition.getEntityType().equals(ScopeEnum.ALL)
225 && condition.getDeviceProfileId().equals(profileId))) { 279 && condition.getDeviceProfileId().equals(profileId))) {
226 TriggerState state = createTriggerState(deviceId, condition.getTriggerCondition()); 280 TriggerState state = createTriggerState(deviceId, condition.getTriggerCondition());
227 - triggerState.put(cacheKey, state); 281 + conditionState.put(cacheKey, state);
228 return state; 282 return state;
229 } 283 }
230 return null; 284 return null;
@@ -242,10 +296,6 @@ class ReactState { @@ -242,10 +296,6 @@ class ReactState {
242 } 296 }
243 297
244 private void pushMsg(TbContext ctx, TbMsg msg, TkDoActionEntity action, String detail) { 298 private void pushMsg(TbContext ctx, TbMsg msg, TkDoActionEntity action, String detail) {
245 - TbMsgMetaData metaData = // lastMsgMetaData != null ? lastMsgMetaData.copy() :  
246 - new TbMsgMetaData();  
247 - String relationType = "";  
248 - TbMsg newMsg = null;  
249 switch (action.getOutTarget()) { 299 switch (action.getOutTarget()) {
250 case DEVICE_OUT: 300 case DEVICE_OUT:
251 List<String> rpcDevices = action.getDeviceId(); 301 List<String> rpcDevices = action.getDeviceId();
@@ -358,7 +408,7 @@ class ReactState { @@ -358,7 +408,7 @@ class ReactState {
358 private void clearAlarm(TbContext ctx, TbMsg msg, String deviceId, String key) 408 private void clearAlarm(TbContext ctx, TbMsg msg, String deviceId, String key)
359 throws ExecutionException, InterruptedException { 409 throws ExecutionException, InterruptedException {
360 TriggerState clearState = getOrCreateClearState(deviceId, key); 410 TriggerState clearState = getOrCreateClearState(deviceId, key);
361 - if (clearState != null && clearState.process(ctx, msg)) { 411 + if (clearState != null && clearState.process(ctx, msg,true)) {
362 ctx.getAlarmService() 412 ctx.getAlarmService()
363 .clearAlarmForResult( 413 .clearAlarmForResult(
364 ctx.getTenantId(), 414 ctx.getTenantId(),
1 /** 1 /**
2 * Copyright © 2016-2022 The Thingsboard Authors 2 * Copyright © 2016-2022 The Thingsboard Authors
3 * 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 4 + * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  5 + * except in compliance with the License. You may obtain a copy of the License at
7 * 6 *
8 - * http://www.apache.org/licenses/LICENSE-2.0 7 + * <p>http://www.apache.org/licenses/LICENSE-2.0
9 * 8 *
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 9 + * <p>Unless required by applicable law or agreed to in writing, software distributed under the
  10 + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  11 + * express or implied. See the License for the specific language governing permissions and
14 * limitations under the License. 12 * limitations under the License.
15 */ 13 */
16 package org.thingsboard.rule.engine.yunteng.scene; 14 package org.thingsboard.rule.engine.yunteng.scene;
17 15
18 import com.fasterxml.jackson.databind.ObjectMapper; 16 import com.fasterxml.jackson.databind.ObjectMapper;
19 -import com.google.gson.JsonParser; 17 +import java.util.List;
  18 +import java.util.Map;
  19 +import java.util.concurrent.ConcurrentHashMap;
20 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.rule.engine.api.*; 21 import org.thingsboard.rule.engine.api.*;
22 import org.thingsboard.rule.engine.api.util.TbNodeUtils; 22 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
@@ -24,129 +24,116 @@ import org.thingsboard.rule.engine.filter.TbCheckAlarmStatusNodeConfig; @@ -24,129 +24,116 @@ import org.thingsboard.rule.engine.filter.TbCheckAlarmStatusNodeConfig;
24 import org.thingsboard.rule.engine.profile.SnapshotUpdate; 24 import org.thingsboard.rule.engine.profile.SnapshotUpdate;
25 import org.thingsboard.rule.engine.yunteng.utils.TriggerRuleState; 25 import org.thingsboard.rule.engine.yunteng.utils.TriggerRuleState;
26 import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; 26 import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
27 -import org.thingsboard.server.common.data.id.DeviceId;  
28 -import org.thingsboard.server.common.data.kv.AttributeKvEntry;  
29 import org.thingsboard.server.common.data.plugin.ComponentType; 27 import org.thingsboard.server.common.data.plugin.ComponentType;
  28 +import org.thingsboard.server.common.data.yunteng.dto.scene.RuleFilterDTO;
30 import org.thingsboard.server.common.msg.TbMsg; 29 import org.thingsboard.server.common.msg.TbMsg;
31 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 30 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
32 -import org.thingsboard.server.common.msg.session.SessionMsgType;  
33 -import org.thingsboard.server.common.transport.adaptor.JsonConverter;  
34 -  
35 -import java.util.List;  
36 -import java.util.Map;  
37 -import java.util.Optional;  
38 -import java.util.Set;  
39 -import java.util.concurrent.ConcurrentHashMap;  
40 -import java.util.concurrent.ExecutionException;  
41 31
42 @Slf4j 32 @Slf4j
43 @RuleNode( 33 @RuleNode(
44 - type = ComponentType.FILTER,  
45 - name = "scene react",  
46 - configClazz = TbCheckAlarmStatusNodeConfig.class,  
47 - relationTypes = {"Message","Alarm Created", "Alarm Updated", "RPC Request"},  
48 - nodeDescription = "基于业务场景,实现设备的交互控制。",  
49 - nodeDetails = "基于业务场景,实现设备的交互控制。",  
50 - uiResources = {"static/rulenode/rulenode-core-config.js"},  
51 - configDirective = "tbSceneReactNodeConfig") 34 + type = ComponentType.FILTER,
  35 + name = "scene react",
  36 + configClazz = TbCheckAlarmStatusNodeConfig.class,
  37 + relationTypes = {"Message", "Alarm Created", "Alarm Updated", "RPC Request"},
  38 + nodeDescription = "基于业务场景,实现设备的交互控制。",
  39 + nodeDetails = "基于业务场景,实现设备的交互控制。",
  40 + uiResources = {"static/rulenode/rulenode-core-config.js"},
  41 + configDirective = "tbSceneReactNodeConfig")
52 public class TbSceneReactNode implements TbNode { 42 public class TbSceneReactNode implements TbNode {
53 - private final ObjectMapper mapper = new ObjectMapper();  
54 - private TbSceneReactNodeConfig config;  
55 -  
56 -  
57 -  
58 -  
59 - /**场景联动状态  
60 - * 键:场景联动主键  
61 - * 值:场景联动状态  
62 - * */  
63 - private final Map<String, ReactState> reactStates = new ConcurrentHashMap<>();  
64 -  
65 -  
66 -  
67 - private TbContext ctx;  
68 -  
69 - @Override  
70 - public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {  
71 - this.config = TbNodeUtils.convert(configuration, TbSceneReactNodeConfig.class);  
72 - this.ctx = ctx; 43 + private final ObjectMapper mapper = new ObjectMapper();
  44 + private TbSceneReactNodeConfig config;
  45 +
  46 + /** 场景联动状态 键:场景联动主键 值:场景联动状态 */
  47 + private final Map<String, ReactState> reactStates = new ConcurrentHashMap<>();
  48 +
  49 + private TbContext ctx;
  50 +
  51 + @Override
  52 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  53 + this.config = TbNodeUtils.convert(configuration, TbSceneReactNodeConfig.class);
  54 + this.ctx = ctx;
  55 + }
  56 +
  57 + @Override
  58 + public void onMsg(TbContext ctx, TbMsg msg) {
  59 + String deviceId = msg.getOriginator().getId().toString();
  60 + String tbProfileId = msg.getMetaData().getValue("deviceProfileId");
  61 + String projectId = config.getProfile().get(tbProfileId);
  62 + boolean deviceHas = config.getScenes().containsKey(deviceId);
  63 + boolean profileHas = config.getProject().containsKey(projectId);
  64 + if (!deviceHas && !profileHas) {
  65 + ctx.tellSuccess(msg);
  66 + return;
73 } 67 }
74 -  
75 - @Override  
76 - public void onMsg(TbContext ctx, TbMsg msg) {  
77 - String deviceId = msg.getOriginator().getId().toString();  
78 - String tbProfileId = msg.getMetaData().getValue("deviceProfileId");  
79 - String projectId = config.getProfile().get(tbProfileId);  
80 - boolean deviceHas = config.getScenes().containsKey(deviceId);  
81 - boolean profileHas = config.getProject().containsKey(projectId);  
82 - if(!deviceHas && !profileHas){  
83 - ctx.tellSuccess(msg);  
84 - return;  
85 - }  
86 - List<String> devScence = config.getScenes().get(deviceId);  
87 - if(devScence !=null && !devScence.isEmpty()){  
88 - devScence.stream().forEach(t ->{  
89 - ReactState react = getOrCreateReactState(ctx,config,t);  
90 - if(react != null){  
91 - try {  
92 - react.process(ctx, msg,projectId,deviceId);  
93 - } catch (Exception e) {  
94 - ctx.tellFailure(msg,e);  
95 - }  
96 - }  
97 - });  
98 - }  
99 - List<String> projectScence = config.getProject().get(projectId);  
100 - if(projectScence !=null && !projectScence.isEmpty()){  
101 - projectScence.stream().forEach(t ->{  
102 - ReactState react = getOrCreateReactState(ctx,config,t);  
103 - if(react != null){  
104 - try {  
105 - react.process(ctx, msg,projectId,deviceId);  
106 - } catch (Exception e) {  
107 - ctx.tellFailure(msg,e);  
108 - } 68 + List<RuleFilterDTO> devScence = config.getScenes().get(deviceId);
  69 + if (devScence != null && !devScence.isEmpty()) {
  70 + devScence.stream()
  71 + .forEach(
  72 + t -> {
  73 + ReactState react = getOrCreateReactState(ctx, config, t.getScenId());
  74 + if (react != null) {
  75 + try {
  76 + react.process(ctx, msg,t.getRuleId(), deviceId);
  77 + } catch (Exception e) {
  78 + ctx.tellFailure(msg, e);
  79 + }
109 } 80 }
110 - });  
111 - }  
112 - }  
113 -  
114 - /**  
115 - * 设备数据是否包含触发器的指标  
116 - * @param update 最新数据  
117 - * @param condition 触发条件工具类  
118 - * @return  
119 - */  
120 - public boolean validateUpdate(SnapshotUpdate update, TriggerRuleState condition) {  
121 - if (update != null) {  
122 - //Check that the update type and that keys match.  
123 - if (update.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {  
124 - return condition.validateTsUpdate(update.getKeys());  
125 - } else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {  
126 - return condition.validateAttrUpdate(update.getKeys());  
127 - }  
128 - }  
129 - return true; 81 + });
130 } 82 }
131 - @Override  
132 - public void destroy() {  
133 - reactStates.clear(); 83 + List<RuleFilterDTO> projectScence = config.getProject().get(projectId);
  84 + if (projectScence != null && !projectScence.isEmpty()) {
  85 + projectScence.stream()
  86 + .forEach(
  87 + t -> {
  88 + ReactState react = getOrCreateReactState(ctx, config, t.getScenId());
  89 + if (react != null) {
  90 + try {
  91 + react.process(ctx, msg,t.getRuleId(), deviceId);
  92 + } catch (Exception e) {
  93 + ctx.tellFailure(msg, e);
  94 + }
  95 + }
  96 + });
134 } 97 }
135 -  
136 - @Override  
137 - public void onPartitionChangeMsg(TbContext ctx, PartitionChangeMsg msg) {  
138 - // Cleanup the cache for all entities that are no longer assigned to current server partitions  
139 - reactStates.clear(); 98 + }
  99 +
  100 + /**
  101 + * 设备数据是否包含触发器的指标
  102 + *
  103 + * @param update 最新数据
  104 + * @param condition 触发条件工具类
  105 + * @return
  106 + */
  107 + public boolean validateUpdate(SnapshotUpdate update, TriggerRuleState condition) {
  108 + if (update != null) {
  109 + // Check that the update type and that keys match.
  110 + if (update.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
  111 + return condition.validateTsUpdate(update.getKeys());
  112 + } else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {
  113 + return condition.validateAttrUpdate(update.getKeys());
  114 + }
140 } 115 }
141 -  
142 - protected ReactState getOrCreateReactState(TbContext ctx, TbSceneReactNodeConfig config, String sceneId) {  
143 - ReactState reactState = reactStates.get(sceneId);  
144 - if (reactState == null) {  
145 - reactState = new ReactState(sceneId,ctx, config);  
146 - reactStates.put(sceneId, reactState);  
147 - }  
148 - return reactState; 116 + return true;
  117 + }
  118 +
  119 + @Override
  120 + public void destroy() {
  121 + reactStates.clear();
  122 + }
  123 +
  124 + @Override
  125 + public void onPartitionChangeMsg(TbContext ctx, PartitionChangeMsg msg) {
  126 + // Cleanup the cache for all entities that are no longer assigned to current server partitions
  127 + reactStates.clear();
  128 + }
  129 +
  130 + protected ReactState getOrCreateReactState(
  131 + TbContext ctx, TbSceneReactNodeConfig config, String sceneId) {
  132 + ReactState reactState = reactStates.get(sceneId);
  133 + if (reactState == null) {
  134 + reactState = new ReactState(sceneId, ctx, config);
  135 + reactStates.put(sceneId, reactState);
149 } 136 }
150 -  
151 - 137 + return reactState;
  138 + }
152 } 139 }
1 -/**  
2 - * 场景联动配置文件  
3 - */ 1 +/** 场景联动配置文件 */
4 package org.thingsboard.rule.engine.yunteng.scene; 2 package org.thingsboard.rule.engine.yunteng.scene;
5 3
6 -import lombok.Data;  
7 -import org.thingsboard.rule.engine.api.NodeConfiguration;  
8 -import org.thingsboard.server.common.data.id.EntityId;  
9 -import org.thingsboard.server.common.data.id.TenantId;  
10 -  
11 import java.util.HashMap; 4 import java.util.HashMap;
12 import java.util.List; 5 import java.util.List;
13 import java.util.Map; 6 import java.util.Map;
  7 +import lombok.Data;
  8 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  9 +import org.thingsboard.server.common.data.yunteng.dto.scene.RuleFilterDTO;
14 10
15 @Data 11 @Data
16 public class TbSceneReactNodeConfig implements NodeConfiguration<TbSceneReactNodeConfig> { 12 public class TbSceneReactNodeConfig implements NodeConfiguration<TbSceneReactNodeConfig> {
17 13
18 - /**【设备ID,场景】设备的哪些指标会触发场景联动*/  
19 - private Map<String, List<String>> scenes;  
20 - /**【产品ID,场景】设备的哪些指标会触发场景联动*/  
21 - private Map<String, List<String>> project;  
22 - /**【场景ID,场景名称】场景联动信息*/  
23 - private Map<String, String> names;  
24 - /**【场景ID,组织ID】场景联动所属组织信息*/  
25 - private Map<String, String> orgs;  
26 - private Map<String, String> profile;  
27 -  
28 -  
29 -  
30 -  
31 -  
32 - @Override  
33 - public TbSceneReactNodeConfig defaultConfiguration() {  
34 - TbSceneReactNodeConfig config = new TbSceneReactNodeConfig();  
35 - Map<String, List<String>> sceneMap = new HashMap<>();  
36 - Map<String, List<String>> projectScenes = new HashMap<>();  
37 -  
38 - config.setScenes(sceneMap);  
39 - config.setProject(projectScenes);  
40 - config.setNames(new HashMap<>());  
41 - config.setOrgs(new HashMap<>());  
42 - config.setProfile(new HashMap<>());  
43 - return config;  
44 - } 14 + /** 【TB设备配置ID,TK产品ID】产品信息 */
  15 + private Map<String, String> profile;
  16 + /** 【TK产品ID,场景】哪些产品会触发场景联动 */
  17 + private Map<String, List<RuleFilterDTO>> project;
  18 + /** 【TB设备ID,场景】哪些设备会触发场景联动 */
  19 + private Map<String, List<RuleFilterDTO>> scenes;
  20 + /** 【TK场景ID,场景名称】场景联动信息 */
  21 + private Map<String, String> names;
  22 + /** 【TK场景ID,组织ID】场景联动所属组织信息 */
  23 + private Map<String, String> orgs;
  24 +
  25 + @Override
  26 + public TbSceneReactNodeConfig defaultConfiguration() {
  27 + TbSceneReactNodeConfig config = new TbSceneReactNodeConfig();
  28 + Map<String, List<RuleFilterDTO>> sceneMap = new HashMap<>();
  29 + Map<String, List<RuleFilterDTO>> projectScenes = new HashMap<>();
  30 +
  31 + config.setScenes(sceneMap);
  32 + config.setProject(projectScenes);
  33 + config.setNames(new HashMap<>());
  34 + config.setOrgs(new HashMap<>());
  35 + config.setProfile(new HashMap<>());
  36 + return config;
  37 + }
45 } 38 }
1 /** 1 /**
2 * Copyright © 2016-2022 The Thingsboard Authors 2 * Copyright © 2016-2022 The Thingsboard Authors
3 - * <p>  
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 - * <p>  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - * <p>  
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 3 + *
  4 + * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  5 + * except in compliance with the License. You may obtain a copy of the License at
  6 + *
  7 + * <p>http://www.apache.org/licenses/LICENSE-2.0
  8 + *
  9 + * <p>Unless required by applicable law or agreed to in writing, software distributed under the
  10 + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  11 + * express or implied. See the License for the specific language governing permissions and
14 * limitations under the License. 12 * limitations under the License.
15 */ 13 */
16 package org.thingsboard.rule.engine.yunteng.scene; 14 package org.thingsboard.rule.engine.yunteng.scene;
17 15
18 import com.google.gson.JsonParser; 16 import com.google.gson.JsonParser;
  17 +import java.util.*;
  18 +import java.util.concurrent.ExecutionException;
  19 +import java.util.function.BiFunction;
19 import lombok.Data; 20 import lombok.Data;
  21 +import lombok.Getter;
20 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.rule.engine.api.TbContext; 23 import org.thingsboard.rule.engine.api.TbContext;
22 import org.thingsboard.rule.engine.profile.*; 24 import org.thingsboard.rule.engine.profile.*;
@@ -36,243 +38,312 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -36,243 +38,312 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
36 import org.thingsboard.server.common.transport.adaptor.JsonConverter; 38 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
37 import org.thingsboard.server.dao.sql.query.EntityKeyMapping; 39 import org.thingsboard.server.dao.sql.query.EntityKeyMapping;
38 40
39 -import java.util.*;  
40 -import java.util.concurrent.ExecutionException;  
41 -import java.util.function.BiFunction;  
42 -  
43 @Data 41 @Data
44 @Slf4j 42 @Slf4j
45 class TriggerState { 43 class TriggerState {
46 - private final String originator;  
47 - private volatile TriggerRuleState ruleState;  
48 - private volatile Alarm currentAlarm;  
49 - private volatile boolean initialFetchDone;  
50 - private volatile TbMsgMetaData lastMsgMetaData;  
51 -  
52 - private final DynamicPredicateValueCtx dynamicPredicateValueCtx;  
53 - private DataSnapshot latestValues;  
54 -  
55 - private final Set<AlarmConditionFilterKey> entityKeys;  
56 - private final String alarmDetails;  
57 -  
58 - TriggerState(String originator, AlarmRule rule, Set<AlarmConditionFilterKey> filterKeys, String alarmDetails, DynamicPredicateValueCtx dynamicPredicateValueCtx) {  
59 -  
60 - this.originator = originator;  
61 - this.dynamicPredicateValueCtx = dynamicPredicateValueCtx;  
62 - ruleState = new TriggerRuleState(rule.getCondition(), filterKeys, new PersistedAlarmRuleState(), rule.getSchedule());  
63 - this.entityKeys = filterKeys;  
64 - this.alarmDetails = alarmDetails; 44 + private final String originator;
  45 + private volatile TriggerRuleState ruleState;
  46 + private volatile Alarm currentAlarm;
  47 + private volatile boolean initialFetchDone;
  48 + private volatile TbMsgMetaData lastMsgMetaData;
  49 +
  50 + private final DynamicPredicateValueCtx dynamicPredicateValueCtx;
  51 + private DataSnapshot latestValues;
  52 +
  53 + private final Set<AlarmConditionFilterKey> entityKeys;
  54 + private final String alarmDetails;
  55 +
  56 + @Getter
  57 + /** 触发条件执行结果 */
  58 + private boolean ruleMatched = false;
  59 +
  60 + TriggerState(
  61 + String originator,
  62 + AlarmRule rule,
  63 + Set<AlarmConditionFilterKey> filterKeys,
  64 + String alarmDetails,
  65 + DynamicPredicateValueCtx dynamicPredicateValueCtx) {
  66 +
  67 + this.originator = originator;
  68 + this.dynamicPredicateValueCtx = dynamicPredicateValueCtx;
  69 + ruleState =
  70 + new TriggerRuleState(
  71 + rule.getCondition(), filterKeys, new PersistedAlarmRuleState(), rule.getSchedule());
  72 + this.entityKeys = filterKeys;
  73 + this.alarmDetails = alarmDetails;
  74 + }
  75 +
  76 + public boolean process(TbContext ctx, TbMsg msg,boolean needFresh) throws ExecutionException, InterruptedException {
  77 + if (!needFresh) {
  78 + return ruleMatched;
65 } 79 }
66 -  
67 -  
68 - public boolean process(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {  
69 - if (latestValues == null) {  
70 - latestValues = fetchLatestValues(ctx, originator);  
71 - }  
72 - lastMsgMetaData = msg.getMetaData();  
73 - SnapshotUpdate update = null;  
74 - if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) {  
75 - update = processTelemetry(ctx, msg);  
76 - } else {  
77 - update = processAttributes(ctx, msg);  
78 - }  
79 -  
80 - if (update != null && update.hasUpdate()) {  
81 - return createOrClearAlarms(ctx, msg, latestValues, update, TriggerRuleState::eval);  
82 - }  
83 - return false; 80 + if (latestValues == null) {
  81 + latestValues = fetchLatestValues(ctx, originator);
84 } 82 }
85 -  
86 -  
87 - public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException {  
88 - return createOrClearAlarms(ctx, null, ts, null, (alarmState, tsParam) -> alarmState.eval(tsParam, latestValues)); 83 + lastMsgMetaData = msg.getMetaData();
  84 + SnapshotUpdate update;
  85 + if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) {
  86 + update = processTelemetry(ctx, msg);
  87 + } else {
  88 + update = processAttributes(ctx, msg);
89 } 89 }
90 90
91 - public <T> boolean createOrClearAlarms(TbContext ctx, TbMsg msg, T data, SnapshotUpdate update, BiFunction<TriggerRuleState, T, AlarmEvalResult> evalFunction) {  
92 - boolean stateUpdate = false;  
93 - if (!validateUpdate(update, ruleState)) {  
94 - return false;  
95 - }  
96 - AlarmEvalResult evalResult = evalFunction.apply(ruleState, data);  
97 - if (AlarmEvalResult.TRUE.equals(evalResult)) {  
98 - stateUpdate = true;  
99 - clearAlarmState(stateUpdate, ruleState);  
100 - } else if (AlarmEvalResult.FALSE.equals(evalResult)) {  
101 - clearAlarmState(stateUpdate, ruleState);  
102 - }  
103 - return stateUpdate; 91 + if (update != null && update.hasUpdate()) {
  92 + ruleMatched = createOrClearAlarms(ctx, msg, latestValues, update, TriggerRuleState::eval);
  93 + return ruleMatched;
104 } 94 }
105 -  
106 -  
107 - public boolean clearAlarmState(boolean stateUpdate, TriggerRuleState state) {  
108 - if (state != null) {  
109 - state.clear();  
110 - stateUpdate |= state.checkUpdate();  
111 - }  
112 - return stateUpdate; 95 + return false;
  96 + }
  97 +
  98 + public boolean process(TbContext ctx, long ts) throws ExecutionException, InterruptedException {
  99 + return createOrClearAlarms(
  100 + ctx, null, ts, null, (alarmState, tsParam) -> alarmState.eval(tsParam, latestValues));
  101 + }
  102 +
  103 + public <T> boolean createOrClearAlarms(
  104 + TbContext ctx,
  105 + TbMsg msg,
  106 + T data,
  107 + SnapshotUpdate update,
  108 + BiFunction<TriggerRuleState, T, AlarmEvalResult> evalFunction) {
  109 + boolean stateUpdate = false;
  110 + if (!validateUpdate(update, ruleState)) {
  111 + return false;
113 } 112 }
114 -  
115 - public boolean validateUpdate(SnapshotUpdate update, TriggerRuleState state) {  
116 - if (update != null) {  
117 - //Check that the update type and that keys match.  
118 - if (update.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {  
119 - return state.validateTsUpdate(update.getKeys());  
120 - } else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {  
121 - return state.validateAttrUpdate(update.getKeys());  
122 - }  
123 - }  
124 - return true; 113 + AlarmEvalResult evalResult = evalFunction.apply(ruleState, data);
  114 + if (AlarmEvalResult.TRUE.equals(evalResult)) {
  115 + stateUpdate = true;
  116 + clearAlarmState(stateUpdate, ruleState);
  117 + } else if (AlarmEvalResult.FALSE.equals(evalResult)) {
  118 + clearAlarmState(stateUpdate, ruleState);
125 } 119 }
  120 + return stateUpdate;
  121 + }
126 122
127 -  
128 - protected void setAlarmConditionMetadata(TriggerRuleState ruleState, TbMsgMetaData metaData) {  
129 - if (ruleState.getSpec().getType() == AlarmConditionSpecType.REPEATING) {  
130 - metaData.putValue(DataConstants.ALARM_CONDITION_REPEATS, String.valueOf(ruleState.getState().getEventCount()));  
131 - }  
132 - if (ruleState.getSpec().getType() == AlarmConditionSpecType.DURATION) {  
133 - metaData.putValue(DataConstants.ALARM_CONDITION_DURATION, String.valueOf(ruleState.getState().getDuration()));  
134 - } 123 + public boolean clearAlarmState(boolean stateUpdate, TriggerRuleState state) {
  124 + if (state != null) {
  125 + state.clear();
  126 + stateUpdate |= state.checkUpdate();
135 } 127 }
136 -  
137 -  
138 - public void processAckAlarm(Alarm alarm) {  
139 - if (currentAlarm != null && currentAlarm.getId().equals(alarm.getId())) {  
140 - currentAlarm.setStatus(alarm.getStatus());  
141 - currentAlarm.setAckTs(alarm.getAckTs());  
142 - } 128 + return stateUpdate;
  129 + }
  130 +
  131 + public boolean validateUpdate(SnapshotUpdate update, TriggerRuleState state) {
  132 + if (update != null) {
  133 + // Check that the update type and that keys match.
  134 + if (update.getType().equals(AlarmConditionKeyType.TIME_SERIES)) {
  135 + return state.validateTsUpdate(update.getKeys());
  136 + } else if (update.getType().equals(AlarmConditionKeyType.ATTRIBUTE)) {
  137 + return state.validateAttrUpdate(update.getKeys());
  138 + }
143 } 139 }
144 -  
145 -  
146 - protected SnapshotUpdate processAttributes(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {  
147 - Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData()));  
148 - if (!attributes.isEmpty()) {  
149 - return merge(attributes);  
150 - }  
151 - return null; 140 + return true;
  141 + }
  142 +
  143 + protected void setAlarmConditionMetadata(TriggerRuleState ruleState, TbMsgMetaData metaData) {
  144 + if (ruleState.getSpec().getType() == AlarmConditionSpecType.REPEATING) {
  145 + metaData.putValue(
  146 + DataConstants.ALARM_CONDITION_REPEATS,
  147 + String.valueOf(ruleState.getState().getEventCount()));
152 } 148 }
153 -  
154 - protected SnapshotUpdate processTelemetry(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException {  
155 - Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToSortedTelemetry(new JsonParser().parse(msg.getData()), msg.getMetaDataTs());  
156 - for (Map.Entry<Long, List<KvEntry>> entry : tsKvMap.entrySet()) {  
157 - Long ts = entry.getKey();  
158 - List<KvEntry> data = entry.getValue();  
159 - return merge(ts, data);  
160 - }  
161 -  
162 - return null; 149 + if (ruleState.getSpec().getType() == AlarmConditionSpecType.DURATION) {
  150 + metaData.putValue(
  151 + DataConstants.ALARM_CONDITION_DURATION,
  152 + String.valueOf(ruleState.getState().getDuration()));
163 } 153 }
  154 + }
164 155
165 -  
166 - private DataSnapshot fetchLatestValues(TbContext ctx, String originator) throws ExecutionException, InterruptedException {  
167 - DataSnapshot result = new DataSnapshot(entityKeys);  
168 - addEntityKeysToSnapshot(ctx, originator, result);  
169 - return result; 156 + public void processAckAlarm(Alarm alarm) {
  157 + if (currentAlarm != null && currentAlarm.getId().equals(alarm.getId())) {
  158 + currentAlarm.setStatus(alarm.getStatus());
  159 + currentAlarm.setAckTs(alarm.getAckTs());
170 } 160 }
171 -  
172 - private void addEntityKeysToSnapshot(TbContext ctx, String originator, DataSnapshot result) throws InterruptedException, ExecutionException {  
173 - Set<String> attributeKeys = new HashSet<>();  
174 - Set<String> latestTsKeys = new HashSet<>();  
175 -  
176 - Device device = null;  
177 - for (AlarmConditionFilterKey entityKey : entityKeys) {  
178 - String key = entityKey.getKey();  
179 - switch (entityKey.getType()) {  
180 - case ATTRIBUTE:  
181 - attributeKeys.add(key);  
182 - break;  
183 - case TIME_SERIES:  
184 - latestTsKeys.add(key);  
185 - break;  
186 - case ENTITY_FIELD:  
187 - if (device == null) {  
188 - device = ctx.getDeviceService().findDeviceById(ctx.getTenantId(), new DeviceId(UUID.fromString(originator)));  
189 - }  
190 - if (device != null) {  
191 - switch (key) {  
192 - case EntityKeyMapping.NAME:  
193 - result.putValue(entityKey, device.getCreatedTime(), EntityKeyValue.fromString(device.getName()));  
194 - break;  
195 - case EntityKeyMapping.TYPE:  
196 - result.putValue(entityKey, device.getCreatedTime(), EntityKeyValue.fromString(device.getType()));  
197 - break;  
198 - case EntityKeyMapping.CREATED_TIME:  
199 - result.putValue(entityKey, device.getCreatedTime(), EntityKeyValue.fromLong(device.getCreatedTime()));  
200 - break;  
201 - case EntityKeyMapping.LABEL:  
202 - result.putValue(entityKey, device.getCreatedTime(), EntityKeyValue.fromString(device.getLabel()));  
203 - break;  
204 - }  
205 - }  
206 - break;  
207 - }  
208 - }  
209 -  
210 - if (!latestTsKeys.isEmpty()) {  
211 - List<TsKvEntry> data = ctx.getTimeseriesService().findLatest(ctx.getTenantId(), new DeviceId(UUID.fromString(originator)), latestTsKeys).get();  
212 - for (TsKvEntry entry : data) {  
213 - if (entry.getValue() != null) {  
214 - result.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey()), entry.getTs(), toEntityValue(entry));  
215 - }  
216 - }  
217 - }  
218 - if (!attributeKeys.isEmpty()) {  
219 - addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), new DeviceId(UUID.fromString(originator)), DataConstants.CLIENT_SCOPE, attributeKeys).get());  
220 - addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), new DeviceId(UUID.fromString(originator)), DataConstants.SHARED_SCOPE, attributeKeys).get());  
221 - addToSnapshot(result, ctx.getAttributesService().find(ctx.getTenantId(), new DeviceId(UUID.fromString(originator)), DataConstants.SERVER_SCOPE, attributeKeys).get());  
222 - } 161 + }
  162 +
  163 + protected SnapshotUpdate processAttributes(TbContext ctx, TbMsg msg)
  164 + throws ExecutionException, InterruptedException {
  165 + Set<AttributeKvEntry> attributes =
  166 + JsonConverter.convertToAttributes(new JsonParser().parse(msg.getData()));
  167 + if (!attributes.isEmpty()) {
  168 + return merge(attributes);
  169 + }
  170 + return null;
  171 + }
  172 +
  173 + protected SnapshotUpdate processTelemetry(TbContext ctx, TbMsg msg)
  174 + throws ExecutionException, InterruptedException {
  175 + Map<Long, List<KvEntry>> tsKvMap =
  176 + JsonConverter.convertToSortedTelemetry(
  177 + new JsonParser().parse(msg.getData()), msg.getMetaDataTs());
  178 + for (Map.Entry<Long, List<KvEntry>> entry : tsKvMap.entrySet()) {
  179 + Long ts = entry.getKey();
  180 + List<KvEntry> data = entry.getValue();
  181 + return merge(ts, data);
223 } 182 }
224 183
225 - private void addToSnapshot(DataSnapshot snapshot, List<AttributeKvEntry> data) {  
226 - for (AttributeKvEntry entry : data) {  
227 - if (entry.getValue() != null) {  
228 - EntityKeyValue value = toEntityValue(entry);  
229 - snapshot.putValue(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey()), entry.getLastUpdateTs(), value); 184 + return null;
  185 + }
  186 +
  187 + private DataSnapshot fetchLatestValues(TbContext ctx, String originator)
  188 + throws ExecutionException, InterruptedException {
  189 + DataSnapshot result = new DataSnapshot(entityKeys);
  190 + addEntityKeysToSnapshot(ctx, originator, result);
  191 + return result;
  192 + }
  193 +
  194 + private void addEntityKeysToSnapshot(TbContext ctx, String originator, DataSnapshot result)
  195 + throws InterruptedException, ExecutionException {
  196 + Set<String> attributeKeys = new HashSet<>();
  197 + Set<String> latestTsKeys = new HashSet<>();
  198 +
  199 + Device device = null;
  200 + for (AlarmConditionFilterKey entityKey : entityKeys) {
  201 + String key = entityKey.getKey();
  202 + switch (entityKey.getType()) {
  203 + case ATTRIBUTE:
  204 + attributeKeys.add(key);
  205 + break;
  206 + case TIME_SERIES:
  207 + latestTsKeys.add(key);
  208 + break;
  209 + case ENTITY_FIELD:
  210 + if (device == null) {
  211 + device =
  212 + ctx.getDeviceService()
  213 + .findDeviceById(ctx.getTenantId(), new DeviceId(UUID.fromString(originator)));
  214 + }
  215 + if (device != null) {
  216 + switch (key) {
  217 + case EntityKeyMapping.NAME:
  218 + result.putValue(
  219 + entityKey,
  220 + device.getCreatedTime(),
  221 + EntityKeyValue.fromString(device.getName()));
  222 + break;
  223 + case EntityKeyMapping.TYPE:
  224 + result.putValue(
  225 + entityKey,
  226 + device.getCreatedTime(),
  227 + EntityKeyValue.fromString(device.getType()));
  228 + break;
  229 + case EntityKeyMapping.CREATED_TIME:
  230 + result.putValue(
  231 + entityKey,
  232 + device.getCreatedTime(),
  233 + EntityKeyValue.fromLong(device.getCreatedTime()));
  234 + break;
  235 + case EntityKeyMapping.LABEL:
  236 + result.putValue(
  237 + entityKey,
  238 + device.getCreatedTime(),
  239 + EntityKeyValue.fromString(device.getLabel()));
  240 + break;
230 } 241 }
231 - } 242 + }
  243 + break;
  244 + }
232 } 245 }
233 246
234 - public static EntityKeyValue toEntityValue(KvEntry entry) {  
235 - switch (entry.getDataType()) {  
236 - case STRING:  
237 - return EntityKeyValue.fromString(entry.getStrValue().get());  
238 - case LONG:  
239 - return EntityKeyValue.fromLong(entry.getLongValue().get());  
240 - case DOUBLE:  
241 - return EntityKeyValue.fromDouble(entry.getDoubleValue().get());  
242 - case BOOLEAN:  
243 - return EntityKeyValue.fromBool(entry.getBooleanValue().get());  
244 - case JSON:  
245 - return EntityKeyValue.fromJson(entry.getJsonValue().get());  
246 - default:  
247 - throw new RuntimeException("Can't parse entry: " + entry.getDataType()); 247 + if (!latestTsKeys.isEmpty()) {
  248 + List<TsKvEntry> data =
  249 + ctx.getTimeseriesService()
  250 + .findLatest(
  251 + ctx.getTenantId(), new DeviceId(UUID.fromString(originator)), latestTsKeys)
  252 + .get();
  253 + for (TsKvEntry entry : data) {
  254 + if (entry.getValue() != null) {
  255 + result.putValue(
  256 + new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey()),
  257 + entry.getTs(),
  258 + toEntityValue(entry));
248 } 259 }
  260 + }
249 } 261 }
250 -  
251 - private SnapshotUpdate merge(Long newTs, List<KvEntry> data) {  
252 - Set<AlarmConditionFilterKey> keys = new HashSet<>();  
253 - for (KvEntry entry : data) {  
254 - AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey());  
255 - if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {  
256 - keys.add(entityKey);  
257 - }  
258 - }  
259 - latestValues.setTs(newTs);  
260 - return new SnapshotUpdate(AlarmConditionKeyType.TIME_SERIES, keys); 262 + if (!attributeKeys.isEmpty()) {
  263 + addToSnapshot(
  264 + result,
  265 + ctx.getAttributesService()
  266 + .find(
  267 + ctx.getTenantId(),
  268 + new DeviceId(UUID.fromString(originator)),
  269 + DataConstants.CLIENT_SCOPE,
  270 + attributeKeys)
  271 + .get());
  272 + addToSnapshot(
  273 + result,
  274 + ctx.getAttributesService()
  275 + .find(
  276 + ctx.getTenantId(),
  277 + new DeviceId(UUID.fromString(originator)),
  278 + DataConstants.SHARED_SCOPE,
  279 + attributeKeys)
  280 + .get());
  281 + addToSnapshot(
  282 + result,
  283 + ctx.getAttributesService()
  284 + .find(
  285 + ctx.getTenantId(),
  286 + new DeviceId(UUID.fromString(originator)),
  287 + DataConstants.SERVER_SCOPE,
  288 + attributeKeys)
  289 + .get());
261 } 290 }
262 -  
263 - private SnapshotUpdate merge(Set<AttributeKvEntry> attributes) {  
264 - long newTs = 0;  
265 - Set<AlarmConditionFilterKey> keys = new HashSet<>();  
266 - for (AttributeKvEntry entry : attributes) {  
267 - newTs = Math.max(newTs, entry.getLastUpdateTs());  
268 - AlarmConditionFilterKey entityKey = new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey());  
269 - if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {  
270 - keys.add(entityKey);  
271 - }  
272 - }  
273 - latestValues.setTs(newTs);  
274 - return new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, keys); 291 + }
  292 +
  293 + private void addToSnapshot(DataSnapshot snapshot, List<AttributeKvEntry> data) {
  294 + for (AttributeKvEntry entry : data) {
  295 + if (entry.getValue() != null) {
  296 + EntityKeyValue value = toEntityValue(entry);
  297 + snapshot.putValue(
  298 + new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey()),
  299 + entry.getLastUpdateTs(),
  300 + value);
  301 + }
275 } 302 }
276 -  
277 - 303 + }
  304 +
  305 + public static EntityKeyValue toEntityValue(KvEntry entry) {
  306 + switch (entry.getDataType()) {
  307 + case STRING:
  308 + return EntityKeyValue.fromString(entry.getStrValue().get());
  309 + case LONG:
  310 + return EntityKeyValue.fromLong(entry.getLongValue().get());
  311 + case DOUBLE:
  312 + return EntityKeyValue.fromDouble(entry.getDoubleValue().get());
  313 + case BOOLEAN:
  314 + return EntityKeyValue.fromBool(entry.getBooleanValue().get());
  315 + case JSON:
  316 + return EntityKeyValue.fromJson(entry.getJsonValue().get());
  317 + default:
  318 + throw new RuntimeException("Can't parse entry: " + entry.getDataType());
  319 + }
  320 + }
  321 +
  322 + private SnapshotUpdate merge(Long newTs, List<KvEntry> data) {
  323 + Set<AlarmConditionFilterKey> keys = new HashSet<>();
  324 + for (KvEntry entry : data) {
  325 + AlarmConditionFilterKey entityKey =
  326 + new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, entry.getKey());
  327 + if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
  328 + keys.add(entityKey);
  329 + }
  330 + }
  331 + latestValues.setTs(newTs);
  332 + return new SnapshotUpdate(AlarmConditionKeyType.TIME_SERIES, keys);
  333 + }
  334 +
  335 + private SnapshotUpdate merge(Set<AttributeKvEntry> attributes) {
  336 + long newTs = 0;
  337 + Set<AlarmConditionFilterKey> keys = new HashSet<>();
  338 + for (AttributeKvEntry entry : attributes) {
  339 + newTs = Math.max(newTs, entry.getLastUpdateTs());
  340 + AlarmConditionFilterKey entityKey =
  341 + new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, entry.getKey());
  342 + if (latestValues.putValue(entityKey, newTs, toEntityValue(entry))) {
  343 + keys.add(entityKey);
  344 + }
  345 + }
  346 + latestValues.setTs(newTs);
  347 + return new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, keys);
  348 + }
278 } 349 }