org.openhab.binding.obd.internal.OBDBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.obd.internal.OBDBinding.java

Source

/**
 * Copyright (c) 2010-2015, 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.obd.internal;

import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Timer;
import java.util.Map.Entry;

//import javax.xml.bind.DatatypeConverter;

//import net.astesana.javaluator.DoubleEvaluator;

import org.apache.commons.lang.StringUtils;
//import org.openhab.binding.gpsd.protocol.GPSdSerialConnector;
import org.openhab.binding.obd.OBDBindingProvider;
import org.openhab.binding.obd.protocol.OBDConnectionHelper;
import org.openhab.binding.obd.protocol.OBDJavaConnector;
import org.openhab.binding.obd.protocol.OBDConnector;
import org.openhab.binding.obd.protocol.OBDDataParser;
import org.openhab.binding.obd.protocol.OBDObject;
import org.openhab.binding.obd.protocol.OBDParserRule;
import org.openhab.binding.obd.protocol.OBDSimulator;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.transform.TransformationException;
import org.openhab.core.transform.TransformationHelper;
import org.openhab.core.transform.TransformationService;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * Binding to receive data from Open Energy Monitor devices.
 * 
 * @author Pauli Anttila
 * @since 1.4.0
 */
public class OBDBinding extends AbstractBinding<OBDBindingProvider> implements ManagedService {

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

    /* configuration variables for communication */
    //   private int udpPort = 9997;
    //   private String serialPort = null;
    private boolean simulate = false;
    private String device = "/dev/ttyUSB01";
    private int speed = 9600;
    private int refresh = 500;
    private int retry = 60000;
    private int delay = 50;

    private OBDDataParser dataParser = null;
    private Timer reconnectTimer = null;
    private boolean running = false;

    /** Thread to handle messages from GPSd Server devices */
    private MessageListener messageListener = null;

    public OBDBinding() {
    }

    public void activate() {
        logger.trace("Activate OBD binding");
    }

    public void deactivate() {
        logger.trace("Deactivate OBD binding");
        messageListener.setInterrupted(true);
    }

    protected String getName() {
        return "OBD Refresh Service";
    }

    /**
     * @{inheritDoc
     */
    @Override
    public synchronized void updated(Dictionary config) throws ConfigurationException {

        logger.trace("OBD Updated");
        if (config != null) {
            logger.trace("OBD Configuration not null");
            if (config != null) {
                String deviceString = (String) config.get("device");
                if (StringUtils.isNotBlank(deviceString)) {
                    device = deviceString;
                    logger.trace("device setting is {} ", device);
                }

                String speedString = (String) config.get("speed");
                if (StringUtils.isNotBlank(speedString)) {
                    speed = Integer.parseInt(speedString);
                    logger.trace("speed setting is{} ", speed);
                }

                String readDelay = (String) config.get("delay");
                if (StringUtils.isNotBlank(readDelay)) {
                    delay = Integer.parseInt(readDelay);
                    logger.trace("comm delay setting is {} ", delay);
                }

                //setProperlyConfigured(true);

            }

            HashMap<String, OBDParserRule> parsingRules = new HashMap<String, OBDParserRule>();

            Enumeration<String> keys = config.keys();

            while (keys.hasMoreElements()) {
                String key = (String) keys.nextElement();

                // the config-key enumeration contains additional keys that we
                // don't want to process here ...
                if ("service.pid".equals(key)) {
                    continue;
                }

                if ("simulate".equals(key)) {
                    continue;
                }

                String value = (String) config.get(key);

                if ("device".equals(key)) {
                    if (StringUtils.isNotBlank(value)) {
                        device = value;
                        logger.trace("Using serial device {}", device.toString());
                    }
                } else if ("speed".equals(key)) {
                    speed = Integer.parseInt(value);
                    logger.trace("Speed  set to {}", speed);
                } else if ("refresh".equals(key)) {
                    refresh = Integer.parseInt(value);
                    logger.trace("Refresh  set to {}", refresh);
                } else if ("retry".equals(key)) {
                    retry = Integer.parseInt(value);
                    logger.trace("Retry set to {}", retry);
                } else if ("delay".equals(key)) {
                    delay = Integer.parseInt(value);
                    logger.trace("Delay set to {}", retry);
                } else {

                    // process all data parsing rules
                    try {
                        OBDParserRule rule = new OBDParserRule(value);
                        parsingRules.put(key, rule);
                    } catch (OBDException e) {
                        throw new ConfigurationException(key, "invalid parser rule", e);
                    }
                    continue;
                }

            }

            if (parsingRules != null) {
                logger.trace("OBD Data Parser called");
                dataParser = new OBDDataParser(parsingRules);
            }

            if (messageListener != null) {

                logger.trace("Close previous message listener");

                messageListener.setInterrupted(true);
                try {
                    messageListener.join();
                } catch (InterruptedException e) {
                    logger.info("Previous message listener closing interrupted", e);
                }
            }

            if (!running) {
                messageListener = new MessageListener(this);
                messageListener.start();
            }
        }
    }

    /**
     * The MessageListener runs as a separate thread.
     * 
     * Thread listening message from Open Energy Monitoring devices and send
     * updates to openHAB bus.
     * 
     */
    private class MessageListener extends Thread {

        private boolean interrupted = false;
        private OBDBinding binding = null;
        private int reInitCount;

        MessageListener(OBDBinding binding) {
            this.binding = binding;
        }

        public void setInterrupted(boolean interrupted) {
            this.interrupted = interrupted;
            messageListener.interrupt();
        }

        @Override
        public void run() {

            logger.info("OBD data pooler  starting");
            running = true;

            OBDConnector connector;

            while (!interrupted) {
                //logger.info("OBD Pooling Starting");
                if (simulate == true)
                    connector = new OBDSimulator();
                else
                    connector = new OBDJavaConnector(device, speed, delay);
                try {
                    while ((connector == null || !connector.isConnected()) && !interrupted) {
                        connector.connect();
                        if (!connector.isConnected()) {
                            Thread.sleep(retry);
                        }
                    }
                } catch (Exception e) {
                    logger.error("Error occured when connecting OBD device", e);
                    logger.warn("Closing OBD message listener");

                    OBDConnectionHelper helper = new OBDConnectionHelper(binding);
                    Timer reconnectTimer = new Timer(true);
                    reconnectTimer.schedule(helper, 10000);

                    // exit
                    interrupted = true;
                }

                reInitCount = 0;
                int retryCount = 0;
                long previousTimer = System.currentTimeMillis();

                // as long as we are connected, continue running
                try {
                    while (connector.isConnected()) {

                        long currentTimer = System.currentTimeMillis();

                        int poolReturnCode = -1;

                        // as long as we are connected and not interruped is requested, continue running

                        try {
                            try {
                                while ((currentTimer - previousTimer) < refresh) {
                                    sleep(refresh - (currentTimer - previousTimer));
                                    currentTimer = System.currentTimeMillis();
                                }
                            } catch (InterruptedException e1) {
                                // TODO Auto-generated catch block
                                logger.error("General Error at OBD listener thread: {}", e1.toString());
                            }

                            logger.debug("OBD Timing - Previous {} ms", currentTimer - previousTimer);
                            previousTimer = currentTimer; //previousTimer marks when the previous read started. 
                            poolReturnCode = connector.poll();

                            logger.trace("OBD Pooling by binding returned {}", poolReturnCode);
                            switch (poolReturnCode) {
                            case -1: {
                                logger.trace("Poll returned false. Sleeping 15 seconds.");
                                sleep(15000);
                                connector.fastInit();
                                if (retryCount++ < 4) {
                                    logger.trace("Poll returned false. Retrying.");
                                } else {
                                    logger.trace("Poll returned false after 4 trys. Disconnecting.");
                                    connector.disconnect();
                                    sleep(30000);
                                }
                                break;
                            }
                            case -2: {
                                logger.trace("Poll returned Unable to Connect. Waiting 60 secs and retrying.");
                                //Thread.sleep(30000);
                                //connector.disconnect();
                                Thread.sleep(60000);
                                break;
                            }
                            case 1: {
                                if (reInitCount > 4) {
                                    logger.warn(
                                            "Too many NODATA. FastInit not efective. Trying fullInit at OBD due to too many NO DATA errors");
                                    //Thread.sleep(30000);
                                    connector.disconnect();
                                    reInitCount = 0;
                                } else {
                                    logger.trace("NO DATA. Trying adapter fast reInit in 30secs. Retry # {}.",
                                            reInitCount);
                                    Thread.sleep(15000);
                                    connector.fastInit();
                                    reInitCount++;
                                }
                                continue;
                            }
                            case 0: {
                                retryCount = 0;
                                break;
                            }
                            case 3: {
                                Thread.sleep(5000);
                                logger.trace("Ignition seems to be off");
                                break;
                            }
                            }

                            logger.trace("Receiving data object");
                            OBDObject data = connector.receiveOBDObject();

                            double beginItemMeasurement = 0;
                            double beginParsingMeasurement = System.currentTimeMillis();

                            logger.debug("Timing - Parsing items");
                            for (OBDBindingProvider provider : providers) {
                                for (String itemName : provider.getItemNames()) {
                                    org.openhab.core.types.State state = null;
                                    boolean found = false;
                                    beginItemMeasurement = System.currentTimeMillis();

                                    logger.trace("Timing - Item being parsed is {}.",
                                            provider.getVariable(itemName).toLowerCase());

                                    try {

                                        switch (provider.getVariable(itemName).toLowerCase()) {
                                        case "enginerpm":
                                            state = new DecimalType(data.getEngineRpm());
                                            found = true;
                                            break;
                                        case "maf":
                                            state = new DecimalType(data.getMaf());
                                            found = true;
                                            break;
                                        case "map":
                                            state = new DecimalType(data.getIntakeManifoldPressure());
                                            found = true;
                                            break;
                                        case "oxygen1voltage":
                                            state = new DecimalType(data.getOxygenSensor1Voltage());
                                            found = true;
                                            break;
                                        case "oxygen1percent":
                                            state = new DecimalType(data.getOxygenSensor1Percent());
                                            found = true;
                                            break;
                                        case "engineload":
                                            state = new DecimalType(data.getEngineLoad());
                                            found = true;
                                            break;
                                        case "airintaketemp":
                                            state = new DecimalType(data.getAirIntakeTemp());
                                            found = true;
                                            break;
                                        case "ambientairtemp":
                                            state = new DecimalType(data.getAmbientAirTemp());
                                            found = true;
                                            break;
                                        case "fuelstatus":
                                            state = new DecimalType(data.getFuelStatus());
                                            found = true;
                                            break;
                                        case "mpg":
                                            state = new DecimalType(data.getMPG());
                                            found = true;
                                            break;
                                        case "mpglt":
                                            state = new DecimalType(data.getMPGLongTerm());
                                            found = true;
                                            break;
                                        case "kml":
                                            state = new DecimalType(data.getKML());
                                            found = true;
                                            break;
                                        case "kmllt":
                                            state = new DecimalType(data.getKMLLongTerm());
                                            found = true;
                                            break;
                                        case "fueltype":
                                            state = new DecimalType(data.getFuelType());
                                            found = true;
                                            break;
                                        case "fuelconsumption":
                                            state = new DecimalType(data.getFuelConsumption());
                                            found = true;
                                            break;
                                        case "enginecoolanttemp":
                                            state = new DecimalType(data.getEngineCoolantTemp());
                                            found = true;
                                            break;
                                        case "throttle":
                                            state = new DecimalType(data.getThrottle());
                                            found = true;
                                            break;
                                        case "fuellevel":
                                            state = new DecimalType(data.getFuelLevel());
                                            found = true;
                                            break;
                                        case "speed":
                                            state = new DecimalType(data.getSpeed());
                                            found = true;
                                            break;
                                        case "gear":
                                            state = new DecimalType(data.getCurrentGear());
                                            found = true;
                                            break;
                                        case "distancetraveled":
                                            state = new DecimalType(data.getDistanceTravaledSinceCodesCleared());
                                            found = true;
                                            break;
                                        case "batteryvoltage":
                                            state = new DecimalType(data.getBatteryVoltage());
                                            found = true;
                                            break;
                                        case "ignition":
                                            if (data.getIgnition()) {
                                                state = OnOffType.ON;
                                            } else {
                                                state = OnOffType.OFF;
                                            }
                                            found = true;
                                            break;
                                        }

                                        logger.trace("Timing  {} result  {} runtime {} miliseconds",
                                                provider.getVariable(itemName).toLowerCase(), state.toString(),
                                                System.currentTimeMillis() - beginItemMeasurement);
                                    } catch (Exception e) {
                                        // TODO Auto-generated catch block
                                        logger.error("Error setting OBD value for {} : {}", itemName.toLowerCase(),
                                                e.toString());
                                        throw e;
                                    }

                                    if (found) {
                                        if (state == null) {
                                            state = new DecimalType("-1");
                                        }
                                        state = transformData(provider.getTransformationType(itemName),
                                                provider.getTransformationFunction(itemName), state);
                                        eventPublisher.postUpdate(itemName, state);

                                    } else {
                                        logger.error("Invalid Item: {}", itemName.toString());
                                    }
                                    if (interrupted) {
                                        break;
                                    }
                                }

                            }
                            logger.debug("Timing - Parsing took {} ms.",
                                    System.currentTimeMillis() - beginParsingMeasurement);
                        }

                        catch (Exception e) {

                            logger.error("Error occured when received data from OBD device", e);
                            continue;

                        }

                    }
                } catch (OBDException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                try {
                    connector.disconnect();
                } catch (OBDException e) {
                    logger.error("Error occured when disconnecting form OBDII  device", e);
                }
                connector = null;
            }
        }
    }

    private String replaceVariables(HashMap<String, Number> vals, String variable) {
        for (Entry<String, Number> entry : vals.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            variable = variable.replace(key, String.valueOf(value));

        }

        return variable;
    }

    /**
     * Transform received data by Transformation service.
     * 
     */
    protected org.openhab.core.types.State transformData(String transformationType, String transformationFunction,
            org.openhab.core.types.State data) {

        if (transformationType != null && transformationFunction != null) {
            String transformedResponse = null;

            try {
                TransformationService transformationService = TransformationHelper
                        .getTransformationService(OBDActivator.getContext(), transformationType);
                if (transformationService != null) {
                    transformedResponse = transformationService.transform(transformationFunction,
                            String.valueOf(data));
                } else {
                    logger.warn(
                            "couldn't transform response because transformationService of type '{}' is unavailable",
                            transformationType);
                }
            } catch (TransformationException te) {
                logger.error("transformation throws exception [transformation type=" + transformationType
                        + ", transformation function=" + transformationFunction + ", response=" + data + "]", te);
            }

            logger.trace("transformed response is '{}'", transformedResponse);

            if (transformedResponse != null) {
                return new DecimalType(transformedResponse);
            }
        }

        return data;
    }
}