org.mycontroller.standalone.message.McMessageEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.mycontroller.standalone.message.McMessageEngine.java

Source

/*
 * Copyright 2015-2016 Jeeva Kandasamy (jkandasa@gmail.com)
 * and other contributors as indicated by the @author tags.
 *
 * 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.mycontroller.standalone.message;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.TimeZone;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.mycontroller.standalone.AppProperties;
import org.mycontroller.standalone.AppProperties.NETWORK_TYPE;
import org.mycontroller.standalone.AppProperties.RESOURCE_TYPE;
import org.mycontroller.standalone.AppProperties.STATE;
import org.mycontroller.standalone.db.DaoUtils;
import org.mycontroller.standalone.db.NodeUtils.NODE_REGISTRATION_STATE;
import org.mycontroller.standalone.db.ResourcesLogsUtils;
import org.mycontroller.standalone.db.ResourcesLogsUtils.LOG_LEVEL;
import org.mycontroller.standalone.db.tables.Firmware;
import org.mycontroller.standalone.db.tables.ForwardPayload;
import org.mycontroller.standalone.db.tables.GatewayTable;
import org.mycontroller.standalone.db.tables.MetricsBatteryUsage;
import org.mycontroller.standalone.db.tables.MetricsBinaryTypeDevice;
import org.mycontroller.standalone.db.tables.MetricsCounterTypeDevice;
import org.mycontroller.standalone.db.tables.MetricsDoubleTypeDevice;
import org.mycontroller.standalone.db.tables.Node;
import org.mycontroller.standalone.db.tables.Sensor;
import org.mycontroller.standalone.db.tables.SensorVariable;
import org.mycontroller.standalone.exceptions.McBadRequestException;
import org.mycontroller.standalone.exceptions.NodeIdException;
import org.mycontroller.standalone.externalserver.ExternalServerEngine;
import org.mycontroller.standalone.fwpayload.ExecuteForwardPayload;
import org.mycontroller.standalone.message.McMessageUtils.MESSAGE_TYPE;
import org.mycontroller.standalone.message.McMessageUtils.MESSAGE_TYPE_INTERNAL;
import org.mycontroller.standalone.message.McMessageUtils.MESSAGE_TYPE_PRESENTATION;
import org.mycontroller.standalone.message.McMessageUtils.MESSAGE_TYPE_SET_REQ;
import org.mycontroller.standalone.message.McMessageUtils.MESSAGE_TYPE_STREAM;
import org.mycontroller.standalone.message.McMessageUtils.PAYLOAD_TYPE;
import org.mycontroller.standalone.metrics.MetricsUtils.AGGREGATION_TYPE;
import org.mycontroller.standalone.metrics.MetricsUtils.METRIC_TYPE;
import org.mycontroller.standalone.provider.mysensors.MySensorsUtils;
import org.mycontroller.standalone.provider.mysensors.firmware.FirmwareUtils;
import org.mycontroller.standalone.provider.mysensors.structs.FirmwareConfigRequest;
import org.mycontroller.standalone.provider.mysensors.structs.FirmwareConfigResponse;
import org.mycontroller.standalone.provider.mysensors.structs.FirmwareRequest;
import org.mycontroller.standalone.provider.mysensors.structs.FirmwareResponse;
import org.mycontroller.standalone.rule.McRuleEngine;
import org.mycontroller.standalone.uidtag.ExecuteUidTag;
import org.mycontroller.standalone.utils.McUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author Jeeva Kandasamy (jkandasa)
 * @since 0.0.3
 */
@Slf4j
public class McMessageEngine implements Runnable {
    private static final int FIRMWARE_PRINT_LOG = 100;

    private Firmware firmware;
    private McMessage mcMessage;

    public McMessageEngine(McMessage mcMessage) {
        switch (mcMessage.getNetworkType()) {
        case MY_SENSORS:
            if (mcMessage.getNodeEui().equals("255")) {
                mcMessage.setNodeEui(McMessage.NODE_BROADCAST_ID);
            }
            if (mcMessage.getSensorId().equals("255")) {
                mcMessage.setSensorId(McMessage.SENSOR_BROADCAST_ID);
            }
            break;
        default:
            //Nothing to do
            break;
        }
        this.mcMessage = mcMessage;
    }

    public void execute() throws McBadRequestException {
        mcMessage.setScreeningDone(true);
        _logger.debug("McMessage:{}", mcMessage);
        switch (mcMessage.getType()) {
        case C_PRESENTATION:
            if (mcMessage.isTxMessage()) {
                //ResourcesLogs message data
                if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
                    this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage, mcMessage.getSubType(), null);
                }

            } else {
                this.presentationSubMessageTypeSelector(mcMessage);
            }
            break;
        case C_SET:
            if (isNodeRegistered(mcMessage)) {
                this.recordSetTypeData(mcMessage);
            } else {
                unauthorizedSensor(mcMessage);
            }
            break;
        case C_REQ:
            if (isNodeRegistered(mcMessage)) {
                this.responseReqTypeData(mcMessage);
            } else {
                unauthorizedSensor(mcMessage);
            }
            break;
        case C_INTERNAL:
            this.internalSubMessageTypeSelector(mcMessage);
            break;
        case C_STREAM:
            if (isNodeRegistered(mcMessage)) {
                streamSubMessageTypeSelector(mcMessage);
            } else {
                unauthorizedSensor(mcMessage);
            }
            break;
        default:
            _logger.warn("Unknown message type, " + "unable to process further. Message[{}] dropped", mcMessage);
            break;
        }
        //update node last seen and status as UP
        if (!mcMessage.isTxMessage()) {
            if (!mcMessage.getNodeEui().equalsIgnoreCase(McMessage.NODE_BROADCAST_ID)) {
                Node node = getNode(mcMessage);
                node.setState(STATE.UP);
                updateNode(node);
            }
        }
    }

    private void unauthorizedSensor(McMessage mcMessage) {
        if (mcMessage.isTxMessage()) {
            _logger.warn("Message cannot send to unauthorized sensor. {}", mcMessage);
        } else {
            _logger.warn("Message received from unauthorized sensor. {}", mcMessage);
        }
    }

    private boolean isNodeRegistered(McMessage mcMessage) {
        Node node = getNode(mcMessage);
        if (node.getRegistrationState() == NODE_REGISTRATION_STATE.BLOCKED) {
            return false;
        } else if (node.getRegistrationState() == NODE_REGISTRATION_STATE.REGISTERED) {
            return true;
        } else if (node.getRegistrationState() == NODE_REGISTRATION_STATE.NEW) {
            if (AppProperties.getInstance().getControllerSettings().getAutoNodeRegistration()) {
                node.setRegistrationState(NODE_REGISTRATION_STATE.REGISTERED);
                updateNode(node);
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }

    }

    private void presentationSubMessageTypeSelector(McMessage mcMessage) {
        if (mcMessage.getSensorId() == McMessage.SENSOR_BROADCAST_ID) {
            Node node = getNode(mcMessage);
            node.setLibVersion(mcMessage.getPayload());
            node.setType(MESSAGE_TYPE_PRESENTATION.fromString(mcMessage.getSubType()));
            updateNode(node);
        } else {
            Node node = getNode(mcMessage);
            Sensor sensor = DaoUtils.getSensorDao().get(node.getId(), mcMessage.getSensorId());
            if (sensor == null) {
                sensor = Sensor.builder().sensorId(String.valueOf(mcMessage.getSensorId()))
                        .type(MESSAGE_TYPE_PRESENTATION.fromString(mcMessage.getSubType()))
                        .name(mcMessage.getPayload()).build();
                sensor.setNode(node);
                DaoUtils.getSensorDao().create(sensor);
            } else {
                sensor.setType(MESSAGE_TYPE_PRESENTATION.fromString(mcMessage.getSubType()));
                sensor.setName(mcMessage.getPayload());
                DaoUtils.getSensorDao().update(sensor);
            }
        }
        _logger.debug("Presentation Message[type:{},payload:{}]",
                MESSAGE_TYPE_PRESENTATION.fromString(mcMessage.getSubType()), mcMessage.getPayload());
        //ResourcesLogs message data
        if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
            this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage,
                    MESSAGE_TYPE_PRESENTATION.fromString(mcMessage.getSubType()).getText(), null);
        }

    }

    private void internalSubMessageTypeSelector(McMessage mcMessage) {
        //Get node, if node not available create
        Node node = null;
        if (!mcMessage.getNodeEui().equalsIgnoreCase(McMessage.NODE_BROADCAST_ID)) {
            node = getNode(mcMessage);
        }
        //ResourcesLogs message data
        if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
            this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage,
                    MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType()).getText(), null);
        }
        _logger.debug("Message Type:{}", MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType()).toString());
        switch (MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType())) {
        case I_BATTERY_LEVEL:
            if (mcMessage.isTxMessage()) {
                return;
            }
            _logger.debug("Battery Level:[nodeId:{},Level:{}%]", mcMessage.getNodeEui(), mcMessage.getPayload());
            node.setBatteryLevel(mcMessage.getPayload());
            updateNode(node);
            //Update battery level in to metrics table
            MetricsBatteryUsage batteryUsage = MetricsBatteryUsage.builder().node(node)
                    .timestamp(System.currentTimeMillis()).aggregationType(AGGREGATION_TYPE.RAW)
                    .avg(McUtils.getDouble(mcMessage.getPayload())).samples(1).build();

            DaoUtils.getMetricsBatteryUsageDao().create(batteryUsage);

            break;
        case I_TIME:
            if (mcMessage.isTxMessage()) {
                return;
            }
            TimeZone timeZone = TimeZone.getDefault();
            long utcTime = System.currentTimeMillis();
            long timeOffset = timeZone.getOffset(utcTime);
            long localTime = utcTime + timeOffset;
            mcMessage.setPayload(String.valueOf(localTime / 1000));
            mcMessage.setTxMessage(true);
            _logger.debug("Time Message:[{}]", mcMessage);
            McMessageUtils.sendToProviderBridge(mcMessage);
            _logger.debug("Time request resolved.");
            break;
        case I_VERSION:
            _logger.debug("GatewayTable version requested by {}! Message:{}", AppProperties.APPLICATION_NAME,
                    mcMessage);
            break;
        case I_ID_REQUEST:
            try {
                if (mcMessage.getNetworkType() == NETWORK_TYPE.MY_SENSORS) {
                    int nodeId = MySensorsUtils.getNextNodeId(mcMessage.getGatewayId());
                    mcMessage.setAcknowledge(false);
                    mcMessage.setSubType(MESSAGE_TYPE_INTERNAL.I_ID_RESPONSE.getText());
                    mcMessage.setPayload(String.valueOf(nodeId));
                    mcMessage.setScreeningDone(false);
                    mcMessage.setTxMessage(true);
                    McMessageUtils.sendToProviderBridge(mcMessage);
                    _logger.debug("New Id[{}] sent to node", nodeId);
                }
            } catch (NodeIdException ex) {
                _logger.error("Unable to generate new node Id,", ex);
                //ResourcesLogs message data
                if (ResourcesLogsUtils.isLevel(LOG_LEVEL.ERROR)) {
                    this.setSensorOtherData(LOG_LEVEL.ERROR, mcMessage,
                            MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType()).getText(), ex.getMessage());
                }

            }
            break;
        case I_INCLUSION_MODE:
            _logger.warn("Inclusion mode not supported by this controller! Message:{}", mcMessage);
            break;
        case I_CONFIG:
            if (mcMessage.isTxMessage()) {
                return;
            }
            mcMessage.setPayload(McMessageUtils.getMetricType());
            mcMessage.setTxMessage(true);
            McMessageUtils.sendToProviderBridge(mcMessage);
            _logger.debug("Configuration sent as follow[M/I]?:{}", mcMessage.getPayload());
            break;
        case I_LOG_MESSAGE:
            if (mcMessage.isTxMessage()) {
                return;
            }
            _logger.debug("Node trace-log message[nodeId:{},sensorId:{},message:{}]", mcMessage.getNodeEui(),
                    mcMessage.getSensorId(), mcMessage.getPayload());
            break;
        case I_SKETCH_NAME:
            if (mcMessage.isTxMessage()) {
                return;
            }
            _logger.debug("Internal Message[type:{},name:{}]",
                    MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType()), mcMessage.getPayload());
            node = getNode(mcMessage);
            node.setName(mcMessage.getPayload());
            updateNode(node);
            break;
        case I_SKETCH_VERSION:
            if (mcMessage.isTxMessage()) {
                return;
            }
            _logger.debug("Internal Message[type:{},version:{}]",
                    MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType()), mcMessage.getPayload());
            node = getNode(mcMessage);
            node.setVersion(mcMessage.getPayload());
            updateNode(node);
            break;
        case I_REBOOT:
            break;
        case I_GATEWAY_READY:
            if (mcMessage.isTxMessage()) {
                return;
            }
            _logger.debug("GatewayTable Ready[nodeId:{},message:{}]", mcMessage.getNodeEui(),
                    mcMessage.getPayload());
            break;

        case I_ID_RESPONSE:
            _logger.debug("Internal Message, Type:I_ID_RESPONSE[{}]", mcMessage);
            return;
        case I_HEARTBEAT:
        case I_HEARTBEAT_RESPONSE:
            if (mcMessage.isTxMessage()) {
                return;
            }
            node = getNode(mcMessage);
            node.setState(STATE.UP);
            updateNode(node);
            break;
        case I_DISCOVER:
            if (mcMessage.isTxMessage()) {
                return;
            }
        case I_DISCOVER_RESPONSE:
            if (mcMessage.isTxMessage()) {
                return;
            }
            node = getNode(mcMessage);
            node.setParentNodeEui(mcMessage.getPayload());
            updateNode(node);
            break;
        case I_DEBUG:
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
                this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage,
                        MESSAGE_TYPE_PRESENTATION.fromString(mcMessage.getSubType()).getText(),
                        mcMessage.getPayload());
            }
            break;
        case I_REGISTRATION_REQUEST:
            if (mcMessage.isTxMessage()) {
                return;
            }
            if (AppProperties.getInstance().getControllerSettings().getAutoNodeRegistration()) {
                mcMessage.setAcknowledge(false);
                mcMessage.setSubType(MESSAGE_TYPE_INTERNAL.I_REGISTRATION_RESPONSE.getText());
                mcMessage.setScreeningDone(false);
                mcMessage.setTxMessage(true);
                McMessageUtils.sendToProviderBridge(mcMessage);
                _logger.debug("Registration response sent to gateway:{}, node:{}", mcMessage.getGatewayId(),
                        mcMessage.getNodeEui());
            }
            break;
        case I_REGISTRATION_RESPONSE:
            if (mcMessage.isTxMessage()) {
                return;
            }
            //TODO: do some action, if controller want to react for this type of message
        case I_PRESENTATION:
            if (mcMessage.isTxMessage()) {
                return;
            }
        default:
            _logger.warn(
                    "Internal Message[type:{},payload:{}], "
                            + "This type may not be supported (or) not implemented yet",
                    MESSAGE_TYPE_INTERNAL.fromString(mcMessage.getSubType()), mcMessage.getPayload());
            break;
        }
    }

    //We are not logging firmware request/response in to db, as it is huge!
    private void streamSubMessageTypeSelector(McMessage mcMessage) {
        switch (MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType())) {
        case ST_FIRMWARE_CONFIG_REQUEST:
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
                this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage,
                        MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType()).getText(), null);
            }
            this.processFirmwareConfigRequest(mcMessage);
            break;
        case ST_FIRMWARE_REQUEST:
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.TRACE)) {
                this.setSensorOtherData(LOG_LEVEL.TRACE, mcMessage,
                        MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType()).getText(), null);
            }
            this.procressFirmwareRequest(mcMessage);
            break;
        case ST_FIRMWARE_CONFIG_RESPONSE:
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
                this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage,
                        MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType()).getText(), null);
            }

            break;
        case ST_FIRMWARE_RESPONSE:
        case ST_IMAGE:
        case ST_SOUND:
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.TRACE)) {
                this.setSensorOtherData(LOG_LEVEL.TRACE, mcMessage,
                        MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType()).getText(), null);
            }
            break;
        default:
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.WARNING)) {
                this.setSensorOtherData(LOG_LEVEL.WARNING, mcMessage,
                        MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType()).getText(), null);
            }
            _logger.debug("Stream Message[type:{},payload:{}], This type not be implemented yet",
                    MESSAGE_TYPE_STREAM.fromString(mcMessage.getSubType()), mcMessage.getPayload());
            break;
        }
    }

    private void procressFirmwareRequest(McMessage mcMessage) {
        FirmwareRequest firmwareRequest = new FirmwareRequest();
        try {
            firmwareRequest.setByteBuffer(ByteBuffer.wrap(Hex.decodeHex(mcMessage.getPayload().toCharArray()))
                    .order(ByteOrder.LITTLE_ENDIAN), 0);
            _logger.debug("Firmware Request:[Type:{},Version:{},Block:{}]", firmwareRequest.getType(),
                    firmwareRequest.getVersion(), firmwareRequest.getBlock());
            boolean requestFirmwareReload = false;
            if (firmware == null) {
                requestFirmwareReload = true;
            } else if (firmware != null) {
                if (firmwareRequest.getBlock() == (firmware.getBlocks() - 1)) {
                    requestFirmwareReload = true;
                } else if (firmwareRequest.getType() == firmware.getType().getId()
                        && firmwareRequest.getVersion() == firmware.getVersion().getId()) {
                    //Nothing to do just continue
                } else {
                    requestFirmwareReload = true;
                }
            } else {
                requestFirmwareReload = true;
            }

            if (requestFirmwareReload) {
                firmware = DaoUtils.getFirmwareDao().get(firmwareRequest.getType(), firmwareRequest.getVersion());
                _logger.debug("Firmware reloaded...");
            }

            if (firmware == null) {
                _logger.debug("selected firmware type/version not available");
                return;
            }

            FirmwareResponse firmwareResponse = new FirmwareResponse();
            firmwareResponse.setByteBufferPosition(0);
            firmwareResponse.setBlock(firmwareRequest.getBlock());
            firmwareResponse.setVersion(firmwareRequest.getVersion());
            firmwareResponse.setType(firmwareRequest.getType());
            StringBuilder builder = new StringBuilder();
            int fromIndex = firmwareRequest.getBlock() * FirmwareUtils.FIRMWARE_BLOCK_SIZE;
            for (int index = fromIndex; index < fromIndex + FirmwareUtils.FIRMWARE_BLOCK_SIZE; index++) {
                builder.append(String.format("%02X", firmware.getData().get(index)));
            }
            if (firmwareRequest.getBlock() == 0) {
                firmware = null;
                _logger.debug("Firmware unloaded...");
            }

            // Print firmware status in sensor logs
            if (firmwareRequest.getBlock() % FIRMWARE_PRINT_LOG == 0
                    || firmwareRequest.getBlock() == (firmware.getBlocks() - 1)) {
                //ResourcesLogs message data
                if (ResourcesLogsUtils.isLevel(LOG_LEVEL.INFO)) {
                    this.setSensorOtherData(LOG_LEVEL.INFO, mcMessage,
                            MESSAGE_TYPE_STREAM.ST_FIRMWARE_REQUEST.getText(),
                            "Block No: " + firmwareRequest.getBlock());
                }
            }

            mcMessage.setTxMessage(true);
            mcMessage.setSubType(MESSAGE_TYPE_STREAM.ST_FIRMWARE_RESPONSE.getText());
            mcMessage
                    .setPayload(Hex.encodeHexString(firmwareResponse.getByteBuffer().array()) + builder.toString());
            McMessageUtils.sendToProviderBridge(mcMessage);
            _logger.debug("FirmwareRespone:[Type:{},Version:{},Block:{}]", firmwareResponse.getType(),
                    firmwareResponse.getVersion(), firmwareResponse.getBlock());
            // Print firmware status in sensor logs
            if (firmwareRequest.getBlock() % FIRMWARE_PRINT_LOG == 0
                    || firmwareRequest.getBlock() == (firmware.getBlocks() - 1)) {
                //ResourcesLogs message data
                if (ResourcesLogsUtils.isLevel(LOG_LEVEL.INFO)) {
                    this.setSensorOtherData(LOG_LEVEL.INFO, mcMessage,
                            MESSAGE_TYPE_STREAM.ST_FIRMWARE_RESPONSE.getText(),
                            "Block No:" + firmwareRequest.getBlock());
                }

            }

        } catch (DecoderException ex) {
            _logger.error("Exception, ", ex);
        }
    }

    private void processFirmwareConfigRequest(McMessage mcMessage) {
        FirmwareConfigRequest firmwareConfigRequest = new FirmwareConfigRequest();
        try {
            firmwareConfigRequest.setByteBuffer(ByteBuffer.wrap(Hex.decodeHex(mcMessage.getPayload().toCharArray()))
                    .order(ByteOrder.LITTLE_ENDIAN), 0);
            boolean bootLoaderCommand = false;
            Firmware firmware = null;

            //Check firmware is configured for this particular node
            Node node = DaoUtils.getNodeDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui());
            if (node != null && node.getEraseConfig() != null && node.getEraseConfig()) {
                bootLoaderCommand = true;
                _logger.debug("Erase EEPROM has been set...");
            } else if (node != null && node.getFirmware() != null) {
                firmware = DaoUtils.getFirmwareDao().getById(node.getFirmware().getId());
                _logger.debug("Firmware selected based on node configuration...");
            } else if (firmwareConfigRequest.getType() == 65535 && firmwareConfigRequest.getVersion() == 65535) {
                if (AppProperties.getInstance().getMySensorsSettings().getDefaultFirmware() != null) {
                    firmware = DaoUtils.getFirmwareDao()
                            .getById(AppProperties.getInstance().getMySensorsSettings().getDefaultFirmware());
                } else {
                    _logger.warn("There is no default firmware set!");
                }
            } else {
                firmware = DaoUtils.getFirmwareDao().get(firmwareConfigRequest.getType(),
                        firmwareConfigRequest.getVersion());
            }

            FirmwareConfigResponse firmwareConfigResponse = new FirmwareConfigResponse();
            firmwareConfigResponse.setByteBufferPosition(0);

            if (bootLoaderCommand) {//If it is bootloader command
                if (node.getEraseConfig() != null && node.getEraseConfig()) {
                    firmwareConfigResponse.loadEraseEepromCommand();
                    node.setEraseConfig(false); //Remove erase EEPROM flag and update in to database
                    DaoUtils.getNodeDao().update(node);
                } else {
                    _logger.warn("Selected booloader command is not available, FirmwareConfigRequest:[{}]",
                            firmwareConfigRequest);
                    return;
                }
            } else if (firmware == null) {//Non bootloader command
                if (AppProperties.getInstance().getMySensorsSettings().getEnbaledDefaultOnNoFirmware()) {
                    _logger.debug("If requested firmware is not available, "
                            + "redirect to default firmware is set, Checking the default firmware");
                    if (AppProperties.getInstance().getMySensorsSettings().getDefaultFirmware() != null) {
                        firmware = DaoUtils.getFirmwareDao()
                                .getById(AppProperties.getInstance().getMySensorsSettings().getDefaultFirmware());
                        _logger.debug("Default firmware:[{}]", firmware.getFirmwareName());
                    } else {
                        _logger.warn("There is no default firmware set!");
                    }
                }
                //Selected, default: No firmware available for this request
                if (firmware == null) {
                    _logger.warn("Selected Firmware is not available, FirmwareConfigRequest:[{}]",
                            firmwareConfigRequest);
                    return;
                }
            }

            if (firmware != null) {
                firmwareConfigResponse.setType(firmware.getType().getId());
                firmwareConfigResponse.setVersion(firmware.getVersion().getId());
                firmwareConfigResponse.setBlocks(firmware.getBlocks());
                firmwareConfigResponse.setCrc(firmware.getCrc());
            }

            mcMessage.setTxMessage(true);
            mcMessage.setSubType(MESSAGE_TYPE_STREAM.ST_FIRMWARE_CONFIG_RESPONSE.getText());
            mcMessage.setPayload(Hex.encodeHexString(firmwareConfigResponse.getByteBuffer().array()).toUpperCase());
            McMessageUtils.sendToProviderBridge(mcMessage);
            _logger.debug("FirmwareConfigRequest:[{}]", firmwareConfigRequest);
            _logger.debug("FirmwareConfigResponse:[{}]", firmwareConfigResponse);
        } catch (DecoderException ex) {
            _logger.error("Exception, ", ex);
        }
    }

    private void responseReqTypeData(McMessage mcMessage) throws McBadRequestException {
        Sensor sensor = this.getSensor(mcMessage);
        SensorVariable sensorVariable = DaoUtils.getSensorVariableDao().get(sensor.getId(),
                MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType()));
        if (mcMessage.isTxMessage()) {
            if (sensorVariable != null) {
                //ResourcesLogs message data
                if (ResourcesLogsUtils.isLevel(LOG_LEVEL.INFO)) {
                    this.setSensorVariableData(LOG_LEVEL.INFO, MESSAGE_TYPE.C_REQ, sensorVariable, mcMessage, null);
                }
            } else {
                throw new McBadRequestException("Selected sensor variable is not available!");
            }
            return;
        }
        if (sensorVariable != null && sensorVariable.getValue() != null) {
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.INFO)) {
                this.setSensorVariableData(LOG_LEVEL.INFO, MESSAGE_TYPE.C_REQ, sensorVariable, mcMessage, null);
            }
            mcMessage.setTxMessage(true);
            mcMessage.setType(MESSAGE_TYPE.C_SET);
            mcMessage.setAcknowledge(false);
            mcMessage.setPayload(sensorVariable.getValue());
            McMessageUtils.sendToProviderBridge(mcMessage);
            _logger.debug("Request processed! Message Sent: {}", mcMessage);
        } else {
            //If sensorVariable not available create new one.
            if (sensorVariable == null) {
                sensorVariable = this.updateSensorVariable(mcMessage, this.getSensor(mcMessage),
                        McMessageUtils.getPayLoadType(MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType())));
            }
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.WARNING)) {
                this.setSensorVariableData(LOG_LEVEL.WARNING, MESSAGE_TYPE.C_REQ, sensorVariable, mcMessage,
                        "Failed: Data not available in " + AppProperties.APPLICATION_NAME);
            }
            _logger.warn("Data not available! but there is request from sensor[{}], Ignored this request!",
                    mcMessage);
        }
    }

    private SensorVariable updateSensorVariable(McMessage mcMessage, Sensor sensor, PAYLOAD_TYPE payloadType) {
        SensorVariable sensorVariable = DaoUtils.getSensorVariableDao().get(sensor.getId(),
                MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType()));
        METRIC_TYPE metricType = McMessageUtils.getMetricType(payloadType);
        if (sensorVariable == null) {
            String data = null;
            switch (metricType) {
            case BINARY:
                data = mcMessage.getPayload().equalsIgnoreCase("0") ? "0" : "1";
                break;
            case COUNTER:
                data = String.valueOf(McUtils.getLong(mcMessage.getPayload()));
                break;
            case DOUBLE:
                data = String.valueOf(McUtils.getDoubleAsString(mcMessage.getPayload()));
                break;
            default:
                data = mcMessage.getPayload();
                break;

            }
            sensorVariable = SensorVariable.builder().sensor(sensor)
                    .variableType(MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType())).value(data)
                    .timestamp(mcMessage.getTimestamp()).metricType(metricType).build().updateUnitAndMetricType();
            _logger.debug("This SensorVariable:[{}] for Sensor:{}] is not available in our DB, Adding...",
                    sensorVariable, sensor);

            DaoUtils.getSensorVariableDao().create(sensorVariable);
            sensorVariable = DaoUtils.getSensorVariableDao().get(sensorVariable);
        } else {
            switch (sensorVariable.getMetricType()) {
            case COUNTER:
                long oldValue = sensorVariable.getValue() == null ? 0L : McUtils.getLong(sensorVariable.getValue());
                long newValue = McUtils.getLong(mcMessage.getPayload());
                sensorVariable.setValue(String.valueOf(oldValue + newValue));
                break;
            case DOUBLE:
                //If it is received message, update with offset
                if (mcMessage.isTxMessage()) {
                    sensorVariable.setValue(McUtils.getDoubleAsString(McUtils.getDouble(mcMessage.getPayload())));
                } else {
                    sensorVariable.setValue(McUtils.getDoubleAsString(
                            McUtils.getDouble(mcMessage.getPayload()) + sensorVariable.getOffset()));
                }
                break;
            case BINARY:
                sensorVariable.setValue(mcMessage.getPayload().equalsIgnoreCase("0") ? "0" : "1");
                break;
            default:
                sensorVariable.setValue(mcMessage.getPayload());
                break;
            }
            sensorVariable.setTimestamp(mcMessage.getTimestamp());
            DaoUtils.getSensorVariableDao().update(sensorVariable);
        }

        //TODO: Add unit
        /* if (rawMessage.getSubType() == MYS_MESSAGE_TYPE_SET_REQ.V_UNIT_PREFIX.ordinal()) {
         sensor.setUnit(rawMessage.getPayLoad());
         }*/
        return sensorVariable;
    }

    private Sensor getSensor(McMessage mcMessage) {
        Sensor sensor = DaoUtils.getSensorDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui(),
                mcMessage.getSensorId());
        if (sensor == null) {
            getNode(mcMessage);
            _logger.debug("This sensor[{} from Node:{}] not available in our DB, Adding...",
                    mcMessage.getSensorId(), mcMessage.getNodeEui());
            sensor = Sensor.builder().sensorId(mcMessage.getSensorId()).build();
            sensor.setNode(this.getNode(mcMessage));
            DaoUtils.getSensorDao().create(sensor);
            sensor = DaoUtils.getSensorDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui(),
                    mcMessage.getSensorId());
        }
        return sensor;
    }

    private Node getNode(McMessage mcMessage) {
        Node node = DaoUtils.getNodeDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui());
        if (node == null) {
            _logger.debug("This Node[{}] not available in our DB, Adding...", mcMessage.getNodeEui());
            node = Node.builder().gatewayTable(GatewayTable.builder().id(mcMessage.getGatewayId()).build())
                    .eui(mcMessage.getNodeEui()).state(STATE.UP)
                    .registrationState(AppProperties.getInstance().getControllerSettings().getAutoNodeRegistration()
                            ? NODE_REGISTRATION_STATE.REGISTERED
                            : NODE_REGISTRATION_STATE.NEW)
                    .build();
            node.setLastSeen(System.currentTimeMillis());
            DaoUtils.getNodeDao().create(node);
            node = DaoUtils.getNodeDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui());
        }
        _logger.debug("Node:[{}], message:[{}]", node, mcMessage);
        return node;
    }

    private void updateNode(Node node) {
        node.setLastSeen(System.currentTimeMillis());
        DaoUtils.getNodeDao().update(node);
    }

    private void recordSetTypeData(McMessage mcMessage) {
        PAYLOAD_TYPE payloadType = McMessageUtils
                .getPayLoadType(MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType()));
        Sensor sensor = this.getSensor(mcMessage);
        //Before updating value into table convert payload types
        //Change RGB and RGBW values
        if (MESSAGE_TYPE_SET_REQ.V_RGB == MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType())
                || MESSAGE_TYPE_SET_REQ.V_RGBW == MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType())) {
            if (!mcMessage.getPayload().startsWith("#")) {
                mcMessage.setPayload("#" + mcMessage.getPayload());
            }
        }

        SensorVariable sensorVariable = this.updateSensorVariable(mcMessage, sensor, payloadType);
        _logger.debug(
                "GatewayName:{}, SensorName:{}, NodeId:{}, SesnorId:{}, SubType:{}, PayloadType:{}, Payload:{}",
                sensor.getName(), sensor.getNode().getGatewayTable().getName(), sensor.getNode().getEui(),
                sensor.getSensorId(), MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType()).getText(),
                payloadType.toString(), mcMessage.getPayload());
        if (mcMessage.getSubType().equals(MESSAGE_TYPE_SET_REQ.V_UNIT_PREFIX.getText())) {
            //TODO: Add fix
            /*
              //Set Unit
              sensor.setUnit(rawMessage.getPayLoad());*/
            DaoUtils.getSensorDao().update(sensor);
            //ResourcesLogs message data
            if (ResourcesLogsUtils.isLevel(LOG_LEVEL.NOTICE)) {
                this.setSensorOtherData(LOG_LEVEL.NOTICE, mcMessage, MESSAGE_TYPE_SET_REQ.V_UNIT_PREFIX.getText(),
                        null);
            }
            return;
        }

        sensor.setLastSeen(System.currentTimeMillis());
        DaoUtils.getSensorDao().update(sensor);

        switch (sensorVariable.getMetricType()) {
        case DOUBLE:
            DaoUtils.getMetricsDoubleTypeDeviceDao()
                    .create(MetricsDoubleTypeDevice.builder().sensorVariable(sensorVariable)
                            .aggregationType(AGGREGATION_TYPE.RAW).timestamp(sensorVariable.getTimestamp())
                            .avg(McUtils.getDouble(sensorVariable.getValue())).samples(1).build());

            break;
        case BINARY:
            DaoUtils.getMetricsBinaryTypeDeviceDao()
                    .create(MetricsBinaryTypeDevice.builder().sensorVariable(sensorVariable)
                            .timestamp(sensorVariable.getTimestamp())
                            .state(McUtils.getBoolean(sensorVariable.getValue())).build());
            break;
        case COUNTER:
            DaoUtils.getMetricsCounterTypeDeviceDao()
                    .create(MetricsCounterTypeDevice.builder().sensorVariable(sensorVariable)
                            .aggregationType(AGGREGATION_TYPE.RAW).timestamp(sensorVariable.getTimestamp())
                            .value(McUtils.getLong(mcMessage.getPayload())).samples(1).build());
            break;
        default:
            _logger.debug("This type not be implemented yet, PayloadType:{}, MessageType:{}, McMessage:{}",
                    payloadType, MESSAGE_TYPE_SET_REQ.fromString(mcMessage.getSubType()).toString(),
                    mcMessage.getPayload());
            break;
        }

        //ResourcesLogs message data
        if (ResourcesLogsUtils.isLevel(LOG_LEVEL.INFO)) {
            this.setSensorVariableData(LOG_LEVEL.INFO, MESSAGE_TYPE.C_SET, sensorVariable, mcMessage, null);
        }

        if (!mcMessage.isTxMessage()) {
            //Execute UidTag
            if (sensor.getType() != null && sensor.getType() != null
                    && sensor.getType() == MESSAGE_TYPE_PRESENTATION.S_CUSTOM
                    && sensorVariable.getVariableType() == MESSAGE_TYPE_SET_REQ.V_ID) {
                ExecuteUidTag executeUidTag = new ExecuteUidTag(sensorVariable);
                new Thread(executeUidTag).start();
            }
        }

        //TODO: Forward Payload to another node, if any and only on receive from gateway
        List<ForwardPayload> forwardPayloads = DaoUtils.getForwardPayloadDao()
                .getAllEnabled(sensorVariable.getId());
        if (forwardPayloads != null && !forwardPayloads.isEmpty()) {
            ExecuteForwardPayload executeForwardPayload = new ExecuteForwardPayload(forwardPayloads, sensor,
                    sensorVariable);
            new Thread(executeForwardPayload).run();
        }

        //Execute Rules for this sensor variable
        new Thread(new McRuleEngine(RESOURCE_TYPE.SENSOR_VARIABLE, sensorVariable.getId())).start();

        //Execute Send Payload to external server
        new Thread(new ExternalServerEngine(sensorVariable)).start();

    }

    private void setSensorVariableData(LOG_LEVEL logLevel, MESSAGE_TYPE type, SensorVariable sensorVariable,
            McMessage mcMessage, String extraMessage) {
        this.setSensorOtherData(RESOURCE_TYPE.SENSOR_VARIABLE, sensorVariable.getId(), logLevel, type, null,
                mcMessage.isTxMessage(), mcMessage.getPayload(), extraMessage);
    }

    private void setSensorOtherData(LOG_LEVEL logLevel, McMessage mcMessage, String messageSubType,
            String extraMessage) {
        if (mcMessage.getNodeEui().equalsIgnoreCase(McMessage.NODE_BROADCAST_ID)) {
            this.setSensorOtherData(RESOURCE_TYPE.GATEWAY, mcMessage.getGatewayId(), logLevel, mcMessage.getType(),
                    messageSubType, mcMessage.isTxMessage(), mcMessage.getPayload(), extraMessage);
        } else if (mcMessage.getSensorId().equalsIgnoreCase(McMessage.SENSOR_BROADCAST_ID)) {
            Node node = DaoUtils.getNodeDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui());
            this.setSensorOtherData(RESOURCE_TYPE.NODE, node.getId(), logLevel, mcMessage.getType(), messageSubType,
                    mcMessage.isTxMessage(), mcMessage.getPayload(), extraMessage);
        } else {
            Sensor sensor = DaoUtils.getSensorDao().get(mcMessage.getGatewayId(), mcMessage.getNodeEui(),
                    mcMessage.getSensorId());
            //TODO: For now creating sensor, if it's not available, we should remove this once this issue resolved
            //http://forum.mysensors.org/topic/2669/gateway-ready-message-with-sensor-id-0-v-1-6-beta
            //-----------------------------------------
            if (sensor == null) {
                sensor = this.getSensor(mcMessage);
            }
            //-----------------------------------------
            this.setSensorOtherData(RESOURCE_TYPE.SENSOR, sensor.getId(), logLevel, mcMessage.getType(),
                    messageSubType, mcMessage.isTxMessage(), mcMessage.getPayload(), extraMessage);
        }
    }

    private void setSensorOtherData(RESOURCE_TYPE resourceType, Integer resourceId, LOG_LEVEL logLevel,
            MESSAGE_TYPE messageType, String subType, boolean isTxMessage, String payload, String extraMessage) {
        StringBuilder builder = new StringBuilder();
        if (subType != null) {
            builder.append("[").append(subType).append("]");
        }
        if (payload != null) {
            builder.append(" ").append(payload);
        }
        if (extraMessage != null) {
            builder.append(" ").append(extraMessage);
        }
        ResourcesLogsUtils.recordSensorsResourcesLog(resourceType, resourceId, logLevel, messageType, isTxMessage,
                builder.toString());
    }

    @Override
    public void run() {
        try {
            this.execute();
        } catch (McBadRequestException ex) {
            _logger.error("Exception on processing {}", mcMessage, ex);
        }

    }
}