|
|
|
@ -15,55 +15,177 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.server.transport.mqtt.session; |
|
|
|
|
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Optional; |
|
|
|
|
|
|
|
import com.google.gson.*; |
|
|
|
import io.netty.buffer.ByteBufAllocator; |
|
|
|
import io.netty.buffer.UnpooledByteBufAllocator; |
|
|
|
import io.netty.channel.ChannelHandlerContext; |
|
|
|
import io.netty.handler.codec.mqtt.MqttMessage; |
|
|
|
import io.netty.handler.codec.mqtt.MqttPublishMessage; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.thingsboard.server.common.data.Device; |
|
|
|
import org.thingsboard.server.common.data.id.SessionId; |
|
|
|
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
|
|
|
import org.thingsboard.server.common.msg.core.BasicTelemetryUploadRequest; |
|
|
|
import org.thingsboard.server.common.msg.core.BasicUpdateAttributesRequest; |
|
|
|
import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; |
|
|
|
import org.thingsboard.server.common.msg.session.BasicAdaptorToSessionActorMsg; |
|
|
|
import org.thingsboard.server.common.msg.session.BasicToDeviceActorSessionMsg; |
|
|
|
import org.thingsboard.server.common.msg.session.ctrl.SessionCloseMsg; |
|
|
|
import org.thingsboard.server.common.transport.SessionMsgProcessor; |
|
|
|
import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
|
|
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
|
|
|
import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
|
|
|
import org.thingsboard.server.dao.device.DeviceService; |
|
|
|
import org.thingsboard.server.transport.mqtt.MqttTransportHandler; |
|
|
|
import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; |
|
|
|
|
|
|
|
import java.nio.charset.Charset; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
import static org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor.validateJsonPayload; |
|
|
|
|
|
|
|
/** |
|
|
|
* Created by ashvayka on 19.01.17. |
|
|
|
*/ |
|
|
|
@Slf4j |
|
|
|
public class GatewaySessionCtx { |
|
|
|
|
|
|
|
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 final Device gateway; |
|
|
|
private final SessionId gatewaySessionId; |
|
|
|
private final SessionMsgProcessor processor; |
|
|
|
private final DeviceService deviceService; |
|
|
|
private final DeviceAuthService authService; |
|
|
|
private final Map<String, GatewayDeviceSessionCtx> devices; |
|
|
|
private ChannelHandlerContext channel; |
|
|
|
|
|
|
|
public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, Device gateway) { |
|
|
|
public GatewaySessionCtx(SessionMsgProcessor processor, DeviceService deviceService, DeviceAuthService authService, DeviceSessionCtx gatewaySessionCtx) { |
|
|
|
this.processor = processor; |
|
|
|
this.deviceService = deviceService; |
|
|
|
this.authService = authService; |
|
|
|
this.gateway = gateway; |
|
|
|
this.gateway = gatewaySessionCtx.getDevice(); |
|
|
|
this.gatewaySessionId = gatewaySessionCtx.getSessionId(); |
|
|
|
this.devices = new HashMap<>(); |
|
|
|
} |
|
|
|
|
|
|
|
public void connect(String deviceName) { |
|
|
|
checkDeviceName(deviceName); |
|
|
|
public void onDeviceConnect(MqttPublishMessage msg) throws AdaptorException { |
|
|
|
String deviceName = checkDeviceName(getDeviceName(msg)); |
|
|
|
Optional<Device> deviceOpt = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), deviceName); |
|
|
|
Device device = deviceOpt.orElseGet(() -> { |
|
|
|
Device newDevice = new Device(); |
|
|
|
newDevice.setTenantId(gateway.getTenantId()); |
|
|
|
newDevice.setName(deviceName); |
|
|
|
return deviceService.saveDevice(newDevice); |
|
|
|
}); |
|
|
|
devices.put(deviceName, new GatewayDeviceSessionCtx(processor, authService, device)); |
|
|
|
devices.put(deviceName, new GatewayDeviceSessionCtx(this, device)); |
|
|
|
ack(msg); |
|
|
|
} |
|
|
|
|
|
|
|
public void onDeviceDisconnect(MqttPublishMessage msg) throws AdaptorException { |
|
|
|
String deviceName = checkDeviceName(getDeviceName(msg)); |
|
|
|
GatewayDeviceSessionCtx deviceSessionCtx = devices.remove(deviceName); |
|
|
|
deviceSessionCtx.setClosed(true); |
|
|
|
ack(msg); |
|
|
|
} |
|
|
|
|
|
|
|
public void onGatewayDisconnect() { |
|
|
|
devices.forEach((k, v) -> { |
|
|
|
processor.process(SessionCloseMsg.onDisconnect(v.getSessionId())); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
public void disconnect(String deviceName) { |
|
|
|
checkDeviceName(deviceName); |
|
|
|
devices.remove(deviceName); |
|
|
|
public void onDeviceTelemetry(MqttPublishMessage mqttMsg) throws AdaptorException { |
|
|
|
JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); |
|
|
|
int requestId = mqttMsg.variableHeader().messageId(); |
|
|
|
if (json.isJsonObject()) { |
|
|
|
JsonObject jsonObj = json.getAsJsonObject(); |
|
|
|
for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { |
|
|
|
String deviceName = checkDeviceConnected(deviceEntry.getKey()); |
|
|
|
if (!deviceEntry.getValue().isJsonArray()) { |
|
|
|
throw new JsonSyntaxException("Can't parse value: " + json); |
|
|
|
} |
|
|
|
BasicTelemetryUploadRequest request = new BasicTelemetryUploadRequest(requestId); |
|
|
|
JsonArray deviceData = deviceEntry.getValue().getAsJsonArray(); |
|
|
|
for (JsonElement element : deviceData) { |
|
|
|
JsonConverter.parseWithTs(request, element.getAsJsonObject()); |
|
|
|
} |
|
|
|
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); |
|
|
|
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), |
|
|
|
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); |
|
|
|
} |
|
|
|
} else { |
|
|
|
throw new JsonSyntaxException("Can't parse value: " + json); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void checkDeviceName(String deviceName) { |
|
|
|
public void onDeviceAttributes(MqttPublishMessage mqttMsg) throws AdaptorException { |
|
|
|
JsonElement json = validateJsonPayload(gatewaySessionId, mqttMsg.payload()); |
|
|
|
int requestId = mqttMsg.variableHeader().messageId(); |
|
|
|
if (json.isJsonObject()) { |
|
|
|
JsonObject jsonObj = json.getAsJsonObject(); |
|
|
|
for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { |
|
|
|
String deviceName = checkDeviceConnected(deviceEntry.getKey()); |
|
|
|
if (!deviceEntry.getValue().isJsonObject()) { |
|
|
|
throw new JsonSyntaxException("Can't parse value: " + json); |
|
|
|
} |
|
|
|
long ts = System.currentTimeMillis(); |
|
|
|
BasicUpdateAttributesRequest request = new BasicUpdateAttributesRequest(requestId); |
|
|
|
JsonObject deviceData = deviceEntry.getValue().getAsJsonObject(); |
|
|
|
request.add(JsonConverter.parseValues(deviceData).stream().map(kv -> new BaseAttributeKvEntry(kv, ts)).collect(Collectors.toList())); |
|
|
|
GatewayDeviceSessionCtx deviceSessionCtx = devices.get(deviceName); |
|
|
|
processor.process(new BasicToDeviceActorSessionMsg(deviceSessionCtx.getDevice(), |
|
|
|
new BasicAdaptorToSessionActorMsg(deviceSessionCtx, request))); |
|
|
|
} |
|
|
|
} else { |
|
|
|
throw new JsonSyntaxException("Can't parse value: " + json); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private String checkDeviceConnected(String deviceName) { |
|
|
|
if (!devices.containsKey(deviceName)) { |
|
|
|
throw new RuntimeException("Device is not connected!"); |
|
|
|
} else { |
|
|
|
return deviceName; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private String checkDeviceName(String deviceName) { |
|
|
|
if (StringUtils.isEmpty(deviceName)) { |
|
|
|
throw new RuntimeException(); |
|
|
|
throw new RuntimeException("Device name is empty!"); |
|
|
|
} else { |
|
|
|
return deviceName; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private String getDeviceName(MqttPublishMessage mqttMsg) throws AdaptorException { |
|
|
|
JsonElement json = JsonMqttAdaptor.validateJsonPayload(gatewaySessionId, mqttMsg.payload()); |
|
|
|
return json.getAsJsonObject().get("device").getAsString(); |
|
|
|
} |
|
|
|
|
|
|
|
protected SessionMsgProcessor getProcessor() { |
|
|
|
return processor; |
|
|
|
} |
|
|
|
|
|
|
|
protected DeviceAuthService getAuthService() { |
|
|
|
return authService; |
|
|
|
} |
|
|
|
|
|
|
|
public void setChannel(ChannelHandlerContext channel) { |
|
|
|
this.channel = channel; |
|
|
|
} |
|
|
|
|
|
|
|
private void ack(MqttPublishMessage msg) { |
|
|
|
writeAndFlush(MqttTransportHandler.createMqttPubAckMsg(msg.variableHeader().messageId())); |
|
|
|
} |
|
|
|
|
|
|
|
protected void writeAndFlush(MqttMessage mqttMessage) { |
|
|
|
channel.writeAndFlush(mqttMessage); |
|
|
|
} |
|
|
|
} |
|
|
|
|