package cc.blynk.server.application.handlers.main.logic.reporting;
import cc.blynk.server.core.BlockingIOProcessor;
import cc.blynk.server.core.dao.ReportingDao;
import cc.blynk.server.core.model.auth.User;
import cc.blynk.server.core.model.enums.PinType;
import cc.blynk.server.core.protocol.exceptions.IllegalCommandBodyException;
import cc.blynk.server.core.protocol.exceptions.IllegalCommandException;
import cc.blynk.server.core.protocol.exceptions.NoDataException;
import cc.blynk.server.core.protocol.model.messages.StringMessage;
import cc.blynk.server.core.reporting.GraphPinRequest;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
import static cc.blynk.server.core.protocol.enums.Command.GET_GRAPH_DATA_RESPONSE;
import static cc.blynk.server.core.protocol.enums.Response.NO_DATA;
import static cc.blynk.server.core.protocol.enums.Response.SERVER_ERROR;
import static cc.blynk.utils.BlynkByteBufUtil.*;
import static cc.blynk.utils.ByteUtils.compress;
import static cc.blynk.utils.StringUtils.split2Device;
/**
* The Blynk Project.
* Created by Dmitriy Dumanskiy.
* Created on 2/1/2015.
*
*/
public class GetGraphDataLogic {
private static final Logger log = LogManager.getLogger(GetGraphDataLogic.class);
private final BlockingIOProcessor blockingIOProcessor;
private final ReportingDao reportingDao;
public GetGraphDataLogic(ReportingDao reportingDao, BlockingIOProcessor blockingIOProcessor) {
this.reportingDao = reportingDao;
this.blockingIOProcessor = blockingIOProcessor;
}
public void messageReceived(ChannelHandlerContext ctx, User user, StringMessage message) {
//warn: split may be optimized
//todo remove space after app migration
String[] messageParts = message.body.split(" |\0");
if (messageParts.length < 3) {
throw new IllegalCommandException("Wrong income message format.");
}
String[] dashIdDeviceId = split2Device(messageParts[0]);
int dashId = Integer.parseInt(dashIdDeviceId[0]);
int deviceId = 0;
if (dashIdDeviceId.length == 2) {
deviceId = Integer.parseInt(dashIdDeviceId[1]);
}
//special case for delete command
if (messageParts.length == 4) {
deleteGraphData(messageParts, user, dashId, deviceId);
ctx.writeAndFlush(ok(message.id), ctx.voidPromise());
} else {
user.profile.validateDashId(dashId);
process(ctx.channel(), dashId, deviceId, Arrays.copyOfRange(messageParts, 1, messageParts.length), user, message.id, 4);
}
}
private void process(Channel channel, int dashId, int deviceId, String[] messageParts, User user, int msgId, int valuesPerPin) {
int numberOfPins = messageParts.length / valuesPerPin;
GraphPinRequest[] requestedPins = new GraphPinRequestData[numberOfPins];
for (int i = 0; i < numberOfPins; i++) {
requestedPins[i] = new GraphPinRequestData(dashId, deviceId, messageParts, i, valuesPerPin);
}
readGraphData(channel, user, requestedPins, msgId);
}
private void readGraphData(Channel channel, User user, GraphPinRequest[] requestedPins, int msgId) {
blockingIOProcessor.executeHistory(() -> {
try {
byte[][] data = reportingDao.getAllFromDisk(user, requestedPins);
byte[] compressed = compress(requestedPins[0].dashId, data);
if (channel.isWritable()) {
channel.writeAndFlush(makeBinaryMessage(GET_GRAPH_DATA_RESPONSE, msgId, compressed), channel.voidPromise());
}
} catch (NoDataException noDataException) {
channel.writeAndFlush(makeResponse(msgId, NO_DATA), channel.voidPromise());
} catch (Exception e) {
log.error("Error reading reporting data. For user {}", user.email);
channel.writeAndFlush(makeResponse(msgId, SERVER_ERROR), channel.voidPromise());
}
});
}
private void deleteGraphData(String[] messageParts, User user, int dashId, int deviceId) {
try {
PinType pinType = PinType.getPinType(messageParts[1].charAt(0));
byte pin = Byte.parseByte(messageParts[2]);
String cmd = messageParts[3];
if (!"del".equals(cmd)) {
throw new IllegalCommandBodyException("Wrong body format. Expecting 'del'.");
}
reportingDao.delete(user, dashId, deviceId, pinType, pin);
} catch (NumberFormatException e) {
throw new IllegalCommandException("HardwareLogic command body incorrect.");
}
}
}