/** * Copyright © 2016-2017 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.thingsboard.server.extensions.core.plugin.telemetry.handlers; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.RuleId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.kv.BasicAttributeKVMsg; import org.thingsboard.server.extensions.api.plugins.PluginCallback; import org.thingsboard.server.extensions.api.plugins.PluginContext; import org.thingsboard.server.extensions.api.plugins.handlers.DefaultRuleMsgHandler; import org.thingsboard.server.extensions.api.plugins.msg.GetAttributesRequestRuleToPluginMsg; import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; import org.thingsboard.server.extensions.api.plugins.msg.TelemetryUploadRequestRuleToPluginMsg; import org.thingsboard.server.extensions.api.plugins.msg.UpdateAttributesRequestRuleToPluginMsg; import org.thingsboard.server.extensions.core.plugin.telemetry.SubscriptionManager; import org.thingsboard.server.extensions.core.plugin.telemetry.sub.SubscriptionType; import java.util.*; import java.util.stream.Collectors; @Slf4j public class TelemetryRuleMsgHandler extends DefaultRuleMsgHandler { private final SubscriptionManager subscriptionManager; public TelemetryRuleMsgHandler(SubscriptionManager subscriptionManager) { this.subscriptionManager = subscriptionManager; } @Override public void handleGetAttributesRequest(PluginContext ctx, TenantId tenantId, RuleId ruleId, GetAttributesRequestRuleToPluginMsg msg) { GetAttributesRequest request = msg.getPayload(); BiPluginCallBack<List<AttributeKvEntry>, List<AttributeKvEntry>> callback = new BiPluginCallBack<List<AttributeKvEntry>, List<AttributeKvEntry>>() { @Override public void onSuccess(PluginContext ctx, List<AttributeKvEntry> clientAttributes, List<AttributeKvEntry> sharedAttributes) { BasicGetAttributesResponse response = BasicGetAttributesResponse.onSuccess(request.getMsgType(), request.getRequestId(), BasicAttributeKVMsg.from(clientAttributes, sharedAttributes)); ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, response)); } @Override public void onFailure(PluginContext ctx, Exception e) { log.error("Failed to process get attributes request", e); ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, BasicStatusCodeResponse.onError(request.getMsgType(), request.getRequestId(), e))); } }; getAttributeKvEntries(ctx, msg.getDeviceId(), DataConstants.CLIENT_SCOPE, request.getClientAttributeNames(), callback.getV1Callback()); getAttributeKvEntries(ctx, msg.getDeviceId(), DataConstants.SHARED_SCOPE, request.getSharedAttributeNames(), callback.getV2Callback()); } private void getAttributeKvEntries(PluginContext ctx, DeviceId deviceId, String scope, Optional<Set<String>> names, PluginCallback<List<AttributeKvEntry>> callback) { if (names.isPresent()) { if (!names.get().isEmpty()) { ctx.loadAttributes(deviceId, scope, new ArrayList<>(names.get()), callback); } else { ctx.loadAttributes(deviceId, scope, callback); } } else { callback.onSuccess(ctx, Collections.emptyList()); } } @Override public void handleTelemetryUploadRequest(PluginContext ctx, TenantId tenantId, RuleId ruleId, TelemetryUploadRequestRuleToPluginMsg msg) { TelemetryUploadRequest request = msg.getPayload(); List<TsKvEntry> tsKvEntries = new ArrayList<>(); for (Map.Entry<Long, List<KvEntry>> entry : request.getData().entrySet()) { for (KvEntry kv : entry.getValue()) { tsKvEntries.add(new BasicTsKvEntry(entry.getKey(), kv)); } } ctx.saveTsData(msg.getDeviceId(), tsKvEntries, new PluginCallback<Void>() { @Override public void onSuccess(PluginContext ctx, Void data) { ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, BasicStatusCodeResponse.onSuccess(request.getMsgType(), request.getRequestId()))); subscriptionManager.onLocalSubscriptionUpdate(ctx, msg.getDeviceId(), SubscriptionType.TIMESERIES, s -> { List<TsKvEntry> subscriptionUpdate = new ArrayList<TsKvEntry>(); for (Map.Entry<Long, List<KvEntry>> entry : request.getData().entrySet()) { for (KvEntry kv : entry.getValue()) { if (s.isAllKeys() || s.getKeyStates().containsKey((kv.getKey()))) { subscriptionUpdate.add(new BasicTsKvEntry(entry.getKey(), kv)); } } } return subscriptionUpdate; }); } @Override public void onFailure(PluginContext ctx, Exception e) { log.error("Failed to process telemetry upload request", e); ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, BasicStatusCodeResponse.onError(request.getMsgType(), request.getRequestId(), e))); } }); } @Override public void handleUpdateAttributesRequest(PluginContext ctx, TenantId tenantId, RuleId ruleId, UpdateAttributesRequestRuleToPluginMsg msg) { UpdateAttributesRequest request = msg.getPayload(); ctx.saveAttributes(msg.getTenantId(), msg.getDeviceId(), DataConstants.CLIENT_SCOPE, request.getAttributes().stream().collect(Collectors.toList()), new PluginCallback<Void>() { @Override public void onSuccess(PluginContext ctx, Void value) { ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, BasicStatusCodeResponse.onSuccess(request.getMsgType(), request.getRequestId()))); subscriptionManager.onLocalSubscriptionUpdate(ctx, msg.getDeviceId(), SubscriptionType.ATTRIBUTES, s -> { List<TsKvEntry> subscriptionUpdate = new ArrayList<TsKvEntry>(); for (AttributeKvEntry kv : request.getAttributes()) { if (s.isAllKeys() || s.getKeyStates().containsKey(kv.getKey())) { subscriptionUpdate.add(new BasicTsKvEntry(kv.getLastUpdateTs(), kv)); } } return subscriptionUpdate; }); } @Override public void onFailure(PluginContext ctx, Exception e) { log.error("Failed to process attributes update request", e); ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, BasicStatusCodeResponse.onError(request.getMsgType(), request.getRequestId(), e))); } }); } }