All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.openxc.interfaces.network.NetworkVehicleInterface Maven / Gradle / Ivy

The newest version!
package com.openxc.interfaces.network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;

import android.content.Context;
import android.util.Log;

import com.google.common.base.Objects;
import com.openxc.interfaces.UriBasedVehicleInterfaceMixin;
import com.openxc.interfaces.VehicleInterface;
import com.openxc.remote.RawMeasurement;
import com.openxc.sources.BytestreamDataSource;
import com.openxc.sources.DataSourceException;
import com.openxc.sources.DataSourceResourceException;
import com.openxc.sources.SourceCallback;

/**
 * A vehicle data source reading measurements from an OpenXC network device.
 *
 * This class looks for a network device and expects to read OpenXC-compatible,
 * newline separated JSON messages.
 */
public class NetworkVehicleInterface extends BytestreamDataSource
        implements VehicleInterface {
    private static final String TAG = "NetworkVehicleInterface";
    private static final int SOCKET_TIMEOUT = 10000;
    private static final String SCHEMA_SPECIFIC_PREFIX = "//";

    private Socket mSocket;
    private InputStream mInStream;
    private OutputStream mOutStream;
    private URI mUri;

    /**
     * Construct an instance of NetworkVehicleInterface with a receiver
     * callback and custom device URI.
     *
     * If the device cannot be found at initialization, the object will block
     * waiting for a signal to check again.
     *
     *
     * @param context
     *            The Activity or Service context, used to get access to the
     *            Android NetworkManager.
     * @param callback
     *            An object implementing the SourceCallback that should receive
     *            data as it is received and parsed.
     * @param uri
     *            The network host's address.
     * @throws DataSourceException
     *             If no connection could be established
     */
    public NetworkVehicleInterface(SourceCallback callback, Context context,
            URI uri) throws DataSourceException {
        super(callback, context);
        setUri(uri);
        start();
    }

    public NetworkVehicleInterface(Context context, URI uri)
            throws DataSourceException {
        this(null, context, uri);
    }

    public NetworkVehicleInterface(Context context, String uriString)
            throws DataSourceException {
        this(context, UriBasedVehicleInterfaceMixin.createUri(
                    massageUri(uriString)));
    }

    public boolean setResource(String otherResource) throws DataSourceException {
        if(!UriBasedVehicleInterfaceMixin.sameResource(mUri,
                massageUri(otherResource))) {
            setUri(otherResource);
            try {
                if(mSocket != null) {
                    mSocket.close();
                }
            } catch(IOException e) {
            }
            return true;
        }
        return false;
    }

    /**
     * Return true if the address and port are valid.
     *
     * @return true if the address and port are valid.
     */
    public static boolean validateResource(String uriString) {
        return UriBasedVehicleInterfaceMixin.validateResource(
                massageUri(uriString));
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
            .add("uri", mUri)
            .toString();
    }

    public boolean receive(RawMeasurement command) {
        String message = command.serialize() + "\u0000";
        byte[] bytes = message.getBytes();
        return write(bytes);
    }

    @Override
    public boolean isConnected() {
        mConnectionLock.readLock().lock();
        boolean connected = mSocket != null && mSocket.isConnected() && super.isConnected();
        mConnectionLock.readLock().unlock();
        return connected;
    }

    protected int read(byte[] bytes) throws IOException {
        mConnectionLock.readLock().lock();
        int bytesRead = -1;
        try {
            if(isConnected()) {
                bytesRead = mInStream.read(bytes, 0, bytes.length);
            }
        } finally {
            mConnectionLock.readLock().unlock();
        }
        return bytesRead;
    }

    protected void connect() throws NetworkSourceException {
        if(!isRunning()) {
            return;
        }

        mConnectionLock.writeLock().lock();
        try {
            mSocket = new Socket();
            mSocket.connect(new InetSocketAddress(mUri.getHost(),
                        mUri.getPort()), SOCKET_TIMEOUT);
            if(!isConnected()) {
                Log.d(TAG, "Could not connect to server");
                disconnected();
            } else {
                connected();
                connectStreams();
            }
        } catch(IOException e) {
            String message = "Error opening streams";
            Log.e(TAG, message, e);
            disconnect();
            throw new NetworkSourceException(message, e);
        } catch(AssertionError e) {
            String message = "Error opening streams";
            Log.e(TAG, message, e);
            disconnect();
            throw new NetworkSourceException(message, e);
        } finally {
            mConnectionLock.writeLock().unlock();
        }
    }

    protected void disconnect() {
        mConnectionLock.writeLock().lock();
        try {
            if(isConnected()) {
                Log.d(TAG, "Disconnecting from the socket " + mSocket);
                try {
                    if(mInStream != null) {
                        mInStream.close();
                        mInStream = null;
                    }
                } catch(IOException e) {
                    Log.w(TAG, "Unable to close the input stream", e);
                }

                try {
                    if(mOutStream != null) {
                        mOutStream.close();
                        mOutStream = null;
                    }
                } catch(IOException e) {
                    Log.w(TAG, "Unable to close the output stream", e);
                }

                try {
                    if(mSocket != null) {
                        mSocket.close();
                        mSocket = null;
                    }
                } catch(IOException e) {
                    Log.w(TAG, "Unable to close the socket", e);
                }
            }
        } finally {
            disconnected();
            mConnectionLock.writeLock().unlock();
        }
        Log.d(TAG, "Disconnected from the socket");
    }

    /**
     * Writes given data to the socket.
     *
     * @param bytes data to write to the socket.
     * @return true if the data was written successfully.
     */
    private synchronized boolean write(byte[] bytes) {
        mConnectionLock.readLock().lock();
        boolean success = true;
        try {
            if(isConnected()) {
                Log.d(TAG, "Writing bytes to socket: " + bytes);
                mOutStream.write(bytes);
            } else {
                Log.w(TAG, "No connection established, could not send anything.");
                success = false;
            }
        } catch(IOException e) {
            Log.w(TAG, "Unable to write CAN message to Network. Error: " + e.toString());
            success = false;
        } finally {
        mConnectionLock.readLock().unlock();
        }
        return success;
    }

    protected String getTag() {
        return TAG;
    }

    private void connectStreams() throws NetworkSourceException {
        mConnectionLock.writeLock().lock();
        try {
            try {
                mInStream = mSocket.getInputStream();
                mOutStream = mSocket.getOutputStream();
                Log.i(TAG, "Socket created, streams assigned");
            } catch(IOException e) {
                String message = "Error opening Network socket streams";
                Log.e(TAG, message, e);
                disconnected();
                throw new NetworkSourceException(message);
            }
        } finally {
            mConnectionLock.writeLock().unlock();
        }
    }

    /**
     * Add the prefix reuqired to parse with URI if it's not already there.
     */
    private static String massageUri(String uriString) {
        if(!uriString.startsWith(SCHEMA_SPECIFIC_PREFIX)) {
            uriString = SCHEMA_SPECIFIC_PREFIX + uriString;
        }
        return uriString;
    }

    private void setUri(String uri) throws DataSourceException {
        setUri(UriBasedVehicleInterfaceMixin.createUri(massageUri(uri)));
    }

    private void setUri(URI uri) throws DataSourceResourceException {
        if(uri == null || !UriBasedVehicleInterfaceMixin.validateResource(uri)) {
            throw new DataSourceResourceException("URI is not valid");
        }

        mUri = uri;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy