Commit 1f39d20ab77ad9ed178f11dc68424879f9732e89

Authored by Andrew Shvayka
Committed by GitHub
2 parents 4ab8b4ae 44e8b2a5

Merge pull request #296 from thingsboard/feature/rpc-rule

RPC rule feature
@@ -44,6 +44,7 @@ import org.thingsboard.server.dao.customer.CustomerService; @@ -44,6 +44,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
44 import org.thingsboard.server.dao.device.DeviceService; 44 import org.thingsboard.server.dao.device.DeviceService;
45 import org.thingsboard.server.dao.event.EventService; 45 import org.thingsboard.server.dao.event.EventService;
46 import org.thingsboard.server.dao.plugin.PluginService; 46 import org.thingsboard.server.dao.plugin.PluginService;
  47 +import org.thingsboard.server.dao.relation.RelationService;
47 import org.thingsboard.server.dao.rule.RuleService; 48 import org.thingsboard.server.dao.rule.RuleService;
48 import org.thingsboard.server.dao.tenant.TenantService; 49 import org.thingsboard.server.dao.tenant.TenantService;
49 import org.thingsboard.server.dao.timeseries.TimeseriesService; 50 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -110,6 +111,9 @@ public class ActorSystemContext { @@ -110,6 +111,9 @@ public class ActorSystemContext {
110 @Getter private AlarmService alarmService; 111 @Getter private AlarmService alarmService;
111 112
112 @Autowired 113 @Autowired
  114 + @Getter private RelationService relationService;
  115 +
  116 + @Autowired
113 @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint; 117 @Getter @Setter private PluginWebSocketMsgEndpoint wsMsgEndpoint;
114 118
115 @Value("${actors.session.sync.timeout}") 119 @Value("${actors.session.sync.timeout}")
@@ -33,6 +33,8 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -33,6 +33,8 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
33 import org.thingsboard.server.common.data.kv.TsKvQuery; 33 import org.thingsboard.server.common.data.kv.TsKvQuery;
34 import org.thingsboard.server.common.data.page.TextPageLink; 34 import org.thingsboard.server.common.data.page.TextPageLink;
35 import org.thingsboard.server.common.data.plugin.PluginMetaData; 35 import org.thingsboard.server.common.data.plugin.PluginMetaData;
  36 +import org.thingsboard.server.common.data.relation.EntityRelation;
  37 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
36 import org.thingsboard.server.common.data.rule.RuleMetaData; 38 import org.thingsboard.server.common.data.rule.RuleMetaData;
37 import org.thingsboard.server.common.msg.cluster.ServerAddress; 39 import org.thingsboard.server.common.msg.cluster.ServerAddress;
38 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg; 40 import org.thingsboard.server.extensions.api.device.DeviceAttributesEventNotificationMsg;
@@ -395,6 +397,16 @@ public final class PluginProcessingContext implements PluginContext { @@ -395,6 +397,16 @@ public final class PluginProcessingContext implements PluginContext {
395 } 397 }
396 398
397 @Override 399 @Override
  400 + public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) {
  401 + return this.pluginCtx.relationService.findByFromAndType(from, relationType, RelationTypeGroup.COMMON);
  402 + }
  403 +
  404 + @Override
  405 + public ListenableFuture<List<EntityRelation>> findByToAndType(EntityId from, String relationType) {
  406 + return this.pluginCtx.relationService.findByToAndType(from, relationType, RelationTypeGroup.COMMON);
  407 + }
  408 +
  409 + @Override
398 public Optional<ServerAddress> resolve(EntityId entityId) { 410 public Optional<ServerAddress> resolve(EntityId entityId) {
399 return pluginCtx.routingService.resolveById(entityId); 411 return pluginCtx.routingService.resolveById(entityId);
400 } 412 }
@@ -30,6 +30,7 @@ import org.thingsboard.server.dao.attributes.AttributesService; @@ -30,6 +30,7 @@ import org.thingsboard.server.dao.attributes.AttributesService;
30 import org.thingsboard.server.dao.customer.CustomerService; 30 import org.thingsboard.server.dao.customer.CustomerService;
31 import org.thingsboard.server.dao.device.DeviceService; 31 import org.thingsboard.server.dao.device.DeviceService;
32 import org.thingsboard.server.dao.plugin.PluginService; 32 import org.thingsboard.server.dao.plugin.PluginService;
  33 +import org.thingsboard.server.dao.relation.RelationService;
33 import org.thingsboard.server.dao.rule.RuleService; 34 import org.thingsboard.server.dao.rule.RuleService;
34 import org.thingsboard.server.dao.tenant.TenantService; 35 import org.thingsboard.server.dao.tenant.TenantService;
35 import org.thingsboard.server.dao.timeseries.TimeseriesService; 36 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -61,6 +62,7 @@ public final class SharedPluginProcessingContext { @@ -61,6 +62,7 @@ public final class SharedPluginProcessingContext {
61 final AttributesService attributesService; 62 final AttributesService attributesService;
62 final ClusterRpcService rpcService; 63 final ClusterRpcService rpcService;
63 final ClusterRoutingService routingService; 64 final ClusterRoutingService routingService;
  65 + final RelationService relationService;
64 final PluginId pluginId; 66 final PluginId pluginId;
65 final TenantId tenantId; 67 final TenantId tenantId;
66 68
@@ -83,6 +85,7 @@ public final class SharedPluginProcessingContext { @@ -83,6 +85,7 @@ public final class SharedPluginProcessingContext {
83 this.pluginService = sysContext.getPluginService(); 85 this.pluginService = sysContext.getPluginService();
84 this.customerService = sysContext.getCustomerService(); 86 this.customerService = sysContext.getCustomerService();
85 this.tenantService = sysContext.getTenantService(); 87 this.tenantService = sysContext.getTenantService();
  88 + this.relationService = sysContext.getRelationService();
86 } 89 }
87 90
88 public PluginId getPluginId() { 91 public PluginId getPluginId() {
@@ -7,13 +7,13 @@ @@ -7,13 +7,13 @@
7 </encoder> 7 </encoder>
8 </appender> 8 </appender>
9 9
10 - <logger name="org.thingsboard.server" level="INFO"/> 10 + <logger name="org.thingsboard.server" level="WARN"/>
11 <logger name="org.springframework" level="WARN"/> 11 <logger name="org.springframework" level="WARN"/>
12 <logger name="org.springframework.boot.test" level="DEBUG"/> 12 <logger name="org.springframework.boot.test" level="DEBUG"/>
13 <logger name="org.apache.cassandra" level="WARN"/> 13 <logger name="org.apache.cassandra" level="WARN"/>
14 <logger name="org.cassandraunit" level="INFO"/> 14 <logger name="org.cassandraunit" level="INFO"/>
15 15
16 - <logger name="akka" level="DEBUG" /> 16 + <logger name="akka" level="INFO" />
17 17
18 <root level="WARN"> 18 <root level="WARN">
19 <appender-ref ref="console"/> 19 <appender-ref ref="console"/>
@@ -9,8 +9,8 @@ @@ -9,8 +9,8 @@
9 9
10 <logger name="org.thingsboard.server.dao" level="WARN"/> 10 <logger name="org.thingsboard.server.dao" level="WARN"/>
11 <logger name="org.apache.cassandra" level="WARN"/> 11 <logger name="org.apache.cassandra" level="WARN"/>
12 - <logger name="org.cassandraunit" level="INFO" />  
13 - <logger name="org.apache.cassandra" level="INFO" /> 12 + <logger name="org.cassandraunit" level="WARN" />
  13 + <logger name="org.apache.cassandra" level="WARN" />
14 14
15 <root level="WARN"> 15 <root level="WARN">
16 <appender-ref ref="console"/> 16 <appender-ref ref="console"/>
@@ -15,11 +15,14 @@ @@ -15,11 +15,14 @@
15 */ 15 */
16 package org.thingsboard.server.extensions.api.plugins; 16 package org.thingsboard.server.extensions.api.plugins;
17 17
  18 +import com.google.common.util.concurrent.ListenableFuture;
18 import org.thingsboard.server.common.data.Device; 19 import org.thingsboard.server.common.data.Device;
19 import org.thingsboard.server.common.data.id.*; 20 import org.thingsboard.server.common.data.id.*;
20 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 21 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
21 import org.thingsboard.server.common.data.kv.TsKvEntry; 22 import org.thingsboard.server.common.data.kv.TsKvEntry;
22 import org.thingsboard.server.common.data.kv.TsKvQuery; 23 import org.thingsboard.server.common.data.kv.TsKvQuery;
  24 +import org.thingsboard.server.common.data.relation.EntityRelation;
  25 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
23 import org.thingsboard.server.common.msg.cluster.ServerAddress; 26 import org.thingsboard.server.common.msg.cluster.ServerAddress;
24 import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; 27 import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg;
25 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; 28 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
@@ -109,4 +112,12 @@ public interface PluginContext { @@ -109,4 +112,12 @@ public interface PluginContext {
109 112
110 void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback<List<Device>> callback); 113 void getCustomerDevices(TenantId tenantId, CustomerId customerId, int limit, PluginCallback<List<Device>> callback);
111 114
  115 +
  116 + /*
  117 + * Relations API
  118 + * */
  119 +
  120 + ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType);
  121 +
  122 + ListenableFuture<List<EntityRelation>> findByToAndType(EntityId from, String relationType);
112 } 123 }
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.action.rpc;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.velocity.Template;
  20 +import org.apache.velocity.VelocityContext;
  21 +import org.apache.velocity.runtime.parser.ParseException;
  22 +import org.springframework.util.StringUtils;
  23 +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
  24 +import org.thingsboard.server.common.msg.session.ToDeviceMsg;
  25 +import org.thingsboard.server.extensions.api.component.Action;
  26 +import org.thingsboard.server.extensions.api.plugins.PluginAction;
  27 +import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg;
  28 +import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg;
  29 +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
  30 +import org.thingsboard.server.extensions.api.rules.RuleContext;
  31 +import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData;
  32 +import org.thingsboard.server.extensions.api.rules.SimpleRuleLifecycleComponent;
  33 +import org.thingsboard.server.extensions.core.utils.VelocityUtils;
  34 +
  35 +import java.util.Optional;
  36 +
  37 +/**
  38 + * Created by ashvayka on 14.09.17.
  39 + */
  40 +@Action(name = "Server Side RPC Call Action", descriptor = "ServerSideRpcCallActionDescriptor.json", configuration = ServerSideRpcCallActionConfiguration.class)
  41 +@Slf4j
  42 +public class ServerSideRpcCallAction extends SimpleRuleLifecycleComponent implements PluginAction<ServerSideRpcCallActionConfiguration> {
  43 +
  44 + private ServerSideRpcCallActionConfiguration configuration;
  45 + private Optional<Template> deviceIdTemplate;
  46 + private Optional<Template> fromDeviceRelationTemplate;
  47 + private Optional<Template> toDeviceRelationTemplate;
  48 + private Optional<Template> rpcCallMethodTemplate;
  49 + private Optional<Template> rpcCallBodyTemplate;
  50 +
  51 + @Override
  52 + public void init(ServerSideRpcCallActionConfiguration configuration) {
  53 + this.configuration = configuration;
  54 + try {
  55 + deviceIdTemplate = toTemplate(configuration.getDeviceIdTemplate(), "Device Id Template");
  56 + fromDeviceRelationTemplate = toTemplate(configuration.getFromDeviceRelationTemplate(), "From Device Relation Template");
  57 + toDeviceRelationTemplate = toTemplate(configuration.getToDeviceRelationTemplate(), "To Device Relation Template");
  58 + rpcCallMethodTemplate = toTemplate(configuration.getRpcCallMethodTemplate(), "RPC Call Method Template");
  59 + rpcCallBodyTemplate = toTemplate(configuration.getRpcCallBodyTemplate(), "RPC Call Body Template");
  60 + } catch (ParseException e) {
  61 + log.error("Failed to create templates based on provided configuration!", e);
  62 + throw new RuntimeException("Failed to create templates based on provided configuration!", e);
  63 + }
  64 + }
  65 +
  66 + @Override
  67 + public Optional<RuleToPluginMsg<?>> convert(RuleContext ctx, ToDeviceActorMsg toDeviceActorMsg, RuleProcessingMetaData metadata) {
  68 + String sendFlag = configuration.getSendFlag();
  69 + if (StringUtils.isEmpty(sendFlag) || (Boolean) metadata.get(sendFlag).orElse(Boolean.FALSE)) {
  70 + VelocityContext context = VelocityUtils.createContext(metadata);
  71 +
  72 + ServerSideRpcCallActionMsg.ServerSideRpcCallActionMsgBuilder builder = ServerSideRpcCallActionMsg.builder();
  73 +
  74 + deviceIdTemplate.ifPresent(t -> builder.deviceId(VelocityUtils.merge(t, context)));
  75 + fromDeviceRelationTemplate.ifPresent(t -> builder.fromDeviceRelation(VelocityUtils.merge(t, context)));
  76 + toDeviceRelationTemplate.ifPresent(t -> builder.toDeviceRelation(VelocityUtils.merge(t, context)));
  77 + rpcCallMethodTemplate.ifPresent(t -> builder.rpcCallMethod(VelocityUtils.merge(t, context)));
  78 + rpcCallBodyTemplate.ifPresent(t -> builder.rpcCallBody(VelocityUtils.merge(t, context)));
  79 + return Optional.of(new ServerSideRpcCallRuleToPluginActionMsg(toDeviceActorMsg.getTenantId(), toDeviceActorMsg.getCustomerId(), toDeviceActorMsg.getDeviceId(),
  80 + builder.build()));
  81 + } else {
  82 + return Optional.empty();
  83 + }
  84 + }
  85 +
  86 + private Optional<Template> toTemplate(String source, String name) throws ParseException {
  87 + if (!StringUtils.isEmpty(source)) {
  88 + return Optional.of(VelocityUtils.create(source, name));
  89 + } else {
  90 + return Optional.empty();
  91 + }
  92 + }
  93 +
  94 + @Override
  95 + public Optional<ToDeviceMsg> convert(PluginToRuleMsg<?> response) {
  96 + if (response instanceof ResponsePluginToRuleMsg) {
  97 + return Optional.of(((ResponsePluginToRuleMsg) response).getPayload());
  98 + }
  99 + return Optional.empty();
  100 + }
  101 +
  102 + @Override
  103 + public boolean isOneWayAction() {
  104 + return true;
  105 + }
  106 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.action.rpc;
  17 +
  18 +import lombok.Data;
  19 +
  20 +/**
  21 + * @author Andrew Shvayka
  22 + */
  23 +@Data
  24 +public class ServerSideRpcCallActionConfiguration {
  25 +
  26 + private String sendFlag;
  27 +
  28 + private String deviceIdTemplate;
  29 + private String rpcCallMethodTemplate;
  30 + private String rpcCallBodyTemplate;
  31 + private long rpcCallTimeoutInSec;
  32 + private String fromDeviceRelationTemplate;
  33 + private String toDeviceRelationTemplate;
  34 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.action.rpc;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Data;
  20 +
  21 +import java.io.Serializable;
  22 +
  23 +/**
  24 + * Created by ashvayka on 14.09.17.
  25 + */
  26 +@Data
  27 +@Builder
  28 +public class ServerSideRpcCallActionMsg implements Serializable {
  29 +
  30 + private String deviceId;
  31 + private String rpcCallMethod;
  32 + private String rpcCallBody;
  33 + private long rpcCallTimeoutInSec;
  34 +
  35 + private String fromDeviceRelation;
  36 + private String toDeviceRelation;
  37 +
  38 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.action.rpc;
  17 +
  18 +import org.thingsboard.server.common.data.id.CustomerId;
  19 +import org.thingsboard.server.common.data.id.DeviceId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg;
  22 +
  23 +/**
  24 + * Created by ashvayka on 14.09.17.
  25 + */
  26 +public class ServerSideRpcCallRuleToPluginActionMsg extends AbstractRuleToPluginMsg<ServerSideRpcCallActionMsg> {
  27 +
  28 + public ServerSideRpcCallRuleToPluginActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId,
  29 + ServerSideRpcCallActionMsg payload) {
  30 + super(tenantId, customerId, deviceId, payload);
  31 + }
  32 +}
@@ -19,15 +19,19 @@ import lombok.extern.slf4j.Slf4j; @@ -19,15 +19,19 @@ import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.server.extensions.api.component.Plugin; 19 import org.thingsboard.server.extensions.api.component.Plugin;
20 import org.thingsboard.server.extensions.api.plugins.AbstractPlugin; 20 import org.thingsboard.server.extensions.api.plugins.AbstractPlugin;
21 import org.thingsboard.server.extensions.api.plugins.PluginContext; 21 import org.thingsboard.server.extensions.api.plugins.PluginContext;
  22 +import org.thingsboard.server.extensions.api.plugins.handlers.DefaultRuleMsgHandler;
22 import org.thingsboard.server.extensions.api.plugins.handlers.RestMsgHandler; 23 import org.thingsboard.server.extensions.api.plugins.handlers.RestMsgHandler;
  24 +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
23 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse; 25 import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
24 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg; 26 import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
  27 +import org.thingsboard.server.extensions.core.action.rpc.ServerSideRpcCallAction;
25 import org.thingsboard.server.extensions.core.plugin.rpc.handlers.RpcRestMsgHandler; 28 import org.thingsboard.server.extensions.core.plugin.rpc.handlers.RpcRestMsgHandler;
  29 +import org.thingsboard.server.extensions.core.plugin.rpc.handlers.RpcRuleMsgHandler;
26 30
27 /** 31 /**
28 * @author Andrew Shvayka 32 * @author Andrew Shvayka
29 */ 33 */
30 -@Plugin(name = "RPC Plugin", actions = {}, descriptor = "RpcPluginDescriptor.json", configuration = RpcPluginConfiguration.class) 34 +@Plugin(name = "RPC Plugin", actions = {ServerSideRpcCallAction.class}, descriptor = "RpcPluginDescriptor.json", configuration = RpcPluginConfiguration.class)
31 @Slf4j 35 @Slf4j
32 public class RpcPlugin extends AbstractPlugin<RpcPluginConfiguration> { 36 public class RpcPlugin extends AbstractPlugin<RpcPluginConfiguration> {
33 37
@@ -61,6 +65,11 @@ public class RpcPlugin extends AbstractPlugin<RpcPluginConfiguration> { @@ -61,6 +65,11 @@ public class RpcPlugin extends AbstractPlugin<RpcPluginConfiguration> {
61 } 65 }
62 66
63 @Override 67 @Override
  68 + protected RuleMsgHandler getRuleMsgHandler() {
  69 + return new RpcRuleMsgHandler();
  70 + }
  71 +
  72 + @Override
64 public void resume(PluginContext ctx) { 73 public void resume(PluginContext ctx) {
65 74
66 } 75 }
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.extensions.core.plugin.rpc.handlers;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.util.StringUtils;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +import org.thingsboard.server.common.data.id.RuleId;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.common.data.relation.EntityRelation;
  25 +import org.thingsboard.server.extensions.api.plugins.PluginCallback;
  26 +import org.thingsboard.server.extensions.api.plugins.PluginContext;
  27 +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler;
  28 +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg;
  29 +import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequest;
  30 +import org.thingsboard.server.extensions.api.plugins.msg.ToDeviceRpcRequestBody;
  31 +import org.thingsboard.server.extensions.api.rules.RuleException;
  32 +import org.thingsboard.server.extensions.core.action.rpc.ServerSideRpcCallActionMsg;
  33 +import org.thingsboard.server.extensions.core.action.rpc.ServerSideRpcCallRuleToPluginActionMsg;
  34 +
  35 +import java.util.Collections;
  36 +import java.util.List;
  37 +import java.util.UUID;
  38 +import java.util.concurrent.TimeUnit;
  39 +import java.util.stream.Collectors;
  40 +
  41 +/**
  42 + * Created by ashvayka on 14.09.17.
  43 + */
  44 +@Slf4j
  45 +public class RpcRuleMsgHandler implements RuleMsgHandler {
  46 +
  47 + @Override
  48 + public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException {
  49 + if (msg instanceof ServerSideRpcCallRuleToPluginActionMsg) {
  50 + handle(ctx, tenantId, ruleId, ((ServerSideRpcCallRuleToPluginActionMsg) msg).getPayload());
  51 + } else {
  52 + throw new RuntimeException("Not supported msg: " + msg + "!");
  53 + }
  54 + }
  55 +
  56 + private void handle(final PluginContext ctx, TenantId tenantId, RuleId ruleId, ServerSideRpcCallActionMsg msg) {
  57 + DeviceId deviceId = new DeviceId(UUID.fromString(msg.getDeviceId()));
  58 + ctx.checkAccess(deviceId, new PluginCallback<Void>() {
  59 + @Override
  60 + public void onSuccess(PluginContext dummy, Void value) {
  61 + try {
  62 + List<EntityId> deviceIds;
  63 + if (StringUtils.isEmpty(msg.getFromDeviceRelation()) && StringUtils.isEmpty(msg.getToDeviceRelation())) {
  64 + deviceIds = Collections.singletonList(deviceId);
  65 + } else if (!StringUtils.isEmpty(msg.getFromDeviceRelation())) {
  66 + List<EntityRelation> relations = ctx.findByFromAndType(deviceId, msg.getFromDeviceRelation()).get();
  67 + deviceIds = relations.stream().map(EntityRelation::getTo).collect(Collectors.toList());
  68 + } else {
  69 + List<EntityRelation> relations = ctx.findByToAndType(deviceId, msg.getFromDeviceRelation()).get();
  70 + deviceIds = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toList());
  71 + }
  72 + ToDeviceRpcRequestBody body = new ToDeviceRpcRequestBody(msg.getRpcCallMethod(), msg.getRpcCallBody());
  73 + long expirationTime = System.currentTimeMillis() + msg.getRpcCallTimeoutInSec();
  74 + for (EntityId address : deviceIds) {
  75 + DeviceId tmpId = new DeviceId(address.getId());
  76 + ctx.checkAccess(tmpId, new PluginCallback<Void>() {
  77 + @Override
  78 + public void onSuccess(PluginContext ctx, Void value) {
  79 + ctx.sendRpcRequest(new ToDeviceRpcRequest(UUID.randomUUID(),
  80 + tenantId, tmpId, true, expirationTime, body)
  81 + );
  82 + log.trace("[{}] Sent RPC Call Action msg", tmpId);
  83 + }
  84 +
  85 + @Override
  86 + public void onFailure(PluginContext ctx, Exception e) {
  87 + log.info("[{}] Failed to process RPC Call Action msg", tmpId, e);
  88 + }
  89 + });
  90 + }
  91 + } catch (Exception e) {
  92 + log.info("Failed to process RPC Call Action msg", e);
  93 + }
  94 + }
  95 +
  96 + @Override
  97 + public void onFailure(PluginContext dummy, Exception e) {
  98 + log.info("[{}] Failed to process RPC Call Action msg", deviceId, e);
  99 + }
  100 + });
  101 + }
  102 +}
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 "type": "object", 4 "type": "object",
5 "properties": { 5 "properties": {
6 "sendFlag": { 6 "sendFlag": {
7 - "title": "Send flag", 7 + "title": "Send flag (empty or 'isNewAlarm', 'isExistingAlarm', 'isClearedAlarm', 'isNewOrClearedAlarm')",
8 "type": "string" 8 "type": "string"
9 }, 9 },
10 "fromTemplate": { 10 "fromTemplate": {
  1 +{
  2 + "schema": {
  3 + "title": "Send Mail Action Configuration",
  4 + "type": "object",
  5 + "properties": {
  6 + "sendFlag": {
  7 + "title": "Send flag (empty or 'isNewAlarm', 'isExistingAlarm', 'isClearedAlarm', 'isNewOrClearedAlarm')",
  8 + "type": "string"
  9 + },
  10 + "deviceIdTemplate": {
  11 + "title": "Device ID template",
  12 + "type": "string",
  13 + "default": "$deviceId"
  14 + },
  15 + "rpcCallMethodTemplate": {
  16 + "title": "RPC Call template",
  17 + "type": "string"
  18 + },
  19 + "rpcCallBodyTemplate": {
  20 + "title": "RPC Call Body template",
  21 + "type": "string"
  22 + },
  23 + "rpcCallTimeoutInSec": {
  24 + "title": "RPC Call timeout in seconds",
  25 + "type": "integer",
  26 + "default": 60
  27 + },
  28 + "fromDeviceRelationTemplate": {
  29 + "title": "From Device Relation template",
  30 + "type": "string"
  31 + },
  32 + "toDeviceRelationTemplate": {
  33 + "title": "To Device Relation template",
  34 + "type": "string"
  35 + }
  36 + },
  37 + "required": [
  38 + "deviceIdTemplate",
  39 + "rpcCallMethodTemplate",
  40 + "rpcCallBodyTemplate",
  41 + "rpcCallTimeoutInSec"
  42 + ]
  43 + },
  44 + "form": [
  45 + "sendFlag",
  46 + "deviceIdTemplate",
  47 + "rpcCallMethodTemplate",
  48 + {
  49 + "key": "rpcCallBodyTemplate",
  50 + "type": "textarea",
  51 + "rows": 5
  52 + },
  53 + "rpcCallTimeoutInSec",
  54 + "fromDeviceRelationTemplate",
  55 + "toDeviceRelationTemplate"
  56 + ]
  57 +}