org.openhab.binding.ebus.parser.EBusTelegramParser.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.ebus.parser.EBusTelegramParser.java

Source

/**
 * Copyright (c) 2010-2014, openHAB.org and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.binding.ebus.parser;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.script.Bindings;
import javax.script.CompiledScript;
import javax.script.ScriptException;

import org.apache.commons.lang.ObjectUtils;
import org.openhab.binding.ebus.EBusTelegram;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Christian Sowada
 * @since 1.7.0
 */
public class EBusTelegramParser {

    private static final Logger logger = LoggerFactory.getLogger(EBusTelegramParser.class);

    private static final Logger loggerAnalyses = LoggerFactory
            .getLogger(EBusTelegramParser.class.getPackage().getName() + ".Analyses");

    private static final Logger loggerBrutforce = LoggerFactory
            .getLogger(EBusTelegramParser.class.getPackage().getName() + ".BruteForce");

    private Map<String, Object> settings;

    private EBusConfigurationProvider configurationProvider;

    public EBusTelegramParser(EBusConfigurationProvider configurationProvider) {
        this.configurationProvider = configurationProvider;
    }

    /**
     * @param byteBuffer
     * @param type
     * @param pos
     * @return
     */
    private Object getValue(ByteBuffer byteBuffer, String type, int pos, BigDecimal min, BigDecimal max,
            BigDecimal replaceValue, BigDecimal factor) {

        Object value = null;
        byte hByte = 0;
        byte lByte = 0;

        BigDecimal repVal = null;

        if (pos > byteBuffer.position()) {
            logger.warn("Wow, buffer pos error!");
        }

        switch (type) {
        case "data2b":
            hByte = byteBuffer.get(pos);
            lByte = byteBuffer.get(pos - 1);
            repVal = BigDecimal.valueOf(-128);
            value = new BigDecimal(EBusUtils.decodeDATA2b(hByte, lByte));
            break;

        case "data2c":
            hByte = byteBuffer.get(pos);
            lByte = byteBuffer.get(pos - 1);
            repVal = BigDecimal.valueOf(-2048);
            value = new BigDecimal(EBusUtils.decodeDATA2c(hByte, lByte));
            break;

        case "data1c":
            lByte = byteBuffer.get(pos - 1);
            repVal = BigDecimal.valueOf(255);
            value = new BigDecimal(EBusUtils.decodeDATA1c(lByte));
            break;

        case "data1b":
            lByte = byteBuffer.get(pos - 1);
            repVal = BigDecimal.valueOf(-128);
            value = new BigDecimal(EBusUtils.decodeDATA1b(lByte));
            break;

        case "bcd":
            lByte = byteBuffer.get(pos - 1);
            repVal = BigDecimal.valueOf(266);
            value = new BigDecimal(EBusUtils.decodeBCD(lByte));
            break;

        case "word":
            hByte = byteBuffer.get(pos);
            lByte = byteBuffer.get(pos - 1);
            repVal = BigDecimal.valueOf(65535);
            value = new BigDecimal(EBusUtils.decodeWORD(hByte, lByte));
            break;

        case "uchar":
        case "byte":
            repVal = BigDecimal.valueOf(255);
            value = new BigDecimal(byteBuffer.get(pos - 1) & 0xFF);
            break;

        case "char":
            repVal = BigDecimal.valueOf(255);
            value = new BigDecimal(byteBuffer.get(pos - 1));
            break;

        case "bit":
            int bit = ((Integer) settings.get("bit"));
            value = byteBuffer.get(pos - 1);

            boolean isSet = ((byte) value >> bit & 0x1) == 1;
            value = isSet;
            break;

        default:
            logger.warn("Configuration Error: Unknown command type! {}", type);
            break;
        }

        if (replaceValue != null) {
            repVal = replaceValue;
        }

        if (value instanceof BigDecimal) {
            BigDecimal b = (BigDecimal) value;

            if (repVal != null && b.compareTo(repVal) == 0) {
                logger.trace("Replace value found, skip value ...");
                value = b = null;
            }

            // multiply before check min and max
            if (b != null && factor != null) {
                logger.trace("Value multiplied ...");
                value = b = b.multiply(factor);
            }

            if (min != null && b != null && b.compareTo(min) == -1) {
                logger.trace("Minimal value reached, skip value ...");
                value = b = null;

            } else if (max != null && b != null && b.compareTo(max) == 1) {
                logger.trace("Maximal value reached, skip value ...");
                value = b = null;
            }

        }

        return value;
    }

    /**
     * @param entry
     * @param bindings2
     * @return
     * @throws ScriptException
     */
    private Object evaluateScript(Entry<String, Map<String, Object>> entry, Map<String, Object> bindings2)
            throws ScriptException {
        Object value = null;
        if (entry.getValue().containsKey("cscript")) {
            CompiledScript cscript = (CompiledScript) entry.getValue().get("cscript");

            // Add global variables thisValue and keyName to JavaScript context
            Bindings bindings = cscript.getEngine().createBindings();
            bindings.putAll(bindings2);
            value = cscript.eval(bindings);
        }

        value = ObjectUtils.defaultIfNull(NumberUtils.toBigDecimal(value), value);

        // round to two digits, maybe not optimal for any result
        if (value instanceof BigDecimal) {
            ((BigDecimal) value).setScale(2, BigDecimal.ROUND_HALF_UP);
        }

        return value;
    }

    /**
     * @param telegram
     */
    private void bruteforceEBusTelegram(EBusTelegram telegram) {

        byte[] data = telegram.getData();

        String format = String.format("%-4s%-13s%-13s%-13s%-13s%-13s%-13s", "Pos", "WORD", "UInt", "DATA2B",
                "DATA2C", "DATA1c", "BCD");
        loggerBrutforce.trace("    " + format);
        loggerBrutforce.trace("    -----------------------------------------------------------------------------");

        for (int i = 0; i < data.length; i++) {

            Object word = i == data.length - 1 ? "---" : EBusUtils.decodeWORD(data[i + 1], data[i]);
            Object data2b = i == data.length - 1 ? "---" : EBusUtils.decodeDATA2b(data[i + 1], data[i]);
            Object data2c = i == data.length - 1 ? "---" : EBusUtils.decodeDATA2c(data[i + 1], data[i]);
            Object data1c = i == data.length - 1 ? "---" : EBusUtils.decodeDATA1c(data[i + 1]);
            int bcd = EBusUtils.decodeBCD(data[i]);
            int uint = data[i] & 0xFF;
            format = String.format("%-4s%-13s%-13s%-13s%-13s%-13s%-13s", i + 6, word, uint, data2b, data2c, data1c,
                    bcd);
            loggerBrutforce.trace("    " + format);
        }

        if (telegram.getType() == EBusTelegram.MASTER_SLAVE) {
            data = telegram.getSlaveData();

            loggerBrutforce
                    .trace("    ---------------------------------- Answer ----------------------------------");

            for (int i = 0; i < data.length; i++) {

                Object word = i == data.length - 1 ? "---" : EBusUtils.decodeWORD(data[i + 1], data[i]);
                Object data2b = i == data.length - 1 ? "---" : EBusUtils.decodeDATA2b(data[i + 1], data[i]);
                Object data2c = i == data.length - 1 ? "---" : EBusUtils.decodeDATA2c(data[i + 1], data[i]);
                Object data1c = i == data.length - 1 ? "---" : EBusUtils.decodeDATA1c(data[i + 1]);
                int bcd = EBusUtils.decodeBCD(data[i]);
                int uint = data[i] & 0xFF;

                format = String.format("%-4s%-13s%-13s%-13s%-13s%-13s%-13s", i + 6, word, uint, data2b, data2c,
                        data1c, bcd);
                loggerBrutforce.trace("    " + format);
            }

        }
    }

    /**
     * @param telegram
     * @return
     */
    public Map<String, Object> parse(EBusTelegram telegram) {

        if (configurationProvider == null) {
            logger.error("Configuration not loaded, can't parse telegram!");
            return null;
        }

        final Map<String, Object> valueRegistry = new HashMap<String, Object>();
        final Map<String, Object> valueRegistry2 = new HashMap<String, Object>();

        if (telegram == null) {
            return null;
        }

        final ByteBuffer byteBuffer = telegram.getBuffer();
        final String bufferString = EBusUtils.toHexDumpString(byteBuffer).toString();

        final List<Map<String, Object>> matchedTelegramRegistry = configurationProvider
                .getCommandsByFilter(bufferString);

        loggerAnalyses.debug(bufferString);

        if (matchedTelegramRegistry.isEmpty()) {
            loggerAnalyses.debug("  >>> Unknown ----------------------------------------");
            if (loggerBrutforce.isTraceEnabled()) {
                loggerBrutforce.trace(bufferString);
                bruteforceEBusTelegram(telegram);
            }

            return null;
        }

        // loop thru all matching telegrams from registry
        for (Map<String, Object> registryEntry : matchedTelegramRegistry) {

            String classKey = registryEntry.containsKey("class") ? (String) registryEntry.get("class") : "";

            int debugLevel = 0;
            if (registryEntry.containsKey("debug")) {
                debugLevel = ((Integer) registryEntry.get("debug"));
            }

            // get values block
            @SuppressWarnings("unchecked")
            Map<String, Map<String, Object>> values = (Map<String, Map<String, Object>>) registryEntry
                    .get("values");

            loggerAnalyses.debug("  >>> {}",
                    registryEntry.containsKey("comment") ? registryEntry.get("comment") : "<No comment available>");

            for (Entry<String, Map<String, Object>> entry : values.entrySet()) {

                String uniqueKey = (classKey != "" ? classKey + "." : "") + entry.getKey();
                settings = entry.getValue();

                String type = ((String) settings.get("type")).toLowerCase();
                int pos = settings.containsKey("pos") ? ((Integer) settings.get("pos")).intValue() : -1;

                BigDecimal valueMin = NumberUtils.toBigDecimal(settings.get("min"));
                BigDecimal valueMax = NumberUtils.toBigDecimal(settings.get("max"));
                BigDecimal replaceValue = NumberUtils.toBigDecimal(settings.get("replaceValue"));
                BigDecimal factor = NumberUtils.toBigDecimal(settings.get("factor"));

                Object value = getValue(byteBuffer, type, pos, valueMin, valueMax, replaceValue, factor);

                // Add global variables thisValue and keyName to JavaScript context
                HashMap<String, Object> bindings = new HashMap<String, Object>();
                bindings.put(entry.getKey(), value);
                bindings.put(uniqueKey, value);
                bindings.put("thisValue", value);

                if (settings.containsKey("cscript")) {
                    try {
                        value = evaluateScript(entry, bindings);
                    } catch (ScriptException e) {
                        logger.error("Error on evaluating JavaScript!", e);
                        break;
                    }
                }

                String label = (String) (settings.containsKey("label") ? settings.get("label") : "");
                String format = String.format("%-35s%-10s%s", uniqueKey, value, label);
                if (debugLevel >= 2) {
                    loggerAnalyses.debug("    >>> " + format);
                } else {
                    loggerAnalyses.trace("    >>> " + format);
                }

                valueRegistry.put(uniqueKey, value);
                valueRegistry2.put(entry.getKey(), value);
            }

            // computes values available? if not exit here
            if (!registryEntry.containsKey("computed_values"))
                continue;

            @SuppressWarnings("unchecked")
            Map<String, Map<String, Object>> cvalues = (Map<String, Map<String, Object>>) registryEntry
                    .get("computed_values");
            for (Entry<String, Map<String, Object>> entry : cvalues.entrySet()) {
                String uniqueKey = (classKey != "" ? classKey + "." : "") + entry.getKey();
                HashMap<String, Object> bindings = new HashMap<String, Object>();
                bindings.putAll(valueRegistry);
                bindings.putAll(valueRegistry2);
                Object value;
                try {
                    value = evaluateScript(entry, bindings);
                    valueRegistry.put(entry.getKey(), value);

                    if (debugLevel >= 2) {
                        String label = (String) (settings.containsKey("label") ? settings.get("label") : "");
                        String format = String.format("%-35s%-10s%s", uniqueKey, value, label);
                        loggerAnalyses.debug("    >>> " + format);
                    }

                } catch (ScriptException e) {
                    logger.error("Error on evaluating JavaScript!", e);
                }
            }
        }

        return valueRegistry;
    }

}