/** * 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.transport.mqtt.session; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.handler.codec.mqtt.*; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.id.SessionId; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.msg.core.*; import org.thingsboard.server.common.msg.kv.AttributesKVMsg; import org.thingsboard.server.common.msg.session.*; import org.thingsboard.server.common.msg.session.ex.SessionException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext; import org.thingsboard.server.transport.mqtt.MqttTopics; import org.thingsboard.server.transport.mqtt.MqttTransportHandler; import java.nio.charset.Charset; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; /** * Created by ashvayka on 19.01.17. */ public class GatewayDeviceSessionCtx extends DeviceAwareSessionContext { private static final Gson GSON = new Gson(); private static final Charset UTF8 = Charset.forName("UTF-8"); private static final ByteBufAllocator ALLOCATOR = new UnpooledByteBufAllocator(false); private GatewaySessionCtx parent; private final MqttSessionId sessionId; private volatile boolean closed; private AtomicInteger msgIdSeq = new AtomicInteger(0); public GatewayDeviceSessionCtx(GatewaySessionCtx parent, Device device) { super(parent.getProcessor(), parent.getAuthService(), device); this.parent = parent; this.sessionId = new MqttSessionId(); } @Override public SessionId getSessionId() { return sessionId; } @Override public SessionType getSessionType() { return SessionType.ASYNC; } @Override public void onMsg(SessionActorToAdaptorMsg sessionMsg) throws SessionException { Optional<MqttMessage> message = getToDeviceMsg(sessionMsg); message.ifPresent(parent::writeAndFlush); } private Optional<MqttMessage> getToDeviceMsg(SessionActorToAdaptorMsg sessionMsg) { ToDeviceMsg msg = sessionMsg.getMsg(); switch (msg.getMsgType()) { case STATUS_CODE_RESPONSE: ResponseMsg<?> responseMsg = (ResponseMsg) msg; if (responseMsg.isSuccess()) { MsgType requestMsgType = responseMsg.getRequestMsgType(); Integer requestId = responseMsg.getRequestId(); if (requestMsgType == MsgType.POST_ATTRIBUTES_REQUEST || requestMsgType == MsgType.POST_TELEMETRY_REQUEST) { return Optional.of(MqttTransportHandler.createMqttPubAckMsg(requestId)); } } break; case GET_ATTRIBUTES_RESPONSE: GetAttributesResponse response = (GetAttributesResponse) msg; if (response.isSuccess()) { return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, response)); } else { //TODO: push error handling to the gateway } break; case ATTRIBUTES_UPDATE_NOTIFICATION: AttributesUpdateNotification notification = (AttributesUpdateNotification) msg; return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, notification.getData())); case TO_DEVICE_RPC_REQUEST: ToDeviceRpcRequestMsg rpcRequest = (ToDeviceRpcRequestMsg) msg; return Optional.of(createMqttPublishMsg(MqttTopics.GATEWAY_RPC_TOPIC, rpcRequest)); } return Optional.empty(); } @Override public void onMsg(SessionCtrlMsg msg) throws SessionException { } @Override public boolean isClosed() { return closed; } public void setClosed(boolean closed) { this.closed = closed; } @Override public long getTimeout() { return 0; } private MqttMessage createMqttPublishMsg(String topic, GetAttributesResponse response) { JsonObject result = new JsonObject(); result.addProperty("id", response.getRequestId()); result.addProperty("device", device.getName()); if (response.getData().isPresent()) { AttributesKVMsg msg = response.getData().get(); if (msg.getClientAttributes() != null) { msg.getClientAttributes().forEach(v -> addValueToJson(result, "value", v)); } if (msg.getSharedAttributes() != null) { msg.getSharedAttributes().forEach(v -> addValueToJson(result, "value", v)); } } return createMqttPublishMsg(topic, result); } private void addValueToJson(JsonObject json, String name, KvEntry entry) { switch (entry.getDataType()) { case BOOLEAN: json.addProperty(name, entry.getBooleanValue().get()); break; case STRING: json.addProperty(name, entry.getStrValue().get()); break; case DOUBLE: json.addProperty(name, entry.getDoubleValue().get()); break; case LONG: json.addProperty(name, entry.getLongValue().get()); break; } } private MqttMessage createMqttPublishMsg(String topic, AttributesKVMsg data) { JsonObject result = new JsonObject(); result.addProperty("device", device.getName()); result.add("data", JsonConverter.toJson(data, false)); return createMqttPublishMsg(topic, result); } private MqttMessage createMqttPublishMsg(String topic, ToDeviceRpcRequestMsg data) { JsonObject result = new JsonObject(); result.addProperty("device", device.getName()); result.add("data", JsonConverter.toJson(data, true)); return createMqttPublishMsg(topic, result); } private MqttPublishMessage createMqttPublishMsg(String topic, JsonElement json) { MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, MqttQoS.AT_LEAST_ONCE, false, 0); MqttPublishVariableHeader header = new MqttPublishVariableHeader(topic, msgIdSeq.incrementAndGet()); ByteBuf payload = ALLOCATOR.buffer(); payload.writeBytes(GSON.toJson(json).getBytes(UTF8)); return new MqttPublishMessage(mqttFixedHeader, header, payload); } }