org.openhab.binding.gpsd.internal.GPSdBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.gpsd.internal.GPSdBinding.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.gpsd.internal;

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

//import javax.xml.bind.DatatypeConverter;

//import net.astesana.javaluator.DoubleEvaluator;

import org.openhab.core.items.ItemNotFoundException;
import org.openhab.core.items.ItemRegistry;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.gpsd.GPSdBindingProvider;
import org.openhab.binding.gpsd.protocol.GPSd4JavaConnector;
import org.openhab.binding.gpsd.protocol.GPSdConnector;
import org.openhab.binding.gpsd.protocol.GPSdDataParser;
import org.openhab.binding.gpsd.protocol.GPSdParserRule;
//import org.openhab.binding.gpsd.protocol.GPSdSerialConnector;
import org.openhab.binding.gpsd.protocol.GPSdSimulator;
import org.openhab.core.binding.AbstractBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
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;

import de.taimos.gpsd4java.types.TPVObject;

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

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

    /* configuration variables for communication */
    //   private int udpPort = 9997;
    //   private String serialPort = null;
    private boolean simulate = false;
    private String hostname = "10.0.1.23";
    private int port = 2947;
    private boolean movementTrack = false;
    private String movementItem = "";
    private ItemRegistry itemRegistry;

    //   private GPSdDataParser dataParser = null;
    private GPSdConnector connector;

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

    private int connectionRetryTime = 30000;

    private int connectionRetryCount = 100;

    private double maxChangeThres = 30;

    private int refresh = 1000;

    public GPSdBinding() {
    }

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

    public void activate() {
        logger.debug("Activate gpsd binding");
    }

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

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

        logger.debug("GPSd Updated");
        if (config != null) {
            logger.debug("GPSD Configuration not null");
            if (config != null) {
                String hostnameString = (String) config.get("hostname");
                if (StringUtils.isNotBlank(hostnameString)) {
                    hostname = hostnameString;
                }

                String portString = (String) config.get("port");
                if (StringUtils.isNotBlank(portString)) {
                    port = Integer.parseInt(portString);
                }

                //setProperlyConfigured(true);

            }

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

            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 (key.equals("connectionretrycount")) {
                    this.setConnectionRetryCount(Integer.parseInt(value));

                }

                if ("hostname".equals(key)) {
                    if (StringUtils.isNotBlank(value)) {
                        hostname = value;
                        logger.info("Hostname set to {}", hostname);
                    }
                } else if ("port".equals(key)) {
                    port = Integer.parseInt(value);
                    logger.info("Port  set to {}", port);

                } else if (key.equals("connectionretrytime")) {
                    this.setConnectionRetryTime(Integer.parseInt(value));
                    logger.info("connectionretrytime  set to {}", value);

                } else if (key.equals("movementtracking")) {
                    this.setMovementTrack(value.toLowerCase().equalsIgnoreCase("true") ? true : false);
                    logger.info("trackMovement  set to {}", this.movementTrack);

                } else if (key.equals("maxchangethres")) {
                    this.maxChangeThres = Double.parseDouble(value);
                    logger.info("Movement Threshold   set to {}", this.maxChangeThres);

                } else if (key.equals("movementitem")) {
                    if (StringUtils.isNotBlank(value)) {
                        this.setMovementItem(value);
                    }
                    logger.info("movementitem  set to {}", value);

                } else if (key.equals("refresh")) {
                    this.setRefresh(Integer.parseInt(value));
                    logger.info("Refresh  set to {}", value);

                } else if (key.equals("connectionretrycount")) {
                    this.setConnectionRetryCount(Integer.parseInt(value));
                    logger.info("connectionretrycount  set to {}", value);

                } else {

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

            }

            if (parsingRules != null) {
                logger.debug("GPSd Data Parser called");
                //            dataParser = new GPSdDataParser(parsingRules);

            }

            if (messageListener != null) {

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

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

            if (simulate == true)
                connector = new GPSdSimulator();
            else
                connector = new GPSd4JavaConnector(hostname);
            try {
                int count = 0;
                while (!connector.isConnected() && connectionRetryCount > count) {
                    count++;
                    connector.connect();
                    if (!connector.isConnected()) {
                        if (connectionRetryCount == count) {
                            throw new GPSdException("Out of retries");
                        } else {
                            Thread.sleep(connectionRetryTime);
                        }
                    }
                }

                messageListener = new MessageListener();
                messageListener.start();

            } catch (Exception e) {
                logger.error("Error occured when connecting GPSD device", e);
                logger.warn("Closing GPSd message listener");

            }

        }
    }

    /**
     * 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;

        TPVObject location = new TPVObject();
        TPVObject previousLocation = new TPVObject();

        MessageListener() {
        }

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

        @Override
        public void run() {

            logger.info("GPSd message listener started");

            long currentTimer = System.currentTimeMillis();
            long previousTimer = System.currentTimeMillis();
            long previousUpdate = System.currentTimeMillis();
            int locationAgeLimt = 300000; //Time in milliseconds we force location to be sent, even if not updated. 
            int pollReturnCode = 0;
            boolean found = false;
            //boolean changed; // Controls weather we should force a new read due to a major change (like course change detection). 
            TPVObject newLocation = null;

            // as long as no interrupt is requested, continue running
            while (!interrupted && connector.isConnected()) {
                //changed = false; //Clears any change detected in previous updates. 

                try {
                    if (!shouldUpdate() && previousLocation.isValid) {
                        Thread.sleep(2000);
                        continue;
                    }
                    while ((currentTimer - previousTimer) < getRefresh()) {
                        sleep(1000);
                        newLocation = connector.receiveGPSObject();

                        //Fix any invalid previous location.
                        if (!previousLocation.isValid && newLocation.isValid) {
                            previousLocation = newLocation;
                        }

                        //Detects course change above 20 degrees 
                        if (newLocation != null && newLocation.isValid) {
                            if ((Math.abs(newLocation.getCourse()
                                    - previousLocation.getCourse()) > getCourseChangeThres())
                                    && (newLocation.getCourse() > 0) && (previousLocation.getCourse() > 0)) {
                                logger.info(
                                        "Location Changed above threshold. Previous Course was {}. New Course is {}",
                                        previousLocation.getCourse(), newLocation.getCourse());
                                break;
                                //changed = true;
                            }
                        } else {
                            //logger.warn("Location is null or invalid. Probably NOFIX");
                            continue;
                        }
                        currentTimer = System.currentTimeMillis();
                        //logger.trace("Gpsd looping");
                    } // Add Code to detect change in direction. 

                    logger.trace("Location being updated");

                } catch (GPSdException exgps) {
                    logger.info("GPS seems not initialized. Waiting for first fix. ");
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    logger.error("General Error at GPSd listener thread: {}", e1.toString());
                    e1.printStackTrace();
                }
                try {
                    previousTimer = currentTimer;

                    //TPVObject newLocation = connector.receiveGPSObject();

                    if (newLocation != null && newLocation.isValid() || location == null) {
                        logger.trace("Valid  location recieved from GPS.");
                        /*if ( newLocation.equals(location)) { 
                           logger.debug("Location did not change. Skipping for now");
                           continue;
                        } else {
                           logger.debug("Updating location to {}", newLocation.toString());
                        }*/
                        previousLocation = location;
                        location = newLocation;
                    } else {
                        logger.debug("Invalid Location recieved from GPS. Not updating location. ");
                        // Add a force overtime if we stay too long with invalid gps dat
                        if ((currentTimer - previousUpdate) > locationAgeLimt) {
                            logger.trace("Continuing location update due to locationAgeLimt ");
                        } else {
                            continue;
                        }
                    }

                    previousUpdate = currentTimer;

                    for (GPSdBindingProvider provider : providers) {
                        for (String itemName : provider.getItemNames()) {
                            org.openhab.core.types.State state = null;
                            found = false;
                            try {
                                switch (provider.getVariable(itemName).toLowerCase()) {
                                case "latitude":
                                    found = true;
                                    state = new DecimalType(location.getLatitude());
                                    break;
                                case "longitude":
                                    found = true;
                                    state = new DecimalType(location.getLongitude());
                                    break;
                                case "longitudeerror":
                                    found = true;
                                    state = new DecimalType(location.getLongitudeError());
                                    break;
                                case "latitudeerror":
                                    found = true;
                                    state = new DecimalType(location.getLatitudeError());
                                    break;
                                case "altitude":
                                    found = true;
                                    state = new DecimalType(location.getAltitude());
                                    break;
                                case "altitudeerror":
                                    found = true;
                                    state = new DecimalType(location.getAltitudeError());
                                    break;
                                case "speed":
                                    found = true;
                                    state = new DecimalType(location.getSpeed());
                                    break;
                                case "speederror":
                                    found = true;
                                    state = new DecimalType(location.getSpeedError());
                                    break;
                                case "nmea":
                                    found = true;
                                    state = new StringType(location.getNmeastring());
                                    break;
                                case "timestamp":
                                    found = true;
                                    state = new DecimalType(location.getTimestamp());
                                    break;
                                case "tag":
                                    found = true;
                                    state = new StringType(location.getTag().toString());
                                    break;
                                case "timestamperror":
                                    found = true;
                                    state = new DecimalType(location.getTimestampError());
                                    break;
                                case "course":
                                    found = true;
                                    state = new DecimalType(location.getCourse());
                                    break;
                                case "climbrate":
                                    found = true;
                                    state = new DecimalType(location.getClimbRate());
                                    break;
                                case "courseerror":
                                    found = true;
                                    //state = new DecimalType(location.getCourseError());
                                    state = new DecimalType(-1);
                                    break;
                                case "climbrateerror":
                                    found = true;
                                    state = new DecimalType(location.getClimbRate());
                                    break;
                                case "mode":
                                    found = true;
                                    state = new StringType(location.getMode().toString());
                                    break;
                                case "location":
                                    found = true;
                                    state = new StringType(String.valueOf(location.getLatitude()) + ","
                                            + String.valueOf(location.getLongitude()));
                                    break;
                                }
                            } catch (Exception e) {
                                // TODO Auto-generated catch block
                                logger.debug("Exception {}", e.toString());
                            }

                            if (found) {
                                if (state == null) {
                                    logger.debug("Item {} found but value is invalid. Leaving previous value",
                                            itemName.toString());
                                    //state = new DecimalType("-1"); 
                                }
                                state = transformData(provider.getTransformationType(itemName),
                                        provider.getTransformationFunction(itemName), state);

                            } else {
                                logger.error("Invalid Item: {}", itemName.toString());
                            }
                            //logger.trace ("Item being parsed is {} with value {}", itemName.toLowerCase(), state.toString() );
                            if (state != null) {
                                eventPublisher.postUpdate(itemName, state);
                            }
                            if (interrupted) {
                                break;
                            }
                        }

                    }
                }

                catch (Exception e) {

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

                }
            }

            logger.debug("GPSd Run Thread exited.");

            try {
                connector.disconnect();
            } catch (GPSdException e) {
                logger.error("Error occured when disconnecting form GPSd device", e);
            }

        }

    }

    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(GPSdActivator.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.debug("transformed response is '{}'", transformedResponse);

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

        return data;
    }

    /**
     * @return the refresh
     */
    private int getRefresh() {
        return refresh;
    }

    /**
     * @param refresh the refresh to set
     */
    private void setRefresh(int refresh) {
        this.refresh = refresh;
    }

    /**
     * @return the connectionRetryCount
     */
    private int getConnectionRetryCount() {
        return connectionRetryCount;
    }

    /**
     * @param connectionRetryCount the connectionRetryCount to set
     */
    private void setConnectionRetryCount(int connectionRetryCount) {
        this.connectionRetryCount = connectionRetryCount;
    }

    /**
     * @return the connectionRetryTime
     */
    private int getConnectionRetryTime() {
        return connectionRetryTime;
    }

    /**
     * @param connectionRetryTime the connectionRetryTime to set
     */
    private void setConnectionRetryTime(int connectionRetryTime) {
        this.connectionRetryTime = connectionRetryTime;
    }

    public boolean isMovementTrack() {
        return movementTrack;
    }

    private boolean shouldUpdate() {
        if (isMovementTrack() && !getMovementItem().isEmpty()) {
            try {
                return itemRegistry.getItem(getMovementItem()).getState().toString() == "ON" ? true : false;
            } catch (ItemNotFoundException e) {
                // TODO Auto-generated catch block
                logger.trace("Movement Tracking Item not found.");
                return false;
            }
        } else {
            return true; //Defaults to Update
        }

    }

    private void setMovementTrack(boolean movementTrack) {
        this.movementTrack = movementTrack;
    }

    public String getMovementItem() {
        return movementItem;
    }

    public void setMovementItem(String movementItem) {
        this.movementItem = movementItem;
    }

    public void setItemRegistry(ItemRegistry itemRegistry) {
        this.itemRegistry = itemRegistry;
    }

    public void unsetItemRegistry(ItemRegistry itemRegistry) {
        this.itemRegistry = null;
    }

    private double getCourseChangeThres() {
        // TODO Auto-generated method stub
        return this.maxChangeThres;
    }

}